Цифровая гистограмма больших массивов

У меня есть набор наборов данных csv размером около 10 ГБ каждый. Я хотел бы генерировать гистограммы из своих столбцов. Но кажется, что единственный способ сделать это в numpy - сначала загрузить весь столбец в массив numpy, а затем вызвать numpy.histogram в этом массиве. Это потребляет ненужный объем памяти.

Поддерживает ли numpy интерактивное бининг? Я надеюсь на что-то, что итерации по моей линии csv по строке и значениям бинов, когда они читают их. Таким образом, не более одной строки в памяти может быть в любой момент.

Не было бы сложно катиться самостоятельно, но интересно, кто-то уже изобрел это колесо.

Ответы

Ответ 1

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

import numpy as np
datamin = -5
datamax = 5
numbins = 20
mybins = np.linspace(datamin, datamax, numbins)
myhist = np.zeros(numbins-1, dtype='int32')
for i in range(100):
    d = np.random.randn(1000,1)
    htemp, jnk = np.histogram(d, mybins)
    myhist += htemp

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

Ответ 2

Здесь вы можете напрямую записать ваши значения:

import numpy as NP

column_of_values = NP.random.randint(10, 99, 10)

# set the bin values:
bins = NP.array([0.0, 20.0, 50.0, 75.0])

binned_values = NP.digitize(column_of_values, bins)

'binned_values' - это индексный массив, содержащий индекс bin, к которому принадлежит каждое значение в столбце_значения.

'bincount' даст вам (очевидно) счетчик bin:

NP.bincount(binned_values)

Учитывая размер вашего набора данных, использование Numpy 'loadtxt' для сборки генератора может оказаться полезным:

data_array = NP.loadtxt(data_file.txt, delimiter=",")
def fnx() :
  for i in range(0, data_array.shape[1]) :
    yield dx[:,i]

Ответ 3

Биннинг с деревом Фенвика (очень большой набор данных, требуемые границы приоритета)

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

Что делать, если у вас есть ОЧЕНЬ большой набор данных (миллиарды образцов), и вы не знаете заранее, ГДЕ должны быть границы вашего бункера? Например, возможно, вы хотите, чтобы все вещи были в квартилях или децилях.

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

Для больших наборов данных, где размер памяти для хранения массива нецелесообразен (не говоря уже о времени сортировки)... затем рассмотрите использование дерева Fenwick, а также "двоичное индексированное дерево".

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

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

Подробнее о деревьях Фенвика:

Ответ 4

Объединение с генераторами (большой набор данных, буферы с фиксированной шириной, данные с плавающей запятой)

Если вы знаете ширину ваших желаемых бункеров заблаговременно - даже если есть сотни или тысячи ведер - тогда я думаю, что скопление вашего собственного решения будет быстрым (как для записи, так и для запуска). Здесь некоторый Python, который предполагает, что у вас есть итератор, который дает вам следующее значение из файла:

from math import floor
binwidth = 20
counts = dict()
filename = "mydata.csv"
for val in next_value_from_file(filename):
   binname = int(floor(val/binwidth)*binwidth)
   if binname not in counts:
      counts[binname] = 0
   counts[binname] += 1
print counts

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

Что касается next_value_from_file(), как упоминалось ранее, вы, вероятно, захотите написать собственный генератор или объект с помощью метода iter(), сделайте это эффективно. Псевдокодом для такого генератора будет следующее:

def next_value_from_file(filename):
  f = open(filename)
  for line in f:
     # parse out from the line the value or values you need
     val = parse_the_value_from_the_line(line)
     yield val

Если заданная строка имеет несколько значений, сделайте parse_the_value_from_the_line() либо возвращением списка, либо себя генератором, и используйте этот псевдокод:

def next_value_from_file(filename):
  f = open(filename)
  for line in f:
     for val in parse_the_values_from_the_line(line):
       yield val