#javascript #d3.js #data-visualization #visualization
#javascript #d3.js #визуализация данных #визуализация
Вопрос:
У меня есть данные в следующем формате, которые я хотел бы отобразить с помощью d3:
data = [
{ x: 0.2, y: [ 1, 2, 4 ] },
{ x: 0.3, y: [ 2 ] },
{ x: 0.5, y: [ 4, 7, 8, 12, 19 ] }
{ x: 1.4, y: [ 1, 3 ] }
]
Обычно значения по оси y являются целыми числами, но здесь они представляют собой массивы, поэтому следующий код работает не так, как предполагалось:
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d){ return x(d.x) })
.attr("cy", function(d){ return y(d.y) })
.attr("r", 2)
Вместо того, чтобы получать несколько кругов, построенных для каждого из значений в массиве, я получаю только один.
Другие подобные вопросы на этом веб-сайте касаются только данных с фиксированным числом значений по оси y, поэтому я не нашел способа изменить эти решения для этой проблемы.
Ответ №1:
Традиционным ответом D3 здесь было бы добавление группы для каждого объекта, а затем добавление круга для каждого y
значения для каждой группы.
Однако, поскольку вы, похоже, новичок в D3 (поправьте меня, если я ошибаюсь), я бы предложил просто создать один массив объектов, к которому вы можете перейти data
.
Для этого есть несколько способов, таких как:
const newData = data.reduce(function(a, c) {
return a.concat(c.y.map(function(d) {
return {
x: c.x,
y: d
}
}));
}, []);
Вот ваш код с этим изменением:
const data = [{
x: 0.2,
y: [1, 2, 4]
},
{
x: 0.3,
y: [2]
},
{
x: 0.5,
y: [4, 7, 8, 12, 19]
}, {
x: 1.4,
y: [1, 3]
}
];
const newData = data.reduce(function(a, c) {
return a.concat(c.y.map(function(d) {
return {
x: c.x,
y: d
}
}));
}, []);
const x = d3.scaleLinear()
.domain([0, 2])
.range([0, 300]);
const y = d3.scaleLinear()
.domain([0, 20])
.range([0, 150]);
const svg = d3.select("svg");
svg.selectAll("circle")
.data(newData)
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d.x)
})
.attr("cy", function(d) {
return y(d.y)
})
.attr("r", 4)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>
Ответ №2:
Почему бы не попробовать развернуть массив данных раньше? Это должно быть легко обработать.
data = [{
x: 0.2,
y: [1, 2, 4]
},
{
x: 0.3,
y: [2]
},
{
x: 0.5,
y: [4, 7, 8, 12, 19],
},
{
x: 1.4,
y: [1, 3]
}
];
unwrappedData = [];
for (b in data) {
var temp = data[b].y.map((foo) => {
return {
x: data[b].x,
y: foo
}
})
unwrappedData = unwrappedData.concat(temp);
}
console.log(unwrappedData);
var svg = d3.select("body").append("svg")
.attr("width", 100)
.attr("height", 100)
.style("margin-top", "58px");
svg.selectAll("circle")
.data(unwrappedData)
.enter()
.append("circle")
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.attr("r", 2)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Кроме того, не должны ли атрибуты cx и cy возвращать d.x
и d.y
вместо этого?
Комментарии:
1. Спасибо за ваш ответ. Ваше решение работает, но я выбрал другое, потому что оно примерно в пять раз быстрее. Я подумал, что дам вам знать, если вам интересно посмотреть на разницу в производительности.