Как я могу получить исходную строку данных в ad c. js boxplot data-point? - PullRequest
1 голос
/ 06 мая 2020

Я использую d c. js для создания Boxplot, который работает должным образом. Чтобы добавить больше интерактивности к диаграмме, я хотел дать пользователю возможность щелкать выбросы для получения дополнительной информации.

Поэтому я создал обработчик событий, как это объясняется в документации D3 . Результат работает, как ожидалось, и я получаю событие, запускаемое, когда пользователь нажимает на точку данных. Я ожидал, что каким-то образом я смогу получить доступ к исходным данным для извлечения атрибута sf c в выбранной точке данных, но я потерпел неудачу и в настоящее время не знаю, как решить эту проблему. Любая помощь будет принята с благодарностью.

const data = [{
    "duration": 248,
    "type": "M248",
    "sfc": "M248BJ0L2809783",
    "pass": 1
  },
  {
    "duration": 249,
    "type": "M248",
    "sfc": "M248BK0L2809676",
    "pass": 1
  },
  {
    "duration": 156,
    "type": "M248",
    "sfc": "M248BK0L2809676",
    "pass": 1
  },
  {
    "duration": 254,
    "type": "M248",
    "sfc": "M248BP0L2809798",
    "pass": 1
  },
  {
    "duration": 134,
    "type": "M248",
    "sfc": "M248BJ0L2809783",
    "pass": 1
  },
  {
    "duration": 128,
    "type": "M248",
    "sfc": "M248BP0L2809798",
    "pass": 0
  },
  {
    "duration": 228,
    "type": "M248",
    "sfc": "M248B90L2809800",
    "pass": 0
  },
  {
    "duration": 125,
    "type": "M248",
    "sfc": "M248B90L2809800",
    "pass": 0
  },
  {
    "duration": 242,
    "type": "M248",
    "sfc": "M248BJ0L2809792",
    "pass": 1
  },
  {
    "duration": 149,
    "type": "M248",
    "sfc": "M248BJ0L2809792",
    "pass": 1
  },
  {
    "duration": 237,
    "type": "M248",
    "sfc": "M248BJ0L2809819",
    "pass": 1
  },
  {
    "duration": 153,
    "type": "M248",
    "sfc": "M248BJ0L2809819",
    "pass": 1
  },
  {
    "duration": 232,
    "type": "M248",
    "sfc": "M248BK0L2809847",
    "pass": 1
  },
  {
    "duration": 482,
    "type": "M248",
    "sfc": "M248BK0L2809847",
    "pass": 1
  },
  {
    "duration": 238,
    "type": "M248",
    "sfc": "M248BK0L2809883",
    "pass": 1
  },
  {
    "duration": 143,
    "type": "M248",
    "sfc": "M248BK0L2809883",
    "pass": 1
  },
  {
    "duration": 213,
    "type": "M247",
    "sfc": "M247B50L2693004",
    "pass": 1
  },
  {
    "duration": 217,
    "type": "M247",
    "sfc": "M247B50L2693004",
    "pass": 0
  },
  {
    "duration": 229,
    "type": "M248",
    "sfc": "M248BC0L2809902",
    "pass": 1
  },
  {
    "duration": 151,
    "type": "M248",
    "sfc": "M248BC0L2809902",
    "pass": 0
  }
];

const cycletimeChart = dc.boxPlot('#cycletime-chart');

const ndx = crossfilter(data),
  typeDimension = ndx.dimension(function(d) {
    return d.type;
  }),
  cycletimeGroupByType = typeDimension.group().reduce(function(p, v) {
    // keep array sorted for efficiency
    p.splice(d3.bisectLeft(p, v.duration), 0, v.duration);
    return p;
  }, function(p, v) {
    p.splice(d3.bisectLeft(p, v.duration), 1);
    return p;
  }, function() {
    return [];
  });

cycletimeChart
  .dimension(typeDimension)
  .group(cycletimeGroupByType)
  .on('pretransition', function(chart) {
    chart.selectAll('circle.outlier').on('click.sfcClick', function(datum, index, nodes) {
      console.log(`Clicked on outlier with array index ${datum}, ${index}, ${nodes}.`);
      //Here I would like to retrieve the the sfc attribute from the original data object.
    });
  });
cycletimeChart.render();
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Boxplot test</title>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/d3.min.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.min.js"></script>
  <script type="text/javascript" src="https://unpkg.com/dc@4/dist/dc.js"></script>
  <link rel="stylesheet" type="text/css" href="https://unpkg.com/dc@4/dist/style/dc.css">
</head>

<body>
  <div id="cycletime-chart"></div>

</body>

</html>

1 Ответ

0 голосов
/ 06 мая 2020

Здесь есть три проблемы:

  1. Поскольку кросс-фильтр объединяет данные, будет доступно только то, что вы предоставите в функции сокращения.
  2. Коробчатая диаграмма связывает индекс массива с выбросами, а не с данными.
  3. У вас возникнут проблемы с фильтрацией, если эти длительности не уникальны.

1. Передача всей строки через кросс-фильтр

Поскольку кросс-фильтр предназначен для агрегирования, исходные данные по умолчанию недоступны. Один из обходных путей - изменить сокращение для хранения v вместо v.duration:

  bisectLeft = d3.bisector(d => d.duration).left,
  cycletimeGroupByType = typeDimension.group().reduce(function(p, v) {
    // keep array sorted for efficiency
    p.splice(bisectLeft(p, v.duration), 0, v);
    return p;
  }, function(p, v) {
    p.splice(bisectLeft(p, v.duration), 1);
    return p;
  }, function() {
    return [];
  });

