numpyでインプレースで行列を置換


27

pythonのnumpyライブラリを使用して、行と列のいくつかの順序を変更することにより、密な正方形の遷移行列をインプレースで変更したい。数学的には、これは、行列に置換行列Pを事前乗算し、P ^ -1 = P ^ Tを事後乗算することに相当しますが、これは計算上合理的な解決策ではありません。

今私は手動で行と列を交換していますが、numpyには素敵な関数f(M、v)があり、Mにはn行と列があり、vにはnエントリがあるので、f(M、v)は更新されますMインデックス順列v。多分、インターネットの検索に失敗しているだけかもしれません。

このようなことは、numpyの「高度なインデックス作成」によって可能になるかもしれませんが、私の理解では、そのようなソリューションは適切ではありません。また、いくつかの単純な状況では、インデックス置換を個別に追跡するだけで十分かもしれませんが、これは私の場合は便利ではありません。

追加:
時々、人々が順列について話すとき、それらはランダムな順列のサンプリングを意味するだけです。例えば、統計のp値を取得する手順の一部として。または、可能なすべての順列をカウントまたは列挙することを意味します。私はこれらのことについて話していません。

追加:
マトリックスはデスクトップRAMに収まるほど小さいが、思いがけずコピーしたくないほど大きい。実際には可能な限り大きなマトリックスを使用したいと思いますが、RAMに保持できないという不便さに対処したくありません。また、マトリックスに対してO(N ^ 3)LAPACK操作を行います。実用的なマトリックスサイズを制限します。私は現在、この大きな行列を不必要にコピーしていますが、これを置換のために簡単に回避できることを望みます。


3
質問を更新して行列のサイズを指定できると便利です。「巨大」とは、すべての人にとって同じことを意味するものではありません。
ビル・バルト

2
高度な(またはいわゆるファンシー)インデックス作成がコピーを作成するのは正しいことです。しかし、あなたがその事実を受け入れて受け入れるなら、あなたのコードはM[v]行を並べ替えるだけです。
ダニエルヴェルコフ

@ダニエル:そして、全体の置換を行うのはM [v、:] [:, v]でしょうか?これは派手なインデックスを使用して順列を取得する最良の方法でしょうか?そして、元の行列のサイズ、行+列の置換行列、および一時的な行の置換行列を含む3倍の行列メモリを使用しますか?
なし

それは正しいです。元のマトリックスと2つのコピーがあります。ところで、なぜ行と列の両方を同時に並べ替える必要があるのですか?
ダニエルヴェルコフ

4
並べ替えられた行列で何をしますか?演算子を適用するときは、ベクトルを単純に並べ替えることをお勧めします。
ジェドブラウン

回答:


9

ドキュメントによると、numpyにはndarray.sortのようなインプレース置換メソッドはありません。

(つまり、想定しているあなたの選択肢はそれほどMある行列と置換ベクトル)N×Np

  1. 拡張モジュールとしてCで独自のアルゴリズムを実装します(ただし少なくとも私にとっては、インプレースアルゴリズムは難しいです!)
  2. Nメモリオーバーヘッド

    for i in range(N):
        M[:,i] = M[p,i]
    for i in range(N):
        M[i,:] = M[i,p]
    
  3. N2メモリオーバーヘッド

    M[:,:] = M[p,:]
    M[:,:] = M[:,p]
    

これらの次善のハッキングが役立つことを願っています。


@noneはハック2です。「行と列の手動交換」と呼ばれるものは何ですか?
ステファノM

1
オプション1と2を組み合わせます。順序Nのバッファーを使用して各置換列に書き込むCコードを記述し、それを元の場所に書き戻します。その後、行に対して同じことを行います。@Stefanoが書いているように、これは余分なメモリのみを取ります。あなたはすでにそもそも順列を保存するために費やしています。pO(N)p
エリックP.

@ErikP。C実装の場合、追加メモリは合理的であり、分散と一時への書き込みとコピーバックのアプローチは適切です。ただし、余分なメモリがある場合、より効率的なアルゴリズムがあるかどうかは興味深い質問です。プロセッサアーキテクチャ、メモリアクセスパターン、キャッシュヒットを考慮する必要があるため、答えは難しいと思います。これは、あなたのアドバイスに従い、シンプルで実装しやすいアルゴリズムを採用することになると言いました。O N O(N)O(N)
ステファノM

2
これは、cython関数の本当に良い候補です。Shoudln'sは10行を超えてはなりません。。。私はそれに亀裂を与えたいですか?
meawoppl

笑。私はこれをCythonに始め、それから私がいつも使っている関数で正しい答えを見つけました。ど 投稿された回答をご覧ください。
meawoppl

