首页 > 解决方案 > d3 来自分组数据数组的每个刻度的多个圆圈

问题描述

我正在尝试非常快速地学习 d3,但我在选择和加入方面陷入了困境。

我希望能够为数组的每个成员绘制一个带有点的轴。一些数组成员具有相同的 x 值,但我仍然希望看到与该值一样多的点。我的数组(在带有 useState 的 React 中)看起来像这样:

const [data, setData] = useState(
    [
      {x: 2020, colour: "purple", y1: 0.001, y2: 0.63},
      {x: 2027, colour: "red", y1: 0.003, y2: 0.84},
      {x: 2031, colour: "yellow", y1: 0.024, y2: 0.56},
      {x: 2031, colour: "green", y1: 0.054, y2: 0.22},
      {x: 2040, colour: "blue", y1: 0.062, y2: 0.15},
      {x: 2050, colour: "orange", y1: 0.062, y2: 0.15}
    ]
);

您可以看到 2031 有两个值,我想在标记为“2031”的 x 轴刻度处画一个黄点,然后在下方画一个紫点。

所以我用这个 reduce 函数(从 SO 中窃取)对我的数据进行分组:

const dot = data.reduce(
    (r, v, _, __, k = v.x) => ((r[k] || (r[k] = [])).push(v), r),
    {}
);

...产生这个:

{ 2020: [{x: 2020, colour: "purple", y1: 0.001, y2: 0.63}],
  2027: [...] }

我启动我的 x 轴并为其创建一个占位符:

const g = d3.axisBottom( scX ).tickValues(
      data.map(d => {
        return d.x 
      })
    )

svg.append( "g" )
    .attr( "transform", "translate(" + 25 + "," + pxY/2 + ")")
    .call( g )
    .selectAll(".tick")

然后我想调用我的 dot 变量并遍历嵌套数组:

svg
    .selectAll(".tick")
    .call( dot )
    .append("circle")
    .attr("cx", 0)
    .attr("cy", 0)
    .attr("r", 5)
    .attr("fill", dot.colour)

我想做的是为每个嵌套数组绘制一个圆圈,并填充该数组中命名的颜色 - 这不起作用?

谁能解释一下?

标签: d3.js

解决方案


无需对数据进行分组。您可以将data其视为一个数组,其中每个元素都对应一个圆圈。多个圆圈可以以相同的x值存在,没有什么强制它们不能。

也不需要像那样设置轴刻度,d3 很可能会为您做所有事情。d3-axis绝对方便 - 您需要调整默认值,而不是在这里从头开始构建所有内容。

您需要了解数据连接,因为您显然也不知道可以使用function(d, i) { ... }(d, i) => ...以这种方式设置颜色来访问元素的数据。

const data = [{
    x: 2020,
    colour: "purple",
    y1: 0.001,
    y2: 0.63
  },
  {
    x: 2027,
    colour: "red",
    y1: 0.003,
    y2: 0.84
  },
  {
    x: 2031,
    colour: "yellow",
    y1: 0.024,
    y2: 0.56
  },
  {
    x: 2031,
    colour: "green",
    y1: 0.054,
    y2: 0.22
  },
  {
    x: 2040,
    colour: "blue",
    y1: 0.062,
    y2: 0.15
  },
  {
    x: 2050,
    colour: "orange",
    y1: 0.062,
    y2: 0.15
  }
];

const width = 600,
  height = 300;

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

const x = d3.scaleLinear()
  .domain(d3.extent(data, d => d.x))
  .range([50, 550]);

const y1 = d3.scaleLinear()
  .domain(d3.extent(data, d => d.y1))
  .range([275, 25]);

const y2 = d3.scaleLinear()
  .domain(d3.extent(data, d => d.y2))
  .range([3, 10]);

svg.selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("fill", d => d.colour)
  .attr("cx", d => x(d.x))
  .attr("cy", d => y1(d.y1))
  .attr("r", d => y2(d.y2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>


推荐阅读