ScottieB
ScottieB

Reputation: 4052

Why is domain not using d3.max(data) in D3?

I'm new to D3 and playing around with a scatterplot. I cannot get d3.max(data) to work correctly in setting up domain!

I have the following setting up a random dataset:

var data = [];
      for (i=0; i < 40; i++){
            data.push({"x": i/40, "y": i/8, "a": Math.floor(Math.random() * 3), "x2": Math.random()});
        }

And then the following to set my coordinates:

 var x = d3.scale.linear().domain([0, 1]).range([0 + margin, w-margin]),
            y = d3.scale.linear().domain([0, d3.max(data)]).range([0 + margin, h-margin]),
            c = d3.scale.linear().domain([0, 3]).range(["hsl(100,50%,50%)", "rgb(350, 50%, 50%)"]).interpolate(d3.interpolateHsl);

This puts all 40 points in a single, horizontal line. If I replace d3.max(data) with '5' then it is a diagonal (albeit from the upper left to the bottom right, I'm still struggling to flip y-coordinates). Why isn't d3.max(data) working as expected?

Upvotes: 6

Views: 12370

Answers (3)

lobgoblin
lobgoblin

Reputation: 75

I was having a similar issue dealing with an associative array. My data looked like the following: [{"year_decided":1982,"total":0},{"year_decided":"1983","Total":"847"},...}]

Simply passing parseInt before returning the value worked.

var yScale = d3.scale.linear()
    .domain([0, d3.max(query,function(d){ return parseInt(d["Total"]); }) ])
    .range([0,h]);

Upvotes: 0

Lars Kotthoff
Lars Kotthoff

Reputation: 109242

d3.max() expects an array of numbers, not of objects. The elements of data have an internal key-value structure and there is no way for d3.max() to know what to take the maximum of. You can use something like jQuery's $.map to get the elements of the objects you want and then take the max, e.g.

var maxy = d3.max($.map(data, function(d) { return d.y; }));

Edit:

As pointed out in the comment below, you don't even need JQuery for this, as .map() is a native Array method. The code then becomes simply

var maxy = d3.max(data.map(function(d) { return d.y; }));

or even simpler (and for those browsers that don't implement Array.map()), using the optional second argument of d3.max that tells it how to access values within the array

var maxy = d3.max(data, function(d) { return d.y; });

Upvotes: 10

AnnanFay
AnnanFay

Reputation: 9759

d3.max API documentation can be found here.

# d3.max(array[, accessor])

Returns the maximum value in the given array using natural order. If the array is empty, returns undefined. An optional accessor function may be specified, which is equivalent to calling array.map(accessor) before computing the maximum value. Unlike the built-in Math.max, this method ignores undefined values; this is useful for computing the domain of a scale while only considering the defined region of the data. In addition, elements are compared using natural order rather than numeric order. For example, the maximum of ["20", "3"] is "3", while the maximum of [20, 3] is 20.

Applying this information to the original question we get:

function accessor(o){
    return o.y;
}
var y = d3.scale.linear()
        .domain([0, d3.max(data, accessor)])
        .range([0 + margin, h-margin]);

If you end up using many accessor functions you can just make a factory.

function accessor(key) {
    return function (o) {
        return o[key];
    };
}
var x = d3.scale.linear()
        .domain([0, d3.max(data, accessor('x'))])
        .range([...]),
    y = d3.scale.linear()
        .domain([0, d3.max(data, accessor('y'))])
        .range([...]);

Upvotes: 4

Related Questions