heavysixer / d4

A friendly reusable charts DSL for D3

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Dual-axis capability on a basic chart

yanofsky opened this issue · comments

I've been working on trying to figure out the best way to allow for a left axis and a right axis on different scales. (and thus series on different scales) Like this:

apples-oranges_chartbuilder 76

At the moment d4 provides no way to override the chart-wide scale on a feature by feature basis.

So far I've tried the following,

  • add alt_scale to the yAxis feature's accessors with a default of null
  • change the first line of yAxis.render from scope.scale(this.y) to scope.scale(scope.alt_scale() ? scope.alt_scale() : this.y);
    • in other words: "if there is a custom scale, use that, otherwise use the default"

but there is no way to access a custom scale (that I can see) from inside of a feature (such as lineSeries) since the accessor is called in the context of the chart not the context of the feature. (e.g. d4.functor(scope.accessors.y).bind(this))

If you want to keep this functionality, perhaps it's better adding alt_y on the chart object instead—a clone of chart.y. Then there can be alt_y accessors on each feature and logic on when to use it in the render based on a use_alt_y accessor boolean on the feature. A similar thing could also be added to the x methods.

Of course once there are two axes...why not three, four, etc? That type of functionality would require the chart.x and chart.y methods to return arrays of scales and features to have something like y_scale_index (defaulting to 0). It could also really mess up the API.

What are your thoughts on how to achieve this @heavysixer?

Hey @yanofsky this sounds like a great addition to d4, let me see if I can figure it out. Do you have a fiddle or codepen you can share for what you've done so far? It is always helpful for me if I can fiddle with actual code.

I understand the issue with the declarative nature of the API, maybe we can do something like this

chart.y(function(){},1) where 1 is the index (defaulting to 0 if omitted)?

I think I might need to wrap my head around it a bit before I am totally sure, however this is something which is very common in data visualization and therefore a worthwhile addition.

Thanks!

Sure thing, here's a fiddle http://jsfiddle.net/26n3znsh/
The idea is that the line and right axis should be on an independent scale from the bars and the left axis

If we were to do it how you have above i can imagine the commented out methods on lines 70, 88, and 125 being the way forward (though maybe 125 isn't necessary?)

@yanofsky thinking this through a bit last night I have a couple questions / comments i'd like to get your thoughts on.

  1. I think this is absolutely an essential feature so we'll add it.
  2. Like you pointed out keeping the API expressive and consistent is an important consideration.
  3. We need to ensure we solve this problem with a pattern or metaphor we can use elsewhere so that once a developer has learned it they can apply that concept everywhere.

With that in mind I am wondering what you think of this approach.

  1. Add xScale and yScale proxies to the features that use scales so that they can be injected from the using method e.g.
var chart = d4.charts.column();
  chart
    .x(function(axis){
      axis
      .scale('linear')
    })
    .using('bars', function(bars) {
      bars
        .xScale(function(axis){ axis.scale('ordinal')})
    })

Sorry for the slow reply!

That looks workable. The things that have been tying me up thinking about it is how to make it easiest to maintain the background auto scaling

Would there be a way to select an scale/axis by index? Or get a list of the current list of scales/axes? I think that would be useful.

Hey @yanofsky i am working on this feature as we speak so this is perfect timing. Can you explain what you mean by auto-scaling? My thinking was that d4 would assume a single scale at the chart level, and then features could be locally overridden with a new scale. In this way you'd declare a single scale object outside the main chart object and then assign it to all the features you'd need like the axis, and line series. This would keep the inner workings of d4 basically the same, while still allowing for feature specific overrides. Thoughts?

Awesome, thanks, for doing this so quickly. It really helps us out a lot

What I was getting at before is that by default the domain of the nth scale could be calculated automatically...but the person who gets this deep into charting with d4 would probably be okay setting the scale too

Thank you for the feedback, if after playing with my implementation you have suggestions for improvements I am very happy to hear about it. Thanks again for your continued support of d4.

@yanofsky sounds like you are using d4 on a regular basis, do you have any other pain points that you've noticed in using the lib?

I do!

I'll put them in as separate issues to keep them organized on here...as such some of them are unique to our use case so I won't be offended by any wontfix