我正在尝试在d3中绘制多线折线图,然后在每个刻度上对其进行更新。我遇到的问题是我最终遇到一个错误,即数据为零的图形。我在这里找到了其他答案,但它们要么全部用于d3的较旧版本,要么用于比我更简单的数据。
我的数据看起来像这样(显然是简化的,但是结构是一样的):
let data = [
[{name: "a", val: 1}, {name: "b", val: 2}],
[{name: "a", val: 2}, {name: "b", val: 3}],
[{name: "a", val: 3}, {name: "b", val: 4}],
[{name: "a", val: 4}, {name: "b", val: 5}],
]
我想要两行:每个索引的A值之一和每个索引B的值之一。 Other posts on SO提出了相同的问题,但问题是d3的3.0。
根据这个问题,我推断出以下内容:
let svg = ...
let g = svg.append("g")...
let line = d3.line()
.x((d:any, i:number) => x(i))
.y((d:any) => y(d.value))
g.selectAll(".line")
.data(data)
.enter().append("path")
.attr("class", "line")
.attr("d", line);
这会在每个刻度上抛出一个错误:Error: <path> attribute d: Expected number, "MNaN,20Z"
,我也不太惊讶,因为TypeScript也抱怨:
TS2345: Argument of type 'Line<[number, number]>' is not assignable to parameter of type 'ValueFn<SVGPathElement, GraphablePacketCount[], string | number | boolean | null>'.
Types of parameters 'data' and 'datum' are incompatible.
我尝试过的其他东西:
for (const datum of data) {
g.append("svg:line").data(datum)...
}
还有d3's general update pattern的行化版本:
let lines = g.selectAll("line").data(data);
lines.enter().append(...);
只会导致一个空图。在这种情况下,console.log(lines.enter())
给了我数据,只是没有渲染。
接下来我要尝试什么?
答案 0 :(得分:0)
您的数据数组具有val
个字段,但是在行的y访问器中,您使用value
。
但是即使如此,您的问题仍然是您按错误的维度对数据进行了切片。您将数据绑定到行,因此数据(外部)数组中的每个元素应该是单行的数据,然后内部数组应该是该行的点(因为行生成器接受点数组)。但是,您却遇到了其他问题,因为您首先要经过点检,然后才可以区分线路。
您必须调整数据,方法是在源头进行调整,或者在绑定之前进行转换。参见下面的示例
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body {
margin: 0;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
</style>
</head>
<body>
<script>
// Feel free to change or delete any of the code you see in this editor!
var svg = d3.select("body").append("svg")
.attr("width", 300)
.attr("height", 100);
let data = [
[{
name: "a",
val: 1
}, {
name: "b",
val: 2
}],
[{
name: "a",
val: 2
}, {
name: "b",
val: 3
}],
[{
name: "a",
val: 3
}, {
name: "b",
val: 4
}],
[{
name: "a",
val: 4
}, {
name: "b",
val: 5
}],
];
var byLineDataMap = data.reduce(function(lineData, point) {
point.forEach(function(linePoint) {
if (!lineData[linePoint.name])
lineData[linePoint.name] = [];
lineData[linePoint.name].push({
val: linePoint.val
});
});
return lineData;
}, {});
var byLineData = Object.entries(byLineDataMap);
/*
var byLineData = [
['a', [{val: 1}, ...]],
['b', [{val: 2}, ...]]
];
*/
var x = d3.scaleLinear()
.domain([0, 4])
.range([50, 250]);
var y = d3.scaleLinear()
.domain([1, 5])
.range([80, 20]);
var color = d3.scaleOrdinal()
.domain(['a', 'b'])
.range(['red', 'blue']);
var lineGen = d3.line()
.x(function(d, i) {
return x(i)
})
.y(function(d, i) {
return y(d.val)
});
svg.selectAll('path')
.data(byLineData)
.enter()
.append('path')
.style('stroke', function(d) {
return color(d[0]);
})
.attr('d', function(d) {
return lineGen(d[1]);
});
</script>
</body>