09. NumPyの操作¶
このページでは、NumPy配列の情報を読む方法、形を変える方法、配列を結合・分割する方法を扱います。
NumPyでは、値そのものだけでなく、「何次元か」「どんな形か」「どんな型か」を読むことがとても大事です。
配列の属性と型¶
属性¶
NumPy配列は、配列についての情報を属性として持っています。
import numpy as np
X = np.array([
[80, 72, 1],
[55, 68, 0],
[90, 86, 1],
])
print(X.dtype)
print(X.ndim)
print(X.shape)
print(X.size)
print(X.T)
| 属性 | 意味 |
|---|---|
dtype |
配列の中の値の型 |
ndim |
配列が何次元か |
shape |
配列の形 |
size |
配列の中の値の数 |
T |
行と列を入れ替えた配列 |
X.shape が (3, 3) なら、3行3列の配列です。
機械学習では、X.shape を見て「何件のデータがあり、特徴量が何個あるか」を確認することがよくあります。
dtype¶
dtype 属性は、配列の中の値の型です。
import numpy as np
scores = np.array([80, 65, 90])
rates = np.array([0.80, 0.65, 0.90])
image = np.array([0, 128, 255], dtype=np.uint8)
complex_values = np.array([1 + 2j, 3 + 4j])
print(scores.dtype)
print(rates.dtype)
print(image.dtype)
print(complex_values.dtype)
int32 や int64 は整数、float64 は小数を表します。
整数が int32 になるか int64 になるかは、実行環境によって変わることがあります。
uint8 は、0から255までの整数を扱う型です。画像の明るさや色を表す値でよく見ます。
complex は複素数の型です。普通の表データではあまり出ませんが、音声や信号処理では見かけることがあります。
uint8 は少ないメモリで0から255までの値を表せる型です。データが小さくなると、結果として処理や転送が軽くなることがあります。
配列の形状¶
reshape¶
reshape() を使うと、要素数を変えずに配列の形を変えられます。
import numpy as np
values = np.arange(6)
matrix1 = values.reshape(2, 3)
matrix2 = values.reshape(2, -1)
print(values)
print(matrix1)
print(matrix2)
print(matrix1.shape)
print(matrix2.shape)
np.arange(6) は6個の値を持ちます。
それを reshape(2, 3) で2行3列に変えています。
-1 を使うと、その次元の大きさはNumPyが自動計算します。
values (shape: (6,))
| 0 | 1 | 2 | 3 | 4 | 5 |
matrix1 = values.reshape(2, 3) (shape: (2, 3))
| 0 | 1 | 2 |
| 3 | 4 | 5 |
matrix2 = values.reshape(2, -1) (shape: (2, 3))
| 0 | 1 | 2 |
| 3 | 4 | 5 |
reshape(2, -1) は「2行で、列はNumPyが計算」という意味です。6個の値を2行に並べるので、列は自動的に3になります。
もっと多次元にする¶
画像や深層学習では、3次元以上の配列もよく出てきます。
import numpy as np
values = np.arange(24)
array_2d = values.reshape(4, 6)
array_3d = values.reshape(2, 3, 4)
array_4d = values.reshape(1, 2, 3, 4)
print(array_2d)
print(array_2d.shape)
print(array_3d)
print(array_3d.shape)
print(array_4d.shape)
reshape(2, 3, 4) は、3行4列が2層あるような形です。
最初は出力を見ながら、「外側から順にまとまりが増えている」と読むとわかりやすいです。
array_2d (shape: (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 |
array_3d (shape: (2, 3, 4)) — 3行4列が2層
1層目
| 0 | 1 | 2 | 3 |
| 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 |
2層目
| 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 |
各shapeの意味は次のとおりです。
| 配列 | shape | 意味 |
|---|---|---|
array_2d |
(4, 6) | 4行6列 |
array_3d |
(2, 3, 4) | 3行4列が2層 |
array_4d |
(1, 2, 3, 4) | (3行4列が2層) のかたまりが1つ |
形を変えるときの注意¶
要素数が合わない形には変えられません。
import numpy as np
values = np.arange(6)
print(values.reshape(3, 2))
6個の値は、3行2列にもできます。
values.reshape(3, 2) (shape: (3, 2))
| 0 | 1 |
| 2 | 3 |
| 4 | 5 |
一方で、4行2列にするには8個の値が必要なので、形が合いません。
-1 は便利ですが、複数の場所を -1 にすると、NumPyがどちらをどの大きさにすればよいか決められません。
配列の結合 分割¶
配列を結合する¶
np.concatenate() は、すでにある軸に沿って配列をつなげます。
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
connected = np.concatenate([a, b])
print(connected)
print(connected.shape)
a (shape: (3,))
| 1 | 2 | 3 |
b (shape: (3,))
| 4 | 5 | 6 |
connected (shape: (6,))
| 1 | 2 | 3 | 4 | 5 | 6 |
2次元配列では、axis を指定すると行方向にも列方向にも結合できます。
import numpy as np
a = np.array([
[1, 2],
[3, 4],
])
b = np.array([
[5, 6],
[7, 8],
])
rows = np.concatenate([a, b], axis=0)
columns = np.concatenate([a, b], axis=1)
print(rows)
print(rows.shape)
print(columns)
print(columns.shape)
a (shape: (2, 2))
| 1 | 2 |
| 3 | 4 |
b (shape: (2, 2))
| 5 | 6 |
| 7 | 8 |
rows = concatenate([a, b], axis=0) (shape: (4, 2))
| 1 | 2 |
| 3 | 4 |
| 5 | 6 |
| 7 | 8 |
columns = concatenate([a, b], axis=1) (shape: (2, 4))
| 1 | 2 | 5 | 6 |
| 3 | 4 | 7 | 8 |
axis=0 では行が増えます。
axis=1 では列が増えます。
stack で新しい軸を作る¶
np.stack() は、新しい軸を作って配列を重ねます。
import numpy as np
image1 = np.array([
[1, 2],
[3, 4],
])
image2 = np.array([
[5, 6],
[7, 8],
])
batch = np.stack([image1, image2])
print(batch)
print(batch.shape)
image1 と image2 はどちらも2行2列です。
image1 (shape: (2, 2))
| 1 | 2 |
| 3 | 4 |
image2 (shape: (2, 2))
| 5 | 6 |
| 7 | 8 |
np.stack([image1, image2]) にすると、「2枚の2行2列画像」のような3次元配列になります。
batch (shape: (2, 2, 2))
1枚目
| 1 | 2 |
| 3 | 4 |
2枚目
| 5 | 6 |
| 7 | 8 |
concatenate は既存の軸に沿ってつなげる、stack は新しい軸を作って重ねる、と考えると読みやすいです。
| 関数 | 軸 | shapeの変化例 |
|---|---|---|
np.concatenate |
既存の軸に沿ってつなぐ | (2, 2) + (2, 2) → (4, 2) or (2, 4) |
np.stack |
新しい軸を作る | (2, 2) + (2, 2) → (2, 2, 2) |
配列を分割する¶
np.split() を使うと、配列を分割できます。
import numpy as np
values = np.arange(12)
parts = np.split(values, 3)
print(parts[0])
print(parts[1])
print(parts[2])
values (shape: (12,))
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
parts[0]
| 0 | 1 | 2 | 3 |
parts[1]
| 4 | 5 | 6 | 7 |
parts[2]
| 8 | 9 | 10 | 11 |
2次元配列も分割できます。
import numpy as np
X = np.arange(12).reshape(4, 3)
upper, lower = np.split(X, 2, axis=0)
print(upper)
print(lower)
X (shape: (4, 3))
| 0 | 1 | 2 |
| 3 | 4 | 5 |
| 6 | 7 | 8 |
| 9 | 10 | 11 |
upper (shape: (2, 3))
| 0 | 1 | 2 |
| 3 | 4 | 5 |
lower (shape: (2, 3))
| 6 | 7 | 8 |
| 9 | 10 | 11 |
axis=0 で分割すると、行方向に分かれます。
このページのまとめ¶
dtypeは配列の中の値の型shapeは配列の形ndimは配列が何次元かsizeは配列の中の値の数reshape()で配列の形を変えられる-1を使うと、reshapeの一部を自動計算できるnp.concatenate()は既存の軸に沿って結合するnp.stack()は新しい軸を作って重ねるnp.split()で配列を分割できる