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

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

import numpy as np

16.1. 作成#

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

x=(x0x1x2)=(123)

を作成し、変数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の要素x1

x[1]
2.0

ベクトルx0番目から1番目までの要素を取り出したベクトルxx0: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.])

要素に値を代入する。x11

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

ベクトルのスライスに値を代入する。(x0,x1)(2,2)

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

16.3. 算術演算#

x=(123),y=(642)

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

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

ベクトルの和

x+y=(123)+(642)=(1+62+43+2)=(765)
x + y
array([7, 6, 5])

ベクトルの差

xy=(123)(642)=(162432)=(521)
x - y
array([-5, -2,  1])

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

xy=(123)(642)=(1×62×43×2)=(686)
x * y
array([6, 8, 6])

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

xy=(123)(642)=1×6+2×4+3×2=20
np.dot(x, y)
20

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

x @ y
20

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

xxyy=(162432)=(1169)
x ** y
array([ 1, 16,  9])

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

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

スカラーとベクトルの和

1+x=(111)+(123)=(234)
1 + x
array([2, 3, 4])

スカラーとベクトルの差

1x=(111)(123)=(012)
1 - x
array([ 0, -1, -2])

ベクトルのスカラー倍

2x=2(123)=(222)(123)=(2×12×22×3)=(246)
2 * x
array([2, 4, 6])

ベクトルの累乗

x2=(122232)=(149)
x ** 2
array([1, 4, 9])

ベクトルの要素数が合わないなどで、演算が実行できないときは例外が発生する。以下の例では、xxR3,zzR2のため、xx+zzの計算ができない。

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. 様々な作成方法#

零ベクトル: 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ベクトル: 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で用意されている数学関数

x=(11223)

を例に説明する。

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

ベクトルの要素の和

i=0N1xi=11+22+3=3
np.sum(x)
3

ベクトルの要素の積

i=0N1xi=1×(1)×2×(2)×3=12
np.prod(x)
12

ベクトルの累積和

(x1x1+x2x1+x2++xN1)=(11111+211+2211+22+3)=(10203)
np.cumsum(x)
array([1, 0, 2, 0, 3])

ベクトルの累積積

(x1x1×x2x1×x2××xN1)=(11×(1)1×(1)×21×(1)×2×(2)1×(1)×2×(2)×3)=(112412)
np.cumprod(x)
array([ 1, -1, -2,  4, 12])

ベクトルの要素の平均: x¯=1Ni=0N1xi

np.mean(x)
0.6

ベクトルの要素の標本分散: σ2=1Ni=0N1(xix¯)2=1Ni=0N1xi2x¯2

np.var(x)
3.4400000000000004

ベクトルの要素の不偏分散: s2=1N1i=0N1(xix¯)2=1N1i=0N1xi2x¯2

np.var(x, ddof=1)
4.300000000000001

ベクトルの要素の(標本)標準偏差: σ=1Ni=0N1(xix¯)2

np.std(x)
1.854723699099141

ベクトルの要素の最小値: mini{0,,N1}xi

np.min(x)
-2

ベクトルの要素の最大値: maxi{0,,N1}xi

np.max(x)
3

ベクトルの要素の最小値に対応するインデックス番号(何番目の要素が最小値か): ベクトルの要素の最小値: argmini{0,,N1}xi

np.argmin(x)
3

ベクトルの要素の最大値に対応するインデックス番号(何番目の要素が最大値か): argmaxi{0,,N1}xi

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

ベクトルのl0ノルム(非零の要素数): x0

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

ベクトルのl1ノルム(絶対値の和): x1=i=0N1|xi|

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

ベクトルのl2ノルム(二乗和の平方根): x2=i=0N1xi2

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)