nvd3 scatter plot with ordinal scale

dgorissen picture dgorissen · Jun 25, 2013 · Viewed 7.7k times · Source

Im relatively new to d3 and nvd3 and wanted to create a simple scatterplot, just like the example but with an ordinal y-axis. So y axis values are categorical strings. This is what I thought I needed to do:

var xfun = function (d) { return d.Pos }    //simple ints
  , yfun = function (d) { return d.Title }  //the ordinal values

var chart = nv.models.scatterChart()
    .showDistX(true)
    .showDistY(true)
    .color(d3.scale.category10().range())
    .margin({ top: 30, right: 20, bottom: 50, left: 130 })
    .tooltips(false)
    .x(xfun)   
    .y(yfun);

// create an ordinal scale with some test values
var ys = d3.scale.ordinal()
    .domain(["this","is","an","ordinal","scale"])
    .range(5);

// tell nvd3 to use it
chart.yAxis.scale(ys);

// add to the page
nv.addGraph(function () {
    d3.select(selector).datum(data).transition().duration(500).call(chart);
    nv.utils.windowResize(chart.update);
    return chart;
});

However, no luck:

Error: Invalid value for <circle> attribute cy="NaN" d3.js:690
Error: Invalid value for <line> attribute y1="NaN" d3.js:690
Error: Invalid value for <line> attribute y2="NaN" d3.js:690
Error: Invalid value for <circle> attribute cy="NaN" d3.js:7532
Uncaught TypeError: Cannot read property '0' of undefined 
..

And the y-axis simply shows a linear axis from -1 to 1. Iterestingly there are some circles plotted at y=-1 and y=1 (the extremes).

To manually force correct values for cy I tried adding after call(chart):

d3.selectAll("#mychart circle").attr("cy", function(d){
        return = ys(yfun(d));
        });

But still the same error. How do I get the ordinal scale to work properly? Note I also need it to update correctly when I use the nvd3 legend to switch between dataseries (which will contain different x/y data).

There is a related question on github, but no solution.


Update: after some debugging I tried replacing chart.yAxis.scale(ys) with chart.scatter.y(ys) and this gets rid of the errors. I can also drop the manual selectAll.

However, the y-axis still shows a linear scale from 0.99-1.01 and all points are plotted at y=1. So a step closer but no ordinal scale yet.

Answer

Andrei picture Andrei · Jan 10, 2014

In case somebody else stumbles upon this, here's what worked for me: instead of trying to force an ordinal scale on the axis (X, in my case), I used a linear scale, but provided a custom tickFormat function that returned the desired label.

chart.xAxis.tickFormat(function(d){
            return labelValues[d];
        });

Where labelValues maps between the numerical value and the desired label.