Поскольку dc.boxPlot ожидает массив чисел, вам также необходимо изменить valueAccessor для извлечения длительности:

  .valueAccessor(d => d.value.map(r => r.duration))

2. Получение данных с использованием индекса массива

Как описано в Выравнивание и цвета выбросов точек данных на рамочной диаграмме , блочная диаграмма d c. js связывает индекс массива до точек выброса.

Поскольку пара ключ / значение кроссфильтра привязана к родительскому элементу <g>, исходные данные можно получить с помощью заклинания

d3.select(this.parentNode).datum().value[datum]

3. Неуникальные длительности

На первом этапе мы начали сохранять строки данных при сокращении, а не только длительности. Если эти длительности не уникальны, возникает проблема: неправильное значение может быть удалено из массива при фильтрации строки.

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

  function(p, v) {
    let i = d3.bisectLeft(p, v.duration);
    while(p[i] !== v) ++i;
    p.splice(i, 1);
    return p;
  }

Предупреждение: это не проверено, поскольку в вашем примере нет фильтрации.

const data = [{
    "duration": 248,
    "type": "M248",
    "sfc": "M248BJ0L2809783",
    "pass": 1
  },
  {
    "duration": 249,
    "type": "M248",
    "sfc": "M248BK0L2809676",
    "pass": 1
  },
  {
    "duration": 156,
    "type": "M248",
    "sfc": "M248BK0L2809676",
    "pass": 1
  },
  {
    "duration": 254,
    "type": "M248",
    "sfc": "M248BP0L2809798",
    "pass": 1
  },
  {
    "duration": 134,
    "type": "M248",
    "sfc": "M248BJ0L2809783",
    "pass": 1
  },
  {
    "duration": 128,
    "type": "M248",
    "sfc": "M248BP0L2809798",
    "pass": 0
  },
  {
    "duration": 228,
    "type": "M248",
    "sfc": "M248B90L2809800",
    "pass": 0
  },
  {
    "duration": 125,
    "type": "M248",
    "sfc": "M248B90L2809800",
    "pass": 0
  },
  {
    "duration": 242,
    "type": "M248",
    "sfc": "M248BJ0L2809792",
    "pass": 1
  },
  {
    "duration": 149,
    "type": "M248",
    "sfc": "M248BJ0L2809792",
    "pass": 1
  },
  {
    "duration": 237,
    "type": "M248",
    "sfc": "M248BJ0L2809819",
    "pass": 1
  },
  {
    "duration": 153,
    "type": "M248",
    "sfc": "M248BJ0L2809819",
    "pass": 1
  },
  {
    "duration": 232,
    "type": "M248",
    "sfc": "M248BK0L2809847",
    "pass": 1
  },
  {
    "duration": 482,
    "type": "M248",
    "sfc": "M248BK0L2809847",
    "pass": 1
  },
  {
    "duration": 238,
    "type": "M248",
    "sfc": "M248BK0L2809883",
    "pass": 1
  },
  {
    "duration": 143,
    "type": "M248",
    "sfc": "M248BK0L2809883",
    "pass": 1
  },
  {
    "duration": 213,
    "type": "M247",
    "sfc": "M247B50L2693004",
    "pass": 1
  },
  {
    "duration": 217,
    "type": "M247",
    "sfc": "M247B50L2693004",
    "pass": 0
  },
  {
    "duration": 229,
    "type": "M248",
    "sfc": "M248BC0L2809902",
    "pass": 1
  },
  {
    "duration": 151,
    "type": "M248",
    "sfc": "M248BC0L2809902",
    "pass": 0
  }
];

const cycletimeChart = dc.boxPlot('#cycletime-chart');

const ndx = crossfilter(data),
  typeDimension = ndx.dimension(function(d) {
    return d.type;
  }),
  bisectLeft = d3.bisector(d => d.duration).left,
  cycletimeGroupByType = typeDimension.group().reduce(function(p, v) {
    // keep array sorted for efficiency
    p.splice(bisectLeft(p, v.duration), 0, v);
    return p;
  }, function(p, v) {
    let i = bisectLeft(p, v.duration);
    while(p[i] !== v) ++i;
    p.splice(i, 1);
    return p;
  }, function() {
    return [];
  });

cycletimeChart
  .dimension(typeDimension)
  .group(cycletimeGroupByType)
  .valueAccessor(d => d.value.map(r => r.duration))
  .on('pretransition', function(chart) {
    chart.selectAll('circle.outlier').on('click.sfcClick', function(datum, index, nodes) {
      console.log(`Clicked on outlier with datum, index, nodes ${datum}, ${index}, ${nodes}.`);
      console.log('parent array', d3.select(this.parentNode).datum().value);
      console.log(`Original datum`, d3.select(this.parentNode).datum().value[datum]);
      //Here I would like to retrieve the the sfc attribute from the original data object.
    });
  });
cycletimeChart.render();
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Boxplot test</title>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/d3.min.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.min.js"></script>
  <script type="text/javascript" src="https://unpkg.com/dc@4/dist/dc.js"></script>
  <link rel="stylesheet" type="text/css" href="https://unpkg.com/dc@4/dist/style/dc.css">
</head>

<body>
  <div id="cycletime-chart"></div>

</body>

</html>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...