开发者问题收集

使用 d3.js 进行可缩放条形图

2018-07-13
1757

尝试将以下内容应用到我的数据集上: https://bl.ocks.org/mbostock/4015254 。我已更改变量以适合我的数据集。

我的代码如下:

<script>

var svg = d3.select("svg"),
    margin = {top: 20, right: 20, bottom: 30, left: 60},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var parseDate = d3.timeParse("%Y-%m-%d"),
    formatDate = d3.timeFormat("%Y");

var x = d3.scaleTime()
    .domain([new Date(2002, 0, 1), new Date(2003, 0, 1)])
    .range([0, width]);

var y = d3.scaleLinear()
    .range([height, 0]);

var xAxis = d3.axisBottom(x);

var yAxis = d3.axisLeft(y);

var area = d3.area()
    .curve(d3.curveStepAfter)
    .y0(y(0))
    .y1(function(d) { return y(d.value); });

var areaPath = g.append("path")
    .attr("clip-path", "url(#clip)")
    .attr("fill", "steelblue");

var yGroup = g.append("g");

var xGroup = g.append("g")
    .attr("transform", "translate(0," + height + ")");

var zoom = d3.zoom()
    .scaleExtent([1 / 4, 8])
    .translateExtent([[-width, -Infinity], [2 * width, Infinity]])
    .on("zoom", zoomed);

var zoomRect = svg.append("rect")
    .attr("width", width)
    .attr("height", height)
    .attr("fill", "none")
    .attr("pointer-events", "all")
    .call(zoom);

g.append("clipPath")
    .attr("id", "clip")
    .append("rect")
    .attr("width", width)
    .attr("height", height);

d3.json("api.php", function(d) {
  d.date = parseDate(d.date);
  d.value = +d.close;
  return d;
}, function(error, data) {
  if (error) throw error;
  var xExtent = d3.extent(data, function(d) { return d.date; });
  zoom.translateExtent([[x(xExtent[0]), -Infinity], [x(xExtent[1]), Infinity]])
  y.domain([0, d3.max(data, function(d) { return d.value; })]);
  yGroup.call(yAxis).select(".domain").remove();
  areaPath.datum(data);
  zoomRect.call(zoom.transform, d3.zoomIdentity);
});

function zoomed() {
  var xz = d3.event.transform.rescaleX(x);
  xGroup.call(xAxis.scale(xz));
  areaPath.attr("d", area.x(function(d) { return xz(d.date); }));
}

</script>

但是我的控制台上出现错误,错误消息如下:

d3.v4.min.js:2 Uncaught TypeError: Cannot read property 'length' of undefined
    at SVGPathElement.t (d3.v4.min.js:2)
    at SVGPathElement.<anonymous> (d3.v4.min.js:2)
    at ut.each (d3.v4.min.js:2)
    at ut.attr (d3.v4.min.js:2)
    at SVGRectElement.zoomed (research.php:123)
    at k.apply (d3.v4.min.js:2)
    at it (d3.v4.min.js:2)
    at a.emit (d3.v4.min.js:2)
    at a.zoom (d3.v4.min.js:2)
    at d3.v4.min.js:2

我的数据集摘录如下:

