Numpy массив из объекта cStringIO и избегая копий - PullRequest
5 голосов
/ 24 июня 2011

Это чтобы лучше понять вещи. Это не настоящая проблема, которую мне нужно решить. Предполагается, что объект cstringIO должен эмулировать строку, файл, а также итератор по строкам. Это также эмулирует буфер? В любом случае, в идеале, можно создать пустой массив следующим образом

import numpy as np
import cstringIO

c = cStringIO.StringIO('\x01\x00\x00\x00\x01\x00\x00\x00')

#Trying the iterartor abstraction
b = np.fromiter(c,int)
# The above fails with: ValueError: setting an array element with a sequence.

#Trying the file abstraction
b = np.fromfile(c,int)
# The above fails with: IOError: first argument must be an open file

#Trying the sequence abstraction
b = np.array(c, int)
# The above fails with: TypeError: long() argument must be a string or a number 

#Trying the string abstraction
b = np.fromstring(c)
#The above fails with: TypeError: argument 1 must be string or read-only buffer

b = np.fromstring(c.getvalue(), int)  # does work

У меня вопрос, почему он так себя ведет.

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

Ответы [ 2 ]

3 голосов
/ 24 июня 2011

Кажется, проблема в том, что numpy не нравится, когда ему дают символы вместо цифр. Помните, что в Python отдельные символы и строки имеют один и тот же тип - numpy должен иметь некоторое обнаружение типов, происходящее под капотом, и принимает '\x01' как вложенную последовательность.

Другая проблема заключается в том, что cStringIO перебирает свои строки, а не символы.

Что-то вроде следующего итератора должно обойти обе эти проблемы:

def chariter(filelike):
    octet = filelike.read(1)
    while octet:
        yield ord(octet)
        octet = filelike.read(1)

Используйте это так (обратите внимание на поиск!):

c.seek(0)
b = np.fromiter(chariter(c), int)
2 голосов
/ 24 июня 2011

Поскольку cStringIO не реализует интерфейс буфера, если его getvalue возвращает копию данных, то нет способа получить его данные без копирования.

Если getvalue возвращает буфер в виде строки без копирования, numpy.frombuffer(x.getvalue(), dtype='S1') выдаст (только для чтения) пустой массив, ссылающийся на строку, без дополнительной копии.


Причина, по которой np.fromiter(c, int) и np.array(c, int) не работают, заключается в том, что cStringIO при повторении возвращает строку за раз, аналогично файлам:

>>> list(iter(c))
['\x01\x00\x00\x00\x01\x00\x00\x00']

Такая длинная строка не может быть преобразована в одно целое число.

***

Лучше не слишком беспокоиться о том, чтобы делать копии, если это действительно не является проблемой. Причина в том, что дополнительные накладные расходы, например, использование генератора и передача его в numpy.fromiter может быть на самом деле больше, чем то, что требуется при создании списка, а затем передача его в numpy.array --- создание копий может быть дешевле по сравнению с накладными расходами времени выполнения Python.

Однако, если проблема связана с памятью, то одним из решений является помещение элементов непосредственно в окончательный массив Numpy. Если вы знаете размер заранее, вы можете предварительно выделить его. Если размер неизвестен, вы можете использовать метод .resize() в массиве, чтобы увеличить его по мере необходимости.

...