Newbie
Newbie

Reputation: 177

Parsing error for nested object?

Can anyone help? My data is being filtered by values chosen from two dropdown lists - then nested/rolled up to get the mean value per day for these same values. I am getting a "problem parsing d error" for the code below. Although both filter and nest functions are returning the correct data - the problem occurs when I try to apply this data to the line graph to update it?

    d3.select("#parameterType").on("change", function() 
    {   
    d3.select("#dateTimeTaken").on("change", function()
    {   
    var selectedParameter = document.getElementById("parameterType").value;
    var selectedMonth = document.getElementById("dateTimeTaken").value;

    //filter data by selected parameter and month    
    var selectedData = data.filter(function(d) { 
      return d.parameterType == selectedParameter  &&
      +d.dateTimeTaken.getMonth() == (selectedMonth - 1);});

    console.log(selectedData);//returning correct data

    //get average reading for each day within selected month 
    var newdata = d3.nest()
        .key(function(d) {return d3.time.day(d.dateTimeTaken);})    
        .sortKeys(d3.ascending)
        .rollup(function(d) 
        {
            return {
            mean: d3.mean(selectedData, function(d) {return +d.reading;})};
        })
    .entries(selectedData);
    console.log(newdata);//returning correct data

    //UPDATE GRAPH

    //not returning correct max values?     
    x.domain(d3.extent(newdata, function(d) { return d.key; }));
    y.domain([0, d3.max(newdata, function(d) { return d.values; })]);

    svg.select("path.line")
        .attr( "d", line(newdata));

    svg.select(".x.axis")
        .transition()
        .duration(750)
        .ease("linear")
        .call(xAxis);

    svg.select(".y.axis")
        .transition()
        .duration(750)
        .ease("linear")
        .call(yAxis);

Upvotes: 1

Views: 570

Answers (1)

musically_ut
musically_ut

Reputation: 34288

In the code that you posted, there are two problems which cause the said issue. Both the problems appear to stem from a misunderstanding of how data is nested and rolled up in D3.

Explanation

var newdata = d3.nest()
    .key(function(d) {return d.dateTimeTaken;})    
    .sortKeys(d3.ascending)
    .entries(data);

This converts an object of the following kind:

var data = [{
    parameterType: 'a',
    dateTimeTaken: '2013-01-01 12:00:00',
    reading: 100
}, {
    parameterType: 'a',
    dateTimeTaken: '2013-01-02 12:00:01',
    reading: 101
}];

to:

[
    {
        "key": "2013-01-01 12:00:00",
        "values": [
            {
                "parameterType": "a",
                "dateTimeTaken": "2013-01-01 12:00:00",
                "reading": 100
            }
        ]
    },
    {
        "key": "2013-01-02 12:00:01",
        "values": [
            {
                "parameterType": "a",
                "dateTimeTaken": "2013-01-02 12:00:01",
                "reading": 101
            }
        ]
    }
]

The values field is an array which accumulates the original data entries together. rollup then takes as argument the array which would have been values, calculates an aggregate metric from these (mean in your case) and puts it in place of the array in the key values.

Your rollup function:

//AVERAGE READING FOR EACH DAY WITHIN SELECTED MONTH FOR SELECTED PARAMETER
// ...

 .rollup(function(d) {
    return {mean:d3.mean(selectedData, function(d) {return +d.reading;})};})

ignores the argument completely. I think it should read:

 .rollup(function(d) {
    return {mean:d3.mean(d, function(d_) {return +d_.reading;})};})

Now the resulting array would like:

[
    {
        "key": "2013-01-01 12:00:00",
        "values": {
            "mean": 100
        }
    },
    {
        "key": "2013-01-02 12:00:01",
        "values": {
            "mean": 101
        }
    }
]

Note that the values key has yet another object inside it which contains the mean. You calculate the range for the y axis as:

y.domain([0, d3.max(newdata, function(d) { return d.values; })]);

The reason why the maximum values are incorrect is because you are asking d3 to calculate the maximum over objects in values. The result of such an operation is ill defined. Instead, you should ask for maximum over values.mean:

y.domain([0, d3.max(newdata, function(d) { return d.values.mean; })]);

This works and produces the correct result as shown in this jsFiddle: http://jsfiddle.net/XLNeP/

Conclusion

That was about how to make the nesting and rollupus work correctly. Now to address a few other problems. Your line variable has the following accessors:

var line = d3.svg.line()
.x(function(d) { return x(d.dateTimeTaken); })
    .y(function(d) { return y(d.reading); });

This works perfectly for the time you call:

svg.append("path")
    .attr("class", "line")
    .attr("d", line(data)); 

However, newdata is of the form shown above, which has key and values at the top level and not dateTimeTaken or reading. Hence you would be better off using the selectedData which is just data filtered and preserves the data structure. However, if you want to draw a different line (e.g., with the mean) then you should define a new d3.svg.line() line generator with different accessors.

Upvotes: 2

Related Questions