Используя Crossfilter, можно ли отслеживать max/min при группировке?

При использовании Crossfilter (https://github.com/square/crossfilter) я указываю функции, которые следует использовать при добавлении и удалении данных из группы. Это довольно тривиально, чтобы отслеживать среднее значение (используя CoffeeScript):

reduceAdd = (p, v) ->
  ++p.count;
  p.sum += v.digit;
  p

reduceRemove = (p, v) ->
  --p.count;
  p.sum -= v.digit;
  p

reduceInitial = ->
  {
    count: 0
    sum: 0
    average: ->
      return 0 if this.count == 0
      return this.sum / this.count
  }

Можно ли отслеживать максимальные и минимальные значения для каждой группы? Я не могу понять, как сохранить все элементы в огромном массиве и сделать d3.min/d3.max. Похоже, что добавление/удаление данных было бы крайне неэффективным.

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

Ответы

Ответ 1

Немного поиграв с этим, вы можете перестроить группу, снова вызвав метод группы.

Ответ 2

Вы можете использовать dimension.top(1) и dimension.bottom(1), чтобы получить текущий мин и макс. Эти методы учитывают любые фильтры, которые могут быть активны на перекрестном фильтре.

Ответ 3

Лучшее решение, которое я придумал, состояло в том, чтобы отслеживать все значения в упорядоченном списке и добавлять элементы с помощью простой функции вставки в стиле быстрой сортировки (cp. how to insert число в отсортированный массив) и удалить их с помощью indexOf.

Общие функции:

function insertElement(element, array) {
    array.splice(locationOfElement(element, array) + 1, 0, element);
    return array;
}

function removeElement(element, array) {
    var index = array.indexOf(element);
    if (index >= 0) array.splice(index, 1);
    return array;
}

function locationOfElement(element, array, start, end) {
    start = start || 0;
    end = end || array.length;
    var pivot = parseInt(start + (end - start) / 2, 10);
    if (array[pivot] === element) return pivot;
    if (end - start <= 1)
        return array[pivot] > element ? pivot - 1 : pivot;
    if (array[pivot] < element) {
        return locationOfElement(element, array, pivot, end);
    } else {
        return locationOfElement(element, array, start, pivot);
    }
}

function maxElement(array) {
    return (array.length > 0) ? 
        array[array.length - 1] : null;
}

function minElement(array) {
    return (array.length > 0) ? 
        array[0] : null;
}

Функции, используемые при добавлении и удалении данных из группы для отслеживания min/max:

minMaxDimension = cf.dimension(function (d) {
    return d.key; 
});

var reduceAdd = function(p, v) {
    insertElement(v.value, p.elements);
    return p;
};

var reduceRemove = function(p, v) {
    removeElement(v.value, p.elements);
    return p;
};

var reduceInitial = function() {
    return { 
        elements: [],
        max: function() { return maxElement(elements); },
        min: function() { return minElement(elements); }
    }
}

minMaxGroup = minMaxDimension
    .group()
    .reduce(reduceAdd, reduceRemove, reduceInitial)
    .orderNatural()
    .top(Infinity);