numpyアレイのリストからnumpy配列を作成するためのPythonic方法
-
21-09-2019 - |
質問
ループ内の1つのディメンシャルnumpy配列のリストを生成し、後でこのリストを2D Numpyアレイに変換します。事前にアイテムの数を知っていれば、2D NumpyアレイをPreallocatedしていましたが、そうではないので、すべてをリストに入れました。
モックアップは以下です:
>>> list_of_arrays = map(lambda x: x*ones(2), range(5))
>>> list_of_arrays
[array([ 0., 0.]), array([ 1., 1.]), array([ 2., 2.]), array([ 3., 3.]), array([ 4., 4.])]
>>> arr = array(list_of_arrays)
>>> arr
array([[ 0., 0.],
[ 1., 1.],
[ 2., 2.],
[ 3., 3.],
[ 4., 4.]])
私の質問は次のとおりです。
リストに載せてからnumpy.arrayを作成するよりも、シーケンシャル数値データ(私の場合はnumpyアレイ)を収集するタスクを実行するより良い方法(パフォーマンスワイズ)はありますか(新しいOBJを作成してコピーしていますかデータ)?よくテストされたモジュールで利用可能な「拡張可能な」マトリックスデータ構造はありますか?
私の2Dマトリックスの典型的なサイズは、100x10から5000x10のフロートです
編集: この例ではMAPを使用していますが、実際のアプリケーションにはループ用のものがあります
解決
最終配列を知っているとします arr
5000x10を超えることはありません。次に、最大サイズの配列を事前に割り当て、ループを通過するときにデータを入力してから使用できます。 arr.resize
ループを終了した後、発見されたサイズに削減します。
以下のテストは、アレイの究極のサイズが何であれ、中間Pythonリストを作成するよりもわずかに高速になることを示唆しています。
また、 arr.resize
使用されていないメモリを解釈するため、最終的な(中間ではないかもしれませんが)メモリフットプリントは、使用されているものよりも小さいです python_lists_to_array
.
これは示しています numpy_all_the_way
より速いです:
% python -mtimeit -s"import test" "test.numpy_all_the_way(100)"
100 loops, best of 3: 1.78 msec per loop
% python -mtimeit -s"import test" "test.numpy_all_the_way(1000)"
100 loops, best of 3: 18.1 msec per loop
% python -mtimeit -s"import test" "test.numpy_all_the_way(5000)"
10 loops, best of 3: 90.4 msec per loop
% python -mtimeit -s"import test" "test.python_lists_to_array(100)"
1000 loops, best of 3: 1.97 msec per loop
% python -mtimeit -s"import test" "test.python_lists_to_array(1000)"
10 loops, best of 3: 20.3 msec per loop
% python -mtimeit -s"import test" "test.python_lists_to_array(5000)"
10 loops, best of 3: 101 msec per loop
これは示しています numpy_all_the_way
より少ないメモリを使用します:
% test.py
Initial memory usage: 19788
After python_lists_to_array: 20976
After numpy_all_the_way: 20348
test.py:
import numpy as np
import os
def memory_usage():
pid = os.getpid()
return next(line for line in open('/proc/%s/status' % pid).read().splitlines()
if line.startswith('VmSize')).split()[-2]
N, M = 5000, 10
def python_lists_to_array(k):
list_of_arrays = list(map(lambda x: x * np.ones(M), range(k)))
arr = np.array(list_of_arrays)
return arr
def numpy_all_the_way(k):
arr = np.empty((N, M))
for x in range(k):
arr[x] = x * np.ones(M)
arr.resize((k, M))
return arr
if __name__ == '__main__':
print('Initial memory usage: %s' % memory_usage())
arr = python_lists_to_array(5000)
print('After python_lists_to_array: %s' % memory_usage())
arr = numpy_all_the_way(5000)
print('After numpy_all_the_way: %s' % memory_usage())
他のヒント
便利な方法、使用 numpy.concatenate
. 。 @unutbuの答えよりも速いと思います。
In [32]: import numpy as np
In [33]: list_of_arrays = list(map(lambda x: x * np.ones(2), range(5)))
In [34]: list_of_arrays
Out[34]:
[array([ 0., 0.]),
array([ 1., 1.]),
array([ 2., 2.]),
array([ 3., 3.]),
array([ 4., 4.])]
In [37]: shape = list(list_of_arrays[0].shape)
In [38]: shape
Out[38]: [2]
In [39]: shape[:0] = [len(list_of_arrays)]
In [40]: shape
Out[40]: [5, 2]
In [41]: arr = np.concatenate(list_of_arrays).reshape(shape)
In [42]: arr
Out[42]:
array([[ 0., 0.],
[ 1., 1.],
[ 2., 2.],
[ 3., 3.],
[ 4., 4.]])
@Gill Batesの答えよりも簡単です。ここに1つのラインコードがあります。
np.stack(list_of_arrays, axis=0)
あなたがしていることは標準的な方法です。 numpyアレイの特性は、連続的なメモリが必要であることです。私が考えることができる「穴」の唯一の可能性は、 strides
のメンバー PyArrayObject
, 、しかし、それはここでの議論に影響しません。 numpyアレイには隣接するメモリがあり、「preallocated」であるため、新しい行/列を追加すると、新しいメモリを割り当て、データをコピーしてから古いメモリを解放することができます。あなたがそれをたくさんするならば、それはあまり効率的ではありません。
誰かがリストを作成してから最終的にそれをnumpy配列に変換したくないかもしれない1つのケースは、リストに多くの数字が含まれている場合です。ネイティブPythonリストにはPythonオブジェクトが保存されます)。あなたの典型的な配列サイズについては、それが問題だとは思いません。
配列のリストから最終配列を作成すると、 それは すべてのデータを、新しい(例の2D)配列の新しい場所にコピーします。これは、numpyアレイを持っているよりもはるかに効率的です next = numpy.vstack((next, new_row))
新しいデータを取得するたびに。 vstack()
すべての「行」のすべてのデータをコピーします。
ありました Numpy-Discussionメーリングリストのスレッド しばらく前に、効率的な拡張/アプローチを可能にする新しいnumpyアレイタイプを追加する可能性について議論しました。当時はこれに大きな関心があったようですが、何かが出てきたかどうかはわかりません。そのスレッドを見たいかもしれません。
あなたがしていることは非常にピトニックで効率的であると言うでしょう。したがって、あなたが本当に何か他のものを必要としない限り(より多くのスペースの効率性、多分?)、あなたは大丈夫です。最初の配列の要素の数がわからないとき、それは私が私のnumpy配列を作成する方法です。
〜Unutbuの答えの独自のバージョンを追加します。 numpy_all_the Wayに似ていますが、インデックスエラーがある場合は動的にサイズを変更します。小さなデータセットでは少し速くなると思いましたが、少し遅くなります。チェックが遅くなりすぎています。
initial_guess = 1000
def my_numpy_all_the_way(k):
arr=np.empty((initial_guess,M))
for x,row in enumerate(make_test_data(k)):
try:
arr[x]=row
except IndexError:
arr.resize((arr.shape[0]*2, arr.shape[1]))
arr[x]=row
arr.resize((k,M))
return arr
さらにシンプルな@FNJN回答
np.vstack(list_of_arrays)