【问题标题】:How to load a CSV and a JSON file and combine both datsets to one dataset using d3.json, d3.csv and d3.zip如何加载 CSV 和 JSON 文件并使用 d3.json、d3.csv 和 d3.zip 将两个数据集组合到一个数据集
【发布时间】:2015-11-29 14:10:58
【问题描述】:

这是问题here 的后续问题。

我想使用d3.csvd3.json 加载几个数据集,然后使用d3.zip 组合这些数据集。在下面的示例中,我只使用了两个。第一个数据集将存储在xyData 中,第二个数据集将存储在colData 中。我的目标是调用类似

var combinedData = d3.zip(colData, xyData);

但是,由于这些数据集只能分别在 d3.csvd3.json 范围内访问,因此这是行不通的。有什么解决方法吗?如果要加载更多数据集,该如何处理?

第一个数据集如下所示:

//xyData.csv
x,y
0,0.00e+000
0.6981317,6.43e-001
1.3962634,9.85e-001

我的JSON 数据集如下所示:

//colData.json
{
    "el1": [
        {"color": "green"},
        {"color": "purple"},
        {"color": "brown"}
    ],

    "el2": [
        {"color": "black"},
        {"color": "red"},
        {"color": "yellow"}
    ],

    "el3":[
        {"color": "brown"},
        {"color": "yellow"},
        {"color": "blue"}
    ]
}

我读取这些数据集如下:

    //using foreach 
    var xyData = [];    
    d3.csv("xyData.csv", function(myData) {
        myData.forEach(function(d) {
            d.x = +d.x; //convert data to numbers
            d.y = +d.y;
          });
          console.log(myData[1]);
          xyData = myData;
          console.log(xyData[1])
    });
    console.log(xyData) //this will be an empty array

    //loading the json data
    var colData = [];        
    d3.json("colData.json", function(error, jsonData) {
      if (error) return console.warn(error);
      colData = jsonData;
      console.log(colData)
      console.log(colData.el1[0])
    });
    console.log(colData) //this will be an empty array

    //my goal would be:
    //var combinedData = d3.zip(colData, xyData);

我的console.log 看起来像这样:

Array [  ]
Array [  ]
Object { x: 0.6981317, y: 0.643 }
Object { x: 0.6981317, y: 0.643 }
Object { el1: Array[3], el2: Array[3], el3: Array[3] }
Object { color: "green" }

这表明加载数据按预期工作。但是由于这些数据加载器的异步特性,将它们存储为全局变量不起作用(因此,这两个数组仍然是空的)。

我的问题是:将两个数据集组合成一个数据集的最佳方法是什么?

【问题讨论】:

  • 您遇到了经典的“我如何从异步调用中返回数据”问题,答案是 - 您不能。您必须在回调中完成所有工作,不能在回调中设置变量然后在回调之外进行工作。除了 D3,您还使用其他库吗?哪个?也许是 jQuery?
  • @LarsKotthoff:我看到了这个问题,也应该提到它。那么你如何处理两个以上的输入文件呢?然后你会创建一个巨大的嵌套结构吗?
  • @Tomalak:我使用了 jQuery,但不是用于非常高级的东西。这整个话题对我来说相当新,所以我确实可能会问一些非常基本的问题。那么您将如何处理大量数据文件?创建一个巨大的嵌套结构?还是先将 CSV 文件和 JSON 文件合并为一个 JSON 文件更合理?
  • @Cleb 对于 2 个或更多文件,最好的解决方案是使用 queue.js。

标签: javascript json csv d3.js scope


【解决方案1】:

D3.js 实际上可以处理 JavaScript 对象而不是文件。如果您将文件名替换为使用 D3.json(myData){...} 存储的对象的变量名(例如,JSON 数据数组),它将有权访问该数据。

假设我们正在使用 jQuery,我们还包含一个名为 Papa Parse 的帮助程序库(它让生活更轻松)。

步骤 1. 将您的 CSV 数据转换为 JSON 数据并将其存储在变量 A 中:

var A = Papa.parse(yourCSV);

第 2 步。读取您的 JSON 数据并将其存储在名为 B 的变量中

