【问题标题】:Extra legend present额外的传奇礼物
【发布时间】:2019-11-01 22:06:33
【问题描述】:

我在条形图中添加了两个图例,但我不知道为什么会出现额外的图例。我不知道我的代码的哪一部分是错误的,因为我在我的代码中只定义了两个图例。

var color_hash = {  0 : ["Male", "blue"],
                    1 : ["Female", "pink"]}

var legend = svg.append("g")
            .attr("class", "legend")
            .attr("x", width - 65)
            .attr("y", 25)
            .attr("height", 100)
            .attr("width", 100);

legend.selectAll('g').data(data)
      .enter()
      .append('g')
      .each(function(d, i) {
        var g = d3.select(this);
        g.append("rect")
         .attr("x", width - 65)
         .attr("y", i*25)
         .attr("width", 10)
         .attr("height", 10)
         .style("fill", color_hash[String(i)][1]);
        g.append("text")
         .attr("x", width - 50)
         .attr("y", i * 25 + 8)
         .attr("height", 30)
         .attr("width", 100)
         .style("fill", color_hash[String(i)[1]])
         .text(color_hash[String(i)][0]);

      });

黑色矩形是多余的:

【问题讨论】:

  • 尝试检查 HTML 以查看额外元素的结构。这可能有线索?另一个想法是 console.log() 正在创建图例时的数据...是否存在男性或女性以外的数据值(例如 null?)?
  • @devlincarnate 传说来自 color_hash 对吧?那里只有女性和男性
  • data 数组中有多少项?

标签: javascript d3.js visualization


【解决方案1】:

使用 D3 中的进入/更新/退出循环,您通常希望拥有一个数据数组,其中包含您要绘制的每个元素的一项。你有:

  • 颜色有对象color_hash这是你真正想用来绘制图例的对象
  • 一些数据数组data,虽然我们不知道里面是什么。

我们正在使用data 来可视化color_hash,这并不理想。

一方面,你只想绘制2个元素,我可以告诉你data的长度至少是3:

你创建一个空的g

var legend = svg.append("g")

然后你选择子 g 的那个元素:

legend.selectAll('g')

因为没有,所以这是一个空选择。然后将数据分配给该选择并输入新的 HTML/SVG 元素:

legend.selectAll('g')
  .data(data)
  .enter()
  .append('g')

由于legend 是一个空选择,因此输入选择将为数据数组中的每个项目创建一个 HTML/SVG 元素。进入(和/或退出)后,HTML/SVG 元素的数量应等于数据数组中的项目数。因此,data 必须至少包含 3 个项目(它可能有更多 if 附加元素被创建,但它们超出了 SVG/容器边界。这也解释了为什么第三个框有没有颜色或文本:颜色哈希没有键为 2 或更大的值)。

D3 是从数据中创建元素,通常在元素和项目之间是一对一的关系。为了创建我们的图例,数据数组应该是我们想要绘制的。因此,我们需要将颜色哈希转换为数组:

var legendData = [
  {name: "A", color:"crimson"},
  {name: "B", color:"steelblue"}
];

现在我们只需将其提供给selection.data()

而且,由于我们现在将要绘制的数据绑定到图例条目,我们还可以简化代码,而不是:

 .style("fill", color_hash[String(i)][1]);

 .text(color_hash[String(i)][0]);

我们可以使用:

  .style("fill",d.color);

  .text(d.name);

这给了我们:

var color_hash = {  0 : ["Male", "blue"],
                    1 : ["Female", "pink"]}

var width = 300;
var height = 200;

var svg = d3.select("svg")
  .attr("width",width)
  .attr("height",height);

var legendData = [
  {name:"A",color:"crimson"},
  {name:"B",color:"steelblue"}
]

var legend = svg.append("g")

legend.selectAll('g')
  .data(legendData)
  .enter()
  .append('g')
  .each(function(d, i) {
        var g = d3.select(this);
        g.append("rect")
         .attr("x", width - 65)
         .attr("y", i*25+25)
         .attr("width", 10)
         .attr("height", 10)
         .style("fill", d.color);
        g.append("text")
         .attr("x", width - 50)
         .attr("y", i * 25 + 33)
         .attr("height", 30)
         .attr("width", 100)
         .style("fill", d.color)
         .text(d.name);

      });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

我在这里只关注输入循环:可能会对放置进行进一步修改,并且与使用 .each 附加子级相比,嵌套附加的不同方法可以提供好处

【讨论】:

  • 在我的数据文件中,有四列,分别是年份、性别、年龄和人口。在性别列中,只有两种值,男性和女性,我想根据性别绘制图例,男性为蓝色,女性为粉色。如何在代码中呈现性别列而不是整个数据?
  • 您需要使用两个图例项创建一个新的数据数组(无论是通过过滤/映射/以其他方式操作绘图的数据数组,还是像答案中那样手动创建它) - 这种方式。 enter 将仅创建两个图例条目,因为 selection.enter() 旨在将数据中的每一行与一个 html 元素匹配。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-15
  • 1970-01-01
  • 2021-10-27
  • 2014-05-04
  • 1970-01-01
相关资源
最近更新 更多