web-dev-qa-db-ja.com

NumPyで列方向に行列を成長させる

純粋なPythonでは、行列を列ごとに非常に簡単に拡張できます。

data = []
for i in something:
    newColumn = getColumnDataAsList(i)
    data.append(newColumn)

NumPy の配列には追加機能がありません。 hstack関数はサイズがゼロの配列では機能しないため、以下は機能しません。

data = numpy.array([])
for i in something:
    newColumn = getColumnDataAsNumpyArray(i)
    data = numpy.hstack((data, newColumn)) # ValueError: arrays must have same number of dimensions

したがって、私のオプションは、適切な条件でループの初期化を削除することです。

data = None
for i in something:
    newColumn = getColumnDataAsNumpyArray(i)
    if data is None:
        data = newColumn
    else:
        data = numpy.hstack((data, newColumn)) # works

...またはPythonリストを使用して、後で配列に変換します。

data = []
for i in something:
    newColumn = getColumnDataAsNumpyArray(i)
    data.append(newColumn)
data = numpy.array(data)

どちらの亜種も少し厄介なようです。より良い解決策はありますか?

20
Boris Gorelik

NumPyには実際にはappend関数がありますが、これはあなたが望むことをするかもしれません、例えば、

_import numpy as NP
my_data = NP.random.random_integers(0, 9, 9).reshape(3, 3)
new_col = NP.array((5, 5, 5)).reshape(3, 1)
res = NP.append(my_data, new_col, axis=1)
_

2番目のスニペット(hstack)は、別の行を追加した場合に機能します。

_my_data = NP.random.random_integers(0, 9, 16).reshape(4, 4)
# the line to add--does not depend on array dimensions
new_col = NP.zeros_like(my_data[:,-1]).reshape(-1, 1)
res = NP.hstack((my_data, new_col))
_

hstackconcatenate((my_data, new_col), axis=1)と同じ結果をもたらしますが、パフォーマンスに関してどのように比較されるかはわかりません。


それがあなたの質問に対する最も直接的な答えですが、データソースをループしてappendを介してターゲットにデータを入力することは、Pythonでは問題ありませんが、慣用的なNumPyではありません。理由は次のとおりです。

NumPy配列の初期化は比較的コストがかかりますそしてこの従来のpythonパターンでは、ループの反復ごとに、多かれ少なかれそのコストが発生します(つまり、 NumPy配列は、サイズの異なる新しい配列を初期化するのとほぼ同じです)。

そのため、2D配列に列を繰り返し追加するためのNumPyの一般的なパターンは、空のターゲット配列を初期化することです1回(またはすべての空の列を持つ単一の2D NumPy配列を事前に割り当てます)目的の列ごとのオフセット(インデックス)を設定することにより、これらの空の列に連続してデータを入力します。説明するよりも表示する方がはるかに簡単です。

_>>> # initialize your skeleton array using 'empty' for lowest-memory footprint 
>>> M = NP.empty(shape=(10, 5), dtype=float)

>>> # create a small function to mimic step-wise populating this empty 2D array:
>>> fnx = lambda v : NP.random.randint(0, 10, v)
_

oPと同様にNumPy配列にデータを入力します。ただし、各反復は、連続する列方向のオフセットでMの値をリセットするだけです。

_>>> for index, itm in enumerate(range(5)):    
        M[:,index] = fnx(10)

>>> M
  array([[ 1.,  7.,  0.,  8.,  7.],
         [ 9.,  0.,  6.,  9.,  4.],
         [ 2.,  3.,  6.,  3.,  4.],
         [ 3.,  4.,  1.,  0.,  5.],
         [ 2.,  3.,  5.,  3.,  0.],
         [ 4.,  6.,  5.,  6.,  2.],
         [ 0.,  6.,  1.,  6.,  8.],
         [ 3.,  8.,  0.,  8.,  0.],
         [ 5.,  2.,  5.,  0.,  1.],
         [ 0.,  6.,  5.,  9.,  1.]])
_

もちろん、配列のサイズが事前にわからない場合は、必要以上に大きい配列を作成し、入力が完了したら「未使用」の部分をトリミングしてください。

_>>> M[:3,:3]
  array([[ 9.,  3.,  1.],
         [ 9.,  6.,  8.],
         [ 9.,  7.,  5.]])
_
18
doug

通常、NumPy配列を作成するときにサイズを変更し続けることはありません。 3番目のソリューションの何が気に入らないのですか?非常に大きな行列/配列の場合は、値の割り当てを開始する前に配列を割り当てる価値があるかもしれません。

x = len(something)
y = getColumnDataAsNumpyArray.someLengthProperty

data = numpy.zeros( (x,y) )
for i in something:
   data[i] = getColumnDataAsNumpyArray(i)
4
Paul

Hstackは、サイズがゼロの配列で機能します。

import numpy as np

N = 5
M = 15

a = np.ndarray(shape = (N, 0))
for i in range(M):
    b = np.random.Rand(N, 1)
    a = np.hstack((a, b))
3
akalinin

一般に、NumPy配列を再割り当てし続けるのはコストがかかるため、3番目のソリューションは本当に最高のパフォーマンスです。

しかし、私はhstackがあなたが望むことをするだろうと思います-手がかりはエラーメッセージにあります、

ValueError:配列は同じ数の次元を持つ必要があります `

NewColumnには(1Dベクトルではなく)2つの次元があると思います。したがって、データにも2つの次元が必要です...たとえば、data = np.array([[]])-または、代わりにnewColumnを1Dベクトルにします(通常は物事が1Dの場合は、NumPyで1Dに保つ方がよいので、ブロードキャストなどがうまく機能します)。この場合、np.squeeze(newColumn)hstackまたはvstackを使用すると、データの元の定義で機能するはずです。

1
robince