【问题标题】:Include json in this d3.js calendar view?在这个 d3.js 日历视图中包含 json?
【发布时间】:2017-05-16 21:18:04
【问题描述】:

我是 d3 图表和 javascript 的新手,这是一场艰苦的战斗。

经过大量研究,我能够使用 CSV 文件填充图表。所以现在,我正在尝试用json 数据填充图表。

这是我的代码。它大致基于this example。但我更喜欢使用我的代码(即 d3.v4):

var width = 960,
  height = 136,
  cellSize = 17;

var color = d3.scaleQuantize()
  .domain([9000, 12000])
  .range(["Blue", "Red", "Green", "Yellow", "Purple", "Black"]);
var dateParse = d3.timeFormat("%Y-%m-%d");
var svg = d3.select("body")
  .selectAll("svg")
  .data(d3.range(2017, 2018))
  .enter().append("svg")
  .attr("width", width)
  .attr("height", height)
  .append("g")
  .attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")");

svg.append("text")
  .attr("transform", "translate(-6," + cellSize * 3.5 + ")rotate(-90)")
  .attr("font-family", "sans-serif")
  .attr("font-size", 10)
  .attr("text-anchor", "middle")
  .text(function(d) {
    return d;
  });

var rect = svg.append("g")
  .attr("fill", "none")
  .attr("stroke", "#ccc")
  .selectAll("rect")
  .data(function(d) {
    return d3.timeDays(new Date(d, 0, 1), new Date(d + 1, 0, 1));
  })
  .enter().append("rect")
  .attr("width", cellSize)
  .attr("height", cellSize)
  .attr("x", function(d) {
    return d3.timeWeek.count(d3.timeYear(d), d) * cellSize;
  })
  .attr("y", function(d) {
    return d.getDay() * cellSize;
  })
  .datum(d3.timeFormat("%Y-%m-%d"));

svg.append("g")
  .attr("fill", "none")
  .attr("stroke", "#000")
  .selectAll("path")
  .data(function(d) {
    return d3.timeMonths(new Date(d, 0, 1), new Date(d + 1, 0, 1));
  })
  .enter().append("path")
  .attr("d", pathMonth);

d3.json("data3.json", function(error, data) {
  //populating data since i don't have the file
  data = [{
    "date": "2017-01-04",
    "open": 10430.69
  }, {
    "date": "2017-01-05",
    "open": 10584.56
  }];

  data.forEach(function(d) {
    d.dd = dateParse(new Date(d.date));
    console.log(d.dd);
  });

  var nest = d3.nest()
    .key(function(d) {
      return d.dd;
    })
    .map(data);

  rect.filter(function(d) {
      return d in data;
    })
    .attr("fill", function(d) {
      return color(data[d]);
    })
    .append("title")
    .text(function(d) {
      return d + ": " + data[d];
    });
});

function pathMonth(t0) {
  var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
    d0 = t0.getDay(),
    w0 = d3.timeWeek.count(d3.timeYear(t0), t0),
    d1 = t1.getDay(),
    w1 = d3.timeWeek.count(d3.timeYear(t1), t1);
  return "M" + (w0 + 1) * cellSize + "," + d0 * cellSize +
    "H" + w0 * cellSize + "V" + 7 * cellSize +
    "H" + w1 * cellSize + "V" + (d1 + 1) * cellSize +
    "H" + (w1 + 1) * cellSize + "V" + 0 +
    "H" + (w0 + 1) * cellSize + "Z";
}
<script src="http://d3js.org/d3.v4.min.js"></script>

