2013-02-25 2 views
9

私が実際に持っている問題は、(float, str)タプルの長いソートされたリストをRAMに保存したいということです。普通のリストは私の4Gb RAMには収まらないので、私は2つのnumpy.ndarrayを使うことができると思った。タプルの1つの反復可能列から2つ(またはそれ以上)のnumpy配列をどのように埋めるのですか?

データのソースは2タプルの繰り返し可能です。 numpyにはfromiterの機能がありますが、どうすれば使用できますか?イテラブル内のアイテムの数は不明です。私は最初にメモリの制限のためにそれをリストに消費することはできません。私はitertools.teeのことを考えましたが、ここでは多くのメモリオーバーヘッドが追加されているようです。

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

もっと良いアプローチがありますか?私が本当に必要とするのは、対応する数値を対数時間で検索することです(そのため、浮動小数点値でソートして、できるだけコンパクトにしたいのです)。

P.S. iterableはソートされません。

+0

'np.fromiter'を使用して、2つの列で1つの配列を作成すれば十分ですか? – unutbu

+0

@unutbu ...なぜ私はそれを考慮していないのか分かりません:)いいアイデアのように思えます。それから、私は長軸に沿ってそれを並べ替えて、そのまま保持します。あなたはそれを答えとして投稿することができます。 –

答えて

8

はおそらくnp.fromiterを使用して、単一の構造アレイ構築:Oを取るタイブレーカのための第二を使用して、最初の列でそれをソート

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') 

を時間(N Nログ):

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

# 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') 
:最初の列の値によって行を検索

がOでsearchsortedで行うことができる時間(Nログ)


コメントには多くの重要な質問があります。私はここではそれらに答えることを試みてみましょう:

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

    おそらく、より徹底した議論がonline documentationです。あなたが言及した例の良い補足物はhereです。

  • Dタイプを使用して列名を持つ構造化配列を定義するか、デフォルトの列名を使用して を定義できます。 'f0','f1'などはデフォルトの列 の名前です。私はdtypeを'<f8,|S20'と定義していたので、 の列名を提供できなかったので、NumPyは最初の列を'f0'とし、2番目の列を 'f1'と命名しました。我々は

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

    を使用していた場合は、構造化された配列arrは、カラム名'fval''text'を持っているでしょう。

  • 残念ながら、np.fromiterが呼び出された時点でdtypeを固定する必要があります。あなたは 多分、一度文字列の 最大の長さを発見するためにgendataを反復あなたのDTYPEを構築し、呼び出し np.fromiter(およびgendataを介して第2回反復)が、むしろ重荷だ ことができます。もちろん、文字列の最大サイズを先読みしている方が良い場合は、 です。 (|S20は、文字列 を固定長20バイトとして定義します)。
  • NumPy配列は、固定サイズの配列で の事前定義サイズのデータ​​を配置します。配列(多次元のものさえも)を1次元メモリの連続したブロックと考えてください。 NumPyは固定サイズ(dtypeで設定)を利用して速度の多くを得て、必要なオフセットをすばやく計算することができます(これは単純すぎる配列ですが、非連続配列です)。配列内の要素にアクセスします。文字列のサイズが可変であれば、 はNumPyが正しいオフセットを見つけるのが難しいでしょう。ハードには、 NumPyにインデックスが必要か、何とか再設計する必要があります。 NumPyは単にこの方法で作られた ではありません。
  • NumPyにはobject dtypeがあります。このタイプを使用すると、希望する任意のPythonオブジェクトにポインタが4バイトの ポインタを配置できます。このようにしてNumPy 配列に任意のPythonデータを持たせることができます。残念ながら、np.fromiter 関数では、dtype objectの配列を作成することはできません。なぜこの制限があるのか​​分かりません...
  • countが の場合、np.fromiterの方がパフォーマンスが優れています。 count(行数)と dtype(したがって各行のサイズ)を知ることによって、NumPyは結果として得られる配列のメモリを正確に にあらかじめ割り当てることができます。 countを指定しない場合、NumPyは 配列の初期サイズを推測し、小さすぎると配列のサイズ変更を試みます。 元のメモリブロックを拡張することができればあなたは運がいいです。しかし、 NumPyが完全に新しいメモリを割り当てなければならない場合、すべての古い データを新しい場所にコピーする必要があります。これにより、パフォーマンスが大幅に遅くなります( )。ここ
+0

うわー、ここにはたくさんの新しいものがあります。 'fX'インデックス構文ですが、主に使用したdtypeです。まず、可能なdtypが文書化されていますか?私は[this](http://docs.scipy.org/doc/numpy/reference/generated/numpy.dtype.html)を見つけましたが、例だけではなく説明を使用したいと思います。サイズは固定でなければならないのですか(それは普通の配列だと思います)?理想的な世界では上限を持たせたくないし、短い文字列で余分なスペースを取ることも望まないから。そんなことをすることはできますか? –

+0

'count'を指定しないと、' np.fromiter'は最初にイテレータからリストを作成して配列に変換する必要はありませんか? – Jaime

+0

@Jaime: 'count'を指定しないと、データがあらかじめ割り当てられた出力配列を超えると、npy.fromiterはnumpy配列のサイズを変更する必要があります。十分な連続メモリがあれば、サイズ変更時にデータをコピーする必要はなく、使用されるPythonリストは一切使用されません。 – unutbu

1

Nタプルの発電機のうち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関数(サイズchunksizeの)チャンクでイテラブルを読み出します。 NumPy配列メソッドresizeを呼び出して、必要に応じて結果の配列を拡張します。

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

+0

ええ、チャンクの読みは私の心にもありました。それをスピードアップするために 'chunksize'を' np.fromiter'に渡すことはできませんか? –

+0

残念ながら、私は方法が表示されません。 'count = chunksize'を使うと、iterableに' chunksize'よりも少ない項目が含まれていると、 'np.fromiter'の呼び出しが失敗することがあります。 'try..except'ブロックでそれをキャッチしようとすると、繰り返し可能なのは1回のパスにしか効果がないため、データを失うことになります。 – unutbu

関連する問題