[英]D3 - Loading data from a second CSV
我有两个单独的CSV文件,共享一个属性:
文件1
name,total
Frank,13
Mary,24
Jim,46
文件2
name,desc
Frank,yellow
Mary,blue
Jim,green
我如何将desc
属性映射到文件1,也就是说,当我将鼠标悬停在Frank上时,会看到“ 13”和“ yellow”?
目前,我的代码如下所示:
d3.csv("data/csv/bilancio-missioni-desc.csv", function(descrizione) {
data = descrizione.map(function(data){
div.html("<h3>" + d.name + "</h3>" + "<p>" + data.desc + "</p>")
})
问题是d.name
和data.desc
(来自文件1和文件2)不匹配-我可以理解这是因为我没有将它们组合在一起,所以它们可以共享公用属性name
,但是我不知道如何编写正确的代码。
到目前为止,我的代码已更新 :
d3.csv("data/csv/bilancio-tipologiedispesa-nest.csv", function(data1) {
d3.csv("data/csv/bilancio-missioni-desc.csv", function(data2) {
//code that depends on both data1 and data2
data1.forEach(d => {
data2.forEach(e => {
if (d.name === e.name) {
d.desc = e.desc;
}
});
});
// Fade all the segments.
d3.selectAll("path")
.style("opacity", .3);
vis.selectAll("path")
.filter(function(node) {
return (sequenceArray.indexOf(node) >= 0);
})
.style("opacity", 1);
div.transition()
.duration(200)
.style("opacity", .9);
div.html("<h3>" + d.name + "</h3>" + "<p>" + d.desc + "</p>");
});
});
}
如果我console.log(data1),“ desc”(来自data2)将附加到data1(是!)。 但是“ d.desc”在HTML中返回“ unidefined”。
加载几种CSV的主要方法有两种:使用d3.queue()
或嵌套它们:
d3.csv("file1.csv", function(data1) {
d3.csv("file2.csv", function(data2) {
//code that depends on both data1 and data2
})
});
其中data1
是“ file1.csv”中的数组,而data2
是“ file2.csv”中的数组。
然后,我们将基于它们的属性合并两个数组。
您可以执行以下操作:创建第三个数组或将值推入两个原始数组之一中。 在这里,我将做第二个选择(实际上,第二个选择是您所要求的: “如何将属性“ desc”“映射”到文件1” )。
请记住,这是我专门针对您的问题制作的临时解决方案(不适用于其他数据结构):
data1.forEach(d => {
data2.forEach(e => {
if (d.name === e.name) {
d.desc = e.desc
}
})
})
现在, data1
具有您想要的所有信息:名称,总数和说明。
这是一个演示,使用数据检查控制台(在此演示中,我使用<pre>
元素加载数据,因为我不能在代码段中使用CSV):
var data1 = d3.csvParse(d3.select("#file1").text()); var data2 = d3.csvParse(d3.select("#file2").text()); data1.forEach(d => { data2.forEach(e => { if (d.name === e.name) { d.desc = e.desc } }) }) console.log(data1);
pre { display: none; }
<script src="https://d3js.org/d3.v4.min.js"></script> <pre id="file1">name,total Frank,13 Jim,46 Mary,24</pre> <pre id="file2">name,desc Frank,yellow Mary,blue Jim,green</pre>
PS :如果文件太大,请在下面的注释中查看@altocumulus备用代码。
正如Gerardo Furtado在回答中已经列出的那样,您将不得不使用d3.queue
或嵌套版本的d3.csv
来保持数据同步加载。 当使用D3的两个内置功能时,可以优化嵌套方法,以使您不必在初始加载后执行显式的嵌套循环。
使用D3 地图可以通过name
属性提供对数据的快速访问。
使用行转换函数 ,该函数可以作为d3.csv(url[[, row], callback])
的第二个可选参数传递:
如果指定了行转换函数,则会为每一行调用指定的函数,并传递代表当前行的对象(
d
)…
这特别方便,因为在解析文件内容时会在内部对行进行迭代。 您可以将行转换功能用作挂钩,以通过在该功能内操作映射来将第二个文件的数据同步到第一个文件的数据。
我设置了一个块来演示这种方法:
d3.csv("file1.csv", function(data1) {
// 1. Build a D3 map from your first file's contents
var map = d3.map(data1, function(d) { return d.name; });
d3.csv("file2.csv", function(row) {
// 2. Use the row function of the second file to match your data.
map.get(row.name).desc = row.desc; // 3. Sync data
return null; // 4. Don't append any row to the array.
}, function() { // 5. There is no parameter present; all data has been synced into data1.
// Just logging below
console.log(data1); // [{ name: "Frank", total: "13", desc: "yellow"}, ...]
})
});
让我逐步介绍您:
我们使用name
属性作为键,根据您第一个文件中的解析数据构建地图。
在内部加载第二个文件时,我们将行转换指定为第二个参数。 通常,这将用于执行一些数据转换等操作,并返回一些表示实际行数据的转换对象。 但是,在我们的场景中,我们对转换本身不感兴趣,而对隐式迭代和对行数据的访问感兴趣。
现在,我们可以通过从地图获取与该行name
相对应的对象来同步数据。
从行转换函数返回null
将避免为第二个文件建立数组,因为我们对内容的独立版本不感兴趣:
如果返回的值为null或未定义,则跳过该行并将其从数组中省略。
在内部d3.csv()
的回调中,您的数据已准备就绪并已同步。 注意,我们没有将任何参数传递给此回调,因为我们将通过data1
访问同步的数据。 我们甚至不必再担心地图,这只是允许从第一个文件快速访问我们的数据的一种方法。 因为地图存储了对数据对象的引用,所以我们从data1
更新了实际的对象,这些对象用于构建地图。
作为改进,您可能想要添加一些ES6糖以使其更加简洁( ES6块 ):
d3.csv("file1.csv", data1 => {
let map = d3.map(data1, d => d.name);
d3.csv("file2.csv",
row => (map.get(row.name).desc = row.desc, null),
() => {console.log(data1)} // Merged data available at this point.
);
});
如另一个答案中所述,您可以嵌套csv加载功能以异步加载文件。 然后,您可以将它们组合在一起:
var file1 = "data/csv/bilancio-missioni-desc.csv";
var file2 = "data/csv/bilancio-missioni-info.csv"; // I named it info just as an example
var div = d3.select('#my-div');
var data = {};
d3.csv(file1, function(descrizione) {
d3.csv(file2, function(info) {
processData(descrizione, info);
})
})
function processData(descrizione, info) {
descrizione.forEach(d => data[d.name] = d);
info.forEach(i => data[i.name] && data[i.name]['desc'] = i.desc);
appendData();
}
function appendData() {
div.data(data).append(d => '<h3>' + d.name + ' (' + d.total ')</h3> <p>' + d.desc + '</p>');
}
当然,如果不进行一些调整,此代码可能无法正常工作,但我希望您能理解。 如果没有,请告诉我您是否有不清楚的地方。 祝好运!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.