web-dev-qa-db-ja.com

NumPyマトリックスクラスの非推奨ステータス

NumPyのmatrixクラスのステータスは何ですか?

代わりにndarrayクラスを使用するように言われ続けます。私が書いた新しいコードでmatrixクラスを使用する価値/安全性はありますか?代わりにndarraysを使用する必要がある理由がわかりません。

42
Andras Deak

tl; dr:numpy.matrixクラスは非推奨になっています。依存関係としてクラスに依存するいくつかの重要なライブラリ(最大のものはscipy.sparse)があり、クラスの短期的な適切な非推奨を妨げますが、ユーザーはndarrayクラス(通常は- numpy.array 便利な関数)代わりに。行列乗算に@演算子が導入されたことにより、行列の相対的な利点の多くが削除されました。

なぜマトリックスクラスではないのですか?

numpy.matrixnumpy.ndarrayのサブクラスです。もともとは線形代数を含む計算で便利に使用するためのものでしたが、より一般的な配列クラスのインスタンスと比較した場合の動作には制限と驚くべき違いがあります。動作の基本的な違いの例:

  • 形状:配列は、0から無限(または32)までの任意の数の次元を持つことができます。行列は常に2次元です。奇妙なことに、マトリックスをより多くの次元で作成にすることはできませんが、シングルトン次元をマトリックスに注入して、技術的には多次元マトリックスにすることができます:np.matrix(np.random.Rand(2,3))[None,...,None].shape == (1,2,3,1)(これは実用的な重要性ではありません)。
  • インデックス付け:配列のインデックス付けは、 どのようにインデックス付けするか に応じて、任意のサイズの配列を提供できます。行列の式にインデックスを付けると、常に行列が得られます。これは、2次元配列のarr[:,0]arr[0,:]の両方が1次元のndarrayを与えるのに対し、mat[:,0](N,1)の形状を持ち、mat[0,:]matrixの場合の形状(1,M)を持つことを意味します。
  • 算術演算:昔の行列を使用する主な理由は、行列の算術演算(特に乗算と累乗)が行列演算(行列の乗算と累乗)を実行することでした。配列についても同じことが、要素ごとの乗算と累乗をもたらします。したがって、mat1 * mat2mat1.shape[1] == mat2.shape[0]の場合に有効ですが、arr1 * arr2arr1.shape == arr2.shapeの場合に有効です(もちろん、結果はまったく異なるものを意味します)。また、驚くべきことに、mat1 / mat2は2つの行列のelementwise除算を実行します。この振る舞いはおそらくndarrayから継承されますが、特に*の意味を考慮すると、行列には​​意味がありません。
  • 特別な属性:マトリックスには、配列に加えて いくつかの便利な属性 があります。mat.Aおよびmat.A1は、それぞれnp.array(mat)およびnp.array(mat).ravel()と同じ値を持つ配列ビューです。 mat.Tおよびmat.Hは、行列の転置および共役転置(随伴)です。 arr.Tは、ndarrayクラスに存在する唯一の属性です。最後に、mat.Imatの逆行列です。

Ndarraysまたはマトリックスのいずれかで機能するコードを書くのは簡単です。しかし、2つのクラスがコード内で相互作用しなければならない可能性がある場合、事態は困難になり始めます。特に、多くのコードcouldは、ndarrayのサブクラスに対して自然に機能しますが、matrixは、ダックタイピングに依存しようとするコードを簡単に破ることができる、不適切なサブクラスです。形状(3,4)の配列と行列を使用した次の例を考えてみましょう。

import numpy as np

shape = (3, 4)
arr = np.arange(np.prod(shape)).reshape(shape) # ndarray
mat = np.matrix(arr) # same data in a matrix
print((arr + mat).shape)           # (3, 4), makes sense
print((arr[0,:] + mat[0,:]).shape) # (1, 4), makes sense
print((arr[:,0] + mat[:,0]).shape) # (3, 3), surprising

2つのオブジェクトのスライスの追加は、スライスするディメンションに応じて壊滅的に異なります。形状が同じ場合、行列と配列の両方で加算が要素ごとに行われます。上記の最初の2つのケースは直感的です。2つの配列(行列)を追加し、それぞれから2つの行を追加します。最後のケースは本当に驚くべきことです。おそらく、2つの列を追加するつもりでしたが、マトリックスになりました。もちろん、arr[:,0]の形状は(3,)であり、形状(1,3)と互換性がありますが、mat[:.0]の形状は(3,1)です。 2つは broadcast で、(3,3)を形成します。

