NumPy配列ndarrayの形状を変換するreshapeの使い方 | note.nkmk.me (original) (raw)

NumPy配列ndarrayの形状shapeを変換するにはndarrayreshape()メソッドかnumpy.reshape()関数を使う。

目次

NumPy配列ndarrayの形状や次元数などを確認したい場合は以下の記事を参照。

reshape()は任意の形状に変換できるが、特定の形状変換には別の方法も用意されている。以下の記事を参照。いずれもreshape()でも実現できる。

また、NumPy配列ndarray同士の二項演算(四則演算など)では自動的に形状が揃えられてから計算されるブロードキャストという仕組みがある。

本記事のサンプルコードのNumPyのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。

`import numpy as np

print(np.version)

1.26.1

`

ndarray.reshape()メソッドの使い方

以下の一次元のNumPy配列ndarrayを例とする。

`a = np.arange(24) print(a)

[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]

print(a.shape)

(24,)

print(a.ndim)

1

`

numpy.ndarrayreshape()メソッドの第一引数に変換後の形状をリストやタプルで指定する。

`a_4_6 = a.reshape([4, 6]) print(a_4_6)

[[ 0 1 2 3 4 5]

[ 6 7 8 9 10 11]

[12 13 14 15 16 17]

[18 19 20 21 22 23]]

print(a_4_6.shape)

(4, 6)

print(a_4_6.ndim)

2

`

`a_2_3_4 = a.reshape((2, 3, 4)) print(a_2_3_4)

[[[ 0 1 2 3]

[ 4 5 6 7]

[ 8 9 10 11]]

[[12 13 14 15]

[16 17 18 19]

[20 21 22 23]]]

print(a_2_3_4.shape)

(2, 3, 4)

print(a_2_3_4.ndim)

3

`

元の配列と要素数が一致する形状でないとエラーValueErrorとなる。

`# a_5_6 = a.reshape([5, 6])

ValueError: cannot reshape array of size 24 into shape (5,6)

`

リストやタプルではなく、各次元の値を順に指定してもよい。

`print(a.reshape(4, 6))

[[ 0 1 2 3 4 5]

[ 6 7 8 9 10 11]

[12 13 14 15 16 17]

[18 19 20 21 22 23]]

print(a.reshape(2, 3, 4))

[[[ 0 1 2 3]

[ 4 5 6 7]

[ 8 9 10 11]]

[[12 13 14 15]

[16 17 18 19]

[20 21 22 23]]]

`

numpy.reshape()関数の使い方

numpy.reshape()関数では、第一引数に元のnumpy.ndarray、第二引数に変換したい形状をリストやタプルで指定する。元の配列と要素数が一致する形状でないとエラーValueErrorとなる。

`a = np.arange(24)

print(np.reshape(a, [4, 6]))

[[ 0 1 2 3 4 5]

[ 6 7 8 9 10 11]

[12 13 14 15 16 17]

[18 19 20 21 22 23]]

print(np.reshape(a, (2, 3, 4)))

[[[ 0 1 2 3]

[ 4 5 6 7]

[ 8 9 10 11]]

[[12 13 14 15]

[16 17 18 19]

[20 21 22 23]]]

print(np.reshape(a, [5, 6]))

ValueError: cannot reshape array of size 24 into shape (5,6)

`

numpy.reshape()関数の第二引数には必ずリストやタプルを指定する。numpy.ndarrayreshape()メソッドのように各次元の値を順に指定するとエラーTypeErrorとなる。

`# print(np.reshape(a, 4, 6))

TypeError: order must be str, not int

print(np.reshape(a, 2, 3, 4))

TypeError: reshape() takes from 2 to 3 positional arguments but 4 were given

`

変換順序を指定: 引数order

引数orderで変換順序を指定できる。order='C'がC言語方式でorder='F'がFORTRAN方式。デフォルトはorder='C'

以下のように結果が異なる。

`a = np.arange(24)

print(a.reshape([4, 6], order='C'))

[[ 0 1 2 3 4 5]

[ 6 7 8 9 10 11]

[12 13 14 15 16 17]

[18 19 20 21 22 23]]

print(a.reshape([4, 6], order='F'))

[[ 0 4 8 12 16 20]

[ 1 5 9 13 17 21]

[ 2 6 10 14 18 22]

[ 3 7 11 15 19 23]]

print(a.reshape([2, 3, 4], order='C'))

[[[ 0 1 2 3]

[ 4 5 6 7]

[ 8 9 10 11]]

[[12 13 14 15]

[16 17 18 19]

[20 21 22 23]]]

print(a.reshape([2, 3, 4], order='F'))

[[[ 0 6 12 18]

[ 2 8 14 20]

[ 4 10 16 22]]

[[ 1 7 13 19]

[ 3 9 15 21]

[ 5 11 17 23]]]

`

numpy.reshape()関数でも同様に指定可能。

`print(np.reshape(a, [4, 6], order='F'))

[[ 0 4 8 12 16 20]

[ 1 5 9 13 17 21]

[ 2 6 10 14 18 22]

[ 3 7 11 15 19 23]]

`

