|
var sales = [ |
|
{'Year':2011,'Month':'Jan','Sales':320}, |
|
{'Year':2011,'Month':'Feb','Sales':230}, |
|
{'Year':2011,'Month':'Mar','Sales':365}, |
|
{'Year':2011,'Month':'Apr','Sales':385}, |
|
{'Year':2011,'Month':'May','Sales':100}, |
|
{'Year':2012,'Month':'Jan','Sales':380}, |
|
{'Year':2012,'Month':'Feb','Sales':180}, |
|
{'Year':2012,'Month':'Mar','Sales':275}, |
|
{'Year':2012,'Month':'Apr','Sales':450}, |
|
{'Year':2012,'Month':'May','Sales':410}, |
|
{'Year':2013,'Month':'Jan','Sales':320}, |
|
{'Year':2013,'Month':'Feb','Sales':170}, |
|
{'Year':2013,'Month':'Mar','Sales':375}, |
|
{'Year':2013,'Month':'Apr','Sales':510}, |
|
{'Year':2013,'Month':'May','Sales':390}, |
|
{'Year':2014,'Month':'Jan','Sales':420}, |
|
{'Year':2014,'Month':'Feb','Sales':125}, |
|
{'Year':2014,'Month':'Mar','Sales':310}, |
|
{'Year':2014,'Month':'Apr','Sales':450}, |
|
{'Year':2014,'Month':'May','Sales':410}, |
|
{'Year':2015,'Month':'Jan','Sales':460}, |
|
{'Year':2015,'Month':'Feb','Sales':195}, |
|
{'Year':2015,'Month':'Mar','Sales':360}, |
|
{'Year':2015,'Month':'Apr','Sales':410}, |
|
{'Year':2015,'Month':'May','Sales':385} |
|
]; |
|
|
|
// group by month, giving our per-month small multiples |
|
var groupedByMonth = d3.nest() |
|
.key(function(d) { return d.Month }) |
|
.entries(sales); |
|
|
|
// the various series components - setting the same crossValue |
|
// and main-value for each is a bit repetitive. |
|
var area = fc.seriesSvgArea() |
|
.crossValue(function(d, i) { return d.Year; }) |
|
.mainValue(function(d, i) { return d.Sales; }); |
|
var line = fc.seriesSvgLine() |
|
.crossValue(function(d, i) { return d.Year; }) |
|
.mainValue(function(d, i) { return d.Sales; }); |
|
var point = fc.seriesSvgPoint() |
|
.crossValue(function(d, i) { return d.Year; }) |
|
.mainValue(function(d, i) { return d.Sales; }); |
|
|
|
// the average line |
|
var average = fc.annotationSvgLine() |
|
.value(function(d) { return d; }); |
|
|
|
// bring all these renderers together into one using the multi-series. The area/point/line |
|
// are mapped to the 'values' returned by d3.nest, and the line annotation maps to the average |
|
var multi = fc.seriesSvgMulti() |
|
.series([area, line, point, average]) |
|
.mapping((data, index, series) => { |
|
switch (series[index]) { |
|
case average: |
|
return [d3.mean(data.values.map(function(d) { return d.Sales; }))]; |
|
default: |
|
return data.values; |
|
} |
|
}); |
|
|
|
// compute the extent of the y-values, adding some padding |
|
var yExtent = fc.extentLinear() |
|
.include([0]) |
|
.pad([0, 0.2]) |
|
.accessors([function(d) { return d.Sales; }]); |
|
|
|
// the chart! |
|
var chart = fc.chartSvgCartesian( |
|
d3.scaleLinear(), |
|
d3.scaleLinear() |
|
) |
|
.xDomain([2010.5, 2015.5]) |
|
.yDomain(yExtent(sales)) |
|
.yOrient('left') |
|
.xTicks(2) |
|
.chartLabel(d => d.key) |
|
.plotArea(multi); |
|
|
|
// data-join to render our small multiples |
|
d3.select('#chart') |
|
.selectAll('div') |
|
.data(groupedByMonth) |
|
.enter() |
|
.append('div') |
|
.call(chart); |