D3 - 从第二个CSV加载数据

时间:2016-12-16 18:24:49

标签: javascript d3.js

我有两个单独的CSV文件,共享一个属性:

文件1

name,total
Frank,13
Mary,24
Jim,46

文件2

name,desc
Frank,yellow
Mary,blue
Jim,green

我如何将<{em>}属性desc映射到文件1,所以,当我将鼠标悬停在Frank上时,我会看到“13”和“黄色”?

目前,我的代码看起来像这样:

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.namedata.desc(来自文件1和文件2)不匹配 - 我可以理解它是因为我没有将它们组合起来因此它们可以共享< / em>公共属性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(yay!)。但是“d.desc”在HTML中返回“unidefined”。

3 个答案:

答案 0 :(得分:2)

加载多个CSV有两种主要方法:使用d3.queue()或嵌套它们:

d3.csv("file1.csv", function(data1) {
    d3.csv("file2.csv", function(data2) {
        //code that depends on both data1 and data2
    })
});

data1是来自&#34; file1.csv&#34;的数组。 data2是来自&#34; file2.csv&#34;的数组。

然后,我们将根据它们的属性合并两个数组。

您可以创建第三个数组或在两个原始数组之一中推送值。在这里,我会做第二个选项(实际上,第二个选项是你提出的问题:&#34;我如何&#34; map&#34;属性&#39; desc&#39;到文件1&#34; )。

请记住,这是一个 ad hoc 解决方案,我专门解决了您的问题(它不适用于其他数据结构):

data1.forEach(d => {
    data2.forEach(e => {
        if (d.name === e.name) {
            d.desc = e.desc
        }
    })
})

现在,data1包含您想要的所有信息:name,total和desc。

这是一个演示,请在控制台中查看数据(在此演示中,我使用<pre>元素加载数据,因为我无法在代码段中使用CSV):

&#13;
&#13;
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);
&#13;
pre {
    display: none;
}
&#13;
<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>
&#13;
&#13;
&#13;

PS :如果您的文件太大,请在下面的评论中查看@altocumulus替代代码。

答案 1 :(得分:1)

正如Gerardo Furtado已经在answer中提出的那样,您将不得不使用d3.queued3.csv的嵌套版本来继续加载数据。当使用D3的两个内置功能时,可以优化嵌套方法,使您无法在初始加载后进行显式嵌套循环。

  • 使用D3 Map可以name属性快速访问您的数据。

  • 使用row conversion function作为第二个可选参数传递给d3.csv(url[[, row], callback])

      

    如果指定了转换函数,则会为每一行调用指定的函数,并传递一个表示当前行的对象(d)...

    这特别方便,因为在解析文件内容时,内部将迭代遍历行。您可以使用行转换功能作为挂钩,通过操作该函数中的映射,将第二个文件的数据同步到第一个文件的数据。

我设置Block演示了这种方法:

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"}, ...]
  })
});

让我一步一步地告诉你:

  1. 我们使用name属性作为密钥,根据您第一个文件中的已解析数据构建地图。

  2. 在执行第二个文件的内部加载时,我们将行转换指定为第二个参数。通常,这将用于进行一些数据转换等,并返回一些表示实际行数据的转换对象。然而,在我们的场景中,我们对转换本身并不感兴趣,而是在隐式迭代和对行数据的访问中。

  3. 现在,我们已准备好通过getting从地图中对应此行name的对象同步数据。

  4. 从行转换函数返回null将避免为第二个文件构建数组,因为我们对内容的独立版本不感兴趣:

      

    如果返回的值为null或未定义,则跳过该行并将从数组中省略...

  5. 在内部d3.csv()的回调中,您的数据已准备就绪并已同步。请注意,我们没有将任何参数传递给此回调,因为我们将通过data1访问同步数据。我们甚至不用再烦扰地图了,这只是一种允许从第一个文件快速访问我们的数据的方法。由于地图存储了对数据对象的引用,因此我们更新了data1中用于构建地图的实际对象。

  6. 作为一种改进,你可能想要添加一些ES6糖,这使得它更简洁(ES6 Block):

    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.
      );
    }); 
    

答案 2 :(得分:0)

another answer中所述,您可以嵌套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>');
}

如果没有一些调整,这段代码可能无法正常工作,但我希望你能理解。如果没有,请告诉我你有什么不清楚的地方。祝你好运!