简体   繁体   中英

Stacking multiple line graphs in d3js without nesting data

I have a lot of data, extracted from a csv and sorted into neat categories, as follows:

return {
                time: d3.time.format('%Y/%m/%d %H:%M:%S').parse(d.Time),
                conditions: d.Conditions.replace(/"/g, ''),
                temperature: +d.Temperature,
                humidity: +d.Humidity.replace(/[",%]/g, ''),    
                windDir: d.WindDir,                             
                windDeg: +d.WindDeg,                            
                pressure: +d.Pressure.replace(/"/g, '')
}

I want to achieve an end result similar to 'Small Stacking Multiples' ( http://bl.ocks.org/mbostock/9490516 ). Looking at the code, it uses symbols and nesting to achieve multiple graphs. However, my data isn't structured in the same way; I don't have multiple different symbols in one column. How can I replicate this result if I wanted to use all or some of my variables?

I did try fiddling with the nest() function, and achieved an array where the time is the key and temperature is the value:

d3.nest().key(function(d) {return d.time;})
    .rollup(function(d) {return d3.sum(d, function(g) {return g.temperature;}); })
    .entries(dataset)

...but that doesn't give me the multiple values per time that I'd like (and presumably need for the Small Stacking example).

To clarify what I have now, here's my domain/range/axis definitions:

    var x = d3.time.scale()
        .range([0, width]);
    var y = d3.scale.linear()
        .range([height, 0]);

    var xAxis = d3.svg.axis().scale(x)
        .orient("bottom")
        .ticks(10);
    var yAxis = d3.svg.axis().scale(y)
        .orient("left")
        .ticks(7);

    x.domain(d3.extent(dataset, function(d) { return d.time; })); 
    y.domain([d3.min(dataset, function(d) { return Math.min(d.temperature - 5, d.absHumidity - 1); }), d3.max(dataset, function(d) { return d.temperature + 2; })]);

My svg.append:

var svg = d3.select('body')
        .append('svg')
            .attr('width', width + margin.left + margin.right)
            .attr('height', height + margin.top + margin.bottom)
        .append('g') 
            .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

There's no intrinsic need for the data to be nested, this is more or less just a convenient way to achieve what's desired in this case. For your data, you already have some kind of nesting, it's just less explicit.

The general approach is that the nested structure determines what's displayed. That is, for each graph there is one "thing" in the outer nesting. These are the different measurements in your case, so you need an array that contains those:

var symbols = ['temperature', 'humidity', 'windDeg', 'pressure', 'absHumidity'];

Everything else hangs off that. The most intuitive (albeit not particularly D3 way) to add one graph per element is to simply loop over the array:

symbols.forEach(function(symbol) {
  // ...
});

Inside the loop, the relevant datum is given by symbol as an index into the array of data, eg

y.domain(d3.extent(dataset, function(d) { return d[symbol]; }));

Everything that deals with the data needs to access it in this way. And that's basically it; full demo here .

To change what's shown, all you need to do is edit the symbols array -- each element denotes an attribute name of a measurement to show.

If you want particular parts (eg the x axis) to show only on one of the graphs, wrap the code that creates it in an if statement that checks eg the current symbol .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM