Цифровая гистограмма больших массивов
У меня есть набор наборов данных 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