这是这里问题的后续问题。
我想使用d3.csv
和d3.json
加载几个数据集,然后使用d3.zip
组合这些数据集。在下面的例子中,我只使用了两个。第一个数据集存储在xyData
中,第二个数据集存储在colData
中。我的目标是调用像
var combinedData = d3.zip(colData, xyData);
但是,由于这些数据集只能分别在d3.csv
和d3.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.js实际上可以处理JavaScript对象而不是文件。如果您将文件名替换为对象存储的变量名称(例如,一个JSON数据数组)D3.json(myData){…},它将有权访问该数据。
假设我们正在使用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重要:在我们稍后将其交给D3之前,您可能需要格式化存储在A中的CSV json以查看您期望的外观
var C={};
$.extend(C, A, B);
步骤4。把C给D3
d3.json(C, function(error, jsonData) {
// Use data here to do stuff
});
我在自己的项目中使用了上述方法。
你可以试着调用D3。json 在 D3.csv,但我没有尝试过这个之前:
d3.csv("A.csv", function(errorA, dataA) {
d3.json("B.json", function(errorB, dataB) {
// Use data to do stuff
});
});
既然你说你有可用的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()
实用程序函数来等待两个资源,并通过一个回调来处理它们。
$.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
返回后立即运行。