Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

NumPy:配列の基礎

NumPy は数値計算のための基盤ライブラリであり,多次元配列(NumPy配列 (ndarray))とそれに対する高速な演算を提供する. Phase 2 以降のシミュレーションは,リストの代わりに ndarray を用いて配列全体をまとめて操作する.

慣例として np という別名で読み込む.

import numpy as np

ndarray の作成

np.array にリストを渡すと ndarray が作られる.多重リストを渡せば多次元配列になる.

a = np.array([1, 2, 3])
b = np.array([6, 3.3, 1])
C = np.array([[1, 5, 6], [7, 8, 9], [4, 2, 3]])
D = np.array([[2.3, 4, 7.2], [7, 9, 1], [11, 2, 9]])

print("a:", a)
print("C:\n", C)
a: [1 2 3]
C:
 [[1 5 6]
 [7 8 9]
 [4 2 3]]

数学の慣例にならい,ベクトル(1次元配列)は小文字,行列(2次元配列)は大文字で表すことが多い.

属性

配列の形状・次元・要素の型は属性として参照できる.

# 形状(各次元の要素数)
print("a.shape:", a.shape)
print("C.shape:", C.shape)

# 次元数
print("b.ndim:", b.ndim)
print("D.ndim:", D.ndim)

# 要素の型
print("a.dtype:", a.dtype)
print("D.dtype:", D.dtype)
a.shape: (3,)
C.shape: (3, 3)
b.ndim: 1
D.ndim: 2
a.dtype: int64
D.dtype: float64

astype で要素の型を変換(キャスト)できる.

e = a.astype(float)
F = D.astype(int)
print("e:", e)
print("F:\n", F)
e: [1. 2. 3.]
F:
 [[ 2  4  7]
 [ 7  9  1]
 [11  2  9]]

要素ごとの演算

同じ形状どうしの四則演算は,対応する要素ごとに計算される.

print("a + b:", a + b)
print("b - a:", b - a)
print("a * b:", a * b)
print("C + D:\n", C + D)
a + b: [7.  5.3 4. ]
b - a: [ 5.   1.3 -2. ]
a * b: [6.  6.6 3. ]
C + D:
 [[ 3.3  9.  13.2]
 [14.  17.  10. ]
 [15.   4.  12. ]]

ブロードキャスト

形状が異なる配列どうしの演算では,小さい方の配列が自動的に引き伸ばされて計算される.これをブロードキャスト (broadcasting)という.

# スカラーとの演算(スカラーが全要素に作用する)
print("3 * a:", 3 * a)
print("a ** 2:", a**2)

# 1次元配列と2次元配列(行ベクトルが各行に作用する)
print("a + C:\n", a + C)
print("C / a:\n", C / a)
3 * a: [3 6 9]
a ** 2: [1 4 9]
a + C:
 [[ 2  7  9]
 [ 8 10 12]
 [ 5  4  6]]
C / a:
 [[1.  2.5 2. ]
 [7.  4.  3. ]
 [4.  1.  1. ]]

数学関数(ユニバーサル関数)

NumPy の数学関数は配列の各要素に作用する.スカラーにも配列にも同じ関数が使える.

# 指数・対数・平方根
print("np.exp(a):", np.exp(a))
print("np.log(b):", np.log(b))
print("np.sqrt(b):", np.sqrt(b))

# 三角関数と円周率 np.pi
print("np.sin(np.pi / 2):", np.sin(np.pi / 2))
print("np.cos(a):", np.cos(a))
np.exp(a): [ 2.71828183  7.3890561  20.08553692]
np.log(b): [1.79175947 1.19392247 0.        ]
np.sqrt(b): [2.44948974 1.81659021 1.        ]
np.sin(np.pi / 2): 1.0
np.cos(a): [ 0.54030231 -0.41614684 -0.9899925 ]

配列の生成

要素を1つずつ書かずに,規則的な配列を生成する関数がある.

# 0 埋め・1 埋め(形状を指定)
Z = np.zeros([3, 4])
ones = np.ones(5)
print("Z:\n", Z)
print("ones:", ones)

# 等差数列
print("np.arange(0, 10, 2):", np.arange(0, 10, 2))  # 0 から 10 未満を 2 刻み

# 区間を等分する数列(端点を含む)
print("np.linspace(0, 1, 5):", np.linspace(0, 1, 5))  # 0 から 1 を 5 点
Z:
 [[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
ones: [1. 1. 1. 1. 1.]
np.arange(0, 10, 2): [0 2 4 6 8]
np.linspace(0, 1, 5): [0.   0.25 0.5  0.75 1.  ]

arange は刻み幅を,linspace は点の個数を指定する.曲線を滑らかに描くときは linspace が便利である.

集約・統計

配列全体を要約する関数がある.関数形式(np.sum(a))とメソッド形式(a.sum())の両方が使える.

print("np.sum(b):", np.sum(b))
print("b.mean():", b.mean())
print("np.max(a):", np.max(a))
print("np.min(C):", np.min(C))
print("np.median(b):", np.median(b))
print("np.std(D):", np.std(D))
np.sum(b): 10.3
b.mean(): 3.4333333333333336
np.max(a): 3
np.min(C): 1
np.median(b): 3.3
np.std(D): 3.3973846149975753

axis を指定すると,特定の方向にだけ集約できる(2次元配列で axis=0 は列方向,axis=1 は行方向).

print("C:\n", C)
print("列ごとの和 C.sum(axis=0):", C.sum(axis=0))
print("行ごとの和 C.sum(axis=1):", C.sum(axis=1))
C:
 [[1 5 6]
 [7 8 9]
 [4 2 3]]
列ごとの和 C.sum(axis=0): [12 15 18]
行ごとの和 C.sum(axis=1): [12 24  9]

コピーと参照

配列の代入(=)は中身を複製せず,同じ配列を指す別名を作るだけである. 一方を書き換えるともう一方も変わってしまうため,独立した複製が必要なときは np.copy を使う. シミュレーションで「現在の状態」を記録に残す場面で重要になる.

x = np.array([1, 2, 3])

# 参照(同じ配列を共有する)
y = x
y[0] = 99
print("参照: x =", x)  # x も変わってしまう

# コピー(独立した複製)
x = np.array([1, 2, 3])
z = np.copy(x)
z[0] = 99
print("コピー: x =", x, ", z =", z)  # x は変わらない
参照: x = [99  2  3]
コピー: x = [1 2 3] , z = [99  2  3]