Emil
Emil

Reputation: 349

How to get maximum value from an array of objects to use in d3.scale.linear().domain()

I have an external csv file with data in columns like so:

name, field_goal_attempts, field_goal_makes

I am trying to use a linear scale but am encountering difficulty getting the maximum value for my domain.

var yScale = d3.scale.linear()
               .domain(0, d3.max(...

I am confused as to:

1) Whether I should put the yScale function outside or inside the

d3.csv("filename.csv", function(data) {

callback function; and

2) How to get the maximum value of items in the field_goal_attempts column to then feed into the yScale function.

Here is my code at present:

var yScale = d3.scale.linear()
    .domain([0, 4000]) //d3.max(data, function(d) {return d })])
    .range([0, 500]);

d3.csv("test.csv", function (data) {
    svg.selectAll("rect")
        .data(data)
        .enter()
        .append("rect")
        .attr("fill", "blue")
        .attr("x", magic_number) // I'm not concerned about the magic numbers at this point :)
        .attr("y", 0)
        .attr("width", another_magic_number)
        .attr("height", function (d) {
            return d.field_goal_attempts
        })
        .attr("id", function (d, i) {
            return i
        });
});

Upvotes: 20

Views: 35119

Answers (2)

Superboggly
Superboggly

Reputation: 5834

The data in your csv file will be in the callback you pass to the csv function (in your case the "data" parameter). So you can define yScale outside of the csv function, but if you want the max to be data dependent you will need to set it inside the callback.

As for finding the max, many D3 functions that work on arrays will accept optional accessor functions for precisely your scenario. So computing the max I would use:

var max = d3.max(data, function(d) { return +d.field_goal_attempts;} );

So you could put it all together one of two ways:

var yScale = d3.scale.linear().domain(0,100);
d3.csv("test.csv", function(data){
    var max = d3.max(data, function(d) { return +d.field_goal_attempts;} );
    yScale.domain([0,max]);
    ...
}

or

d3.csv("test.csv", function(data){
    var max = d3.max(data, function(d) { return +d.field_goal_attempts;} );
    var yScale = d3.scale.linear().domain([0,max]);
    ...
}

If you want to find both max and min then I suggest using d3.extent(...) which should also accept an accessor function and will return an array of length 2 with the min and max values.

Upvotes: 39

Christopher Chiche
Christopher Chiche

Reputation: 15345

1) The yScale can stay outside of the d3.csv() function as long as you update the domain inside of the function when having computed the max. For example you could do the following inside of d3.csv():

yScale.domain([0, my_max])

2) To compute the max, here is the method I usually use:

//Create list containing only field_goal_attempts
field_goal_attempts_list = data.forEach(function(d){return d.field_goal_attempts})

//Compute max using d3.max() function
field_goal_attempts_max = d3.max(field_goal_attempts_list)

It is true that passing such function to d3.max() would be great but as far as I know it is not. The advantage of first computing the list and then computing the max is that you do less computation if you want to compute the min, the mean or anything else.

Upvotes: 2

Related Questions