logarithmic percentage graph that shows positive and negative values.

184 views
Skip to first unread message

Stephen Gower

unread,
Dec 12, 2015, 11:31:22 PM12/12/15
to d3-js

Can someone provide tips on how to create the following graph?

It's a percentage change graph for stocks so the graph always starts at zero, and then each stock can gain (positive values on the chart) value or lose (negative values on the chart).
As you can see in the example, this is a logarithmic graph.  Normally logarithmic graphs don't allow for zero, or negative values, but this is somehow handled nicely in the example.
I see lots of logarithmic graph examples online, but none that handle the positive and negative values as show in this example.

Another thing about the logarithmic examples I see is that the values from 0-10 end up taking a lot of vertical space because there is a y axis ticker for .001, .01, .1, 10 etc.  The example doesn't have this problem.

I'd appreciate any help you can provide, or if you can point me to an working example that would be ideal. thanks!




Mike Bostock

unread,
Dec 14, 2015, 11:58:40 AM12/14/15
to d3...@googlegroups.com
Use a log scale in absolute price for the y-axis, but then use a second linear scale to compute percentage change ticks. Pass those ticks to the axis.tickValues for the y-axis.


Mike

Stephen Gower

unread,
Dec 14, 2015, 1:13:39 PM12/14/15
to d3-js, mi...@ocks.org
Holy Smokes batman!  This does indeed seem to show the properly logarithmic display for both positive and negative percentages.  The link is dated Dec-14th, so did code this entire example in response to my question?  If so, you really went above and beyond.

I'm entirely new to d3. So far I've tried to pull this off using Google charts, highcharts etc, but wasn't able to make it work.  Can you think of any reason why (in d3) I couldn't perfectly recreate the example I provided?  The graph line of course needs to be correct but there is also text that would need to be drawn on the graph itself.  I assume setting the background, line thickness, colors etc would be trivial, but I'm not sure about adding custom text to the graph.

I REALLY appreciate the response you provided, so thanks a lot.

Mike Bostock

unread,
Dec 14, 2015, 2:25:56 PM12/14/15
to Stephen Gower, d3-js
Yep, I wrote the example in response to your query. It’s a common form and I don’t think I had an example of it already. Thank you for the suggestion.

As for the other stuff, that all looks doable, though I question the value of an exact recreation: the design you linked is quite noisy (saturated colors, heavy dashed lines, etc.). It’d be better to create a new, clean design. But as I like to say, everything is possible—it’s just a question of effort.

Mike

Stephen Gower

unread,
Dec 14, 2015, 8:36:36 PM12/14/15
to d3-js, stephe...@gmail.com, mi...@ocks.org
Yikes.  So I hate to ask for anything else given the amazing help you already provided, but I had attempted to take your example and add additional series, and unfortunately I'm falling flat on my face.

I had found your Multi-Series chart example:

And I attempted to add some of your logarithmic percent change goodness to this example, but without any success. I just don't understand d3 quite well enough to pull this off.  I really don't want to take up your time, but if you're feeling especially generous, a multi-series example would be wonderful.  It's a selfish request, but since you're posting this for all to see, others might benefit from this also since many people who would want such a chart, would likely want multiple series.

Stephen Gower

unread,
Dec 14, 2015, 9:52:47 PM12/14/15
to d3-js, stephe...@gmail.com, mi...@ocks.org
The main problem I'm having is that in your example there are functions using "baseValue" which is the initial value in the single series example.  Multiple series would need multiple baseValue variables:


 asChange = function(x,y) { 
    console.log("in asChange x="+x);
    console.log("in asChange y="+y);
     
    var returnVar=(x - baseValue1) / baseValue1; 
    console.log("in returnVar="+returnVar);
     
    return returnVar; 
    }


Then you have the following which uses these functions when mapping the ticker values.  

 yAxis.tickValues(d3.scale.linear()
      .domain(y.domain().map(asChange))
      .ticks(10)
      .map(asAbsolute));

I don't have a complete understanding of exactly how this tickValue mapping works, and this is part of the reason I'm having difficulty with this.

Mike Bostock

unread,
Dec 14, 2015, 11:19:56 PM12/14/15
to Stephen Gower, d3-js
Perhaps I led you astray slightly. Better than absolute value is to compute the relative value, i.e., the current value divided by a base (initial) value. Thus, 0% change = 1.0, +20% = 1.2, -20% = 0.8, and so on.

Convert the relative value for each series using the appropriate base value, and then plot it with a log scale. As long as the current value never reaches 0, then the relative price is also never zero, so a log scale is fine.

With this approach it becomes less important to use the secondary linear scale to compute ticks, but it’s still fine if you want all your ticks uniformly distributed in percentage space.

Reload this:


M
Reply all
Reply to author
Forward
0 new messages