来自cStringIO对象的Numpy数组并避免复制

时间:2011-06-24 06:46:07

标签: python numpy stringio

这样可以更好地理解事物。这不是我需要解决的实际问题。 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(),但会创建并返回副本。什么是避免额外复制的好方法。

2 个答案:

答案 0 :(得分:3)

问题似乎是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)

答案 1 :(得分:2)

由于cStringIO未实现缓冲区接口,如果其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()方法根据需要增大它。