d3 несколько кругов на тик из сгруппированного массива данных

#d3.js

#d3.js

Вопрос:

Я пытаюсь очень быстро освоить d3, и я сильно зацикливаюсь на выборе и объединении.

Я хочу иметь возможность рисовать ось с точками для каждого элемента массива. Некоторые элементы массива имеют одинаковое значение x, но я все равно хочу видеть столько точек, сколько есть с этим значением. Мой массив (в реакции с useState) выглядит так:

 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 есть два значения, и я хочу нарисовать желтую точку, затем фиолетовую точку ниже, на галочке оси x с надписью «2031».

Итак, я группирую свои данные с помощью этой функции уменьшения (украденной из 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)
  

Что я хотел бы сделать, так это нарисовать круг для каждого из вложенных массивов с заливкой цвета, указанного в этом массиве — это не работает?

Кто-нибудь может объяснить?

Ответ №1:

Нет необходимости группировать ваши данные. Вы можете просто видеть 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>