import numpy as np3 NumPy で配列を扱う
NumPy は,数値計算を効率的に行うためのライブラリであり,多次元配列を扱うことができる.本書では,ベクトルや行列の計算,データの集計などに用いる.
通常は,次のようにインポートし,np という別名を付けて使う.
以降のコードでは,このインポートがすでに行われているものとする.
3.1 配列の作成
3.1.1 np.array() 関数
配列は,np.array() 関数を使って作成する.次のコードは,1次元配列を作成する例である.1次元配列は,線形代数におけるベクトルを表すために使う.
a = np.array([1, 2, 3, 4, 5])
print(a)[1 2 3 4 5]
次のコードは,2次元配列を作成する例である.2次元配列は,線形代数における行列を表すために使う.
b = np.array([[1, 2, 3], [4, 5, 6]])
print(b)[[1 2 3]
[4 5 6]]
NumPy 配列で要素が並ぶ各方向を軸(axis)という.ndim は軸の数,すなわち配列の次元数を表す.ここでいう次元数は,ベクトルの成分数とは異なる.例えば,上の a は5次元ベクトルを表せるが,配列としては1次元である.
配列の基本的な属性には,ndim のほかに,shape,size,dtype がある.shape は各軸の長さを並べた組,size は配列全体の要素数,dtype は要素のデータ型を表す.NumPy 配列では,すべての要素が共通のデータ型で格納される.
print(b.ndim) # 2
print(b.shape)
print(b.size) # 6
print(b.dtype)2
(2, 3)
6
int64
b は2次元配列であり,2行3列であるため,shape は (2, 3) となる.
練習 3.1 np.array() を使って,次の行列に対応する2次元配列を作成し,表示せよ.
\begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix}
3.1.2 np.arange() 関数
規則的に並んだ数の配列は,一つずつ書き並べなくても作成できる.
a と b が整数で a < b のとき,np.arange(a, b) は,a から b-1 までの整数を並べた配列を返す.終点の b を含まない点は,range() と同じである.np.arange(a, b, step) と書くと,値の増分を step で指定できる.
print(np.arange(1, 7))
print(np.arange(1, 7, 2))[1 2 3 4 5 6]
[1 3 5]
3.1.3 np.linspace() 関数
np.linspace(a, b, n) は,a から b までの範囲に等間隔に並ぶ n 個の値を返す.n \geq 2 のとき,デフォルトでは両端の a と b を含む.np.arange() では値の増分を指定するのに対し,np.linspace() では値の個数を指定する.
print(np.linspace(0, 1, 5))[0. 0.25 0.5 0.75 1. ]
np.linspace() は,グラフを描くときに横軸の値を作るためによく使う.
練習 3.2 np.linspace() を使って,-1 と 1 を両端として等間隔に並ぶ 5 個の値を生成し,表示せよ.
3.2 要素の取り出し
配列の要素は,[] の中に位置を表すインデックスを指定して取り出す.インデックスは 0 から始まるため,先頭の要素は a[0] である.
a = np.array([10, 20, 30, 40, 50])
print(a[0])
print(a[2])10
30
インデックスに負の値を指定すると,末尾から数える.a[-1] は最後の要素である.
print(a[-1])50
a[i:j] と書くと,インデックス i の位置から j の直前までの要素をまとめて取り出せる.これをスライスという.終点の j に対応する要素は含まれない.
print(a[1:3])[20 30]
2次元配列では,[行インデックス, 列インデックス] の順に指定する.次のコードは,行インデックス 0,列インデックス 2 の要素,すなわち1行3列目の要素を取り出す.
A = np.array([[1, 2, 3], [4, 5, 6]])
print(A[0, 2])3
スライスの開始位置と終了位置を省略して : だけを書くと,その軸のすべての要素を指定できる.A[:, 0] は,すべての行から列インデックス 0,すなわち1列目を取り出す.データから特定の列だけを取り出すときによく使う.
print(A[:, 0])[1 4]
練習 3.3 上の2次元配列 A から,行インデックス 1,すなわち2行目の [4, 5, 6] を取り出して表示せよ.
3.3 条件による要素の取り出し
配列と数値を比較すると,各要素について条件を満たすかどうかを表すブール配列が得られる.条件を満たす位置には True,満たさない位置には False が入る.
scores = np.array([65, 80, 72, 91, 58])
condition = scores >= 80
print(condition)[False True False True False]
このブール配列をインデックスとして指定すると,True に対応する要素だけを取り出せる.この方法を,ブール配列によるインデックス指定(boolean indexing)という.
print(scores[condition])[80 91]
条件を直接書いて,scores[scores >= 80] とすることもできる.
3.4 配列の形状の変更
reshape() メソッドを使うと,要素数を保ったまま形状を変えた配列が得られる.次のコードでは,要素数が 6 の1次元配列 a から,2行3列の2次元配列 A を作成する.a 自体の形状は変わらない.
a = np.arange(1, 7)
A = a.reshape(2, 3)
print(A)[[1 2 3]
[4 5 6]]
指定する各軸の長さの積は,元の配列の要素数と一致しなければならない.例えば,要素数が 6 の配列を2行4列に変形することはできない.
一つの軸の長さに -1 を指定すると,その長さは全体の要素数とほかの軸の長さから自動的に決まる.a.reshape(-1, 1) は,1次元配列 a を1列に並べた2次元配列を返す.この例では,形状が (6,) から (6, 1) になる.
column = a.reshape(-1, 1)
print(column)
print(a.shape) # (6,)
print(column.shape) # (6, 1)[[1]
[2]
[3]
[4]
[5]
[6]]
(6,)
(6, 1)
3.5 配列の演算
Python のリストに + を使うと,二つのリストが連結される.一方,NumPy 配列に + を使うと,要素ごとの加算が行われる.
list_a = [1, 2, 3]
list_b = [4, 5, 6]
print(list_a + list_b)
array_a = np.array([1, 2, 3])
array_b = np.array([4, 5, 6])
print(array_a + array_b)[1, 2, 3, 4, 5, 6]
[5 7 9]
NumPy 配列では,+,-,*,/,** によって要素ごとの演算を行う.同じ形状の配列同士では,同じ位置にある要素を組み合わせて計算する.例えば,二つの2次元配列の和は,次のように計算できる.
A = np.array([[1, 2, 3], [4, 5, 6]])
B = np.array([[7, 8, 9], [10, 11, 12]])
C = A + B
print(C)[[ 8 10 12]
[14 16 18]]
この演算を数式で表すと,次のようになる.
\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix}+ \begin{bmatrix} 7 & 8 & 9 \\ 10 & 11 & 12 \end{bmatrix}= \begin{bmatrix} 8 & 10 & 12 \\ 14 & 16 & 18 \end{bmatrix}
なお,* は要素ごとの乗算を表し,行列の積ではない.
異なる形状の配列同士でも,一定の条件を満たせば,ブロードキャストという仕組みによって演算できる.線形代数の章では,配列とスカラーの演算を例に,この仕組みを紹介する.
練習 3.4 要素がそれぞれ [5, 7, 9] と [1, 2, 3] である NumPy の1次元配列 a と b を作成し,その差 a - b を求めよ.
3.6 配列の集計
NumPy の集計関数を使うと,配列の要素の合計や平均値などを計算できる.np.sum() は合計,np.mean() は平均値を返す.以下のように配列だけを引数に指定すると,配列全体が集計の対象となる.
array = np.array([1, 2, 3])
total = np.sum(array)
print(total)
print(np.mean(array))6
2.0
同じように,np.max() は最大値を,np.min() は最小値を返す.
print(np.max(array))
print(np.min(array))3
1
3.7 乱数
乱数は,手法を試すための架空のデータを作るときなどに用いる.NumPy では,np.random.default_rng() を使って乱数生成器(Generator オブジェクト)を作成し,そこから乱数を生成する方法が推奨されている.
乱数生成器を初期化するための値をシードという.引数を指定せずに np.random.default_rng() を実行すると,通常は作成するたびに異なる乱数列が得られる.同じ実行環境で,同じ非負整数をシードに指定して生成器を作り直し,同じ順序・引数で乱数を生成すれば,同じ乱数列を再現できる.次のコードでは,シードを 0 として乱数生成器 rng を作成する.
rng.random(n) は,0 以上 1 未満の一様分布に従う乱数を n 個返す.
rng = np.random.default_rng(0)
print(rng.random(3))[0.63696169 0.26978671 0.04097352]
同じ rng で続けて乱数を生成すると,乱数列の続きが得られる.以下の例では,それぞれを単独でも再現できるように,例ごとに rng を初期化する.
rng.normal(mu, sigma, n) は,平均 mu,標準偏差 sigma の正規分布に従う乱数を n 個返す.正規分布については,確率統計の章で扱う.
rng = np.random.default_rng(0)
print(rng.normal(50, 10, 3))[51.25730221 48.67895137 56.4042265 ]
ここで指定する平均と標準偏差は,乱数の生成元となる分布の値である.生成した有限個の値の平均や標準偏差が,指定した値と一致するとは限らない.
練習 3.5 平均 170,標準偏差 6 の正規分布に従う乱数を 5 個生成し,表示せよ.ただし,np.random.default_rng(0) を使って Generator オブジェクトを作成すること.
3.8 総合練習
練習 3.6 次の1次元配列 data から,2行3列に変形した配列 matrix を作成し,表示せよ.次に,元の1次元配列 data から 10 以上の要素だけを取り出し,それらの要素と平均値を表示せよ.
data = np.array([12, 5, 8, 20, 16, 3])3.9 まとめ
- NumPy 配列の次元数,形状,要素数,データ型は,
ndim,shape,size,dtypeで確認できる. - インデックス,スライス,ブール配列によるインデックス指定を使うと,必要な要素を取り出せる.
reshape()を使うと,要素数を変えずに配列の形状を変更できる.- NumPy 配列では,要素ごとの演算や,合計,平均値,最大値,最小値などの集計ができる.
- 同じ実行環境で,同じシードと手順を使うと,
Generatorが生成する乱数列を再現できる.