Подтвердить что ты не робот

Как сохранить динамическую гистограмму?

существует ли известный алгоритм + структура данных для поддержания динамической гистограммы?

Представьте, что у меня есть поток данных (x_1, w_1), (x_2, w_2),... где x_t - двойники, представляющие некоторую измеренную переменную, а w_t - связанный вес.

Я мог бы просто сделать очевидный (псевдо-питонный код):

x0,xN = 0, 10
numbins = 100
hist = [(x0 + i * delta , 0) for i in xrange(numbins)]
def updateHistogram(x, w):
    k = lookup(x,  hist)    #find the adequated bin where to put x
    hist[k][1] += 1

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

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

Итак, я хочу динамически определять бункеры. Я мог бы сделать глупость:

 for x in data_stream:
      data.append(x)
      hist = make_histogram(data)

но я думаю, что это будет очень быстро...

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

data = sortedarray();
for x in data_stream:
     data.insert(x)
     bins = [ data[int(i * data.size()/numbins)] for i in xrange(numbins)]

и подсчет внутри каждого бина будет равен data.size()/numbins для всех бинов.

Я не могу придумать способ включения весов в это, хотя... есть ли у кого-нибудь предложение? (знание о библиотеках С++, которые это делают, также приветствуется).

EDIT: (для запрошенного разъяснения)

x_t - числа с плавающей запятой. Чтобы вычислить гистограмму, я должен разделить непрерывный диапазон, где x принадлежит в нескольких ячейках. Поэтому у меня будет последовательность чисел bin [0], bin [1] и т.д., Поэтому я должен определить, для чего я делаю bin [i] < x < бен [г + 1].

Вот как вы обычно делаете гистограмму, когда у вас есть все данные заранее. Затем вы должны знать пределы max (x) и min (x), и было бы легко определить соответствующие ячейки. Например, вы могли бы разделить их на равные промежутки между min (x) и max (x).

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

4b9b3361

Ответ 1

Как определить количество ящиков

В гистограмме существует ряд правил определения количества ящиков. Для вашей проблемы я бы пошел с выбором Скотта:

bin_width = 3.5*sd*n^{-1/3}

где sd - стандартное отклонение, а n - количество точек данных. Реально, вы можете использовать алгоритм онлайн для расчета стандартного отклонения. Число бинов k задается выражением:

k = ceil((max(x) - min(x))/bin_width)

Сохранение данных

Предположим, что мы наблюдали N точек данных. Тогда доверительный интервал для стандартного отклонения,

Lower limit: sd*sqrt((N-1)/CHIINV((alpha/2), N-1))
Upper limit: sd*sqrt((N-1)/CHIINV(1-(alpha/2), N-1))

где CHIINV является значением из хи-квадратного распределения. Когда N = 1000, CI для sd:

(0.96*sd, 1.05*sd)

и, следовательно, 95% CI ширина бина:

(3.5*0.96*sd*1000^{-1/3}, 3.5*1.05*sd*1000^{-1/3})
(0.336*sd, 0.3675*sd)

Вы можете получить что-то похожее для количества ящиков.

Алгоритм

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

Комментарии

  • Правило Freedman-Diaconis лучше выбирать количество ящиков, но оно включает в себя диапазон между квантилями, который немного сложнее рассчитать в Интернете.
  • Технически, интервал CI неверен, когда данные являются последовательными. Но если вы установите разумное минимальное количество точек данных для наблюдения, скажем ~ 100 или 1000, вы должны быть в порядке.
  • Это предполагает, что все данные следуют за тем же распределением.
  • Количество ячеек зависит от n ^ {- 1/3}. Если вы знаете примерно, сколько очков ожидать, т.е. 10 ^ 5, 10 ^ 6 или 10 ^ 7, то вы могли бы создать небольшие бункеры с ожиданием изменения ширины бункера в будущем.

Ответ 2

Звучит так, как будто вы хотите реализовать следующий абстрактный тип данных.

insert(x, w): add item x to the collection with weight x
select(p): return the item greater than a p weighted fraction of the items

Например, select(0) возвращает минимум, select(0.5) возвращает взвешенную медианную форму, а select(1) возвращает максимум.

Я бы реализовал этот ADT одним из двух способов. Если выбор нечасто, я бы поместил данные в массив и использовал алгоритм выбора по линейному времени, для O (1) -временных вставок и O (n) -time выбирает. Если выбор частый, я бы использовал двоичное дерево поиска, где каждый node хранит общий вес в своем поддереве. Например, после

insert(2, 10)
insert(1, 5)
insert(3, 100)
insert(4, 20)

дерево может выглядеть как

   2 (135)
  / \
 /   \
1 (5) 4 (120)
     /
    /
   3 (100)

Теперь, чтобы найти взвешенную медиану, умножьте 135 на 0.5 и получим 67.5 как желаемый "индекс". Начиная с корня 2, мы находим, что 5 меньше 67.5, поэтому элемент не находится в левом поддереве и мы вычитаем 5, чтобы получить 62.5, индекс в остальную часть дерева, Так как 135 - 120 = 15 меньше 62.5, медиана не является 2. Вычитаем 15 из 62.5, чтобы получить 47.5 и опуститься до 4. При 4 мы находим, что 100 больше, чем 47.5, поэтому 3 является медианом.

Предполагая сбалансированное дерево, время работы как insert, так и select равно O(log n). Если бы я реализовал с нуля, я бы предпочел использовать splay tree.

Ответ 3

ROOT - это инструмент, используемый физиками частиц для такого рода работ... и он связан с привязками python. Имейте в виду, что это не легкая часть программного обеспечения.

В С++ вы бы сделали что-то вроде

TH1D hist("hist","longer title for hist",numbins,lowlimit,highimit);

...

for (int i=0; i<num; ++i){
   hist.Fill(x[i],w[i]);
}

...

hist.Draw();

ROOT не содержит встроенного решения проблемы с биннингами, в нижнем и верхнем потоках добавляются входные данные ниже/выше диапазона бит.

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

Ответ 4

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

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