【问题标题】:Numpy array from cStringIO object and avoiding copies来自 cStringIO 对象的 Numpy 数组并避免复制
【发布时间】:2011-09-21 19:16:48
【问题描述】:

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

【问题讨论】:

  • 这个projects.scipy.org/numpy/ticket/1634似乎有一张罚单不知道这是否意味着这被认为是一个错误。我仍然想知道如何避免复制。
  • 案例 1 的故障症状已更改:ValueError: invalid literal for long() with base 10: '\x01'

标签: python numpy stringio


【解决方案1】:

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

【讨论】:

  • 不错。有没有办法避免 Python 中的循环。正如您正确指出的那样, cStringIO 对象迭代行,但它在 C 上下文中这样做。是否有一个 Python 对象可以迭代一个类似文件的对象的字符,我可以在 C 上下文中填充或迭代它。
  • @san - Re.循环:我想不出办法。我查看了itertools 中的一些实用程序,但看不到任何调整它们的方法。而且我也不认为有一个基于字符的迭代器——我过去需要一个,最后只是写了这样的东西。 (我可能是错的,所以如果有的话,我想知道!)
  • @san - 我没想到要问 - 你熟悉 yield 符号吗?如果你不是,我在那里写的“函数”实际上不是一个持续循环的函数,而是一个在调用 next() 之间保持状态的生成器。
  • 是的,我知道发电机。但我认为它仍然会产生类似于循环的开销。对于实际问题,我很惊讶使用a = array.array('d') 并运行map(a.extend(), iterator) 的效果和它一样好。
【解决方案2】:

由于cStringIO没有实现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()方法,按需增长。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-15
    • 2013-04-29
    相关资源
    最近更新 更多