7

同様の質問がいくつかありますが、この単純な質問に直接答えるものはありません。

一時的な文字列オブジェクトを作成せずに、コマンド出力をキャッチし、そのコンテンツを 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.fromfileでも機能しません(シークを実装する必要があります)。

これに対する回避策はありますか? これを試すのは私が最初ではありません (これが PEBKAC ケースでない限り?)

解決策: これは現在の解決策です。unutbu の指示と PIPE で Popen を使用する方法と、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)

データが本当に別のコピーを作成していないかどうかを確認しませんでした。方法がわかりません。しかし、これは以前に試したすべてのものよりもはるかに高速に機能することに気付きました。両方の回答の作成者に感謝します!

2022年更新: bytearray() ステップを使用せずに上記のソリューションステップを試したところ、問題なく動作しました。Python 3 のおかげでしょうか。

4

2 に答える 2

6

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')
于 2012-10-25T05:20:10.787 に答える
2

データは RAM に簡単に収まるので、データを numpy 配列にロードする最も簡単な方法はramfsを使用することだと思います。

Linux では、

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

次に、たとえば、これがバイナリ データのプロデューサーである場合:

ライター.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 配列にロードできます。

リーダー.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)
于 2012-10-25T01:10:52.807 に答える