6

警告:以下の例は適切に機能しますが、ポストエンドで提案されたパラメーターの完全なセットを使用すると、バグ、または少なくともnumpy.take()関数の「文書化されていない機能」が公開されます。詳細については、以下のコメントを参照してください。 バグレポートを提出しました

これは、numpyのtake()関数を使用してインプレースで実行できますが、フープを少しジャンプする必要があります。

恒等行列の行のランダム置換を行う例は次のとおりです。

import numpy as np
i = np.identity(10)
rr = range(10)
np.random.shuffle(rr)
np.take(i, rr, axis=0)
array([[ 0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.],
       [ 1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.]])

インプレース、それを行うには、あなたがする必要があるすべては、入力配列と同じように「アウト」パラメータを指定しているあなたはモード=「クリップ」またはモード=「ラップ」を設定する必要があります。モードを設定しない場合、Python例外でアレイの状態を復元するためのコピーが作成されます(こちらを参照)

最後に、テイクは配列メソッドのようですので、代わりに

np.take(i, rr, axis=0)

あなたは電話することができます

i.take(rr, axis=0)

それがあなたの好みに合っているなら。したがって、合計すると、次のようになります。

#Inplace Rearrange
arr = makeMyBixMatrix()
pVec0, pVec1 = calcMyPermutationVectors()
arr.take(pVec0, axis=0, out=arr, mode="clip")
arr.take(pVec1, axis=1, out=arr, mode="clip")

行と列の両方を並べ替えるには、2回実行するか、頭を痛めたnumpy.unravel_indexでいシェナンガン引っ張る必要があると思います。


前述のとおり、インプレースアルゴリズムは困難です。あなたのソリューション numpy 1.6.2 では動作しません。および1.7.1(行/列の複製)。1.8.xがこの問題を修正するかどうかを確認する時間がありませんでした
Stefano M 14年

うーん。どこかにテストコードを投稿できますか?私の頭の中で、摘み取る前に最初に起こるインデックスのソート操作が必要なように感じます。このPMについて詳しく調査します。
meawoppl 14年

1
私は実行すると、このコードは私が取得します1.6.2test take, not overwriting: Truetest not-in-place take: Truetest in-place take: Falserr [3, 7, 8, 1, 4, 5, 9, 0, 2, 6]arr [30 70 80 70 40 50 90 30 80 90]ref [30 70 80 10 40 50 90 0 20 60]。そのためnp.take、少なくともnumpy 1.6.2では、インプレース置換を実行することを認識しておらず、混乱します。
ステファノM 14年

うん よく実証されています。これはおそらくバグと見なされます。少なくともドキュメントでは、入力と出力を同じ配列にすることはできません。おそらく確認して、そうでない場合を除いてください。
meawoppl

バグに同意しました。あなたのソリューションに間違った結果が生じる可能性があることを読者に警告するために、投稿にメモを追加する必要があるかもしれません。
ステファノM 14

2

スパース行列がCOO形式で保存されている場合、次が役立つ可能性があります

    A.row = perm[A.row];
    A.col = perm[A.col];

それは仮定A含有COOマトリックスを、そしてpermれるnumpy.array置換を含みます。これはありますメモリオーバーヘッド、行列の非零要素の数です。メートルmm


しかし、完全な密行列をまばらな行列として保存するためのメモリオーバーヘッドは何C00ですか?
フェデリコポロニ

スパース表現と(フル)デンス表現の両方で要素の数が等しいため、メモリの違いは単なる定数です(要素ごとのスパース表現では2 intsおよび1 、デンス表現ではfloat1つfloat)。しかし、このメソッドのメモリオーバーヘッドは、高密度の場合はになるため、通常のsに固執する可能性があります。n2numpy.ndarray
ビンセントトラッグ14年

1

コメントするほどの評判はありませんが、次のSOの質問が役立つと思います:https : //stackoverflow.com/questions/4370745/view-onto-a-numpy-array

基本的なポイントは、あなたが使用できることをしている基本的なスライシングをし、それはコピーせずに、アレイへのビューを作成しますが、あなたがしなければ、高度なスライシング/インデックスを、それはなりますコピーを作成します。


OPは順列を要求していますが、これは基本的なスライスでは不可能です。
ステファノM

もちろんあなたは正しいです。コピーがいつ行われるかを心配しているので、OPがスライシングで何が起こっているのかを理解することは(彼らが知らない場合に)役に立つと思いました。もし彼があなたの答えから何かを使ったなら、あなたはそれをあなたのループ内で使うので知っておくと良いと思います。
13

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.