YutaKaのPython教室

Python の文法やライブラリ、API、環境構築について画像・動画・ソースコード付きで徹底解説!

【徹底図解】numpy reshape | ndarrayの形状変更!基本から意外過ぎる応用まで【サンプルコード】

numpyの配列はndarrayという型で取り扱われます。

配列ndarrayの形状shapeは、reshape()関数・メソッドによって変更可能です。

reshape()の使用方法について整理しました。

配列の形状とは?

配列の形状とは、配列の次元数と各次元の要素数がいくつであるかを指します。

下記のサンプルコードで、配列ndarrayの形状shape属性を確認してみます。

# 一次元配列の形状
A = np.array([0, 1, 2, 3])
# A.shape → (4,)
 
# 二次元配列の形状
B = np.array([[0, 1, 2],
              [3, 4, 5]])
# B.shape → (2, 3)
shapeのポイント

・配列ndarrayの形状はshapeで確認できる。

shapeの要素数は配列の次元と一致(1次元なら1つ、2次元なら2つ…)

shapeの値は、各次元の要素数を表す

二次元配列を例にポイントを図解してみます。

【基本】reshape()の使い方

reshape()を使用すると、配列の形状shapeを変更することができます。

reshape()には、関数とメソッドがありますが、機能は同じです。

メソッドの場合には、引数に変形後の形状を与えます。

A = np.array([0, 1, 2, 3])
# A.shape → (4,)
B = A.reshape((2,2))
# B.shape → (2, 2)
# B → array([[0, 1],
#           [2, 3]])

関数の場合には、変形前の配列と変形後の形状を引数に与えます。

A = np.array([0, 1, 2, 3])
# A.shape → (4,)
B = np.reshape(A, (2,2))
# B.shape → (2, 2)
# B → array([[0, 1],
#            [2, 3]])

reshape()使用時の注意:引数、要素数、ビュー

reshape()で配列ndarrayの形状を変更する際の注意点を紹介します。

Note

① 引数はリストやタプル

② 変更前後で配列の要素数は一致

③ reshape()はビューを返す

① 引数はリストやタプル

変更後の配列の形状は、リストやタプルで与えます。

直接入力することも、変数で渡すことも可能です。

# リスト直接入力でreshape
A = np.array([0, 1, 2, 3])
# A.shape → (4,)
B = A.reshape([2, 2])
# B.shape → (2, 2)
# B → array([[0, 1],
#            [2, 3]])
 
# リストの変数でreshape
A = np.array([0, 1, 2, 3, 4, 5])
# A.shape → (4,)
b = [2, 3]
B = A.reshape(b)
# B.shape → (2, 2)
# B → array([[0, 1, 2],
#            [3, 4, 5]])

② 変更前後で配列の要素数は一致

変更前後の要素数は、必ず一致するようにreshape()の引数を渡します。

過不足分を自動で削ったり、補完したりはしてくれないので注意しましょう。

配列の要素数は、shape要素の積またはsize属性の値で確認できます。

A = np.array([[0, 1, 2, 3], [4, 5, 6, 7]])
# A.shape → (2, 4) # 2×4 = 8
# A.size → 8

変更前後で、要素数が異なるとエラーになります。

A = np.array([[0, 1, 2, 3], [4, 5, 6, 7]])
# A.size → 8
B = A.reshape(2, 3) # エラーになります
# ValueError: cannot reshape array of size 8 into shape (2,3)

③ reshape()はビューを返す

reshape()は、可能な限りビューを返します。

この機能が便利であって、初めのうちはわかりにくいところです。

ここでのビュー(view)の意味は、見え方や眺めに近い概念です。

重要な点は、変更前後どちらかの配列要素を書き換えると、もう片方の配列要素にも影響する点です。

ビューの概念を図解します。必ずしも厳密ではないですが、使用に耐えうる概念です。

要素の一部を変更します。

引数に-1を指定して形状予測

変更後の形状の一部に-1を設定すると、要素数に応じて適切な形状を予測します。

例えば、一次元配列を二次元配列にすることを考えます。

A = np.array([[0, 1, 2, 3], [4, 5, 6, 7]])
# A.size → 8
B = A.reshape(2, -1)
# B.shape → (2, 4) # 4が自動で予測されました。
# B → array([[0, 1, 2, 3],
#            [4, 5, 6, 7]])

ここでは、次のようなフローで形状の自動予測が行われています。

reshape()での -1 使用時のフロー

【ルール】reshape()では、変更前後で配列の要素数は等しい。

⇒二次元配列の行数を与えれば、列数は一意に決まる。

⇒列数に-1を指定すれば、自動で列数が予測される。

配列形状を一意に決めるため、一度に使用できる-1は一つまでです。

形状の2か所以上はpythonが自動で予測することができないためです。

【応用】一次元配列を一次元配列にreshape()する理由

配列計算でブロードキャストを使用する際に、非常に重要なテクニックです。

次のように一次元配列をreshape()しないで、二次元配列にかけるとエラーになります。

A = np.array([[1, 2, 3], [4, 5, 6]])
x = np.array([0, 1])
# B = x * A  # ここでValueErrorになる

ブロードキャストする際は、一次元配列を行方向か列方向に繰り返して、二次元配列と同じ形状を作り出します。

しかし、下図のように行方向、列方向、どちらに一次元配列を繰り返しても二次元配列と同じ形状の配列は作れません。

次に一次元配列を縦方向に並ぶようにreshape()します

一次元配列を行方向に繰り返して、二次元配列と同じ形状を作り出せるようになるので、ブロードキャスト可能になります。

A = np.array([[1, 2, 3], [4, 5, 6]])
x = np.array([0, 1]).reshape((2, 1))
# x → array([[0],
#            [1]])
B = x * A  # これはエラーになりません。