gpt4 book ai didi

d3.js - 如何改进我在 d3 中的简单嵌套数据绑定(bind)?

转载 作者:行者123 更新时间:2023-12-04 07:36:09 26 4
gpt4 key购买 nike

我有一个非常简单的嵌套数据结构,我想用 d3 将它呈现为条形图。 “嵌套”是每个数组元素本身都包含一个数组:

interface MyData {
name: string
values: number[]
}

const allData: MyData[] = [
{ name: "one", values: [4, 6, 10] },
{ name: "two", values: [1, 3, 4, 7] },
{ name: "three", values: [2, 2.5, 4] },
{ name: "four", values: [5, 8, 9] },
{ name: "five", values: [3, 5, 5.5, 6, 9] }
]
我想用 d3 在 svg 中渲染它,使它看起来像这样,每个 value 都有离散行在值数组和包含值范围的容器矩形中,我希望能够切换主数据数组中各个元素的可见性:
enter image description here
我拼凑了一个工作版本,在不同的步骤中创建容器矩形和点线,我觉得我错过了如何以更简单的方式渲染它们( codesandbox here)。
    // Range container rectangle
svg
.select("g.plot-area")
.selectAll("rect.range")
.data(allData, (d: any) => d.name)
.join(
(enter) =>
enter
.append("rect")
.classed("range", true)
.attr("all my attrs for the range rect", ...)
(update) =>
update
.attr("attrs for updated range rect", ...),
(exit) => exit.remove()
);

// Individual point lines
svg
.select("g.plot-area")
.selectAll("g.points")
.data(allData, (d: any) => d.name) // <-- this seems weird
.join(
(enter) => {
const points = enter.append("g").classed("points", true);
points
.selectAll("rect.point")
.data((d) => d.values)
.join("rect")
.classed("point", true)
.attr("attrs for this point", ...)
return points;
},
(update) => {
update
.selectAll("rect.point")
.attr("attrs for this point", ...)
return update;
}
);
特别是,对于单个点,我不得不再次绑定(bind)到主数据集似乎很愚蠢,除了随后绑定(bind)到每个点数组之外,没有其他原因。我觉得这应该能够以更少的步骤发生。
如何最有效地使用 d3 来渲染容器矩形和单个点矩形?

最佳答案

因为你只画rect然后简单地取消嵌套数据似乎是一种可行的方法。您的 .range 之间的唯一区别和 .point rect s 仅在于它们的不透明度和宽度。
例如。

const unnestedData = allData.reduce((a, c) => {
a.push({
name: c.name,
color: c.color,
type: "range",
min: d3.min(c.values),
max: d3.max(c.values)
});
c.values.map(n => {
a.push({
name: c.name,
color: c.color,
type: "line",
min: n,
max: n // redundant
});
});
return a;
}, []);
返回:
0: {name: "one", color: "red", type: "range", min: 4, max: 10}
1: {name: "one", color: "red", type: "line", min: 4, max: 4}
2: {name: "one", color: "red", type: "line", min: 6, max: 6}
3: {name: "one", color: "red", type: "line", min: 10, max: 10}
4: {name: "two", color: "green", type: "range", min: 1, max: 7}
5: {name: "two", color: "green", type: "line", min: 1, max: 1}
6: {name: "two", color: "green", type: "line", min: 3, max: 3}
7: {name: "two", color: "green", type: "line", min: 4, max: 4}
8: {name: "two", color: "green", type: "line", min: 7, max: 7}
... etc
那么您只需要一个循环并为 opacity 使用几个条件。和 width属性:
svg.selectAll(".rects")
.data(unnestedData) // d => d
.join(
enter =>
enter.append("rect")
.attr("class", d => d.type)
.attr("opacity", d => d.type === "range" ? 0.3 : 1)
.attr("fill", d => d.color)
.attr("x", d => xScale(d.min))
.attr("y", d => yScale(d.name))
.attr("width", d => d.type === "range" ? xScale(d.max) - xScale(d.min) : 2)
.attr("height", yScale.bandwidth())
)
例子:

// input data
const allData = [
{ name: "one", values: [4, 6, 10], color: "red" },
{ name: "two", values: [1, 3, 4, 7], color: "green" },
{ name: "three", values: [2, 2.5, 4], color: "cyan" },
{ name: "four", values: [5, 8, 9], color: "purple" },
{ name: "five", values: [3, 5, 5.5, 6, 9], color: "orange" }
];

// un-nest the data
const unnestedData = allData.reduce((a, c) => {
a.push({
name: c.name,
color: c.color,
type: "range",
min: d3.min(c.values),
max: d3.max(c.values)
});
c.values.map(n => {
a.push({
name: c.name,
color: c.color,
type: "line",
min: n,
max: n // redundant
});
});
return a;
}, []);

// basic viz
const width = 500;
const height = 200;
const margin = {top: 5, right: 5, bottom: 20, left: 50}

const svg = d3.select("#graph")
.append("svg")
.attr("width", width)
.attr("height", height);

const xScale = d3.scaleLinear()
.domain([1, 10])
.range([margin.left, width - margin.right]);

const xAxis = svg.append("g")
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(xScale));

const yScale = d3.scaleBand()
.padding(0.1)
.domain(allData.map(k => k.name))
.range([margin.top, height - margin.bottom]);

const yAxis = svg.append("g")
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(yScale));

const plotG = svg.append("g")
.attr("class", "plot-area");

// render
svg.selectAll(".rects")
.data(unnestedData, d => d)
.join(
enter =>
enter
.append("rect")
.attr("class", d => d.type)
.attr("opacity", d => d.type === "range" ? 0.3 : 1)
.attr("fill", d => d.color)
.attr("x", d => xScale(d.min))
.attr("y", d => yScale(d.name))
.attr("width", d => d.type === "range" ? xScale(d.max) - xScale(d.min) : 2)
.attr("height", yScale.bandwidth())
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
<div id="graph"></div>

关于d3.js - 如何改进我在 d3 中的简单嵌套数据绑定(bind)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67730227/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com