如何在没有tmp存储的情况下将二进制数据传输到numpy数组?

时间:2012-10-24 23:30:56

标签: python numpy stdin stringio

有几个类似的问题,但没有一个直接回答这个简单的问题:

如何捕获命令输出并将该内容流式传输到numpy数组而不创建要读取的临时字符串对象?

所以,我想做的是:

import subprocess
import numpy
import StringIO

def parse_header(fileobject):
    # this function moves the filepointer and returns a dictionary
    d = do_some_parsing(fileobject)
    return d

sio = StringIO.StringIO(subprocess.check_output(cmd))
d = parse_header(sio)
# now the file pointer is at the start of data, parse_header takes care of that.
# ALL of the data is now available in the next line of sio
dt = numpy.dtype([(key, 'f8') for key in d.keys()])

# i don't know how do make this work:
data = numpy.fromxxxx(sio , dt)

# if i would do this, I create another copy besides the StringIO object, don't I?
# so this works, but isn't this 'bad' ?
datastring = sio.read()
data = numpy.fromstring(datastring, dtype=dt)

我尝试使用StringIO和cStringIO但numpy.frombuffer和numpy.fromfile都不接受它们。

使用StringIO对象我首先要将流读入字符串然后使用numpy.fromstring,但我想避免创建中间对象(几千兆字节)。

对我来说,如果我可以将sys.stdin流式传输到numpy数组中,那么另一种选择就是numpy.from文件也无效(寻求需要实现)。

这有什么解决方法吗?我不能成为第一个尝试这个的人(除非这是一个PEBKAC案件?)

解决方案: 这是当前的解决方案,它是unutbu指令的混合,如何使用POPE和PIPE的暗示和eryksun使用bytearray的暗示,所以我不知道接受谁!? :S

proc = sp.Popen(cmd, stdout = sp.PIPE, shell=True)
d = parse_des_header(proc.stdout)
rec_dtype = np.dtype([(key,'f8') for key in d.keys()])
data = bytearray(proc.stdout.read())
ndata = np.frombuffer(data, dtype = rec_dtype)

我没有检查数据是否真的没有创建另一个副本,不知道如何。但是我注意到这比我之前尝试的所有内容都快得多,所以非常感谢答案的作者!

2 个答案:

答案 0 :(得分:5)

您可以将Popenstdout=subprocess.PIPE一起使用。阅读标题,然后将其余内容加载到bytearray以与np.frombuffer一起使用。

基于您的修改的其他评论:

如果您打算致电proc.stdout.read(),则相当于使用check_output()。两者都创建临时字符串。如果您预先分配data,则可以使用proc.stdout.readinto(data)。然后,如果读入data的字节数小于len(data),则释放多余的内存,否则将剩余的data扩展为剩余的内容。

data = bytearray(2**32) # 4 GiB
n = proc.stdout.readinto(data)
if n < len(data):
    data[n:] = ''        
else:
    data += proc.stdout.read()

您也可以从预先分配的ndarray ndata开始,并使用buf = np.getbuffer(ndata)。然后readinto(buf)如上所述。

以下示例显示内存在bytearraynp.ndarray之间共享:

>>> data = bytearray('\x01')
>>> ndata = np.frombuffer(data, np.int8)
>>> ndata
array([1], dtype=int8)
>>> ndata[0] = 2
>>> data
bytearray(b'\x02')

答案 1 :(得分:2)

由于您的数据可以很容易地放入RAM,我认为将数据加载到numpy数组中的最简单方法是使用ramfs

在Linux上,

sudo mkdir /mnt/ramfs
sudo mount -t ramfs -o size=5G ramfs /mnt/ramfs
sudo chmod 777 /mnt/ramfs

然后,例如,如果这是二进制数据的生产者:

<强> writer.py:

from __future__ import print_function
import random
import struct
N = random.randrange(100)
print('a b')
for i in range(2*N):
    print(struct.pack('<d',random.random()), end = '')

然后你可以将它加载到像这样的numpy数组中:

<强> reader.py:

import subprocess
import numpy

def parse_header(f):
    # this function moves the filepointer and returns a dictionary
    header = f.readline()
    d = dict.fromkeys(header.split())
    return d

filename = '/mnt/ramfs/data.out'
with open(filename, 'w') as f:  
    cmd = 'writer.py'
    proc = subprocess.Popen([cmd], stdout = f)
    proc.communicate()
with open(filename, 'r') as f:      
    header = parse_header(f)
    dt = numpy.dtype([(key, 'f8') for key in header.keys()])
    data = numpy.fromfile(f, dt)