web-dev-qa-db-ja.com

numpy.array形状(R、1)と(R、)の違い

numpyでは、いくつかの操作は(R, 1)という形で戻りますが、いくつかは(R,)を返します。明示的なreshapeが必要なので、これは行列の乗算をより面倒にします。たとえば、行列Mが与えられたときにnumpy.dot(M[:,0], numpy.ones((1, R)))を実行したい場合、Rは行数です(もちろん、同じ問題が列方向にも発生します)。 matrices are not alignedの形状はM[:,0]ですがnumpy.ones((1, R))の形状は(R,)であるため、(1, R)エラーが発生します。

だから私の質問は次のとおりです。

  1. Shape (R, 1)(R,)の違いは何ですか。私は文字通りそれが数のリストとすべてのリストが数だけを含むリストのリストであることを知っています。行列乗算を簡単にするために、(R, 1)ではなく形状(R,)を優先するようにnumpyを設計しないのはなぜだろうか。

  2. 上記の例にもっと良い方法はありますか?明示的にこのような形を変えることなく:numpy.dot(M[:,0].reshape(R, 1), numpy.ones((1, R)))

258
clwen

1. NumPyにおける形の意味

あなたは「文字通りそれが数のリストとすべてのリストが数だけを含むリストのリストであることを私は知っています」と書いています、しかしそれはそれについて考えるための少し役に立たない方法です。

NumPy配列について考える最良の方法は、それらが2つの部分、つまり生の要素のブロックである data buffer と、データの解釈方法を記述する view で構成されることです。バッファ。

たとえば、12個の整数の配列を作成するとします。

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

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

┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  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,)

ここで、 shape (12,)は、配列が0から11の範囲の単一のインデックスでインデックス付けされていることを意味します。概念的には、この単一のインデックスをiname__とすると、配列aname__は次のようになります。

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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

reshape であれば、データバッファは変わりません。代わりに、データを解釈するための異なる方法を説明する新しいビューを作成します。だから後:

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

配列bname__は、aname__と同じデータバッファを持ちますが、現在は0から2および0から3まで続く two indexによってインデックスが付けられています。 2つのインデックスにiname__とjname__というラベルを付けると、配列bname__は次のようになります。

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番目のインデックスはすばやく変化し、最初のインデックスはゆっくり変化することがわかります。これを逆にしたい場合は、ordername__パラメーターを指定できます。

>>> 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))

配列dname__は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内部ドキュメント を参照してください。

何をする?

numpy.reshape は新しいビューを作成するだけなので、必要に応じて使用することを怖がってはいけません。別の方法で配列にインデックスを付けたい場合は、これが適切なツールです。

しかし、長い計算では、そもそも「正しい」形状を持つ配列を構築するように手配することが可能です。したがって、変形や転置の数を最小限に抑えることができます。しかし、改革の必要性につながった実際の状況を見ずに、何を変更すべきかを言うのは困難です。

あなたの質問の例は次のとおりです。

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

しかしこれは現実的ではありません。まず、この式は

M[:,0].sum()

結果をより簡単に計算します。第二に、本当にコラム0に関して何か特別なことがありますか?おそらく、あなたが実際に必要としているものは、

M.sum(axis=0)
453
Gareth Rees

(R,)(1,R)の違いは文字通りあなたが使う必要があるインデックスの数です。 ones((1,R))はたまたま1行しか持たない2-D配列です。 ones(R)はベクトルです。一般的に、変数が複数の行/列を持つことが意味を成さないのであれば、シングルトン次元の行列ではなく、ベクトルを使うべきです。

あなたの特定のケースでは、いくつかの選択肢があります。

1)2番目の引数をベクトルにするだけです。以下はうまくいきます:

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

2)行列演算のようなmatlabが欲しい場合は、matrixの代わりにクラスndarrayを使用してください。すべての行列は2次元配列であることを余儀なくされ、演算子*は要素ごとではなく行列の乗算を行います(したがって、ドットは不要です)。私の経験では、これはそれが価値があるより多くの問題ですが、あなたがmatlabに慣れていればそれはいいかもしれません。

13
Evan

形はタプルです。次元が1つしかない場合、形状は1つの数字になり、カンマの後は空白になります。 2次元以上の場合は、すべてのコンマの後に番号が付きます。

# 1 dimension with 2 elements, shape = (2,). 
# Note there's nothing after the comma.
z=np.array([  # start dimension
    10,       # not a dimension
    20        # not a dimension
])            # end dimension
print(z.shape)

(2)

# 2 dimensions, each with 1 element, shape = (2,1)
w=np.array([  # start outer dimension 
    [10],     # element is in an inner dimension
    [20]      # element is in an inner dimension
])            # end outer dimension
print(w.shape)

(2,1)

6
Katie Jergens

その基本配列クラスにとって、2次元配列は1次元や3次元の配列に比べて特別なものではありません。寸法を保持する操作、寸法を小さくする操作、他の組み合わせ、あるいは拡大する操作さえあります。

M=np.arange(9).reshape(3,3)
M[:,0].shape # (3,) selects one column, returns a 1d array
M[0,:].shape # same, one row, 1d array
M[:,[0]].shape # (3,1), index with a list (or array), returns 2d
M[:,[0,1]].shape # (3,2)

In [20]: np.dot(M[:,0].reshape(3,1),np.ones((1,3)))

Out[20]: 
array([[ 0.,  0.,  0.],
       [ 3.,  3.,  3.],
       [ 6.,  6.,  6.]])

In [21]: np.dot(M[:,[0]],np.ones((1,3)))
Out[21]: 
array([[ 0.,  0.,  0.],
       [ 3.,  3.,  3.],
       [ 6.,  6.,  6.]])

同じ配列を与える他の式

np.dot(M[:,0][:,np.newaxis],np.ones((1,3)))
np.dot(np.atleast_2d(M[:,0]).T,np.ones((1,3)))
np.einsum('i,j',M[:,0],np.ones((3)))
M1=M[:,0]; R=np.ones((3)); np.dot(M1[:,None], R[None,:])

MATLABは2次元配列から始めました。新しいバージョンでは、より多くの次元を使用できますが、下限は2のままです。ただし、(1,3) v (3,1)という形の行行列と列行列の違いに注意する必要があります。どのくらいの頻度で[1,2,3].'を書きましたか。私はrow vectorcolumn vectorを書くつもりでしたが、その2d制約では、MATLABにはベクトルがありません - 少なくとも1dであるというベクトルの数学的意味ではありません。

np.atleast_2d(_1dと_3dのバージョンも)を見ましたか?

4
hpaulj

1)(R, 1)より(R,)の形を好まないのは、それが不必要に事を複雑にするからです。その上、(R, 1)の代わりにデフォルトで長さRのベクトルのために(1, R)の形を持つことが好ましいのでしょうか?それを単純に保ち、追加の次元が必要なときは明示的にするのが良いでしょう。

2)あなたの例では、あなたはnp.outerを使うことによってreshape呼び出しなしでこれを行うことができるように外積を計算しています:

np.outer(M[:,0], numpy.ones((1, R)))
0
bogatron