javascript - 行函数在此 D3 多系列折线图中起什么作用?

标签 javascript d3.js data-visualization

我正在尝试根据 Mike Bostock 的这个示例使用 D3 制作多系列折线图:

https://bl.ocks.org/mbostock/3884955

我很难理解加载 data.tsv 文件后会发生什么,尤其是“type”函数。它似乎解析数据并将温度字符串转换为数字。但它还能做什么,为什么有必要?:

function type(d, _, columns) {
  d.date = parseTime(d.date);
  for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
  return d;
}

完整代码如下:

var svg = d3.select("svg"),
    margin = {top: 20, right: 80, bottom: 30, left: 50},
    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 parseTime = d3.timeParse("%Y%m%d");

var x = d3.scaleTime().range([0, width]),
    y = d3.scaleLinear().range([height, 0]),
    z = d3.scaleOrdinal(d3.schemeCategory10);

var line = d3.line()
    .curve(d3.curveBasis)
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.temperature); });

d3.tsv("data.tsv", type, function(error, data) {
  if (error) throw error;

  var cities = data.columns.slice(1).map(function(id) {
    return {
      id: id,
      values: data.map(function(d) {
        return {date: d.date, temperature: d[id]};
      })
    };
  });

  x.domain(d3.extent(data, function(d) { return d.date; }));

  y.domain([
    d3.min(cities, function(c) { return d3.min(c.values, function(d) { return d.temperature; }); }),
    d3.max(cities, function(c) { return d3.max(c.values, function(d) { return d.temperature; }); })
  ]);

  z.domain(cities.map(function(c) { return c.id; }));

  g.append("g")
      .attr("class", "axis axis--x")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

  g.append("g")
      .attr("class", "axis axis--y")
      .call(d3.axisLeft(y))
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", "0.71em")
      .attr("fill", "#000")
      .text("Temperature, ºF");

  var city = g.selectAll(".city")
    .data(cities)
    .enter().append("g")
      .attr("class", "city");

  city.append("path")
      .attr("class", "line")
      .attr("d", function(d) { return line(d.values); })
      .style("stroke", function(d) { return z(d.id); });

  city.append("text")
      .datum(function(d) { return {id: d.id, value: d.values[d.values.length - 1]}; })
      .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")"; })
      .attr("x", 3)
      .attr("dy", "0.35em")
      .style("font", "10px sans-serif")
      .text(function(d) { return d.id; });
});

function type(d, _, columns) {
  d.date = parseTime(d.date);
  for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
  return d;
}

最佳答案

那个行函数(这里命名为 type)实际上就是这样做的:

function type(d){
    d.date = parseTime(d.date);
    d["New Your"] = +d["New Your"];
    d["San Francisco"] = +d["San Francisco"];
    d["Austin"] = +d["Austin"];
    return d;
}

因此,它基本上解析了 date 属性并将所有其他属性强制转换为数字,正如您猜对的那样。

What else does it do?

没什么,就是这样。

Why is it necessary?

即使在您的 CSV 或 TSV 中,值是数字,d3.csvd3.tsv 也会使用 字符串 填充对象,而不是数字。它们需要转换为数字。

关于 date 很明显它们需要被解析。

解释

d3.csvd3.tsv 接受行转换函数。该函数具有三个已定义的参数:

If a row conversion function is specified, the specified function is invoked for each row, being passed an object representing the current row (d), the index (i) starting at zero for the first non-header row, and the array of column names.

也就是说,在 type 函数中:

  • d 是代表每一行的整个对象
  • _ 是该对象的索引,
  • columns 是标题行。

在您的例子中,这是:

["date", "New York", "San Francisco", "Austin"]

现在,让我们分析一下 for... 循环。

变量i从1开始(避免date)到3结束。对于每个循环,它都有一个属性...

d[c = columns[i]]

...并将其值(字符串)强制转换为数字:

columns[i]] = +d[c];

因此,例如,在第一个循环中,当 i = 1 时,会发生以下情况:

d[c = "New York"] = +d[c];

所有其他属性也是如此。然后,对于下一行(对象),for.. 再次循环,依此类推,直到数据数组的末尾。

这是一个只有三行的演示:

var parseTime = d3.timeParse("%Y%m%d");
var data = d3.csvParse(d3.select("#csv").text(), type);

function type(d, _, columns) {
  d.date = parseTime(d.date);
  for (var i = 1, n = columns.length, c; i < n; ++i) {
    d[c = columns[i]] = +d[c];
    console.log("row is " + _ +",columns[i] is " + columns[i] + ", +d[c] is " + (+d[c]))
  }
  return d;
}
pre {
  display: none;
}
<script src="//d3js.org/d3.v4.min.js"></script>
<pre id="csv">date,New York,San Francisco,Austin
20111001,63.4,62.7,72.2
20111002,58.0,59.9,67.7</pre>

PS:我建议您编辑问题的标题。标题是问题中最重要的部分,96.38% 的用户(来源:FakeData Inc.)只看了标题。现在,您的标题与您的真实问题不符。因此,它可能是“行函数在这段代码中做了什么?” 或类似这样的内容。

关于javascript - 行函数在此 D3 多系列折线图中起什么作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42639779/

相关文章:

javascript - 可排序用户列表最佳实践

d3.js - D3 (v4) : update pattern multiple elements in group

javascript - '[string, string] 类型的参数错误? (在 Angular 和 d3 中)

r - 使用条形图可视化与目标值的差异

python - 使用 Dash/Plotly 绘制分类散点图

javascript - 交互式 JavaScript 栏

javascript - 使用javascript旋转和取消旋转图像onClick

javascript - React Native 需要一个字符串或类函数,但未定义

javascript - 如何更新d3中的子元素?

java - Java 中用于显示数据的简单可视化库