【问题标题】:d3 appears to silently truncate array in .data() methodd3 似乎在 .data() 方法中默默地截断数组
【发布时间】:2017-11-12 14:40:03
【问题描述】:

通过一些 D3 示例,我相信已经发现了一个我无法解释的 d3 错误或行为。

D3 在调用 .data() 方法时似乎截断了数组。

<script>

    d3.csv('medium_january.csv', function(error, data) {
      if (error) {
          console.error('Error getting or parsing the data.');
          throw error;
      }
      var chart = bubbleChart().width(600).height(400);
      d3.select('#chart').data(data).call(chart);
      //here data has 43 elements with the 1st beginning "how flex...
    });

</script>

在气泡图.js中

function bubbleChart() {
    var width = 960,
        height = 960,
        maxRadius = 6,
        columnForColors = "category",
        columnForRadius = "views";

    function chart(selection) {
        var data = selection.enter().data();
//here data has 42 elements with the first being "How I went from zero


        var div = selection,
            svg = div.selectAll('svg');
        svg.attr('width', width).attr('height', height);
        //rest of script

这里的工作示例

https://bl.ocks.org/dmesquita/37d8efdb3d854db8469af4679b8f984a

问题出现在bubblechart.js

如果第一个块中数据的条目数为43,则在chart()函数中变为42。 selection.enter() 的长度是 43,但是在 .data() 之后我们只有 42 个条目。

我尝试使用不同的数据集,认为错误的数据可能会迫使 .data() 失败,但使用其他数据得到相同的结果。如果有坏数据,我会期待一个错误,而不是默默地跳过。

为什么 .data() 会默默地截断第一个数组值?

【问题讨论】:

  • 我编辑了示例以显示数组被截断的位置。

标签: javascript arrays d3.js


【解决方案1】:

我写第二个答案只是为了向您展示 Mark's answer 确实是正确的,并解释了这里发生的事情(我不是 来自权威的争论的忠实粉丝,但如果你点击在D3 top users,你会看到马克是所有时间的最佳回答者之一......当然没有人是完美的,但认真对待最佳回答者的意见并仔细研究他/她是什么总是一个好主意说)。

输入选择的大小

显然,您的数据数组有 43 个元素。这不会改变,除非您使用修改该数组的方法。

但这里的问题是您测量的是“输入”选择的大小,而不是数据数组的大小。而data() 绝对不会改变数据数组的大小。

话虽如此,让我们看看什么是“输入”选择。这张图片提供了一个很好的解释:

简而言之,“输入”选择包含所有与 DOM 元素不对应的数据元素。

但是,在您的代码中,“输入”选择的数据数组大小不同,因为正如 Mark 所说,您将数据绑定到已经存在的 DOM 元素。我们可以通过修改您的代码来展示这个...

d3.select('#chart').data(data).call(chart);

...到这个,也是一样的:

var sel = d3.select('#chart').data(data);
chart(sel);

现在,使用sel,我们可以比较数据数组的大小和输入选择的大小,你调用`chart:

console.log("the data array has " + data.length + " elements");
console.log("the enter selection has " + sel.enter().size() + " elements");

这将是结果:

the data array has 43 elements
the enter selection has 42 elements

这里是修改后的 bl.ocks,看看控制台:https://bl.ocks.org/anonymous/5204c7ee3e4e15a2b1bbfd7633b1deb6

如果您仍然怀疑您的第一个数据是否绑定到该 div,请执行...

console.log(d3.select("#chart").datum())

你会看到这个:

Object {
    title: "How Flexbox works — explained with big, colorful, animated gifs", 
    category: "Design", 
    views: "5700"}

输入选择:一个实际的例子

很多人对data() 感到困惑的是,它根据成功绑定到数据的元素定义了“进入”和“退出”选择。

话虽如此,人们倾向于认为,要创建 div,您必须 selectAll("div"),或者要创建段落,您必须 selectAll("p"),如:

selection.selectAll("p")
    .data(data)
    .enter()
    .append("p")
    etc...

这是不正确的。实际上,如果您在data() 函数之前selectAll("p"),您将面临选择该页面中任何现有段落的风险,并且您的“输入”选择将比应有的短。

例如,看看这个例子:

var data = ["foo", "bar", "baz"];
var body = d3.select("body");

var newDivs = body.selectAll(null)
  .data(data)
  .enter()
  .append("div")
  .html(function(d){ return d})
<script src="https://d3js.org/d3.v4.min.js"></script>
<div>Hello</div>

当我使用selectAll(null) 时,我的“输入”选择包含我的数据数组的所有元素,而不管该页面中是否已经存在一个 div。

现在,看看另一个例子:

var data = ["foo", "bar", "baz"];
var body = d3.select("body");

var newDivs = body.selectAll("div")
  .data(data)
  .enter()
  .append("div")
  .html(function(d){ return d})
<script src="https://d3js.org/d3.v4.min.js"></script>
<div>Hello</div>

如您所见,由于该页面中已经有一个 div,当我在 data() 函数之前执行 selectAll("div") 时,我的第一个数据元素(“foo”)绑定到该 div,我的“输入”选择最终只有两个 div。

【讨论】:

  • @Gerado 感谢您的澄清。所以原始代码错误地绘制了输入选择而不是数据?
  • @stimpy 代码没有错误地绘制输入选择而不是数据。与任何 D3 代码一样,它使用“输入”选择附加元素。问题是您定义输入选择的方式。此外,该代码中的数据绑定不是惯用的 D3。
  • @Gerado 不是我的代码,而是一个使用/结果令人困惑的示例。 bubble chat.js 绘制了 var "data" 的图表。如果我理解正确的话,绘制的数据不是实际的 cvs 数据,而是从 select 传递给 bubble chart.js 的“非 dom 绑定”数据。是这样吗?
  • 是的,那个bubble_chart.js中的data不是d3.csv函数创建的数据。
【解决方案2】:

这里发生的情况是,由于您使用了.select('#chart').data(data),因此数组中的第一项正在数据绑定到您的div.enter() 然后只是返回数组的其余部分。这是一种使用数据绑定的奇怪方式。本质上,您所追求的只是一种将数据传递到函数中的方法。这可以通过更传统的方式完成:

d3.select('#chart').call(chart, data);

你的函数定义变成:

function chart(selection, data) {
    var div = selection,
        svg = div.selectAll('svg');
    svg.attr('width', width).attr('height', height);

    ...

更新block

对评论的回应

抱歉,如果我不清楚。您在页面上有一个带有id 图表的元素。然后将数据绑定到它。由于元素存在,因此数组中的第一项绑定到它。然后.enter() 选择,这将返回数组的其余部分,表示 具有现有元素的项目。这有意义吗?

【讨论】:

  • 感谢更清晰的功能,但它不能解释为什么我们会丢失数据元素。第一个 csv 条目消失了第一个数组项应该是“Flexbox ...”而不是。
  • 我明白你在说什么,但不清楚为什么数据会丢失数组元素。不是 div 而是 data 的元素。如果您查看我发布的示例链接,请在我在代码示例中注释的点处设置断点。比较断点之间的数据。第一个数组元素不同,数组短一个。
  • @stimpy,我明白你在说什么。你的元素根本没有丢失。 d3 数据绑定就是将数据绑定到dom 元素。您最初的.data 调用将您的数据绑定到您的divdiv 存在第一个数组元素绑定到它。 .enter() 然后返回尚未绑定的数据项。试试selection.data(),这是update 选择(那些已经存在的项目),它会返回一个数组1;你的第一个项目。
  • 这并不能解释为什么我们开始使用的 csv 数据会丢失原始代码示例中的一个元素。在没有发生绑定的情况下,我们不会绑定到 svg,直到后面的行,我们的数据数组已经丢失了它的第一个元素。
  • @stimpy Mark 的回答确实解释了为什么 在输入选择中少了一个元素。然而,数据数组保持不变。
猜你喜欢
  • 1970-01-01
  • 2010-11-13
  • 2021-08-22
  • 2022-11-17
  • 2012-04-20
  • 2014-02-07
  • 2010-09-24
  • 1970-01-01
  • 2017-09-14
相关资源
最近更新 更多