var B;
$(document).ready(function() {
$.getJSON('yourJSON.json', function(json){
    B = json;
});

});

步骤 3. 将数据集 A 和 B 合并到变量 C 重要提示:您可能需要格式化存储在 A 中的 CSV json 以查看您期望它的外观,然后再将其提供给 D3

var C={};
$.extend(C, A, B);

第 4 步。将 C 交给 D3

d3.json(C, function(error, jsonData) {
  // Use data here to do stuff
});

我在自己的项目中使用了上述方法。

您也许可以尝试在 D3.csv 内调用 D3.json,但我之前没有尝试过:

d3.csv("A.csv", function(errorA, dataA) {
  d3.json("B.json", function(errorB, dataB) {
    // Use data to do stuff
  });
});

【讨论】:

  • 谢谢格蕾丝,我明天或周末会测试一下。第二种方法是建议的here,但是一旦想要加载两个以上的数据集,这可能会变得混乱。感谢您的努力,欢迎来到 stackoverflow :)
  • 不客气!谢谢,@Cleb。如果您在实施过程中遇到任何问题,请告诉我。
【解决方案2】:

既然你说你有 jQuery 可用 (*),我们可以使用它的 Deferred 功能来管理你正在查看的两个异步操作。

我们通过将 D3 的基于回调的方法转换为基于承诺的方法来做到这一点。

为此,我们设置了两个帮助函数来包装 D3 的 .csv.json 帮助函数并返回 jQuery 承诺:

d3.csvAsync = function (url, accessor) {
    var result = $.Deferred();

    this.csv(url, accessor, function (data) {
        if (data) {
            result.resolve(data);
        } else {
            result.reject("failed to load " + url);
        }
    });
    return result.promise();
};

d3.jsonAsync = function (url) {
    var result = $.Deferred();

    this.json(url, function (error, data) {
        if (error) {
            result.reject("failed to load " + url + ", " + error);
        } else {
            result.resolve(data);
        }
    });
    return result.promise();
};

现在我们可以并行调用请求并将它们存储在变量中。我们也可以使用.then() 即时转换结果:

var colDataReq = d3.jsonAsync("colData.json");
var xyDataReq = d3.csvAsync("xyData.csv").then(function (data) {
    data.forEach(function (d) {
        d.x = +d.x;
        d.y = +d.y;
    });
    return data;
});

最后,我们使用$.when() utility function 来等待这两个资源,并让它们由单个回调处理。

$.when(xyDataReq, colDataReq).done(function (xyData, colData) {
    var combinedData = d3.zip(colData, xyData);

    // now do something with combinedData
}).fail(function (error) {
    console.warn(error);
});

这样我们可以避免嵌套(因此不必要地序列化)两个请求。

此外,由于请求存储在变量中,我们可以简单地重复使用它们,而无需更改现有函数。例如,如果您想记录其中一个请求的内容,您可以在代码中的任何位置执行此操作:

xyDataReq.done(function (data) {
    console.log(data);
});

它会在xyDataReq 返回后立即运行。

这种方法的另一个结果是——因为我们已经将加载资源与使用解耦了——我们可以很早地执行加载,甚至在其余部分之前页面已呈现。这可以节省额外的时间。

【讨论】:

  • 非常感谢,我会尽快测试(可能只是明天或周末),一旦出现问题就会回复您。经常听说 jQuery 和 D3 混用有时会出现问题;你也经历过吗?
  • 我不能说,到目前为止我从来没有使用过 D3。如果您使用 jQuery 完成一半的 UI 工作,而另一半使用 D3,我会假设这可能是正确的。但在这种情况下,我没有使用任何 jQuery 的 UI 功能,所以应该没有重叠区域。话虽如此,jQuery 的 Promise 实现并不是最好的,如果您认为该方法本身值得追求,您可能会切换到单独的 Promise 实现来实现相同的目标。我用 jQuery 做只是因为你说它已经在项目中了。
  • 好的,感谢您的意见;让我们看看我是否可以运行它,因为我仍然需要学习很多关于这个主题的知识。
猜你喜欢
  • 2017-07-08
  • 2013-02-05
  • 1970-01-01
  • 1970-01-01
  • 2016-07-26
  • 2017-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多