使用D3.js从CSV文件列提取唯一值的问题及解决尝试
在D3.js中从CSV提取某列唯一值的正确姿势
我来帮你理清楚这个问题,核心是D3 v5及以上版本的d3.csv()已经从回调式改成了返回Promise的异步模式,这也是你一开始拿到Promise而不是直接数据的原因。咱们一步步拆解解决:
先搞懂问题出在哪
你最初的代码:
d3.csv("Data/FabSDO.csv", function(data) { return d3.map(data, function(d){return d.Distretto;}).keys() })
这里的问题是,d3.csv()的第二个参数是行转换函数,它会对每一行数据单独处理,而不是接收整个数据集。所以你这里其实是把每一行的Distretto值当成了整行的返回值,最后Promise解析后得到的是所有行的Distretto值组成的数组,但你用d3.map去处理单个行数据,自然得不到正确结果。
然后你后来的代码:
foo = d3.csv("Data/FabSDO.csv", function(data) { return data }) foo.then(function(col) { return dl.unique(col.Distretto) })
这里的col其实是整个CSV的数据集,是一个对象数组(每个元素对应一行数据),所以col.Distretto是不存在的,你需要遍历数组里的每个对象,取出它们的Distretto属性,这就是为什么你会得到Cannot read property 'length' of undefined的错误。
正确的解决方案
方案1:纯D3+原生JS(无需额外库)
利用D3的d3.map或者原生JS的Set来提取唯一值,代码结构更清晰,把CSV加载和数据处理分离:
// 先加载CSV,得到数据集的Promise const dataPromise = d3.csv("Data/FabSDO.csv"); // 处理Promise,提取唯一值 dataPromise.then(function(dataset) { // 提取所有Distretto列的值 const distrettoValues = dataset.map(row => row.Distretto); // 用Set去重,再转回数组 const uniqueDistretto = [...new Set(distrettoValues)]; // 或者用D3的d3.map方式 // const uniqueDistretto = d3.map(dataset, d => d.Distretto).keys(); console.log(uniqueDistretto); });
方案2:优化你的datalib用法
如果你坚持用datalib的dl.unique,只需要修正遍历逻辑即可,不需要jQuery:
const dataPromise = d3.csv("Data/FabSDO.csv"); const group = 'Distretto'; dataPromise.then(function(dataset) { const distrettoValues = dataset.map(row => row[group]); const uniqueDistretto = dl.unique(distrettoValues); console.log(uniqueDistretto); });
这里用原生的Array.map代替jQuery的each,代码更简洁。
方案3:行转换函数的正确用法(如果想在加载时就处理)
如果你想在CSV加载阶段就把需要的列提取出来,可以这样写行转换函数:
d3.csv("Data/FabSDO.csv", function(row) { // 只保留需要的列,减少内存占用 return { distretto: row.Distretto }; }).then(function(dataset) { const uniqueDistretto = [...new Set(dataset.map(d => d.distretto))]; console.log(uniqueDistretto); });
总结
- D3 v5+的
d3.csv()返回Promise,必须用.then()或者async/await来处理异步数据 - 数据集是对象数组,不能直接用
dataset.columnName,需要遍历每个对象取属性 - 去重可以用原生
Set或者D3的d3.map().keys(),不需要额外库也能搞定
内容的提问来源于stack exchange,提问作者Bakaburg
相关产品推荐
相关产品推荐

