xxxxxxxxxx
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
#chart {
width: 100%;
height: 500px;
}
.point path {
opacity: 0.5
}
.y-axis-label {
white-space: nowrap;
transform: rotate(-90deg) translateY(-3em) !important;
}
.y-axis-label, .x-axis-label {
font-size: 1.5em;
}
</style>
<script src="https://unpkg.com/d3@4.6.0"></script>
<script src="https://unpkg.com/d3fc@12.1.0"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.18.0/d3-legend.min.js"></script>
<div id="chart"></div>
<script>
var box = (values) => {
var sorted = values.slice();
sorted.sort((a, b) => a - b);
var max = sorted[sorted.length - 1];
var min = sorted[0];
var upper = d3.quantile(sorted, 0.75);
var mid = d3.quantile(sorted, 0.5);
var lower = d3.quantile(sorted, 0.25);
var lowerLimit = mid - (mid - lower) * 1.5;
var lowerWhisker = sorted.find(function(d) { return d > lowerLimit; });
var upperLimit = mid + (upper - mid) * 1.5;
var upperWhisker = sorted.reverse().find(function(d) { return d < upperLimit; });
return {
upper: upper, mid: mid, lower: lower, lowerWhisker: lowerWhisker,
upperWhisker: upperWhisker, max: max, min:min,
outliers: values.filter(function(d) { return d > upperWhisker || d < lowerWhisker; })
}
}
var flatten = function(arrays) {
return arrays.reduce(function(a, b) {
return a.concat(b);
}, [])
}
d3.csv("data.csv", function(error, data) {
var quarters = Object.keys(data[0]);
var series = quarters.map(function(q) {
return {
quarter: q,
data: box(data.map(function(d) { return Number(d[q]); }))
};
})
var yFormat = d3.format(',.0f');
var yExtent = fc.extentLinear()
.accessors([function(d) { return d.max; }, function(d) { return d.min; }])
.pad([0, 0.1])
.include([0])
var boxplot = fc.seriesSvgBoxPlot()
.crossValue(function(d) { return d.quarter; })
.medianValue(function(d) { return d.data.mid; })
.barWidth(50)
.upperQuartileValue(function(d) { return d.data.upper; })
.lowerQuartileValue(function(d) { return d.data.lower; })
.highValue(function(d) { return d.data.upperWhisker; })
.lowValue(function(d) { return d.data.lowerWhisker; });
var point = fc.seriesSvgPoint()
.crossValue(function(d) { return d[0]; })
.mainValue(function(d) { return d[1]; });
var label = fc.seriesSvgPoint()
.crossValue(function(d) { return d[0]; })
.mainValue(function(d) { return d[1]; })
.decorate(function(selection) {
selection.enter()
.select('path')
.attr('display', 'none')
selection.enter()
.append('text')
.style('text-anchor', 'middle')
.attr('transform', function(d, i) { return 'translate(' + (i % 2 === 0 ? -50 : 50) + ', 5)'; })
.text(function(d) { return yFormat(d[1]); })
.attr('stroke', 'transparent')
.attr('fill', 'black');
});
var multi = fc.seriesSvgMulti()
.series([boxplot, point, label])
.mapping((data, index, series) => {
switch(series[index]) {
case point:
return flatten(data.map(function(s) {
return s.data.outliers.map(function(o) { return [s.quarter, o]; })
}))
case boxplot:
return data;
case label:
return flatten(data.map(function(s) { return [
[s.quarter, s.data.upperWhisker],
[s.quarter, s.data.upper],
[s.quarter, s.data.mid],
[s.quarter, s.data.lower],
[s.quarter, s.data.lowerWhisker]
]; }));
}
});
var chart = fc.chartSvgCartesian(
d3.scalePoint(),
d3.scaleLinear()
)
.xDomain(quarters)
.xPadding(0.5)
.xLabel('Quarter')
.yLabel('Revenue in €')
.yDomain(yExtent(series.map(function(d) { return d.data; })))
.yTickFormat(yFormat)
.plotArea(multi);
d3.select('#chart')
.datum(series)
.call(chart);
});
</script>
https://unpkg.com/d3@4.6.0
https://unpkg.com/d3fc@12.1.0
https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.18.0/d3-legend.min.js