5

这样可以更好地理解事情。这不是我需要解决的实际问题。一个cstringIO对象应该在行上模拟一个字符串、文件和一个迭代器。它是否也模拟缓冲区?无论如何,理想情况下,应该能够如下构造一个 numpy 数组

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

我的问题是它为什么会这样。

出现这种情况的实际问题如下:我有一个生成元组的迭代器。我有兴趣从元组的一个组件中创建一个 numpy 数组,尽可能少地复制和重复。我的第一个切入点是继续将产生的元组的有趣组件写入 StringIO 对象,然后将其内存缓冲区用于数组。我当然可以使用getvalue(),但会创建并返回一个副本。什么是避免额外复制的好方法。

4

2 回答 2

3

问题似乎是 numpy 不喜欢被赋予字符而不是数字。请记住,在 Python 中,单个字符和字符串具有相同的类型 - numpy 必须在底层进行一些类型检测,并'\x01'作为嵌套序列。

另一个问题是 acStringIO迭代它的行,而不是它的字符。

类似下面的迭代器应该可以解决这两个问题:

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)
于 2011-06-24T11:05:35.017 回答
2

ascStringIO没有实现buffer接口,如果它getvalue返回一个数据的副本,那么没有复制就没有办法得到它的数据。

如果getvalue将缓冲区作为字符串返回而不进行复制,numpy.frombuffer(x.getvalue(), dtype='S1')则将给出一个引用该字符串的(只读)numpy 数组,而无需额外的副本。


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()根据需要使用数组中的方法进行增长。

于 2011-06-24T13:10:13.183 回答