最後に、マトリックスクラスの最大の利点(つまり、多数のマトリックス製品を含む複雑なマトリックス式を簡潔に定式化できること)は、 python 3.5 で導入されたとき、@ matmul演算子が削除されました最初に実装されました numpy 1.1 。単純な2次形式の計算を比較します。

v = np.random.Rand(3); v_row = np.matrix(v)
arr = np.random.Rand(3,3); mat = np.matrix(arr)

print(v.dot(arr.dot(v))) # pre-matmul style
# 0.713447037658556, yours will vary
print(v_row * mat * v_row.T) # pre-matmul matrix style
# [[0.71344704]]
print(v @ arr @ v) # matmul style
# 0.713447037658556

上記を見ると、線形代数を扱うためにマトリックスクラスが広く好まれた理由は明らかです。中置*演算子により、式の冗長性が低くなり、読みやすくなりました。ただし、最新のpythonとnumpyを使用する@演算子でも同じ可読性が得られます。さらに、行列の場合、形状(1,1)の行列が得られることに注意してください。これは技術的にはスカラーでなければなりません。これは、列ベクトルをこの「スカラー」で乗算できないことも意味します。上記の例の(v_row * mat * v_row.T) * v_row.Tは、形状(1,1)および(3,1)の行列はこの順序で乗算できないため、エラーが発生します。

完全を期すために、matmul演算子はndarraysが行列と比較して準最適である最も一般的なシナリオを修正しますが、ndarraysを使用して線形代数をエレガントに処理することにはいくつかの欠点があります(人々はまだ全体的に後者に固執することが望ましい)。そのような例の1つが行列の累乗です:mat ** 3は、行列の適切な3番目の行列の累乗です(一方、ndarrayの要素単位の立方体です)。残念ながら、numpy.linalg.matrix_powerはかなり冗長です。さらに、インプレースマトリックス乗算は、マトリックスクラスに対してのみ正常に機能します。対照的に、 PEP 465python grammar の両方は、@=をmatmulの拡張割り当てとして許可しますが、これはnumpy 1.15のndarrayには実装されていません。

非推奨の履歴

matrixクラスに関する上記の複雑さを考慮すると、その廃止の可能性について長い間議論が繰り返されてきました。このプロセスの大きな前提条件だった@挿入演算子の導入 2015年9月に発生 。残念ながら、初期のマトリックスクラスの利点は、その使用が広範囲に広がることを意味していました。マトリックスクラスに依存するライブラリがあります(最も重要な依存関係の1つは scipy.sparse で、これはnumpy.matrixの両方のセマンティクスを使用し、高密度化するとマトリックスを返すことがよくあります)。

すでに 2009年のnumpyメーリングリストスレッド などの発言が見つかりました

numpyは、数学の1つのブランチではなく、汎用の計算ニーズに合わせて設計されました。 nd-arrayは、多くのことに非常に役立ちます。対照的に、たとえば、Matlabは元々、線形代数パッケージへの簡単なフロントエンドとして設計されました。個人的に、Matlabを使用したとき、私は非常に厄介なことに気付きました-私は通常、実際に行列計算を行った数行ごとに、線形代数とは関係のない数百行のコードを書いていました。だから、私はnumpyの方法をはるかに好みます-コードの線形代数行はより長く厄介ですが、残りははるかに優れています。

Matrixクラスはこの例外です。線形代数を表現する自然な方法を提供するために作成されました。ただし、行列と配列を混在させると事態は少し複雑になり、行列に固執する場合でも混乱と制限があります。行ベクトルと列ベクトルをどのように表現しますか?行列を反復処理すると何が得られますか?等.

これらの問題に関する多くの議論、多くの良いアイデア、それを改善する方法についての少しのコンセンサスがありましたが、それをするスキルを持った人はそれをする十分な動機を持っていません。

これらは、マトリックスクラスから生じる利点と困難を反映しています。私が見つけることができる非推奨の最初の提案は 2008年から です。ただし、それ以降に変更された直感に反する動作に一部動機付けられています(特に、マトリックスのスライスと反復は、予想される)。提案は、これが非常に物議を醸す主題であり、行列乗算のための中置演算子が重要であることの両方を示しました。

私が見つけることができた次の言及 2014年から非常に実りのあるスレッドであることが判明しました。次の議論では、numpyサブクラス全般の処理の問題を提起します この一般的なテーマはまだ非常にテーブルにあります強い批判 :もあります

(Githubで)この議論のきっかけとなったのは、正しく機能するアヒル型コードを書くことができないことです。

  • ndarrays
  • 行列
  • scipy.sparseスパース行列

3つすべてのセマンティクスは異なります。 scipy.sparseは、マトリックスとndarrayの間のどこかにあり、マトリックスのようにランダムに機能するものとそうでないものがあります。

