1. ホーム
  2. python

[解決済み] numpy.arrayの形状(R, 1)と(R,)の違いについて

2022-03-25 06:48:27

質問

numpy のように、いくつかの操作は形状で返されます。 (R, 1) を返しますが、一部は (R,) . このため,行列の乗算はより面倒になります. reshape が必要です。例えば、行列 M を行いたい場合 numpy.dot(M[:,0], numpy.ones((1, R))) ここで R は行数です(もちろん、列単位でも同じ問題が発生します)。次のようになる。 matrices are not aligned というエラーが発生します。 M[:,0] は形状 (R,) しかし numpy.ones((1, R)) が形になっている (1, R) .

そこで質問なのですが

  1. 形状はどう違うのですか (R, 1)(R,) . 文字どおり、数字のリストとリストのリストで、すべてのリストが数字だけを含んでいることは知っています。ただ、なぜ numpy 形状を優先するように (R, 1) ではなく (R,) を使用すると、行列の乗算が容易になります。

  2. 上記の例でもっと良い方法はないでしょうか?このように明示的にリシェイプすることなく numpy.dot(M[:,0].reshape(R, 1), numpy.ones((1, R)))

解決方法は?

1. NumPyにおける図形の意味

文字どおり数字のリストとリストのリストで、すべてのリストが数字だけを含んでいることは知っています" しかし、それは少し役に立たない考え方です。

NumPyの配列について考える最も良い方法は、配列は2つの部分から構成されているということです。 データバッファ は生の要素のブロック、そして ビュー は、データバッファをどのように解釈するかを記述します。

例えば、12個の整数の配列を作成する場合。

>>> a = numpy.arange(12)
>>> a
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

次に a はデータバッファで構成され、このように配置されます。

┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

と、データの解釈方法を説明するビューがあります。

>>> a.flags
  C_CONTIGUOUS : True
  F_CONTIGUOUS : True
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False
>>> a.dtype
dtype('int64')
>>> a.itemsize
8
>>> a.strides
(8,)
>>> a.shape
(12,)

ここでは 形状 (12,) は,配列のインデックスが0から11までの1つであることを意味します.概念的には、この単一のインデックスにラベルを付けると i の場合、配列は a はこのようになります。

i= 0    1    2    3    4    5    6    7    8    9   10   11
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

もし、私たちが リシェイプ の配列の場合、これはデータバッファを変更するものではありません。その代わり、データを解釈する異なる方法を記述した新しいビューを作成します。だから、その後。

>>> b = a.reshape((3, 4))

配列 b と同じデータ・バッファを持ちます。 a でインデックスされます。 2 はそれぞれ0から2、0から3までのインデックスを持つ。2つのインデックスにラベルを付けると ij の場合、配列 b はこのようになります。

i= 0    0    0    0    1    1    1    1    2    2    2    2
j= 0    1    2    3    0    1    2    3    0    1    2    3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

ということになります。

>>> b[2,1]
9

2番目のインデックスは素早く変化し、1番目のインデックスはゆっくりと変化しているのがわかると思います。もし、これを逆にしたい場合は order パラメータを使用します。

>>> c = a.reshape((3, 4), order='F')

で、このようなインデックスを持つ配列になります。

i= 0    1    2    0    1    2    0    1    2    0    1    2
j= 0    0    0    1    1    1    2    2    2    3    3    3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

ということになります。

>>> c[2,1]
5

これで、配列がサイズ1の1つ以上の次元を持つ形状を持つことの意味が明らかになったはずです。後ほど。

>>> d = a.reshape((12, 1))

配列 d は2つのインデックスで示され、最初のインデックスは0から11まで、2番目のインデックスは常に0である。

i= 0    1    2    3    4    5    6    7    8    9   10   11
j= 0    0    0    0    0    0    0    0    0    0    0    0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

といった具合です。

>>> d[10,0]
10

長さ1の次元は(ある意味で)自由であり、あなたが街に出ることを止めるものは何もありません。

>>> e = a.reshape((1, 2, 1, 6, 1))

このようなインデックスを持つ配列を与えます。

i= 0    0    0    0    0    0    0    0    0    0    0    0
j= 0    0    0    0    0    0    1    1    1    1    1    1
k= 0    0    0    0    0    0    0    0    0    0    0    0
l= 0    1    2    3    4    5    0    1    2    3    4    5
m= 0    0    0    0    0    0    0    0    0    0    0    0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

といった具合です。

>>> e[0,1,0,0,0]
6

をご覧ください。 NumPy内部のドキュメント は、配列の実装方法についての詳細です。

2. どうすればいい?

以来 numpy.reshape は新しいビューを作成するだけなので、必要なときにいつでも使用することを恐れる必要はありません。これは、配列のインデックスを別の方法で作成したいときに使用する正しいツールです。

しかし、長い計算では、最初に正しい形状の配列を作成し、再形成や転置の回数を最小限に抑えるように手配することが通常可能です。しかし、再形成が必要になった実際の状況を見なければ、何を変えるべきかを言うのは難しいのです。

ご質問の例では

numpy.dot(M[:,0], numpy.ones((1, R)))

が、これは現実的ではありません。まず、この表現。

M[:,0].sum()

はよりシンプルに結果を計算します。次に、0列について本当に特別なことがあるのでしょうか?おそらく、実際に必要なのは

M.sum(axis=0)