d3.js - 避免在 d3.layout.stack()["group 1", "group 2"] 中枚举组名
问题描述
当未手动列出组时,我很难理解 d3.layout.stack() 。在下面的示例中,与我在其他问题中发现的类似,组在 [] 中列为“Apple”等,但据我了解,这必须手动输入。我正在寻找一种不必手动输入“Apple”、“Blueberry”等的方法。
var dataset = d3.layout.stack()(["Apple", "Blueberry", "Lettuce", "Orange"].map(function(fruit) {
return data.map(function(d) {
return {x: d.orchard, y: +d[fruit]};
});
}));
我尝试在我的数据对象中插入一行,如下所示,称为“名称”:
[{names='Apple','Blueberry','Lettuce','Orange'}, {Apple=1.0, Orange=2.0, Lettuce=1.0, orchard=小明, Blueberry=1.0}, {Apple=1.0, Orange=1.0, Lettuce=1.0, orchard=小陈, Blueberry=1.0}, {Apple=1.0, Orange=1.0, Lettuce=1.0, orchard=小虎, Blueberry=1.0}, {Orange=1.0, Lettuce=1.0, orchard=小桃, Blueberry=1.0, Apple=1.0}]
有没有办法编写类似于下面的代码?
var dataset = d3.layout.stack()([d3.keys(names)].map(function(fruit) {
我应该更专注于将唯一的名称列表插入到我的数据对象中,还是通过在我的 d3 代码本身中解析我的数据来累积唯一组名列表来做到这一点?
我想知道,如果 d3.keys 逻辑有意义,它是否也可以应用于以下上下文,而不是枚举每个案例:
legend.append("text")
.attr("x", width + 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
for(var j =0; j<4; j++){
switch (i) {
case j: return d3.keys[j]
// switch (i) {
//
// case 0: return "orange"
// case 1: return "apple"
// case 2: return "blueberry"
// case 3: return "lettuce"
}
}
});
解决方案
我最终只是将整个图表转换为 d3 v5。下面是一些笔记,这些笔记基于我查看的大量资源以及我自己的工作:
堆叠条的更好做法是使用
.data(d3.stack().keys(keys)(data))
在哪里
var keys = d3.keys(data[0]).filter(function(d){
return d != "orchard";
});
或者换句话说:
var keys = d3.keys(data[0]).filter(d => d != "orchard")
这对于在 javascript 中预先解析的数据很有用。假设您在 csv 中只有列:
var keys = csv.columns.slice(0);
很有用,但同样适用于堆叠的理念。
小问题:如果数据中稍后出现新的类别,即新的水果菠萝是 data[1] 的一部分但不是 data[0] 的一部分,则 key 将无法识别菠萝。它只响应第一个条目的数据对象。
在保持相同过滤器的同时不依赖data[1], data[0], etc.,
和“累积”密钥:data[0], data[1], etc.
var key = [];
for(var i =0; i < d3.keys(data).length; i++){
var joinin = d3.keys(data[i]).filter(d => d != "orchard")
var key = key.concat(joinin)
// console.log(key)
}
编写该代码很可能有更好的方法,但解释是:
如果你写了这样的东西,你只会得到一组数据的键:
var key = d3.keys(data[2]).filter(function(d){
return d != "orchard";
});
如果你写了这个,你会得到每次数据迭代的键:
var key = [];
for(var i =0; i < d3.keys(data).length; i++){
var key = d3.keys(data[i]).filter(d => d != "orchard")
console.log(key)
key.push(key);
}
所以诀窍是使用 for 循环来获取数据的每次迭代,但将其连接到一个单一的列表中,该列表没有重复。
[编辑] 如果你想要赋予每个键的值怎么办?同样,这是针对这样的数据结构,这有点不合常规:
data = [
{0:
{"Apple": 1}
{"orchard": xx}
}
{1:
{"Apple": 2}
{"orchard": xx}
}
]
您可以使用下面的 where key
will return["Apple"]
和key_values
will return [1, 2]
。基本上,过滤器 d > 0 会阻止任何字符串,例如果园名称“xx”。与过滤掉果园一样。
var key = [];
var key_values = [];
for(var i =0; i < d3.keys(data).length; i++){
var key_value = d3.entries(data[i]).map(d => d.value).filter(d => d > 0)
var key_values = key_values.concat(key_value)
var joinin = d3.keys(data[i]).filter(d => d != "orchard")
var key = key.concat(joinin)
}
(EDIT2)关于传说..
我刚刚替换了我的代码,我知道 d3v5 可以简化以下(?),
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter()
.append("g")
.attr("class","legend")
.attr("transform",function(d,i) {
return "translate(1300," + i * 15 + ")";
});
在我的例子中,输入变量是离散的,比如“Apple”等,而不是整数,所以我们使用 scaleOrdinal 并且只使用域的“keys”。你真的不需要写那个,我认为它默认为离散变量的输入列表?不知道为什么它有效。
var color = d3.scaleOrdinal()
.domain(keys)
// .range (whatever you want)
推荐阅读
- c - zsh shell 中的百分号从何而来?
- javascript - Angular 上的缓存破坏
- docker - 如何使用自定义配置通过 docker 运行 hyperledger besu 节点?
- python - 如何绘制用于分组子图的括号
- javascript - 在 JS 中监听基础切换器
- docker - 在 windows 10 pro 上安装 Docker 失败:需要 Windows 10 Pro/Enterprise/Home (18363+)
- tensorflow - 构建用于参数预测的自动编码器网络
- javascript - 如果我使用 cypress-cucumber,是否必须为每个功能文件创建步骤定义文件夹?
- javascript - opencv.js Mat乘法与标量
- java - 如何在 Java 中执行分布式任务处理