有几个类似的问题,但没有一个直接回答这个简单的问题:
如何捕获命令输出并将该内容流式传输到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)
我没有检查数据是否真的没有创建另一个副本,不知道如何。但是我注意到这比我之前尝试的所有内容都快得多,所以非常感谢答案的作者!
答案 0 :(得分:5)
您可以将Popen
与stdout=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)
如上所述。
以下示例显示内存在bytearray
和np.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)