Ответ 1
Общее правило при использовании data-join заключается в том, что вы хотите, чтобы сопоставление "один-к-одному" от данных к элементам. Итак, если у вас есть две серии в вашей диаграмме рассеяния, вам понадобятся два элемента контейнера (например, G elements) для представления серии. Поскольку в настоящее время у вас есть только один массив data
, вы также захотите использовать array.map для преобразования представления данных в два параллельных массива с тем же представление. Таким образом, вам не нужно дублировать код для каждой серии.
Скажите, что ваши данные были представлены в CSV файле с одним столбцом для значений x и несколькими другими столбцами для y-значений для каждой серии:
x,y1,y2
5,90,22
25,30,25
45,50,80
65,55,9
85,25,95
Если вы хотите, чтобы код был полностью общим, вам сначала нужно вычислить имена серий, например ["y1", "y2"]
. (Если вы добавили третий столбец в файл CSV, это может быть ["y1", "y2", "y3"]
.) Вы можете вычислить имена, используя d3.keys, которые извлекает именованные свойства из объекта. Например, d3.keys({foo: 1, bar: 2})
возвращает ["foo", "bar"]
.
// Compute the series names ("y1", "y2", etc.) from the loaded CSV.
var seriesNames = d3.keys(data[0])
.filter(function(d) { return d !== "x"; })
.sort();
Теперь, когда у вас есть имена серий, вы можете создать массив массивов точек. Внешний массив представляет собой серию (из которых две), а внутренние массивы хранят точки данных. Вы можете одновременно преобразовать точки в согласованное представление (объекты с параметрами x
и y
), что позволяет повторно использовать код по серии.
// Map the data to an array of arrays of {x, y} tuples.
var series = seriesNames.map(function(series) {
return data.map(function(d) {
return {x: +d.x, y: +d[series]};
});
});
Обратите внимание, что в этом коде используется оператор +
для принуждения значений CSV к номерам. (CSV файлы являются нетипизированными, поэтому они изначально являются строками.)
Теперь, когда вы перенесли данные в обычный формат, вы можете создавать элементы G для каждой серии, а затем крутить элементы внутри каждой точки. Полученная структура SVG будет выглядеть так:
<g class="series">
<circle class="point" r="4.5" cx="1" cy="2"/>
<circle class="point" r="4.5" cx="3" cy="2"/>
…
</g>
<g class="series">
<circle class="point" r="4.5" cx="5" cy="4"/>
<circle class="point" r="4.5" cx="7" cy="6"/>
…
</g>
И соответствующий код D3:
// Add the points!
svg.selectAll(".series")
.data(series)
.enter().append("g")
.attr("class", "series")
.style("fill", function(d, i) { return z(i); })
.selectAll(".point")
.data(function(d) { return d; })
.enter().append("circle")
.attr("class", "point")
.attr("r", 4.5)
.attr("cx", function(d) { return x(d.x); })
.attr("cy", function(d) { return y(d.y); });
Ive также добавил немного кода, чтобы присвоить каждой серии уникальный цвет, добавив стиль заливки в содержащий G элемент. Конечно, есть много разных способов сделать это. (Например, вы можете быть более конкретными относительно цвета для каждой серии.) Ive также не учитывал код, который вычисляет домены ваших x и y масштабов (а также рендеринг осей), но если вы хотите увидеть весь рабочий пример: