Numpy гистограмма больших массивов

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

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

Было бы несложно свернуть свое собственное, но интересно, изобрел ли кто-нибудь уже это колесо.


person pseudosudo    schedule 17.03.2010    source источник


Ответы (4)


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

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 о генераторе кажется хорошим способом решить эту проблему. проблема.

person mtrw    schedule 17.03.2010
comment
Хорошее решение. Если вы хотите сделать это немного быстрее, вы можете сделать myhist += htemp (я думаю, что это быстрее, потому что обновляет гистограмму на месте). - person Eric O Lebigot; 18.03.2010
comment
Спасибо @EOL. Я забыл о некоторых приятных функциях Python, потому что не полностью перешел с Octave. А еще есть расширенные функции, такие как генераторы, которые мне еще предстоит изучить. - person mtrw; 18.03.2010

Вот способ напрямую связать ваши значения:

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' — это массив индексов, содержащий индекс ячейки, к которой принадлежит каждое значение в столбце_значений_столбца.

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

NP.bincount(binned_values)

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

data_array = NP.loadtxt(data_file.txt, delimiter=",")
def fnx() :
  for i in range(0, data_array.shape[1]) :
    yield dx[:,i]
person doug    schedule 17.03.2010
comment
Но не будет ли loadtxt сначала загружать весь файл в память? Это именно та проблема, которую я хочу избежать. - person pseudosudo; 19.03.2010
comment
@pseudosudo См. ответ Дэна Х. ниже, чтобы узнать, как создать генератор без предварительной загрузки всего файла. - person Cai; 18.03.2021

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

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

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

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

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

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

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

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

person Dan H    schedule 29.12.2013
comment
Подсчет не зависит от порядка и не требует одновременной загрузки данных в массив или их сортировки. - person rafaelvalle; 03.11.2017

Биннинг с помощью генераторов (большой набор данных; бины фиксированной ширины; плавающие данные)

Если вы заранее знаете ширину желаемых корзин - даже если есть сотни или тысячи корзин - тогда я думаю, что развертывание вашего собственного решения будет быстрым (как для записи, так и для запуска). Вот некоторый 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
person Dan H    schedule 29.12.2013