いくつかの誇大広告を追加すると、開発者の観点から言えば、np.matrixはPythonのndarrayセマンティクスの無言のルールを台無しにすることで、存在するだけで既に悪を行っていると言えます。

その後、行列の将来の可能性について多くの貴重な議論が続きます。その時点で@演算子がなくても、マトリックスクラスの廃止と、それが下流のユーザーにどのように影響するかについて、多くの考えがあります。私が知る限り、この議論は直接matmulを導入するPEP 465の始まりにつながりました。

2015年初頭

私の意見では、np.matrixの「修正済み」バージョンは、(1)np.ndarrayサブクラスではなく、(2)numpy自体ではなくサードパーティのライブラリに存在する必要があります。

現在の状態のnp.matrixをndarrayサブクラスとして修正することは実際に実行可能ではないと思いますが、修正されたマトリックスクラスでさえ、numpy自体には属していません。 numpyにマトリックスクラスが存在するだけで、新しいユーザーが迷うことは言うまでもありません。

@演算子がしばらく利用可能になったら、 非推奨の議論が再び浮上しましたトピックのリレイズ マトリックスの非推奨とscipy.sparseの関係について。

最終的に、 2017年11月下旬にnumpy.matrixを廃止する最初のアクションが取られました 。クラスの扶養家族について:

コミュニティはscipy.sparseマトリックスのサブクラスをどのように処理しますか?これらはまだ一般的に使用されています。

彼らはしばらくの間どこにも行きません(少なくともまばらなndarrayが具体化するまで)。したがって、np.matrixは削除するのではなく、移動する必要があります。

source )および

誰でもできる限りnp.matrixを削除したいのですが、すぐにそれを行うとreally破壊的になります。

  • よく知らない人によって書かれた小さなスクリプトがたくさんあります。 np.matrixを使用しないことを学んでもらいたいのですが、スクリプトをすべて壊すことはそれを行うのに苦痛な方法です

  • Scikit-learnのような主要なプロジェクトには、scipy.sparseのためにnp.matrixを使用する以外の選択肢はありません。

だから私は次のようなものだと思う:

  • 今、または誰かがPRをまとめるとき:np.matrix .__ init__でPendingDeprecationWarningを発行し(scikit-learnや友人のパフォーマンスを低下させない限り)、ドキュメントの上部に大きな警告ボックスを置きます。ここでのアイデアは、実際に誰かのコードを壊すことではなく、代替手段があればこれを使うべきだとは絶対に思わないというメッセージを出し始めることです。

  • Scipy.sparseに代わる手段が追加された後、既存のスクリプトが破損しないように警告音を増やし、おそらくFutureWarningに至るまで、警告音を増やします

  • 最終的に、メンテナンスコストを削減できると考える場合は、サブパッケージに分割します

ソース )。

現状

2018年5月(numpy 1.15、関連する pull request および commit )の時点で matrix class docstring には次の注記が含まれています:

線形代数の場合でも、このクラスを使用することは推奨されなくなりました。代わりに、通常の配列を使用してください。このクラスは将来削除される可能性があります。

そして、同時にPendingDeprecationWarningmatrix.__new__に追加されました。残念ながら、 非推奨の警告は(ほとんどの場合)デフォルトで沈黙します なので、numpyのほとんどのエンドユーザーにはこの強力なヒントは表示されません。

最後に、 numpyロードマップ 2018年11月現在、「タスクと機能[numpyコミュニティ]はリソースに投資しています "の1つとして、複数の関連トピックに言及しています。

NumPyの中には、実際にはNumPyのスコープと一致しないものがあります。

  • Numpy.fftのバックエンドシステム(そのため、例えばfft-mklがnumpyにパッチを適用する必要はありません)
  • マスクされた配列をndarrayサブクラスにならないように書き直します-おそらく別のプロジェクトで?
  • Duck-arrayタイプとしてのMaskedArray、および/または
  • 欠損値をサポートするdtype
  • Linalgとfftのnumpyとscipyのオーバーラップを処理する(および実装する)方法の戦略を記述します。
  • Np.matrixの廃止

大規模なライブラリ/多くのユーザー(特にscipy.sparse)がマトリックスクラスに依存している限り、この状態が続く可能性があります。ただし、 _ onsome Discussion があります。これは、 scipy.sparse など、pydata/sparseを他の何かに依存するように移動するためのものです。非推奨プロセスの開発に関係なく、ユーザーはndarrayクラスを新しいコードで使用し、可能であれば古いコードを移植する必要があります。最終的には、マトリックスクラスはおそらく現在の形式での存在によって引き起こされる負担の一部を取り除くために、おそらく別のパッケージになります。

57
Andras Deak