我有一个条形图,显示JSON数据,其中x轴的时间刻度设置为1天,y轴为线性刻度。
以下是我的代表性数据示例:
[{"date":"2013-04-20","load_volume":400},{"date":"2013-04-23","load_volume":400},{"date":"2013-04-24","load_volume":400},{"date":"2013-04-28","load_volume":1732},{"date":"2013-04-30","load_volume":400}]
以下是该数据的图表:
我想要做的是为数据集中的缺失日期键绘制“stub”rect元素,以清楚地表明该日期没有相应的值,如下图所示:
我应该怎么做?
我正在考虑尝试在javascript中使用.elementFromPoint()函数选择元素,如果在xAxis的指定点没有rects,那么继续绘制一个“stub”rect元素,但我不确定会工作,我想知道在D3中是否有更简单的方法来实现这一点。
答案 0 :(得分:1)
我认为你在按摩数据阶段试图解决这个问题是正确的。这就是我想出的:
首先,创建一个以日期为键的对象(上面的数组为var inputData
),您可以在以后查找该对象:
inputData.forEach(
function(d){
d.date = new Date(d.date).setHours(0)
}
);
var data = inputData.reduce(function(o,d){
o[+d.date] = d.load_volume;
return o;
}, {});
然后,为x轴制作日期比例:
var extent = d3.extent(inputData, function(d){
return d.date;
});
var x = d3.time.scale().range([0,chartWidth]).domain(extent);
您现在可以使用比例的输出来创建适合您所考虑的图表的数据数组:
var chartData = x.ticks(d3.time.days).map(function(d){
return data[+d] ? {date: d, stub: false, value: data[+d]} : {date:d, stub: true, value: 10};
});
输出:
[{"date":1366383600000,"stub":false,"value":400},{"date":1366470000000,"stub":true,"value":10},{"date":1366556400000,"stub":true,"value":10},{"date":1366642800000,"stub":false,"value":400},{"date":1366729200000,"stub":false,"value":400},{"date":1366815600000,"stub":true,"value":10},{"date":1366902000000,"stub":true,"value":10},{"date":1366988400000,"stub":true,"value":10},{"date":1367074800000,"stub":false,"value":1732},{"date":1367161200000,"stub":true,"value":10},{"date":1367247600000,"stub":false,"value":400}]
在上面的函数中根据需要调整存根对象的值,以获得所需的高度。您可以使用stub
布尔值在每个矩形上设置一个类来更改蓝色或灰色之间的颜色。
答案 1 :(得分:0)
当迈克说你将花费大部分时间来按摩数据时,我猜他不是在开玩笑。好吧,我最后写了一个相当长的算法,在数据集中插入缺少的日期。它的工作原理是将所有当前日期提取到一个数组中,将它们转换为毫秒时间,成对迭代日期,并填充数量相等的天数(对于getMissingDates()函数中的循环),然后构建一个带有它们的JSON字符串,最后将这个新的JSON字符串与原始输入数据合并,然后按升序排序。
以下是我提出的建议:
d3.json("/users/" + user_id + "/workouts/analyze.json", function(error, response) {
data = response;
deriveMissingDates();
//
function deriveMissingDates() {
date_array = getCurrentDates();
missingDates = getMissingDates(date_array);
new_json = buildJSONFromMissingDates(missingDates);
new_data = mergeMissingDates(new_json);
sortJSON(data);
}
// Get all current dates in dataset
// @param date_array.
function getCurrentDates(date_array) {
var date_array = [];
for (i = 0; i < data.length; i++) {
var json_object = " {\"date\":\"" + data[i].date + "\", \"load_volume\":\"" + data[i].load_volume + "\"},"
date_array.push(json_object.slice(10,20));
}
return date_array;
}
// Interpolates missing dates.
// @param arr. Array of current dates in original data set.
function getMissingDates(arr) {
var predptr = 0, leadptr = 1, missingDates = []; // initialize predecessor pointer, lead pointer, and missing dates array
while (true) {
if (predptr == arr.length) break;
var firstDate = new Date(arr[predptr]).getTime();
var secondDate = new Date(arr[leadptr]).getTime();
var currentDate = firstDate + ((24 * 60 * 60 * 1000) * 2);
var d = new Date(currentDate);
while (currentDate <= secondDate) { // Push missing dates onto array.
var d = new Date(currentDate);
missingDates.push(d.getFullYear() + '-' + ('0' + (d.getMonth()+1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2));
currentDate += (24 * 60 * 60 * 1000); // add one day
}
predptr++;
leadptr++;
}
return missingDates;
}
// Builds JSON string from missingDates array.
// @param arr. Array with each missing date.
function buildJSONFromMissingDates(arr) {
json = ""
for (i = 0; i < arr.length; i++) {
json += "{\"date\":" + "\"" + arr[i] + "\",\"stub\":" + true + ",\"load_volume\":" + 200 + "},";
}
json = json.slice(0,json.length-1);
json = "[" + json + "]";
json = $.parseJSON(json);
return json;
}
// Concatenate missingDates array with original input data
// @param new_json. New JSON string built from missing date values.
function mergeMissingDates(new_json) {
data = data.concat(new_json);
var content = [];
for (i=0; i < data.length; i++) {
content += "{" + data[i].date + "," + data[i].load_volume + "},";
}
return data;
}
// Sort new JSON dataset in ascending order.
// @param data. New JSON data with missing date objects.
function sortJSON(data) {
for (i = 0; i < data.length; i++) {
data[i].date = new Date(data[i].date).getTime(); // convert dates to millisecond time
}
data.sort(function(a,b) { return parseInt(a.date) - parseInt(b.date) });
}
然后我连接到stub布尔值以应用不同的样式,如下所示:
.style("fill", function(d) {
if (d.stub == true) {
return "#dddddd"
} else {
return "#00e0fe"
}});
JSON输出(日期以毫秒为单位,存根高度为200):
[{"date":1366416000000,"load_volume":400},{"date":1366502400000,"stub":true,"load_volume":200},{"date":1366588800000,"stub":true,"load_volume":200},{"date":1366675200000,"load_volume":400},{"date":1366761600000,"load_volume":400},{"date":1366848000000,"stub":true,"load_volume":200},{"date":1366934400000,"stub":true,"load_volume":200},{"date":1367020800000,"stub":true,"load_volume":200},{"date":1367107200000,"load_volume":1732},{"date":1367193600000,"stub":true,"load_volume":200},{"date":1367280000000,"load_volume":400}]
结果如下:
我可能会尝试重构代码,看看我是否可以让它更简洁,但我只想让它先工作。