NumPy配列ndarrayの形状を変換するreshapeの使い方 | note.nkmk.me (original) (raw)
NumPy配列ndarray
の形状shape
を変換するにはndarray
のreshape()
メソッドかnumpy.reshape()
関数を使う。
目次
NumPy配列ndarray
の形状や次元数などを確認したい場合は以下の記事を参照。
reshape()
は任意の形状に変換できるが、特定の形状変換には別の方法も用意されている。以下の記事を参照。いずれもreshape()
でも実現できる。
- 関連記事: NumPy配列ndarrayに次元を追加するnp.newaxis, np.expand_dims()
- 関連記事: NumPy配列ndarrayのサイズ1の次元を削除するnp.squeeze()
- 関連記事: NumPy配列ndarrayを一次元化(平坦化)するravelとflatten
また、NumPy配列ndarray
同士の二項演算(四則演算など)では自動的に形状が揃えられてから計算されるブロードキャストという仕組みがある。
- 関連記事: NumPyのブロードキャスト(形状の自動変換)
本記事のサンプルコードの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.ndarray
のreshape()
メソッドの第一引数に変換後の形状をリストやタプルで指定する。
`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.ndarray
のreshape()
メソッドのように各次元の値を順に指定するとエラー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.ndarray
のreshape()
メソッドは各次元の値を順に指定することを許可しているので、引数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.ndarray
のreshape()
メソッドを例とするが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.ndarray
のreshape()
メソッドもnumpy.reshape()
関数も可能な限りコピーではなくビュー(参照)を返す。あくまでも「可能な限り」なのでメモリレイアウトなどによってはビューではなくコピーを返す場合もある。後述。
numpy.ndarray
におけるビューとコピーについての詳細は以下の記事を参照。
以下、numpy.ndarray
のreshape()
メソッドを例とするが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
`