0

我有一个条形图,它显示 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}]

这是图表与该数据的样子:

在此处输入图像描述

我想要做的是为数据集中缺少的日期键绘制“存根”矩形元素,以明确该日期没有对应的值,如下图所示:

在此处输入图像描述

我该怎么办?

我正在考虑尝试在 javascript 中使用 .elementFromPoint() 函数选择元素,如果沿 xAxis 的指定点没有矩形,则继续绘制“存根”矩形元素,但不确定是否可行,我想知道在 D3 中是否有更简单的方法来实现这一点。

4

2 回答 2

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布尔值在每个矩形上设置一个类,以在蓝色或灰色之间更改颜色。

于 2013-05-03T04:17:11.607 回答
0

当迈克说你将把大部分时间花在处理数据上时,我想他不是在开玩笑。好吧,我最终编写了一个相当长的算法来插入数据集中缺失的日期。它的工作原理是将所有当前日期提取到一个数组中,将它们转换为毫秒时间,成对迭代日期并填充天数等于对之间差异的天数(getMissingDates() 函数中的 while 循环),然后构建一个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) });
}

然后我连接到存根布尔值以应用不同的样式,如下所示:

.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}]

结果如下:

在此处输入图像描述

我可能会尝试重构代码以查看是否可以使其更简洁,但我只是想让它首先工作。

于 2013-05-04T02:18:41.637 回答