Вопрос

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

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

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

Это было полезно?

Решение

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

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

Другие советы

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

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' - это массив индексов, содержащий индекс ячейки, к которой относится каждое значение в column_of_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]

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

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

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

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

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

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

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

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

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

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

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

Что касается 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top