Я создал пример диаграммы горизонта для d c. js.

Основная проблема с Интеграция d3-horizon-chart с d c. js заключается в том, что он не имеет шкалы X - он просто отображает каждое значение в виде 1-пиксельного столбца цвета в порядке массива.
Это означает, что он не использует групповые ключи, и данные должны быть отсортированы в правильном порядке перед построением диаграммы горизонта.
Вот определение диаграммы:
class HorizonChart {
constructor(parent, group) {
this._group = null;
this._colors = null;
this._seriesAccessor = null;
this._root = d3.select(parent);
dc.registerChart(this, group);
}
// initialization functions for user
group(group) {
if(!arguments.length)
return this._group;
this._group = group;
return this;
}
// takes array of colors (not scale)
colors(colors) {
if(!arguments.length)
return this._colors;
this._colors = colors;
return this;
}
seriesAccessor(seriesAccessor) {
if(!arguments.length)
return this._seriesAccessor;
this._seriesAccessor = seriesAccessor;
return this;
}
valueAccessor(valueAccessor) {
if(!arguments.length)
return this._valueAccessor;
this._valueAccessor = valueAccessor;
return this;
}
// interface for dc.js chart registry
render() {
this.redraw();
}
redraw() {
const nester = d3.nest().key(this._seriesAccessor),
nesting = nester.entries(this._group.all());
let horizon = this._root.selectAll('.horizon')
.data(nesting);
horizon = horizon.enter()
.append('div')
.attr('class', 'horizon')
.merge(horizon);
const colors = this._colors,
valueAccessor = this._valueAccessor;
horizon
.each(function(series) {
d3.select(this).selectAll('*').remove();
d3.horizonChart()
.colors(typeof colors === 'function' ? colors(series.key) : colors)
.title(series.key)
.call(this, series.values.map(valueAccessor));
});
}
Он реализует небольшую часть интерфейса диаграммы серии d c. js . Одно из отличий состоит в том, что d3-horizon-chart принимает массив цветов и решает, сколько положительных и отрицательных полос нарисовать, основываясь на длине этого массива. Так что HorizonChart.colors()
принимает либо массив цветов, либо функцию, которая берет ключ серии и возвращает массив цветов.
Используется d3.nest () для разделения данных на серийный аксессор. Затем он добавляет или удаляет div для диаграмм горизонта, удаляет что-либо внутри каждого div и dr aws там горизонтальную диаграмму.
сортировка данных
Поскольку данные должны быть отсортированы, и то, что мы получаем из group.all()
для мультиключа, будет неправильно отсортировано, мы можем использовать поддельную группу для упорядочения по двум элементам ключа:
function sort_multikey_group(group) {
return {
all: () => {
return group.all().slice().sort(
({key: keyA}, {key: keyB}) => d3.ascending(keyA[0],keyB[0]) || d3.ascending(keyA[1],keyB[1]));
}
};
}
Мы применяем поддельную группу при инициализации диаграммы горизонта :
var horizonChart = new HorizonChart("#horizon"),
horizonChart
.group(sort_multikey_group(exptRunGroup))
.colors(n => [d3.schemeBlues, d3.schemeOranges, d3.schemeGreens, d3.schemeReds, d3.schemePurples][n-1][6]) // levels * 2
.seriesAccessor(d => d.key[0])
.valueAccessor(d => d.value - 500)
Параметр функции .colors()
выбирает одну из цветовых схем D3 для каждой из диаграмм горизонта. Он выбирает схему с 6 цветами для отображения 3 положительных и 3 отрицательных полос.
Он помещает данные в каждую из диаграмм серии на основе первого элемента ключа. В этом примере данные смещены ниже, чтобы использовать отрицательные цветные полосы.
примерные данные
Данные в этом примере - это данные эксперимента Майкельсона-Морли, использованные в d c. js Пример диаграммы серии , с интерполяцией 20 точек для каждого:
const experiments2 = d3.range(experiments.length-1).flatMap(i => {
if(experiments[i].Expt !== experiments[i+1].Expt)
return [];
let {Expt, Run, Speed: Speed0} = experiments[i],
{Speed: Speed1} = experiments[i+1];
Expt = +Expt; Run = +Run; Speed0 = +Speed0; Speed1 = +Speed1;
const terp = d3.scaleLinear().range([Speed0, Speed1]);
return d3.range(mult).map(j => ({Expt, Run: Run + j/mult, Speed: terp(j/mult)}));
});
Это еще одно различие между d3-horizon-chart и встроенным d c. js диаграммы: нужны данные для каждого пикселя.