[{"id":"1","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2006-12-29","time":"15:00:00.000000","close":"2388.023438000000000000","volume":"23700.000000000000000000","active":"1","exchange_id":"0"},{"id":"2","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-04","time":"15:00:00.000000","close":"2416.452637000000000000","volume":"16500.000000000000000000","active":"1","exchange_id":"0"},{"id":"3","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-05","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"45400.000000000000000000","active":"1","exchange_id":"0"},{"id":"4","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-09","time":"15:00:00.000000","close":"2388.023438000000000000","volume":"28800.000000000000000000","active":"1","exchange_id":"0"},{"id":"5","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-10","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"27800.000000000000000000","active":"1","exchange_id":"0"},{"id":"6","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-11","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"25500.000000000000000000","active":"1","exchange_id":"0"},{"id":"7","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-12","time":"15:00:00.000000","close":"2378.546875000000000000","volume":"28100.000000000000000000","active":"1","exchange_id":"0"},{"id":"8","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-15","time":"15:00:00.000000","close":"2397.500244000000000000","volume":"23400.000000000000000000","active":"1","exchange_id":"0"}]

它包含 2831 条记录。我不明白为什么我的 x 轴和 y 轴都无法正确打印。提前致谢。

2个回答

您引用的 bl.ocks 使用 d3.csv ,它(如果您查看 docs )有一个 accessor 函数,该函数传递每一行数据,然后在回调中访问整个日期。这是访问器函数:

function(d) {
  d.date = parseDate(d.date);
  d.value = +d.value;
  return d;
}

但是在 d3.json 的情况下,没有这样的访问器: d3.json(url[, callback]) ,这意味着您必须在回调中解析每一行。方法如下:

d3.json("test.json", function(data) {
  data.forEach(e => {
    e.date = parseDate(e.date);
    e.value = +e.close;
  });
  var xExtent = d3.extent(data, function(d) { return d.date; });
  .....

这是您的代码的一个分支(我不确定为什么您将文件命名为“api.php”,而它是一个 JSON 文件。我使用了“test.json”文件。

https://bl.ocks.org/shashank2104/21358032ac5507f7a6b7d620b1a4ef69/12b6089547e3b07edeca6214834d7e7a340be6a7

如果您无法查看该代码段,或者您正在寻找代码段,这里有一个:

var svg = d3.select("svg"),
    margin = {top: 20, right: 20, bottom: 30, left: 60},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var parseDate = d3.timeParse("%Y-%m-%d"),
    formatDate = d3.timeFormat("%Y");

var x = d3.scaleTime()
    .domain([new Date(2006, 12, 1), new Date(2007, 1, 1)])
    .range([0, width]);

var y = d3.scaleLinear()
    .range([height, 0]);

var xAxis = d3.axisBottom(x);

var yAxis = d3.axisLeft(y);

var area = d3.area()
    .curve(d3.curveStepAfter)
    .y0(y(0))
    .y1(function(d) { return y(d.value); });

var areaPath = g.append("path")
    .attr("clip-path", "url(#clip)")
    .attr("fill", "steelblue");

var yGroup = g.append("g");

var xGroup = g.append("g")
    .attr("transform", "translate(0," + height + ")");

var zoom = d3.zoom()
    .scaleExtent([1 / 4, 8])
    .translateExtent([[-width, -Infinity], [2 * width, Infinity]])
    .on("zoom", zoomed);

var zoomRect = svg.append("rect")
    .attr("width", width)
    .attr("height", height)
    .attr("fill", "none")
    .attr("pointer-events", "all")
    .call(zoom);

g.append("clipPath")
    .attr("id", "clip")
    .append("rect")
    .attr("width", width)
    .attr("height", height);

var data = [{"id":"1","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2006-12-29","time":"15:00:00.000000","close":"2388.023438000000000000","volume":"23700.000000000000000000","active":"1","exchange_id":"0"},{"id":"2","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-04","time":"15:00:00.000000","close":"2416.452637000000000000","volume":"16500.000000000000000000","active":"1","exchange_id":"0"},{"id":"3","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-05","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"45400.000000000000000000","active":"1","exchange_id":"0"},{"id":"4","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-09","time":"15:00:00.000000","close":"2388.023438000000000000","volume":"28800.000000000000000000","active":"1","exchange_id":"0"},{"id":"5","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-10","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"27800.000000000000000000","active":"1","exchange_id":"0"},{"id":"6","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-11","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"25500.000000000000000000","active":"1","exchange_id":"0"},{"id":"7","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-12","time":"15:00:00.000000","close":"2378.546875000000000000","volume":"28100.000000000000000000","active":"1","exchange_id":"0"},{"id":"8","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-15","time":"15:00:00.000000","close":"2397.500244000000000000","volume":"23400.000000000000000000","active":"1","exchange_id":"0"}];

  data.forEach(e => {
    e.date = parseDate(e.date);
    e.value = +e.close;
  });
  var xExtent = d3.extent(data, function(d) { return d.date; });
  x.domain(xExtent);
  zoom.translateExtent([[x(xExtent[0]), -Infinity], [x(xExtent[1]), Infinity]])
  y.domain([0, d3.max(data, function(d) { return d.value; })]);
  yGroup.call(yAxis).select(".domain").remove();
  areaPath.datum(data);
  zoomRect.call(zoom.transform, d3.zoomIdentity);

function zoomed() {
  var xz = d3.event.transform.rescaleX(x);
  xGroup.call(xAxis.scale(xz));
  areaPath.attr("d", area.x(function(d) { return xz(d.date); }));
}
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>

希望这有意义。此外,我将 x 域设置为根据数据计算出的 xExtent 。尝试一下以查看差异。

Shashank
2018-07-13

不知何故,加载函数属于不同类型并使用以下格式。

d3.json("api.php", function(data) {
  data.forEach(e => {
    e.date = parseDate(e.date);
    e.value = +e.close;
  });
  var xExtent = d3.extent(data, function(d) { return d.date; });
  zoom.translateExtent([[x(xExtent[0]), -Infinity], [x(xExtent[1]), Infinity]])
  y.domain([0, d3.max(data, function(d) { return d.value; })]);
  yGroup.call(yAxis).select(".domain").remove();
  areaPath.datum(data);
  zoomRect.call(zoom.transform, d3.zoomIdentity);
});
rioV8
2018-07-13