Сортировка по многомерной гистограмме набора данных - PullRequest
1 голос
/ 29 июня 2019

Я пытаюсь создать гистограмму, используя d3 с набором данных, содержащим наборы из 3 значений. Первое значение - это высота полосы, второе - ширина полосы, а третье - непрозрачность. Моя цель сейчас - иметь возможность сортировать по высоте, ширине, а затем по непрозрачности в цикле, используя щелчок. Однако я чувствую, что получаю, казалось бы, случайные результаты после первого клика.

Я должен использовать операцию модуля, поэтому я устанавливаю var s = -1 вне функции click, а затем s = (s + 1)% 3. Внутри функции сортировки я возвращаю d3.ascending(a[s],b[s]);.

Это мой код для клика и сортировки.

var s = -1;
svg.on("click", function() {
    s = (s + 1)
    svg.selectAll("rect")
        .data(dataset)
        .sort(function(a, b) {
            return d3.ascending(a[s], b[s]);
        })
        .transition()
        .duration(500)
        .attr("x", function(d, i) {
            return xScale(i);
        })
});

Я ожидал сортировку по высоте, ширине и непрозрачности. Я получаю высоту при первом клике, но следующие клики кажутся случайными.

1 Ответ

0 голосов
/ 29 июня 2019

Проблема заключается в такой, казалось бы, безобидной привязке данных:

svg.selectAll("rect")
    .data(dataset)// <----- here
    .sort(function(a, b) {
    //etc...

Проблема в том, что вы добавляете данные к ранее отсортированным прямоугольникам по их индексам .Это мешает с сортировкой.

Вот простое объяснение.Предположим, у вас есть 5 элементов (в верхнем регистре), соответствующих массиву данных [d, a, e, c, b] (в нижнем регистре), который вы хотите отсортировать по алфавиту:

+------+------+------+------+------+
| D(d) | A(a) | E(e) | C(c) | B(b) | 
+------+------+------+------+------+

После сортировки у вас есть:

+------+------+------+------+------+
| A(a) | B(b) | C(c) | D(d) | E(e) | 
+------+------+------+------+------+

При повторном связывании данных (помните, тот же массив [d, a, e, c, b], в этом порядке) с уже отсортированными элементами, у вас есть:

+------+------+------+------+------+
| A(d) | B(a) | C(e) | D(c) | E(b) | 
+------+------+------+------+------+

Если вы отсортируете по данным (что и делает selection.sort()), у вас будет:

+------+------+------+------+------+
| B(a) | E(b) | D(c) | A(d) | C(e) | 
+------+------+------+------+------+

И поэтому кажется, что random , как вы сказали.

Существует два решения:

Решение 1: сбросить привязку данных

Самое простое - просто сбросить привязку данных, поскольку у прямоугольников уже есть данные.Вот пример с 3 прямоугольниками, например, каждый из которых имеет наибольшую высоту, ширину или непрозрачность:

var data = [
  [45, 90, 0.5],
  [95, 10, 0.2],
  [15, 40, 0.9]
];

var xScale = d3.scaleOrdinal()
  .domain([0, 1, 2])
  .range([0, 100, 200]);

var svg = d3.select("svg");

var span = d3.select("span")

svg.selectAll(null)
  .data(data)
  .enter()
  .append("rect")
  .attr("width", d => d[1])
  .attr("height", d => d[0])
  .style("opacity", d => d[2])
  .attr("x", (_, i) => xScale(i));

var s = -1;
svg.on("click", function() {
  s = (s + 1);
  span.html(["height", "width", "opacity"][s % 3]);
  svg.selectAll("rect")
    .sort(function(a, b) {
      return d3.ascending(a[s % 3], b[s % 3]);
    })
    .transition()
    .duration(500)
    .attr("x", function(d, i) {
      return xScale(i);
    })
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<p>Sort by: <span></span></p>
<svg></svg>

Решение 2: использование ключевой функции

Второе решение - использование ключевой функции , поэтому вы привязываете данные не по их индексам, а по уникальному идентификатору для каждого прямоугольника.

В моих фиктивных данных каждое значение высоты уникально, поэтому я просто делаю:

.data(dataset, d=>d[0])

Вот демоверсия:

var data = [
  [45, 90, 0.5],
  [95, 10, 0.2],
  [15, 40, 0.9]
];

var xScale = d3.scaleOrdinal()
  .domain([0, 1, 2])
  .range([0, 100, 200]);

var svg = d3.select("svg");

var span = d3.select("span")

svg.selectAll(null)
  .data(data)
  .enter()
  .append("rect")
  .attr("width", d => d[1])
  .attr("height", d => d[0])
  .style("opacity", d => d[2])
  .attr("x", (_, i) => xScale(i));

var s = -1;
svg.on("click", function() {
  s = (s + 1);
  span.html(["height", "width", "opacity"][s % 3]);
  svg.selectAll("rect")
  	.data(data, d=>d[0])
    .sort(function(a, b) {
      return d3.ascending(a[s % 3], b[s % 3]);
    })
    .transition()
    .duration(500)
    .attr("x", function(d, i) {
      return xScale(i);
    })
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<p>Sort by: <span></span></p>
<svg></svg>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...