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