【问题讨论】:

    标签: javascript jquery html json d3.js


    【解决方案1】:

    您的代码需要进行一些更改才能正常工作。这些主要与使用data 而不是nest 有关,并且与d3 v3 相比,d3 v4 中的一个小变化(我找不到相关信息)。

    过滤器

    首先,您没有正确过滤数据:

    你不想这样过滤:

    return d in data;
    

    in 运算符用于对象的属性,data 是一个数组。 您想按巢过滤(如示例中所示):

    return d in nest;
    

    其次,至少在我的简短测试中,d3.nest 在 d3 v4 中的行为略有不同(这可能取决于版本,我在下面的 sn-p 中使用了 4.9.1(最小))。当使用以数字开头的键时,d3 似乎在使用 d3.nest 时在每个键的开头附加了一个美元符号:

    D3v4 示例:

    data = [{
        "date": "2017-01-04", "open": 10430.69
      }, {
        "date": "2017-01-05", "open": 10584.56
      }];
    
      var nest = d3.nest()
        .key(function(d) { return d.date; })
        .map(data);
    	
    	console.log(nest);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>

    与 D3v3 相比:

    data = [{
        "date": "2017-01-04", "open": 10430.69
      }, {
        "date": "2017-01-05", "open": 10584.56
      }];
    
      var nest = d3.nest()
        .key(function(d) { return d.date; })
        .map(data);
    	
    	console.log(nest);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

    如果您看到这种行为,您需要相应地更改您的过滤器:

    return ("$" + d) in nest;
    

    访问嵌套属性

    第三,data 只是一个数组,data[d] 不可能得到想要的结果,因为d 将是一个日期字符串,你需要访问nest 对象。记录nest 可能有助于找到合适的属性。而不是:

    return color(data[d]);
    

    试试:

    return color(nest[("$" + d)][0].open);
    

    这与问题中的链接示例非常相似(除了那个美元符号之外)。


    优化

    与你最近的另一个question有关,这段代码

        var date = "2017-01-02";
        var dateParse = d3.timeFormat("%Y-%m-%d");
        console.log(dateParse(new Date(date)));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>

    什么都不做。它接受一个表示日期的字符串并将其转换为日期对象,然后将其转换回与您开始时相同的字符串表示形式。您可以删除这部分代码,它在链接示例中使用,因为它正在从 m/d/Y 日期字符串转换为日期对象,然后再转换为 Y-m-d 日期字符串。您的初始日期格式已经是您想要的格式,因此无需修改它。仅使用:

      var nest = d3.nest()
        .key(function(d) {
          return d.date;
        })
        .map(data);
    

    而不是:

      data.forEach(function(d) {
        d.dd = dateParse(d.date);
      });
    
      var nest = d3.nest()
        .key(function(d) {
          return d.dd;
        })
        .map(data);
    

    结果

    这些更改(我删除了文本以使示例更简单,删除了外部文件引用等)导致:

    var width = 960,
      height = 136,
      cellSize = 17;
    
    var color = d3.scaleQuantize()
      .domain([9000, 12000])
      .range(["Blue", "Red", "Green", "Yellow", "Purple", "Black"]);
      
    var dateFormat = d3.timeFormat("%Y-%m-%d");
    
    var svg = d3.select("body")
      .selectAll("svg")
      .data(d3.range(2017, 2018))
      .enter().append("svg")
      .attr("width", width)
      .attr("height", height)
      .append("g")
      .attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")");
    
    var rect = svg.append("g")
      .attr("fill", "none")
      .attr("stroke", "#ccc")
      .selectAll("rect")
      .data(function(d) {
        return d3.timeDays(new Date(d, 0, 1), new Date(d + 1, 0, 1));
      })
      .enter().append("rect")
      .attr("width", cellSize)
      .attr("height", cellSize)
      .attr("x", function(d) {
        return d3.timeWeek.count(d3.timeYear(d), d) * cellSize;
      })
      .attr("y", function(d) {
        return d.getDay() * cellSize;
      })
      .datum(dateFormat);
    
    svg.append("g")
      .attr("fill", "none")
      .attr("stroke", "#000")
      .selectAll("path")
      .data(function(d) {
        return d3.timeMonths(new Date(d, 0, 1), new Date(d + 1, 0, 1));
      })
      .enter().append("path")
      .attr("d", pathMonth);
    
      data = [{
        "date": "2017-01-04",
        "open": 10430.69
      }, {
        "date": "2017-01-05",
        "open": 10584.56
      }];
    
      var nest = d3.nest()
        .key(function(d) {
          return d.date;
        })
        .map(data);
    
    	rect.filter(function(d) {
          return ("$" + d) in nest;
        })
        .attr("fill", function(d) { 
          return color(nest[("$" + d)][0].open);
        })
    
    
    function pathMonth(t0) {
      var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
        d0 = t0.getDay(),
        w0 = d3.timeWeek.count(d3.timeYear(t0), t0),
        d1 = t1.getDay(),
        w1 = d3.timeWeek.count(d3.timeYear(t1), t1);
      return "M" + (w0 + 1) * cellSize + "," + d0 * cellSize +
        "H" + w0 * cellSize + "V" + 7 * cellSize +
        "H" + w1 * cellSize + "V" + (d1 + 1) * cellSize +
        "H" + (w1 + 1) * cellSize + "V" + 0 +
        "H" + (w0 + 1) * cellSize + "Z";
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>

    【讨论】:

    • 非常感谢。最后一个问题:如果示例中的数据来自名为data.json 的文件,我将如何将其集成到代码中?
    • 和你原来的一样,有d3.json("file.json",...但没有var data = ...
    • 非常感谢。我试过了,但最后我错过了});
    【解决方案2】:

    看起来您忘记声明 dateParse(并且使用错误)。

    var dateParse = d3.timeParse("%Y-%m-%d");

    var width = 960,
      height = 136,
      cellSize = 17;
    
    var color = d3.scaleQuantize()
      .domain([9000, 12000])
      .range(["Blue", "Red", "Green", "Yellow", "Purple", "Black"]);
    var dateParse = d3.timeParse("%Y-%m-%d");
    var svg = d3.select("body")
      .selectAll("svg")
      .data(d3.range(2017, 2018))
      .enter().append("svg")
      .attr("width", width)
      .attr("height", height)
      .append("g")
      .attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")");
    
    svg.append("text")
      .attr("transform", "translate(-6," + cellSize * 3.5 + ")rotate(-90)")
      .attr("font-family", "sans-serif")
      .attr("font-size", 10)
      .attr("text-anchor", "middle")
      .text(function(d) {
        return d;
      });
    
    var rect = svg.append("g")
      .attr("fill", "none")
      .attr("stroke", "#ccc")
      .selectAll("rect")
      .data(function(d) {
        return d3.timeDays(new Date(d, 0, 1), new Date(d + 1, 0, 1));
      })
      .enter().append("rect")
      .attr("width", cellSize)
      .attr("height", cellSize)
      .attr("x", function(d) {
        return d3.timeWeek.count(d3.timeYear(d), d) * cellSize;
      })
      .attr("y", function(d) {
        return d.getDay() * cellSize;
      })
      .datum(d3.timeFormat("%Y-%m-%d"));
    
    svg.append("g")
      .attr("fill", "none")
      .attr("stroke", "#000")
      .selectAll("path")
      .data(function(d) {
        return d3.timeMonths(new Date(d, 0, 1), new Date(d + 1, 0, 1));
      })
      .enter().append("path")
      .attr("d", pathMonth);
    
    d3.json("data3.json", function(error, data) {
      //populating data since i don't have the file
      data = [{
        "date": "2017-01-04",
        "open": 10430.69
      }, {
        "date": "2017-01-05",
        "open": 10584.56
      }];
    
      data.forEach(function(d) {
        d.dd = dateParse(d.date);
        console.log(d.dd);
      });
    
      var nest = d3.nest()
        .key(function(d) {
          return d.dd;
        })
        .map(data);
    
      rect.filter(function(d) {
          return d in data;
        })
        .attr("fill", function(d) {
          return color(data[d]);
        })
        .append("title")
        .text(function(d) {
          return d + ": " + data[d];
        });
    });
    
    function pathMonth(t0) {
      var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
        d0 = t0.getDay(),
        w0 = d3.timeWeek.count(d3.timeYear(t0), t0),
        d1 = t1.getDay(),
        w1 = d3.timeWeek.count(d3.timeYear(t1), t1);
      return "M" + (w0 + 1) * cellSize + "," + d0 * cellSize +
        "H" + w0 * cellSize + "V" + 7 * cellSize +
        "H" + w1 * cellSize + "V" + (d1 + 1) * cellSize +
        "H" + (w1 + 1) * cellSize + "V" + 0 +
        "H" + (w0 + 1) * cellSize + "Z";
    }
    <script src="http://d3js.org/d3.v4.min.js"></script>

    【讨论】:

    • 谢谢,我没有再遇到 javascript 问题了。
    • 我已经使用您的示例对代码进行了一些更改,但图表仍未填充。你能看看代码有没有问题吗?
    • 日期格式正确。我不确定为什么它没有填满图表。对此的任何帮助表示赞赏。
    猜你喜欢
    • 2016-10-23
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 2012-11-10
    • 1970-01-01
    • 1970-01-01
    • 2012-11-26
    • 1970-01-01
    相关资源
    最近更新 更多