NVD3 Logarithmic Y axis with barchart

2019-07-24 06:15发布

I have a barchart made with NVD3 that shows datas with a huge gap between min value and max value.

This makes the chart not really nice/usefull.

Example : enter image description here

Y value are between 4,000 and 60,000 then some value are near 3m.

I would like to have an Y Axis value increasing logarithmically, that show something like this (see the y Axis):

enter image description here

I tried to change yAxis scale, yAxis Domain, but didn't find any working solution

(FYI: I use nvd3 through angularJs directives)

2条回答
家丑人穷心不美
2楼-- · 2019-07-24 06:37

The root of the issue is that barcharts in nvd3 start at 0, and log(0) == Infinity.

Unfortunately there is a particular line in nvd3 that uses y(0) when calculating the height of the bar for your chart. When y(0) evaluates to Infinity things break. Furthermore, nvd3 is currently undergoing a refactor so, they aren't taking any pull requests for the old code base.

Here is what worked for me. It's not a general soltuion, but I couldn't even find this level of info online so I'm posting it here.

In this case I'm using a Line plus Bar Chart, this may not work for other bar charts, but hopefully it gives you and idea of what needs to be done.

First, make sure your chart is setup properly.

  yourChart.bars.yScale(d3.scale.log());
  yourChart.bars.forceY([1]); //Make sure y axis starts at 1 not 0
  yourChart.y1Axis.tickValues([1, 10, 100]); //Continue pattern for more ticks

now in nvd3.js around line 1600 (in nvd3 1.8 line number is 5605) you should see:

      bars.transition()
          .attr('y', function(d,i) {
            var rval = getY(d,i) < 0 ?
                    y(0) :
                    y(0) - y(getY(d,i)) < 1 ?
                      y(0) - 1 :
                      y(getY(d,i));
            return nv.utils.NaNtoZero(rval);
          })
          .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.max(Math.abs(y(getY(d,i)) - y(1)),1)) });

In the very last statement:

.attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.max(Math.abs(y(getY(d,i)) - y(0)),1)) });

You need to replace y(0) with y(1) which will give you this:

.attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.max(Math.abs(y(getY(d,i)) - y(1)),1)) });

If this doesn't work, look for other cases where nvd3 sets up bars.transition() and calculates the height. You may need to fix up other cases where 0 is passed to y().

Hope this helps.

查看更多
萌系小妹纸
3楼-- · 2019-07-24 06:46

This will work for you

chart.y(function(d) { return d.value>0?Math.log10(d.value+1):0 })
chart.yAxis.tickFormat(function(d){return (Math.pow(10,d)-1).toFixed(2)})

Works only for positive values

查看更多
登录 后发表回答