Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use rangeBand() on an ordinal scale with a dynamic domain?

Tags:

d3.js

My data looks like this...

var data = [{name:'a', value : 97},
          {name:'b', value : 24},
          {name:'c', value : 10}];

I have an ordinal scale created like this...

var y = d3.scale.ordinal().rangeBands([0, 30 * data.length]);

and I am using it to create a basic graph like this...

    chart.selectAll("rect")
        .data(data)
        .enter().append("svg:rect")
        .attr("y", function(d,i){ return y(d.name);})
        .attr("width", function(d,i){ return x(d.value);})
        .attr("height", y.rangeBand());

According to the d3 docs (here) you dont HAVE to specify a domain for an ordinal scale, and it will get populated as you try to use it. In my situation however y(d.name) always returns 0 and y.rangeBand() raises an exception, even though when I look the domain array has been populated inside the scale object.

So, I have worked around this by pre-defining my domain...

.domain(data.map(function (d){ return d.name;}))

but I would like to be able to dynamically add to this domain and right now if I do, it doesn't expand beyond the original values set on creation. Is this even possible?

I'm very new to d3, so I may be missing something important here.

like image 631
Fraser Graham Avatar asked Apr 15 '12 04:04

Fraser Graham


2 Answers

With rangeBands and rangePoints, you must define a domain in order to access the range. The domain tells the ordinal scale how many bands or points you want to display. However, you can change the domain later (by calling scale.domain(newDomain)) and the range will update. You just can't do that implicitly by passing a previously-unseen value to the scale. It's generally a good idea to define domains with ordinal scales anyway, so that you always get deterministic behavior.

like image 98
mbostock Avatar answered Sep 28 '22 01:09

mbostock


Elements in the domain must be unique when coerced to a string so if you're passing in an array of objects to domain, there might be unexpected results. For example, d3 might view the objects as all the same if the string representation of each object looks like "[object Object]". I've run into this problem before and the way I've worked around it is to do something like

d3.scale.ordinal(d3.range(0,myData.length)).rangeBands([0,myRangeBound])

which maps each data element to a unique array index. Keep in mind your data should be sorted if necessary before you set your scale in this case, and you'll have to reset the domain whenever your data changes. It's generally good to define a render method or the like to reset the scales and redraw everything when new data comes in, and to set the domain explicitly whenever you can.

like image 22
dishelevedest Avatar answered Sep 28 '22 00:09

dishelevedest