Ленивая оценка для перебора массивов NumPy - PullRequest
4 голосов
/ 03 августа 2010

У меня есть программа на Python, которая обрабатывает довольно большие массивы NumPy (в сотнях мегабайт), которые хранятся на диске в файлах рассылок (один массив ~ 100 МБ на файл).Когда я хочу выполнить запрос к данным, я загружаю весь массив с помощью pickle, а затем выполняю запрос (так что с точки зрения программы Python весь массив находится в памяти, даже если ОС его выгружает),Я сделал это главным образом потому, что считал, что возможность использовать векторизованные операции над массивами NumPy будет значительно быстрее, чем использовать для циклов для каждого элемента.быстро столкнуться с.У меня есть много разных типов запросов, которые я выполняю для данных, поэтому написание «чанкованного» кода, который загружает части данных из отдельных файлов pickle, обрабатывает их, а затем переходит к следующему чанку, вероятно, увеличит сложность.Определенно было бы предпочтительнее сделать этот «чанкинг» прозрачным для любой функции, которая обрабатывает эти большие массивы.

Кажется, что идеальным решением было бы что-то вроде генератора, который периодически загружал блок данных с диска и затем передавал значения массива одно за другим.Это существенно уменьшило бы объем памяти, требуемый программой, не требуя дополнительной работы со стороны отдельных функций запросов.Можно ли сделать что-то подобное?

Ответы [ 3 ]

9 голосов
/ 03 августа 2010

PyTables - это пакет для управления иерархическими наборами данных. Он предназначен для решения этой проблемы для вас.

4 голосов
/ 03 августа 2010

Структура отображаемых в памяти данных NumPy ( memmap ) может быть хорошим выбором здесь.

Вы получаете доступ к массивам NumPy издвоичный файл на диске, без загрузки всего файла в память сразу.

(Обратите внимание, я верю, но я не уверен, что объект memmap Numpys не такой же, как Pythons-в частности, NumPys похож на массив, Python - на файл.)

Сигнатура метода:

A = NP.memmap(filename, dtype, mode, shape, order='C')

Все аргументы являются прямыми (т. е. они имеют одинаковыеэто означает, что используется в другом месте в NumPy) за исключением «порядка», который относится к порядку расположения памяти ndarray.Я считаю, что по умолчанию используется значение «C», а (только) другой параметр - «F», для Fortran - как и везде, эти два параметра представляют порядок основной строки и основной столбца соответственно.

Два метода:

flush (который записывает на диск все изменения, которые вы вносите в массив);и

close (который записывает данные в массив memmap или, точнее, в карту памяти в виде массива в данные, хранящиеся на диске)

пример использования:

import numpy as NP
from tempfile import mkdtemp
import os.path as PH

my_data = NP.random.randint(10, 100, 10000).reshape(1000, 10)
my_data = NP.array(my_data, dtype="float")

fname = PH.join(mkdtemp(), 'tempfile.dat')

mm_obj = NP.memmap(fname, dtype="float32", mode="w+", shape=1000, 10)

# now write the data to the memmap array:
mm_obj[:] = data[:]

# reload the memmap:
mm_obj = NP.memmap(fname, dtype="float32", mode="r", shape=(1000, 10))

# verify that it's there!:
print(mm_obj[:20,:])
2 голосов
/ 03 августа 2010

Кажется, что идеальным решением было бы что-то вроде генератора, который периодически загружал блок данных с диска и затем передавал значения массива одно за другим.Это существенно уменьшило бы объем памяти, требуемый программой, не требуя дополнительной работы со стороны отдельных функций запросов.Можно ли сделать что-то вроде этого?

Да, но не путем хранения массивов на диске в единственном дескрипторе - протокол рассола просто не предназначен для «инкрементной десериализации».

Вы можете записать несколько солений в один и тот же открытый файл один за другим (используйте dump, не dumps), а затем "ленивый оценщикИтерации "просто необходимо использовать pickle.load каждый раз.

Пример кода (Python 3.1 - в 2. любой вы захотите cPickle вместо pickle и -1 для протокола и т. д., конечно; -):

>>> import pickle
>>> lol = [range(i) for i in range(5)]
>>> fp = open('/tmp/bah.dat', 'wb')
>>> for subl in lol: pickle.dump(subl, fp)
... 
>>> fp.close()
>>> fp = open('/tmp/bah.dat', 'rb')
>>> def lazy(fp):
...   while True:
...     try: yield pickle.load(fp)
...     except EOFError: break
... 
>>> list(lazy(fp))
[range(0, 0), range(0, 1), range(0, 2), range(0, 3), range(0, 4)]
>>> fp.close()
...