Как использовать точечную диаграмму со сложными данными?

1

Я пытаюсь реализовать диаграмму рассеяния на примере.

В примере мы можем видеть, как создается измерение:

runDimension = ndx.dimension(function(d) {return [+d.Expt, +d.Run]; });

Пример данных:

Expt Run Speed
1    1   850
1    2   740
1    3   900

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

[
 {
  "Timestamp":"2016-12-15T17:29:53Z",
  "idgame":"euro",
  "users":{
    "Jo": {
      "energy":200,
      "jump_height":0.5
     },
    "Bob": {
      "energy":220,
      "jump_height":0.35
     }
  }
 },
 {
  "Timestamp":"2016-12-15T17:29:55Z",
  "idgame":"euro",
  "users":{
    "Jo": {
      "energy":120,
      "jump_height":0.15
     },
    "Bob": {
      "energy":240,
      "jump_height":0.75
     }
  }
 }
]

Мне нужно построить следующую диаграмму, где ось x - это timestamp а ось y - jump_height:

Изображение 174551

Мои данные уже в crossfilter, поэтому я не могу его изменить. Как я могу создать хорошее измерение в текущем формате?

  • 0
    Что вы подразумеваете под "мои данные уже в перекрестном фильтре"? Crossfilter принимает массив объектов, поэтому мне не ясно, как эти данные уже находятся в Crossfilter.
  • 0
    Я использую этот тип данных на многих других графиках с одним перекрестным фильтром, и у меня уже есть некоторая логика для создания измерений для них. Если я изменю свои данные перед добавлением в перекрестный фильтр, мне нужно будет переписать измерение и создать группу для других диаграмм
Показать ещё 6 комментариев
Теги:
dc.js
crossfilter

1 ответ

1
Лучший ответ

Я все еще не убежден, что это стоит усилий, против кусания пули и сглаживания ваших данных и фиксации других карт. Если ваши данные не плоские, вы будете бороться с crossfilter и dc.js на каждом шагу.

При этом, как обычно, это возможно!

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

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

Во-первых, нам нужно проанализировать эти временные метки:

data.forEach(function(d) {
  d.Timestamp = new Date(d.Timestamp);
});

Затем мы получим список всех пользователей, вытаскивая пользовательские ключи из каждого события (timestamp), объединяя их, а затем используя набор d3.set для их уникальности:

// retrieve all users from all events
var users = data.reduce(function(p, event) {
  return p.concat(Object.keys(event.users));
}, []);
users = d3.set(users).values();

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

Мы создадим диаграмму, кроссфильтр и масштаб, который будет присвоить пользователям символы:

var chart = dc.compositeChart("#test");
var ndx = crossfilter(data);
var symbolScale = d3.scale.ordinal().range(d3.svg.symbolTypes);

Теперь мы можем создать составную диаграмму. (В следующем шаге мы добавим диаграммы рассеяния.)

chart
  .width(768)
  .height(480)
  .x(d3.time.scale())
  .xUnits(d3.time.seconds)
  .elasticX(true)
  .elasticY(true)
  .brushOn(false)
  .clipPadding(10)
  .shareTitle(false) // allow default scatter title to work
  .shareColors(true) // but use different colors for subcharts
  .legend(dc.legend().x(350).y(350).itemHeight(13)
          .gap(5).horizontal(1).legendWidth(140).itemWidth(70));

Мы установили ось X с временной шкалой с разрешением секунды. Обе оси имеют эластичную форму. Нам нужно обмениваться цветами, чтобы каждому подзаголу был присвоен собственный цвет. (Легенда, возможно, слишком высока - я скопировал это из другого примера.)

Наконец мы добираемся до мяса. Для каждого пользователя мы создадим подзаголовок, и мы расскажем составной диаграмме, чтобы составить все из них:

chart.compose(users.map(function(user) {
  var userDimension = ndx.dimension(function(d) {
    return [d.Timestamp, d.users[user].jump_height];
  })
  var jumpGroup = userDimension.group();
  console.log(user, jumpGroup.all());
  var scatter = dc.scatterPlot(chart)
    .symbol(symbolScale(user))
    .dimension(userDimension)
    .group(jumpGroup)
    .colorAccessor(function() { return user; })
    .symbolSize(8)
    .highlightedSize(10);
  return scatter;
}))

Мы создаем новое измерение для каждого графика. Это связано с тем, что график рассеяния dc.js предполагает, что ключи включают в себя как координаты X, так и Y, и мы можем получить доступ только к координате Y (jump_height), как только мы узнаем пользователя. Как только мы закончим это, группа проста.

График назначит символ и цвет на основе пользовательского ключа. Они оба работают одинаково; порядковый масштаб присваивает новое значение из диапазона для каждого нового значения, которое он встречает в домене. Единственное различие заключается в том, что мы используем цветовую шкалу по умолчанию, тогда как нам пришлось указывать нашу собственную шкалу символов.

Здесь скрипка: https://jsfiddle.net/gordonwoodhull/3m4mv3xf/19/

  • 0
    Спасибо, это прекрасно. В вашей скрипке присутствует интересная ошибка, когда вы загружаете страницу - вы можете видеть крестики и кружки в виде символов на графике, но после mouseOver на некоторых легендах символы меняются на одну фигуру. Можете ли вы помочь мне исправить это?
  • 0
    Вау, это странно. Я смотрю на это.
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню