16. NumPy (1): ベクトル#

このノートブックではnumpyモジュールのエイリアス(短縮表記)としてnpを用いる。

import numpy as np

16.1. 作成#

NumPyのベクトルをnp.arrayで作成する。以下のコードは2次元ベクトル

\[\begin{split} \boldsymbol{x} = \left(\begin{array}{c} x_0 \\ x_1 \\ x_2 \end{array}\right) = \left(\begin{array}{c} 1 \\ 2 \\ 3 \end{array}\right) \end{split}\]

を作成し、変数xに代入する。

インデックス番号

数学において先頭要素のインデックス番号は\(1\)とすることが多いが、Pythonでは先頭要素のインデックス番号が\(0\)である。両方が混在すると紛らわしいので、この資料でベクトルや行列を数式で表現するときも、先頭要素のインデックス番号は\(0\)とする。

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

作成されたオブジェクトの型はnumpy.ndarray

type(x)
numpy.ndarray

ベクトルの次元数(軸の数)。xは1次元ベクトル。

x.ndim
1

各次元の要素数を表すタプル。xの1次元目には\(5\)つの要素が格納されている。

x.shape
(5,)

オブジェクトに含まれる要素数。xは1次元ベクトルなので1次元目の要素数に等しい。

x.size
5

各要素の型。xは整数を格納するベクトルとして定義されている。

x.dtype
dtype('int64')

ベクトルを作成するときに要素の型を指定できる。

x = np.array([1, 2, 3, 4, 5], dtype='float')
x
array([1., 2., 3., 4., 5.])
x.dtype
dtype('float64')

16.2. スライス#

ベクトルxの要素\(x_1\)

x[1]
2.0

ベクトルx\(0\)番目から\(1\)番目までの要素を取り出したベクトル\(\pmb{x}_{0:2}\)(スライス)。

x[0:2]
array([1., 2.])

スライスの開始位置が\(0\)の場合は省略できる(リストと同様)。

x[:2]
array([1., 2.])

要素を逆順に並べる。

x[::-1]
array([5., 4., 3., 2., 1.])

スライスで取り出す要素をリストで指定してもよい。

x[[1,3]]
array([2., 4.])

要素に値を代入する。\(x_1 \leftarrow -1\)

x[1] = -1
x
array([ 1., -1.,  3.,  4.,  5.])

ベクトルのスライスに値を代入する。\((x_0, x_1) \leftarrow (2, 2)\)

x[:2] = 2.
x
array([2., 2., 3., 4., 5.])

16.3. 算術演算#

\[\begin{split} \boldsymbol{x} = \left(\begin{array}{c} 1 \\ 2 \\ 3 \end{array}\right), \boldsymbol{y} = \left(\begin{array}{c} 6 \\ 4 \\ 2 \end{array}\right) \end{split}\]

に対して、様々な演算を紹介する。

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

ベクトルの和

\[\begin{split} \boldsymbol{x} + \boldsymbol{y} = \left(\begin{array}{c} 1 \\ 2 \\ 3 \end{array}\right) + \left(\begin{array}{c} 6 \\ 4 \\ 2 \end{array}\right) = \left(\begin{array}{c} 1+6 \\ 2+4 \\ 3+2 \end{array}\right) = \left(\begin{array}{c} 7 \\ 6 \\ 5 \end{array}\right) \end{split}\]
x + y
array([7, 6, 5])

ベクトルの差

\[\begin{split} \boldsymbol{x} - \boldsymbol{y} = \left(\begin{array}{c} 1 \\ 2 \\ 3 \end{array}\right) - \left(\begin{array}{c} 6 \\ 4 \\ 2 \end{array}\right) = \left(\begin{array}{c} 1-6 \\ 2-4 \\ 3-2 \end{array}\right) = \left(\begin{array}{c} -5 \\ -2 \\ 1 \end{array}\right) \end{split}\]
x - y
array([-5, -2,  1])

ベクトルとベクトルの要素積(アダマール積)

\[\begin{split} \boldsymbol{x} \odot \boldsymbol{y} = \left(\begin{array}{c} 1 \\ 2 \\ 3 \end{array}\right) \odot \left(\begin{array}{c} 6 \\ 4 \\ 2 \end{array}\right) = \left(\begin{array}{c} 1 \times 6 \\ 2 \times 4 \\ 3 \times 2 \end{array}\right) = \left(\begin{array}{c} 6 \\ 8 \\ 6 \end{array}\right) \end{split}\]
x * y
array([6, 8, 6])

ベクトルとベクトルの内積

\[\begin{split} \boldsymbol{x} \cdot \boldsymbol{y} = \left(\begin{array}{c} 1 \\ 2 \\ 3 \end{array}\right) \cdot \left(\begin{array}{c} 6 \\ 4 \\ 2 \end{array}\right) = 1 \times 6 + 2 \times 4 + 3 \times 2 = 20 \end{split}\]
np.dot(x, y)
20

ベクトルとベクトルの内積は@演算子で書くこともできる(Python 3.5以上)。

x @ y
20

ベクトルとベクトルの累乗

\[\begin{split} \pmb{x}^{\pmb{y}} = \left(\begin{array}{c} 1^6 \\ 2^4 \\ 3^2 \end{array}\right) = \left(\begin{array}{c} 1 \\ 16 \\ 9 \end{array}\right) \end{split}\]
x ** y
array([ 1, 16,  9])

16.3.1. ブロードキャスティング#

ブロードキャスティングとは、ベクトルとスカラーなど、本来は形が合わずに計算ができないオブジェクト間において、算術計算時に自動的に形を合わせてくれる機能である。以下に紹介する例はスカラーとベクトル間で演算を行う例であるが、これらはブロードキャスティングにより説明できる。

スカラーとベクトルの和

\[\begin{split} \boldsymbol{1} + \boldsymbol{x} = \left(\begin{array}{c} 1 \\ 1 \\ 1 \end{array}\right) + \left(\begin{array}{c} 1 \\ 2 \\ 3 \end{array}\right) = \left(\begin{array}{c} 2 \\ 3 \\ 4 \end{array}\right) \end{split}\]
1 + x
array([2, 3, 4])

スカラーとベクトルの差

\[\begin{split} \boldsymbol{1} - \boldsymbol{x} = \left(\begin{array}{c} 1 \\ 1 \\ 1 \end{array}\right) - \left(\begin{array}{c} 1 \\ 2 \\ 3 \end{array}\right) = \left(\begin{array}{c} 0 \\ -1 \\ -2 \end{array}\right) \end{split}\]
1 - x
array([ 0, -1, -2])

ベクトルのスカラー倍

\[\begin{split} 2\boldsymbol{x} = 2 \left(\begin{array}{c} 1 \\ 2 \\ 3 \end{array}\right) = \left(\begin{array}{c} 2 \\ 2 \\ 2 \end{array}\right) \odot \left(\begin{array}{c} 1 \\ 2 \\ 3 \end{array}\right) = \left(\begin{array}{c} 2 \times 1 \\ 2 \times 2 \\ 2 \times 3 \end{array}\right) = \left(\begin{array}{c} 2 \\ 4 \\ 6 \end{array}\right) \end{split}\]
2 * x
array([2, 4, 6])

ベクトルの累乗

\[\begin{split} \boldsymbol{x}^2 = \left(\begin{array}{c} 1^2 \\ 2^2 \\ 3^2 \end{array}\right) = \left(\begin{array}{c} 1 \\ 4 \\ 9 \end{array}\right) \end{split}\]
x ** 2
array([1, 4, 9])

ベクトルの要素数が合わないなどで、演算が実行できないときは例外が発生する。以下の例では、\(\pmb{x} \in \mathbb{R}^3, \pmb{z} \in \mathbb{R}^2\)のため、\(\pmb{x}+\pmb{z}\)の計算ができない。

z = np.array([1, 2])
x + z
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/tmp/ipykernel_205166/2063851844.py in <module>
      1 z = np.array([1, 2])
----> 2 x + z

ValueError: operands could not be broadcast together with shapes (3,) (2,) 

16.4. 様々な作成方法#

零ベクトル: \(\boldsymbol{0}\)

np.zeros(4)
array([0., 0., 0., 0.])

xと同じ要素数の零ベクトル。

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

\(1\)ベクトル: \(\boldsymbol{1}\)

np.ones(4)
array([1., 1., 1., 1.])

xと同じ要素数の\(1\)ベクトル。

np.ones_like(x)
array([1, 1, 1])

任意の値で初期化したベクトル

np.full(4, -2.)
array([-2., -2., -2., -2.])

xと同じ要素数で任意の値で初期化したベクトル。

np.full_like(x, -2.)
array([-2, -2, -2])

\(0\)以上\(10\)未満の整数を並べたベクトル(np.arange関数の使い方はrange関数と同様)。

np.arange(0, 10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

\(1\)から\(100\)未満の範囲で\(20\)ずつ整数を並べたベクトル。

np.arange(1, 100, 20)
array([ 1, 21, 41, 61, 81])

始点\(0\)から終点\(360\)までの範囲を等分して、始点と終点を含む\(5\)個の境界値を要素としたベクトル。

np.linspace(0, 360, 5)
array([  0.,  90., 180., 270., 360.])

始点\(0\)から終点\(360\)までの範囲を等分して、始点を含むが終点を含まない\(4\)個の境界値を要素としたベクトル。

np.linspace(0, 360, 4, endpoint=False)
array([  0.,  90., 180., 270.])

\([0, 1)\)の範囲の一様分布からランダムに要素を抽出して作ったベクトル。

np.random.rand(4)
array([0.12162432, 0.19088018, 0.24584746, 0.3852018 ])

標準正規分布(平均\(0\)、分散\(1\)の正規分布)からランダムに要素を抽出して作ったベクトル。

np.random.randn(4)
array([0.85354541, 1.09364516, 0.22995723, 0.15205818])

正規分布(以下の例では平均\(0.5\)、分散\(2\)の正規分布)からランダムに要素を抽出して作ったベクトル。

np.random.normal(0.5, 2, 4)
array([-1.03402695,  0.12491045,  2.41473327, -0.64760535])

16.5. 数学関数#

NumPyで用意されている数学関数

\[ \boldsymbol{x} = \left(\begin{array}{ccccc}1 & -1 & 2 & -2 & 3\end{array}\right)^\top \]

を例に説明する。

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

ベクトルの要素の和

\[ \sum_{i=0}^{N-1} x_i = 1 - 1 + 2 - 2 + 3 = 3 \]
np.sum(x)
3

ベクトルの要素の積

\[ \prod_{i=0}^{N-1} x_i = 1 \times (-1) \times 2 \times (-2) \times 3 = 12 \]
np.prod(x)
12

ベクトルの累積和

\[\begin{split} \left(\begin{array}{l} x_1 \\ x_1 + x_2 \\ \dots \\ x_1 + x_2 + \dots + x_{N-1} \end{array}\right) = \left(\begin{array}{l} 1 \\ 1 - 1 \\ 1 - 1 + 2 \\ 1 - 1 + 2 - 2 \\ 1 - 1 + 2 - 2 + 3 \end{array}\right) = \left(\begin{array}{c} 1 \\ 0 \\ 2 \\ 0 \\ 3 \end{array}\right) \end{split}\]
np.cumsum(x)
array([1, 0, 2, 0, 3])

ベクトルの累積積

\[\begin{split} \left(\begin{array}{l} x_1 \\ x_1 \times x_2 \\ \dots \\ x_1 \times x_2 \times \dots \times x_{N-1} \end{array}\right) = \left(\begin{array}{l} 1 \\ 1 \times (-1) \\ 1 \times (-1) \times 2 \\ 1 \times (-1) \times 2 \times (-2) \\ 1 \times (-1) \times 2 \times (-2) \times 3 \\ \end{array}\right) = \left(\begin{array}{c} 1 \\ -1 \\ -2 \\ 4 \\ 12 \end{array}\right) \end{split}\]
np.cumprod(x)
array([ 1, -1, -2,  4, 12])

ベクトルの要素の平均: \(\displaystyle \bar{x} = \frac{1}{N}\sum_{i=0}^{N-1} x_i\)

np.mean(x)
0.6

ベクトルの要素の標本分散: \(\displaystyle \sigma^2 = \frac{1}{N}\sum_{i=0}^{N-1} (x_i - \bar{x})^2 = \frac{1}{N}\sum_{i=0}^{N-1} x_i^2 - \bar{x}^2\)

np.var(x)
3.4400000000000004

ベクトルの要素の不偏分散: \(\displaystyle s^2 = \frac{1}{N-1}\sum_{i=0}^{N-1} (x_i - \bar{x})^2 = \frac{1}{N-1}\sum_{i=0}^{N-1} x_i^2 - \bar{x}^2\)

np.var(x, ddof=1)
4.300000000000001

ベクトルの要素の(標本)標準偏差: \(\displaystyle \sigma = \sqrt{\frac{1}{N}\sum_{i=0}^{N-1} (x_i - \bar{x})^2}\)

np.std(x)
1.854723699099141

ベクトルの要素の最小値: \(\displaystyle \min\limits_{i \in \{0, \dots, N-1\}} x_i\)

np.min(x)
-2

ベクトルの要素の最大値: \(\displaystyle \max\limits_{i \in \{0, \dots, N-1\}} x_i\)

np.max(x)
3

ベクトルの要素の最小値に対応するインデックス番号(何番目の要素が最小値か): ベクトルの要素の最小値: \(\displaystyle \mathop{\rm argmin}\limits_{i \in \{0, \dots, N-1\}} x_i\)

np.argmin(x)
3

ベクトルの要素の最大値に対応するインデックス番号(何番目の要素が最大値か): \(\displaystyle \mathop{\rm argmax}\limits_{i \in \{0, \dots, N-1\}} x_i\)

np.argmax(x)
4

なお、これまでの処理はndarrayクラスのメソッドとして呼び出すことも可能である。

x.sum()
3
x.mean()
0.6
x.var()
3.4400000000000004
x.var(ddof=1)
4.300000000000001
x.std()
1.854723699099141
x.min()
-2
x.max()
3
x.argmin()
3
x.argmax()
4

ベクトルの\(l_0\)ノルム(非零の要素数): \(\|\boldsymbol{x}\|_0\)

np.linalg.norm(x, ord=0)
5.0

ベクトルの\(l_1\)ノルム(絶対値の和): \(\|\boldsymbol{x}\|_1 = \displaystyle \sum_{i=0}^{N-1} |x_i|\)

np.linalg.norm(x, ord=1)
9.0

ベクトルの\(l_2\)ノルム(二乗和の平方根): \(\|\boldsymbol{x}\|_2 = \displaystyle \sqrt{\sum_{i=0}^{N-1} x_i^2}\)

np.linalg.norm(x, ord=2)
4.358898943540674

16.6. ユニバーサル関数#

NumPyにはベクトルの要素数によらず、要素ごとに数学的な計算を行うユニバーサル関数が用意されている。

x = np.array([1, -1, 2, -2, 3])
theta = np.linspace(0, 360, 13)   # [0, 30, 60, ..., 360]

ベクトルの累乗(**と同じ)。

y = np.power(x, 4)
y
array([ 1,  1, 16, 16, 81])

ベクトルの平方根。

np.sqrt(y)
array([1., 1., 4., 4., 9.])

\(e\)に対する指数

y = np.exp(x)
y
array([ 2.71828183,  0.36787944,  7.3890561 ,  0.13533528, 20.08553692])

自然対数

np.log(y)
array([ 1., -1.,  2., -2.,  3.])

三角関数

np.sin(theta / 360 * 2 * np.pi)
array([ 0.00000000e+00,  5.00000000e-01,  8.66025404e-01,  1.00000000e+00,
        8.66025404e-01,  5.00000000e-01,  1.22464680e-16, -5.00000000e-01,
       -8.66025404e-01, -1.00000000e+00, -8.66025404e-01, -5.00000000e-01,
       -2.44929360e-16])
np.cos(theta / 360 * 2 * np.pi)
array([ 1.00000000e+00,  8.66025404e-01,  5.00000000e-01,  6.12323400e-17,
       -5.00000000e-01, -8.66025404e-01, -1.00000000e+00, -8.66025404e-01,
       -5.00000000e-01, -1.83697020e-16,  5.00000000e-01,  8.66025404e-01,
        1.00000000e+00])
np.tan(theta / 360 * 2 * np.pi)
array([ 0.00000000e+00,  5.77350269e-01,  1.73205081e+00,  1.63312394e+16,
       -1.73205081e+00, -5.77350269e-01, -1.22464680e-16,  5.77350269e-01,
        1.73205081e+00,  5.44374645e+15, -1.73205081e+00, -5.77350269e-01,
       -2.44929360e-16])
x = np.linspace(-2, 2, 11)
x
array([-2. , -1.6, -1.2, -0.8, -0.4,  0. ,  0.4,  0.8,  1.2,  1.6,  2. ])

ベクトルの絶対値

np.abs(x)
array([2. , 1.6, 1.2, 0.8, 0.4, 0. , 0.4, 0.8, 1.2, 1.6, 2. ])

切り捨て

np.floor(x)
array([-2., -2., -2., -1., -1.,  0.,  0.,  0.,  1.,  1.,  2.])

切り上げ

np.ceil(x)
array([-2., -1., -1., -0., -0.,  0.,  1.,  1.,  2.,  2.,  2.])

原点方向へ切り捨て

np.fix(x)
array([-2., -1., -1., -0., -0.,  0.,  0.,  0.,  1.,  1.,  2.])

四捨五入

np.round(x)
array([-2., -2., -1., -1., -0.,  0.,  0.,  1.,  1.,  2.,  2.])

比較演算子でも要素ごとの比較演算が行われる。

x < 1
array([ True,  True,  True,  True,  True,  True,  True,  True, False,
       False, False])
x != 0
array([ True,  True,  True,  True,  True, False,  True,  True,  True,
        True,  True])

16.7. 反復処理#

x = np.arange(0, 10000)

ベクトルの要素に対する反復。

s = 0
for v in x:
    s += v
s
49995000
s = 0
for i in range(x.shape[-1]):
    s += x[i]
s
49995000

Pythonのインタプリタの実行速度は遅いので、NumPy上の演算や関数で済む処理をPythonの反復処理として実装してしまうと、大変遅くなる。したがって、やむを得ない場合を除き、for文は避ける。

%%timeit
np.sum(x)
5.43 µs ± 202 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%%timeit
s = 0
for v in x:
    s += v
463 µs ± 25.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%%timeit
s = 0
for i in range(x.shape[-1]):
    s += x[i]
699 µs ± 16.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)