@data-ui/histogram
Version:
React + d3 library for creating histograms
64 lines (60 loc) • 2.73 kB
JavaScript
import { histogram as d3Histogram, extent as d3Extent } from 'd3-array';
import { scaleLinear } from 'd3-scale';
var DEFAULT_BIN_COUNT = 10;
/*
* handles binning of numeric data by series index
* if binValues are passed, ignores other bin values that are encountered
*
* returns an object of bins keyed on series index with the following shape
* {
* [seriesIdx]: Array<Object{
* bin0: String,
* bin1: String,
* data: Array<datum>,
* count: Number,
* id: String,
* }>,
* }
*/
export default function binNumericData(_ref) {
var allData = _ref.allData,
_ref$binCount = _ref.binCount,
userBinCount = _ref$binCount === void 0 ? DEFAULT_BIN_COUNT : _ref$binCount,
binValues = _ref.binValues,
limits = _ref.limits,
rawDataByIndex = _ref.rawDataByIndex,
valueAccessor = _ref.valueAccessor;
var binCount = Array.isArray(binValues) ? binValues.length : userBinCount;
var binsByIndex = {};
var histogram = d3Histogram();
var extent = d3Extent(allData, valueAccessor);
if (binValues) {
// account for extent of binValues if passed
var binExtent = d3Extent(binValues);
extent = [Math.min(binExtent[0], extent[0]), Math.max(binExtent[1], extent[1])];
}
var scale = scaleLinear().domain(extent).nice(binCount);
histogram.domain(limits || scale.domain()).thresholds(binValues || scale.ticks(binCount));
Object.keys(rawDataByIndex).forEach(function (index) {
var data = rawDataByIndex[index];
var seriesBins = histogram.value(valueAccessor)(data); // if the last bin equals the upper bound of the second to last bin, combine them
// see https://github.com/d3/d3-array/issues/46#issuecomment-269873644
var lastBinIndex = seriesBins.length - 1;
var lastBin = seriesBins[lastBinIndex];
var nextToLastBin = seriesBins[lastBinIndex - 1];
var shouldCombineEndBins = nextToLastBin && nextToLastBin.x1 === lastBin.x0 && lastBin.x1 === lastBin.x0;
var filteredBins = shouldCombineEndBins ? seriesBins.slice(0, -1) : seriesBins;
binsByIndex[index] = filteredBins.map(function (bin, i) {
return {
bin0: bin.x0,
// if the upper limit equals the lower one, use the delta between this bin and the last
bin1: bin.x0 === bin.x1 ? i > 0 && bin.x0 + bin.x0 - seriesBins[i - 1].x0 || bin.x1 + 1 : bin.x1,
data: [].concat(bin).concat(shouldCombineEndBins && (shouldCombineEndBins && i === lastBinIndex - 1 ? lastBin : [])),
// if the last bin was inclusive / omitted, add its count to the last bin
count: bin.length + (shouldCombineEndBins && i === lastBinIndex - 1 ? lastBin.length || 0 : 0),
id: i.toString()
};
});
});
return binsByIndex;
}