Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NVD3 javascript: add colors to points in a scatter plot

I am trying to make a scatter plot from some data with x, y, and z values.

My code is the same as the example on the NVD3 website, http://nvd3.org/ghpages/scatter.html, except I also calculate a z value. Lets say z = x + y. Instead of changing the value of the radius, I would like to set the color by interpolating between two colors.

All the points are displayed on the chart, but I can not figure out how to set the color for individual points, only for the series. For simplicity, I first tried setting the points to a static color like this:

[{"key":"X Plus Y","values":[{"x":0,"y":0,"z":0,"color":"#ff0000"}, ...]

but this does not work, so I guess I need to do this in javascript. I took a look in scatterChart.js but didn't see an easy way. But I am in no way a javascript expert so I may have easily missed something.

Any advice on how to do this? Do I need to learn how to create a new model file?

I would also like to display the z value in a tool tip, but that will be step 2.

Thanks

like image 472
Scott Avatar asked Feb 06 '13 02:02

Scott


2 Answers

i have just run into the same issue and found a workaround that fits my use case - YMMV.

basically, my understanding (from NVD3's source code) is that you can indeed only set a color for series, not for individual points: therefore i do add a color: '#rrggbb' item to the values object anyways as you do in your example (just because it's easier for me to build the data structure like this), then set each series' color value from this (i'm using underscore.js but you should be able to do this in pure JavaScript):

final_groups = _.map(groups, function(values, group_id) {
    return { key: group_id, color: values[0].color, values: values };
});

If you need to use a color continuum rather than setting your colours point by point or using a discrete scale, i used mbostock's advice from this answer to let D3 generate color values for me:

function compute_color_on_linear_scale(data, value, colorProperty, colorRange) {
    // get a list of all the groups (this works if the colorProperty,
    // aka z in your case, is numeric, otherwise the sort function
    // needs to be adjusted accordingly)
    // In your case, you could just write item.z rather than item[colorProperty]
    // if your function doesn't need to be generic, and in that case you can
    // omit the colorProperty argument from the argument list altogether
    categories = _.uniq(_.map(data, function(item) {
        return item[colorProperty];
    }).sort(function(a,b){return a - b}), true);

    // if no color range was provided, set a default one
    if(typeof colorRange === 'undefined') { colorRange = ['red', 'green']; }

    // this is mbostock's magic enchantment from his reply linked above
    var color = d3.scale.ordinal()
      .domain(categories)
      .range(d3.range(categories.length).map(d3.scale.linear()
      .domain([0, categories.length - 1])
      .range(colorRange)
      .interpolate(d3.interpolateLab)));

    return color(value);
}

This should do the trick. My final touch was to hide the legend as that wouldn't make much sense in this case.

As for displaying the z value in the tooltip, you need to override the scatterChart()'s default tooltipContent() function, e.g.

var chart = nv.models.scatterChart()
  .showDistX(true)
  .showDistY(true)
  .tooltipContent(function(key, x, y, obj) {
      return '<h3>' + obj.point.label + ' (' + key + ')</h3>';
  });

You can customize this as much as needed - if you just add console.log(arguments); at the beginning of the function, you can inspect in your browser's developers tools exactly which data is available for you to use in your tooltips:

[...]
.tooltipContent(function(key, x, y, obj) {
    console.log(arguments);
    return '<h3>' + obj.point.label + ' (' + key + ')</h3>';
});
like image 100
hotzeplotz Avatar answered Sep 18 '22 19:09

hotzeplotz


To add color attribute for bubbles in scatter chart, we can add getColor accessor like getSize in nv.models.scatter.

 , getSize      = function(d) { return d.size || 1} // accessor to get the point size
 , getColor     = function(d) { 
                var colors = ["red", "orange", "yellow", "green", "blue"];
                return colors[d.color] || 'red'} // accessor to get the point color

You can add the color using "fill" attribute where the circle is painted

//Info:May be on Mousehover??
points.enter().append('circle')
    .attr('cx', function(d,i) { return x0(getX(d,i)) })
    .attr('cy', function(d,i) { return y0(getY(d,i)) })
    .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) })
    .attr('fill', function(d,i) { return getColor(d,i)});
like image 27
Naga Kiran Avatar answered Sep 16 '22 19:09

Naga Kiran