Почему мой редуктор ведет себя по-разному между первым и последующими фильтрами, применяемыми в dc.js? - PullRequest
1 голос
/ 08 мая 2019

Я работаю над визуализацией данных , в которой есть странная маленькая ошибка:

gif of the data visualization

Это немного сложно увидеть, но по сути, когда я нажимаю на точку на линейном графике, эта точка соответствует определенной проблеме журнала. Обновления хороплета отражают геоданные для этой проблемы, но, что особенно важно, геоданные относятся к периоду выборки, соответствующему проблеме. По сути, хороплет будет выглядеть одинаково для любой проблемы в период с января по июнь или июль -Декабрь данного года.

Как видите, у меня есть ключ с именем Выборочная дата выпуска (для геоданных), и значение должно быть датой выпуска, для которого основаны геоданные (в основном, они получат географическое распределение для одной конкретной проблемы). и назовите его представителем ВСЕХ данных за шесть месяцев.) Тем не менее, когда я первоначально нажимаю на проблему, я всегда получаю последнюю дату выборки в моих данных. Все геоданные верны, и, к сожалению, все последующие клики отображают правильную информацию. Таким образом, проблема возникает только при первом щелчке (после обновления страницы или устранения проблемы).

Честно говоря, мой код сейчас является кошмаром, потому что я сосредоточен на отладке, но вы можете увидеть мой редуктор для функции удаления на GitHub , которая также копируется / вставляется ниже:

  // Reducer function for raw geodata
  function geoReducerAdd(p, v) {
    // console.log(p.sampled_issue_date, v.sampled_issue_date, state.periodEnding, state.periodStart)
    ++p.count
    p.sampled_mail_subscriptions += v.sampled_mail_subscriptions
    p.sampled_single_copy_sales += v.sampled_single_copy_sales
    p.sampled_total_sales += v.sampled_total_sales
    p.state_population = v.state_population // only valid for population viz
    p.sampled_issue_date = v.sampled_issue_date
    return p
  }

  function geoReducerRemove(p, v) {
    const currDate = new Date(v.sampled_issue_date)
    // if(currDate.getFullYear() === 1921) {
    //   console.log(currDate)
    // }
    currDate <= state.periodEnding && currDate >= state.periodStart ? console.log(v.sampled_issue_date, p.sampled_issue_date) : null
    const dateToRender = currDate <= state.periodEnding && currDate >= state.periodStart ? v.sampled_issue_date : p.sampled_issue_date
    --p.count
    p.sampled_mail_subscriptions -= v.sampled_mail_subscriptions
    p.sampled_single_copy_sales -= v.sampled_single_copy_sales
    p.sampled_total_sales -= v.sampled_total_sales
    p.state_population = v.state_population // only valid for population viz
    p.sampled_issue_date = dateToRender
    return p
  }

  // generic georeducer
  function geoReducerDefault() {
    return {
      count: 0,
      sampled_mail_subscriptions: 0,
      sampled_single_copy_sales: 0,
      sampled_total_sales: 0,
      state_population: 0,
      sampled_issue_date: ""
    }
  }

Проблема может быть где-то еще, но я не думаю, что это проблема перекрестного фильтра (я точно не сталкиваюсь с проблемой "две группы из одного измерения"), и добавление дополнительной логики в редуктор добавления делает вещи еще менее предсказуемым (понятно - мне все равно не нужно отображать дату выборки для всех значений). Дело в том, что я совершенно заблудился в том, в чем заключается недостаток в моей логике, и мне бы хотелось немного помощь!

EDIT: обратите внимание, что редукторы предназначены для метода reduce в измерении dc.js, а не для собственного редуктора javascript! : D

1 Ответ

1 голос
/ 10 мая 2019

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

Проблема

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

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

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

Общее решение

Я пойду с общим решением, которое вам больше нужно, номожет быть полезным в подобных ситуациях.Единственная известная мне альтернатива - полностью выйти за пределы перекрестного фильтра и посмотреть в исходном наборе данных.Это тоже хорошо, и, возможно, более эффективно.Но он может содержать ошибки и приятно работать в системе.

Так что давайте посмотрим, какие даты мы видели в бине.Когда мы начнем, у каждого бина будут все даты.После того, как дата выбрана, будет только одна дата (но не совсем та, которая была выбрана из-за настройки двух кроссфильтров).

Вместо материала sampled_issue_date мы будем отслеживатьобъекта с именем date_counts сейчас:

  // Reducer function for raw geodata
  function geoReducerAdd(p, v) {
    // ...
    const canonDate = new Date(v.sampled_issue_date).getTime()
    p.date_counts[canonDate] = (p.date_counts[canonDate] || 0) + 1
    return p
  }
  function geoReducerRemove(p, v) {
    // ...
    const canonDate = new Date(v.sampled_issue_date).getTime()
    if(!--p.date_counts[canonDate])
      delete p.date_counts[canonDate]
    return p
  }
  // generic georeducer
  function geoReducerDefault() {
    return {
    // ...
      date_counts: {}
    }
  }

Что он делает?

Строка за строкой

    const canonDate = new Date(v.sampled_issue_date).getTime()

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

Вы можетеЧтобы индексировать объект с помощью объекта даты, необходимо преобразовать его в целое число.

    p.date_counts[canonDate] = (p.date_counts[canonDate] || 0) + 1

Когда мы добавим строку, мы проверим, есть ли у нас счетчик для даты строки.Если это так, мы будем использовать количество, которое у нас есть.В противном случае мы по умолчанию установим ноль.Затем мы добавим один.

    if(!--p.date_counts[canonDate])
      delete p.date_counts[canonDate]

Когда мы удаляем строку, мы знаем, что у нас есть счетчик для даты для этой строки (потому что перекрестный фильтр не скажет нам, что он удаляет строку, если это не былодобавлено ранее).Таким образом, мы можем пойти дальше и уменьшить счет.Затем, если он достигнет нуля, мы можем удалить запись.

Как я уже сказал, это излишне.В вашем случае счет будет только к 1, а затем упадет до 0. Но это не намного дороже, чем просто сохранить

Рендеринг боковой панели

Когда мы рендерим сторонуНа панели должна быть только одна дата в date_counts для этого выбранного элемента.

console.assert(Object.keys(date_counts).length === 1) // only one entry
console.assert(Object.entries(date_counts)[0][1] === 1) // with count 1
document.getElementById('geo-issue-date').textContent = new Date(+Object.keys(date_counts)[0]).format('mmm dd, yyyy')

Замечания по юзабилити

С точки зрения юзабилити, я бы порекомендовал не filter(null) при отпуске мыши, или, если вы действительно этого хотите, затем установить тайм-аут, который отменяется приВы видите мышиный центр.Нужно уметь «пролистывать» линейный график и видеть изменения во времени в хороплете, не переключаясь случайно на нефильтрованные цвета.

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

...