NumPy配列にはshape
というプロパティがあり、これは各次元の要素数を表したものです。例えば、2次元配列なら(行数、列数)で表すことができます。
NumPyのndarrayのインスタンス変数shapeの意味 /features/numpy-shape.html
今回は、NumPyの配列のshape
の形状変換をする関数np.reshape
と、似たような機能を持つnp.resize
について解説します。
形状変換は、あらゆる場面でよく目にする使用頻度の高い機能です。この記事を通して使い方を学ぶと参考になるはずです。
np.reshape
まずは、一般的にもよく使用されるnp.reshape
から解説します。APIドキュメントは以下のようになっています。
numpy.reshape(a, newshape, order=’C’)
params:
パラメータ名 | 型 | 概要 |
---|---|---|
a |
ndarray | 変換元の配列 |
newshape |
intもしくは intのタプル orリスト |
変換後の配列の形状を指定します。intの場合、指定した要素数分の1次元配列になります。タプルの場合は、変換後のshapeを指定します。 |
order |
‘C’,’F’,’A’の いずれか |
(省略可能)初期値’C’ 指定したモードでインデックスを読み込み、shapeを再整形します。 |
returns:
形状変換後のndarrayが返されます。
reshape
の引数には、第一引数に変換元になるndarray、第二引数に変換後のarrayの形状(shape
)を指定します。最後の3つ目の引数はFortranのような順序の指定をする場合に使われるものであまり使用されません。
ポイントとしては、配列のshape
を指定する際に (n, -1) のように-1を指定すると要素数に合わせてn × mの2次元配列となります。このmはもとの配列の要素数に応じて変化する値で、変形後の要素数と変形前の要素数が変化しないような値になります。
以下のコードで使い方を確認してみます。
In [1]: import numpy as np
In [2]: a = np.arange(12) # 1つ1次元配列を生成。
In [3]: a
Out[3]: array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
In [4]: b = np.reshape(a, (3, 4)) # 3×4の2次元配列に変形。
In [5]: b # しっかり変形ができているか確認。
Out[5]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
元々12個の要素を持つndarrayを第一引数として、第二引数のnewshape
に(3, 4)を指定すると、3 × 4の多次元配列に変換されました。
reshape前のndarrayの要素は、reshape後のndarrayと共有されているので、変換後のある値を変化すると変換前の値も変更になります。
In [6]: b[0,1] = 0 # 1つだけ要素を変更する。
In [7]: b
Out[7]:
array([[ 0, 0, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [8]: a # aにも変更が反映されている。
Out[8]: array([ 0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
続いて第三引数のorder
の使い方を見ていきます。
In [9]: c = np.arange(12) # もう一度同じ配列を生成。
In [10]: d = np.reshape(c, (3,4), order = 'C') # orderを設定することで、並べ替え方を設定できる。
In [11]: d # 'C'はデフォルトで設定されているので、特に変化はない。
Out[11]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [12]: d = np.reshape(c, (3,4), order = 'F') # ここを'F'にすると高い次元のindexがまず変化していくように変形される。
In [13]: d
Out[13]:
array([[ 0, 3, 6, 9],
[ 1, 4, 7, 10],
[ 2, 5, 8, 11]])
変換前の要素数と、変換後の要素数が一致しない場合はValueError
の例外が発生します。また、-1を使用すると、元の要素数に合わせて自動で適切な値が設定されます。
In [14]: np.reshape(c, (3,5)) # 変形後の配列の`shape`が要素数に合わないとエラーが返ってくる。
---------------------------------------------------------------------------
(エラーメッセージがここに表示される)
ValueError: cannot reshape array of size 12 into shape (3,5)
In [15]: a = np.arange(12) # もう一度配列を生成。
In [16]: np.reshape(a, (3,-1)) # (n, -1)と`shape`を指定するとn×m (mは配列の要素数に合わせた値)の配列を返す。
Out[16]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [17]: np.reshape(a, (-1, 6)) # 違う値でもやってみる。
Out[17]:
array([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11]])
ndarray.reshape
ndarrayは、np.reshape
と同様の機能のメソッドを持っています。
以下のコードで確認してみます。
In [1]: import numpy as np
In [2]: a = np.arange(12).reshape((3, 4))
In [3]: a
Out[3]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [4]: b = np.arange(12).reshape((3,-1)) # -1も使える。
In [5]: b
Out[5]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [6]: c = np.arange(15).reshape((3,4)) # 要素数と出力する配列が異なると、エラーが返る。
---------------------------------------------------------------------------
(エラーメッセージがここに表示される)
ValueError: cannot reshape array of size 15 into shape (3,4)
resize
続いて、reshape
と同様の機能を持つreshape
を紹介します。APIドキュメントは以下の通りです。
numpy.resize(a, new_shape)
params:
パラメータ名 | 型 | 概要 |
---|---|---|
a |
ndarray | 変換元のndarray |
new_shape |
intもしくは intのタプルorリスト |
変換後の配列のshapeを指定します。intの場合、指定した要素数分の1次元配列になります。タプルの場合は、変換後のshapeを指定します。 |
returns:
形状変換後のndarrayが返されます。
ほとんどreshape
と相違点はありませんが、こちらは引数にorder
がありません。
また、変形後の配列が元の配列の要素数に合致しない時の挙動が異なります。
reshape
は元の配列の要素数と合致しないとエラーを返しますが、resize
はそのような処理でもエラーを返さずに強制的に実行します。
どのような結果になるのかを確かめるために、以下のコードで確認してみましょう。
In [1]: import numpy as np
In [2]: a = np.arange(12)
In [3]: np.reshape(a, (3,4)) # まずは3×3の2次元配列を生成する。
Out[3]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [4]: np.resize(a, (3,5)) # 配列のサイズが要素数より大きいと繰り返される。
Out[4]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 0, 1, 2]])
In [5]: np.resize(a, (3,2)) # 逆に配列のサイズが小さいと、元のデータが使用されない。
Out[5]:
array([[0, 1],
[2, 3],
[4, 5]])
さらに、reshape
の時には、変換後と変換前の要素は共有されていましたが、resize
は共有されません。処理を実行したあとの配列に値の変更を施しても変換前の配列の要素には変更が反映されません。
In [6]: b = np.resize(a, (3,4))
In [7]: b[0, 1] = 0 # 配列の要素を変更してみる。
In [8]: b
Out[8]:
array([[ 0, 0, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [9]: a # オリジナルの配列を確かめると、変更が反映されていない。
Out[9]: array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
ndarray.resize
resize
はndarray
のメソッドとして使用されることが多いので、メソッドとしての使い方を紹介します。APIドキュメントは以下の通りです。
ndarray.resize(new_shape, refcheck=True)
params:
パラメータ名 | 型 | 概要 |
---|---|---|
new_shape |
int/intのタプルorリスト | int、intのタプルorリスト - 変換後のarrayの形状を指定します。intの場合、指定した要素数分の1次元配列になります。タプルの場合は、変換後のshapeを指定します。 |
refcheck |
bool値 | (省略可能)初期値True Falseなら、参照カウントはチェックされません。 |
returns:
形状変換後のndarrayが返されます。
numpy.resize
の場合は、変換前と変換後の要素数が違っても強制的に形状変換を試みましたが、こちらはresize
と同様にValueError
例外を発生させます。
In [1]: import numpy as np
In [2]: a = np.arange(12) # 元となる配列を1つ生成。
In [3]: a.resize((3,4)) # 変形。
In [4]: a
Out[4]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [5]: a.resize((3,5)) #先程までのresizeとは異なり、 要素数と出力される配列の形と合致しないと、エラーが返ってくる。
---------------------------------------------------------------------------
(エラーメッセージ)
ValueError: cannot resize an array that references or is referenced
by another array in this way. Use the resize function
また、こちらはrefcheck
オプションが引数に追加されています。refcheck
にFalse
を指定すると、先程まで扱っていたresize
と同様に、組み替える前の配列の要素数に関係なく、新しいshape
の配列を出力します。
In [6]]: a.resize((3,5), refcheck = False) # 引数であるrefcheckをFalseにしておくと、配列の形に要素を合わせてくれる。ただし、入る値は0。
In [7]: a
Out[7]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 0, 0, 0]])
In [8]: b = np.arange(12) # また1つ新たに配列を生成。
In [9]: c = b # cにbを代入。
In [10]: c.resize((3,4)) # cだけ形を変える。
In [11]: c
Out[11]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [12]: b # bにもcの変更が反映されている。
Out[12]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
reshape
とresize
の違いまとめ
最後に、reshape
とresize
との違いをまとめた表を以下に掲載しておきます。
関数名 | 元と変更後の配列の要素数が 合致しないときの挙動 |
引数orderの 有無 |
要素変更の 反映 |
---|---|---|---|
reshape |
エラーを返す。 | 有 | 有 |
ndarray.reshape |
エラーを返す。 | 有 | 有 |
resize |
変更後の配列のshape に合わせて出力。変更後の要素数が超過している場合は 最初からの要素を繰り返していく。 |
無 | 無 |
ndarray.resize |
refcheck = True(default)なら、 エラーを返す。 refcheck = Falseなら変更後の shape に合わせて出力。変更後の要素数が超過している場合は 0が要素として入っていく。 |
無 | 有 |