9

(float, str)私が抱えている実際の問題は、並べ替えられたタプルの長いリストを RAMに保存したいということです。単純なリストは私の 4Gb RAM に収まらないので、 を 2 つ使用できると考えnumpy.ndarrayました。

データのソースは、反復可能な 2 タプルです。numpy機能がありfromiterますが、どのように使用できますか?iterable の項目数は不明です。メモリの制限により、最初にリストに消費することはできません。と思いましたがitertools.tee、ここで多くのメモリ オーバーヘッドが追加されるようです。

私ができると思うのは、イテレータをチャンクで消費し、それらを配列に追加することです。次に、私の質問は、それを効率的に行う方法は? 2 つの 2D 配列を作成して、それらに行を追加する必要がありますか? (その後、それらを 1D に変換する必要があります)。

それとも、より良いアプローチがありますか?本当に必要なのは、対数時間で対応する数値の値で文字列の配列を検索し (そのため、float の値で並べ替えたいのです)、可能な限りコンパクトに保つ​​ことです。

PS iterable はソートされていません。

4

2 に答える 2

8

おそらく、次を使用して単一の構造化配列を構築しnp.fromiterます。

import numpy as np


def gendata():
    # You, of course, have a different gendata...
    for i in xrange(N):
        yield (np.random.random(), str(i))

N = 100

arr = np.fromiter(gendata(), dtype='<f8,|S20')

最初の列で並べ替え、2 番目の列をタイブレーカーに使用すると、O(N log N) の時間がかかります。

arr.sort(order=['f0','f1'])

最初の列の値による行の検索はsearchsorted、O(log N) 時間で実行できます。

# Some pseudo-random value in arr['f0']
val = arr['f0'][10]
print(arr[10])
# (0.049875262239617246, '46')

idx = arr['f0'].searchsorted(val)
print(arr[idx])
# (0.049875262239617246, '46')

コメントで多くの重要な質問をしました。ここでそれらに答えようとしましょう:

  • 基本的な dtype は numpybook で説明されています。追加の dtype が 1 つか 2 つあるかもしれません (float16この本が書かれてから追加されたようなものですが、基本はすべてそこで説明されています)。

    おそらく、より詳細な説明はオンライン ドキュメントにあります。これは、ここで言及した例の良い補足です。

  • Dtype を使用して、列名またはデフォルトの列名で構造化配列を定義できます。'f0''f1'などはデフォルトの列名です。列名を指定できなかったので dtype を定義した'<f8,|S20'ので、NumPy は最初の column'f0'と 2 番目の column に名前を付けました'f1'。もし使っていたら

    dtype='[('fval','<f8'), ('text','|S20')]
    

    その場合、構造化配列arrには列名'fval''text'.

  • 残念ながら、呼び出された時点で dtype を修正する必要がありnp.fromiterます。gendata文字列の最大長を発見するために 1 回反復して、dtype を作成してから呼び出す np.fromiter(そして 2 回反復する)ことも考えられますがgendata、それはかなり面倒です。もちろん、文字列の最大サイズが事前にわかっている方がよいでしょう。(|S20文字列フィールドを 20 バイトの固定長として定義します。)
  • NumPy 配列は、事前に定義されたサイズのデータ​​を固定サイズの配列に配置します。配列 (多次元配列であっても) を 1 次元メモリの連続したブロックと考えてください。(これは単純化しすぎです -- 連続していない配列があります -- しかし、次のことを想像するのに役立ちます。) NumPy は、固定サイズ ( によって設定されるdtype) を利用して、アクセスに必要なオフセットをすばやく計算することによって、その速度の多くを引き出します。配列内の要素。文字列のサイズが可変の場合、NumPy が正しいオフセットを見つけるのは困難です。ハードとは、NumPy にはインデックスが必要か、何らかの方法で再設計されることを意味します。NumPy は単純にこの方法で構築されていません。
  • NumPy には、object任意の Python オブジェクトへの 4 バイト ポインターを配置できる dtype があります。このようにして、任意の Python データを含む NumPy 配列を使用できます。残念ながら、このnp.fromiter 関数では dtype の配列を作成できませんobject。なぜこの制限があるのか​​ わかりません...
  • np.fromiterを指定するとパフォーマンスが向上することに注意してくださいcountcount(行数) と (したがって各行のサイズ) を知ることで、dtypeNumPy は結果の配列に正確に十分なメモリを事前に割り当てることができます。を指定しない場合count、NumPy は配列の初期サイズを推測し、小さすぎる場合は配列のサイズを変更しようとします。元のメモリ ブロックを拡張できる場合は、幸運です。しかし、NumPy がまったく新しいメモリの塊を割り当てなければならない場合、すべての古いデータを新しい場所にコピーする必要があり、パフォーマンスが大幅に低下します。
于 2013-02-25T21:28:58.887 に答える
1

-tuplesNのジェネレーターから個別の配列を構築する方法を次に示します。N

import numpy as np
import itertools as IT


def gendata():
    # You, of course, have a different gendata...
    N = 100
    for i in xrange(N):
        yield (np.random.random(), str(i))


def fromiter(iterable, dtype, chunksize=7):
    chunk = np.fromiter(IT.islice(iterable, chunksize), dtype=dtype)
    result = [chunk[name].copy() for name in chunk.dtype.names]
    size = len(chunk)
    while True:
        chunk = np.fromiter(IT.islice(iterable, chunksize), dtype=dtype)
        N = len(chunk)
        if N == 0:
            break
        newsize = size + N
        for arr, name in zip(result, chunk.dtype.names):
            col = chunk[name]
            arr.resize(newsize, refcheck=0)
            arr[size:] = col
        size = newsize
    return result

x, y = fromiter(gendata(), '<f8,|S20')

order = np.argsort(x)
x = x[order]
y = y[order]

# Some pseudo-random value in x
N = 10
val = x[N]
print(x[N], y[N])
# (0.049875262239617246, '46')

idx = x.searchsorted(val)
print(x[idx], y[idx])
# (0.049875262239617246, '46')

上記のfromiter関数は、 iterable を ( size のchunksize) チャンクで読み取ります。NumPy 配列メソッドresizeを呼び出して、結果の配列を必要に応じて拡張します。

chunksize小さなデータでこのコードをテストしていたので、小さなデフォルトを使用しました。もちろん、デフォルトのチャンクサイズを変更するかchunksize、より大きな値のパラメーターを渡す必要があります。

于 2013-02-28T14:22:59.463 に答える