上述のようにnumpy.ndarrayreshape()メソッドは各次元の値を順に指定することを許可しているので、引数orderはキーワード引数で指定しないとエラーTypeErrorとなる。

numpy.reshape()関数では第三引数がorderとなるのでキーワードは省略可。

`# print(a.reshape([4, 6], 'F'))

TypeError: 'list' object cannot be interpreted as an integer

print(np.reshape(a, [4, 6], 'F'))

[[ 0 4 8 12 16 20]

[ 1 5 9 13 17 21]

[ 2 6 10 14 18 22]

[ 3 7 11 15 19 23]]

`

-1による形状の指定

形状の指定では-1を使うことができる。

以下、numpy.ndarrayreshape()メソッドを例とするがnumpy.reshape()関数でも同様。

-1とした次元の長さは他の次元の指定値から推測されて自動的に決定される。サイズの大きい配列の形状を変換するときに便利。

`a = np.arange(24)

print(a.reshape([4, -1]))

[[ 0 1 2 3 4 5]

[ 6 7 8 9 10 11]

[12 13 14 15 16 17]

[18 19 20 21 22 23]]

print(a.reshape([2, -1, 4]))

[[[ 0 1 2 3]

[ 4 5 6 7]

[ 8 9 10 11]]

[[12 13 14 15]

[16 17 18 19]

[20 21 22 23]]]

`

-1を指定できるのは一つの次元に対してのみ。二つ以上の次元に指定するとエラーValueError

`# print(a.reshape([2, -1, -1]))

ValueError: can only specify one unknown dimension

`

また、条件を満たす値が存在しない場合もエラーValueErrorとなる。

`# print(a.reshape([2, -1, 5]))

ValueError: cannot reshape array of size 24 into shape (2,newaxis,5)

`

ドキュメントには明記されていないが、-1に限らず負の値は同様に扱われる模様。

`print(a.reshape([4, -100]))

[[ 0 1 2 3 4 5]

[ 6 7 8 9 10 11]

[12 13 14 15 16 17]

[18 19 20 21 22 23]]

`

reshape()が返すのはビュー

numpy.ndarrayreshape()メソッドもnumpy.reshape()関数も可能な限りコピーではなくビュー(参照)を返す。あくまでも「可能な限り」なのでメモリレイアウトなどによってはビューではなくコピーを返す場合もある。後述。

numpy.ndarrayにおけるビューとコピーについての詳細は以下の記事を参照。

以下、numpy.ndarrayreshape()メソッドを例とするがnumpy.reshape()関数でも同様。

reshape()はビューを返し、元のnumpy.ndarrayとメモリを共有する。

`a = np.arange(8) print(a)

[0 1 2 3 4 5 6 7]

a_2_4 = a.reshape([2, 4]) print(a_2_4)

[[0 1 2 3]

[4 5 6 7]]

print(np.shares_memory(a, a_2_4))

True

`

元のオブジェクトを変更するとreshape()メソッドが返したビューも変更される。

`a[0] = 100 print(a)

[100 1 2 3 4 5 6 7]

print(a_2_4)

[[100 1 2 3]

[ 4 5 6 7]]

`

逆の場合も同様。reshape()メソッドが返したビューを変更すると元のオブジェクトも変更される。

`a_2_4[0, 0] = 0 print(a_2_4)

[[0 1 2 3]

[4 5 6 7]]

print(a)

[0 1 2 3 4 5 6 7]

`

コピーを取得したい場合はcopy()メソッドを使う。この場合、それぞれのオブジェクトに変更を加えても他方のオブジェクトは影響されない。

`a_2_4_copy = a.reshape([2, 4]).copy() print(a_2_4_copy)

[[0 1 2 3]

[4 5 6 7]]

print(np.shares_memory(a, a_2_4_copy))

False

a[0] = 100 print(a)

[100 1 2 3 4 5 6 7]

print(a_2_4_copy)

[[0 1 2 3]

[4 5 6 7]]

a_2_4_copy[0, 0] = 200 print(a_2_4_copy)

[[200 1 2 3]

[ 4 5 6 7]]

print(a)

[100 1 2 3 4 5 6 7]

`

デフォルトでビューではなくコピーが返される例は以下の通り。ステップを指定したスライスをreshape()で変換した結果、メモリ上のstrideが一定にならない場合などはコピーが返される。

`a = np.arange(8).reshape(2, 4) print(a)

[[0 1 2 3]

[4 5 6 7]]

a_step3 = a[:, ::3] print(a_step3)

[[0 3]

[4 7]]

a_step3_reshape = a_step3.reshape(-1) print(a_step3_reshape)

[0 3 4 7]

print(np.shares_memory(a_step3, a_step3_reshape))

False

`

ステップを指定したスライスでもstrideが一定であればビューが返される。

`a_step2 = a[:, ::2] print(a_step2)

[[0 2]

[4 6]]

a_step2_reshape = a_step2.reshape(-1) print(a_step2_reshape)

[0 2 4 6]

print(np.shares_memory(a_step2, a_step2_reshape))

True

`