note.nkmk.me

NumPy配列ndarrayの形状を変換するreshapeの使い方と-1の意味

Date: 2018-08-09 / Modified: 2019-12-02 / tags: Python, NumPy

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

ここでは以下の内容について説明する。

  • ndarray.reshape()メソッドの使い方
  • numpy.reshape()関数の使い方
  • 変換順序を指定: 引数order
  • -1による形状の指定
  • reshape()が返すのはビュー

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

サイズが1の新たな次元を追加する方法としてnumpy.newaxis、一次元配列に変換する方法としてravel(), flatten()もある。いずれもreshape()でも実現可能。

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

スポンサーリンク

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

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

import numpy as np

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

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

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

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となる。

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()関数の第二引数には必ずリストやタプルを指定する。numppy.ndarrayreshape()メソッドのように各次元の値を順に指定するとエラーValueErrorとなる。

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(np.reshape(a, 4, 6))
# ValueError: cannot reshape array of size 24 into shape (4,)

変換順序を指定: 引数order

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

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

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

numppy.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を使うことができる。

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

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

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)

reshape()が返すのはビュー

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

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

以下、numppy.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]

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

a = np.arange(6).reshape(2, 3)
print(a)
# [[0 1 2]
#  [3 4 5]]

a_step = a[:, ::2]
print(a_step)
# [[0 2]
#  [3 5]]

print(a_step.reshape(-1))
# [0 2 3 5]

print(np.shares_memory(a_step, a_step.reshape(-1)))
# False

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

a = np.arange(8).reshape(2, 4)
print(a)
# [[0 1 2 3]
#  [4 5 6 7]]

a_step = a[:, ::2]
print(a_step)
# [[0 2]
#  [4 6]]

print(a_step.reshape(-1))
# [0 2 4 6]

print(np.shares_memory(a_step, a_step.reshape(-1)))
# True
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事