NumPy#
# 警告メッセージを非表示
import warnings
warnings.filterwarnings("ignore")
array#
このパッケージは,数値計算をする上で重要な役割を果たし,特に,行列計算に威力を発揮する。NumPy
は「ナンパイ」と読む。
慣例としてnp
として読み込む。
import numpy as np
基本となる関数がnp.array()
であり,次のコードでは1次元配列を作る。
arr = np.array([10, 20, 30, 40, 50])
arr
array([10, 20, 30, 40, 50])
type(arr)
numpy.ndarray
次に2次元配列、即ち、行列を作る。
mat = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
mat
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]])
print(type(arr))
print(type(mat))
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
arr
とmat
はNumPy
のndarray
(n
次元array
)というデータ型(クラス)である。[]
に囲まれた数字が並んでいるがリストとではない。従って,要素を抽出して直接リストとして扱うことはできない。そのためにはリストへの変換が必要になるが,その方法については後述する。
1次元配列:要素の抽出#
arr
の要素を抽出するには要素のインデックスを使う。
arr[1]
20
複数の要素を抽出するにはインデックスをリストとして使う。
arr[[0,1,3]]
array([10, 20, 40])
要素を連続で抽出するスライシング(slicing)も使うことができる。
arr[1:4]
array([20, 30, 40])
2次元配列:要素の抽出#
第\(i\)行目の第\(j\)列目要素にアクセスするためにはmat[i,j]
と書く。[,]
の,
を挟んで左が行を表し,右が列を示す。
[行のインデックス,列のインデックス]
コードはキーストロークが少なく,簡単なものが良いと考えられている。一方で,Python
は初心者に易しい言語だと言われるが,それでも関数・メソッドの数は膨大で,そのオプションとの組み合わせを考えるとまさしく「無数」にあると言っても過言ではない。そのため初心者にとって関数の使い方やオプションの書き方を間違う可能性は小さくない。さらに,自分が書いたコードを数週間・数ヶ月後に読み直すと,何をしようとしているのか分からないという状況が生じることもある。従って,初心者にとっては以下の点が非常に重要になる。
間違いにくいコードの書き方を覚える。
高い可読性を意識したコードの書き方を覚える。
このスタンスに基づいて以下のルールに従って説明する。
[,]
内の,
を省略可能な場合であっても省略しない。[,]
内の,
の左または右を省略できる場合であっても省略しない。
行または列を連続選択する(slicing)場合を考えよう。以下で説明するように:
を使うが
start:end
となる。ここでstart
とは選択する要素の最初インデックスであり,end
は選択する最後の要素の次のインデックスである(リストの場合と同じ)。上のルールに従うと,
,
の左または右が:
のみの場合,「全て」という意味になる。:
の左を省略すると「最初から」という意味になる。:
の右を省略すると「最後まで」という意味になる。
これを読むだけでは分かりにくいと思うので,以下の例に目を通してもう一度この箇所の説明を読んむことを推奨する。
mat[0,1]
2
第\(i\)行の抽出はmat[i,:]
で可能である。:
は「列の全て」という意味になる。
r0 = mat[0,:]
r0
array([1, 2, 3, 4])
抽出した行は上で説明した1次元配列なのでインデックスやスライシングを使って要素にアクセスすることができる。
複数の行の抽出は次の方法で可能である。
mat[1:3,:]
array([[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
第3行目は含まれないことに注意しよう。リストの要素の取り出し方と同じように:
の右のインデックスの行は含まれない。
(注意)
,:
を省略してmat[1:3]
と書いてもエラーは発生せず同じ結果が返されるが,,:
があることにより,行を抽出しており列は全て選択されていることが明示的になる。
第\(i\) 列目を抽出したい場合は次のコードになる。
mat[:, 1]
array([ 2, 6, 10, 14])
複数列の抽出は以下のようにする。
mat[:, 1:3]
array([[ 2, 3],
[ 6, 7],
[10, 11],
[14, 15]])
:
の役割を考えると以下はmat
自体である。
mat[:,:]
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]])
行列計算#
まず2つの行列(2次元配列array
)を作成する。
m1 = np.array([[1, 1], [1, 1]])
m1
array([[1, 1],
[1, 1]])
m2 = np.array([[2, 2], [2, 2]])
m2
array([[2, 2],
[2, 2]])
行列の和
要素ごとの和となる。
m2 + m1
array([[3, 3],
[3, 3]])
行列の差
要素ごとの差となる。
m2 - m1
array([[1, 1],
[1, 1]])
行列のスカラー積
与えられた定数とそれぞれの要素の積となる。
10 * m1
array([[10, 10],
[10, 10]])
m1/2 # 1/2をかけるのと同じ
array([[0.5, 0.5],
[0.5, 0.5]])
行列の積:バージョン1
*
を使うと要素どうしの積となる。数学で学んだ行列の式とは異なるので注意すること。
m1*m2
array([[2, 2],
[2, 2]])
行列の積:バージョン2
@
を使うと数学で学ぶ行列の積となる。
m1@m2
array([[4, 4],
[4, 4]])
numpy
の関数dot()
は@
と同じとなる。
np.dot(m1,m2)
array([[4, 4],
[4, 4]])
転置行列
数学で学ぶ転置行列と同じ。m3
を使って説明する。
m3 = np.array([[1,2,3],[4,5,6]])
m3
array([[1, 2, 3],
[4, 5, 6]])
m3
のメソッドtranspose()
を使う。
m3.transpose()
array([[1, 4],
[2, 5],
[3, 6]])
.transpose()
の省略形として.T
を使うこともできる。
m3.T
array([[1, 4],
[2, 5],
[3, 6]])
逆行列
数学で学ぶ逆行列である。m4
を使い説明する。逆行列を計算するためにnumpy
のlinalg
(linear algebra, 線形代数)というサブパッケージの中にあるinv
という関数を読み込む。
from numpy.linalg import inv
m4 = np.array([[1,2],[3,4]])
inv(m4)
array([[-2. , 1. ],
[ 1.5, -0.5]])
NumPy使用時によく使う属性とメソッド#
以前も説明したが、メソッドとはオブジェクト特有の関数であり、オブジェクトの属性の1つである。もう1つの属性の種類にデータ属性(例えば,行数)がある。あるオブジェクトにどのような属性があるかは関数dir()
を使うことにより確認できる。
dir(m3)
['T',
'__abs__',
'__add__',
'__and__',
'__array__',
'__array_finalize__',
'__array_function__',
'__array_interface__',
'__array_prepare__',
'__array_priority__',
'__array_struct__',
'__array_ufunc__',
'__array_wrap__',
'__bool__',
'__buffer__',
'__class__',
'__class_getitem__',
'__complex__',
'__contains__',
'__copy__',
'__deepcopy__',
'__delattr__',
'__delitem__',
'__dir__',
'__divmod__',
'__dlpack__',
'__dlpack_device__',
'__doc__',
'__eq__',
'__float__',
'__floordiv__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__getstate__',
'__gt__',
'__hash__',
'__iadd__',
'__iand__',
'__ifloordiv__',
'__ilshift__',
'__imatmul__',
'__imod__',
'__imul__',
'__index__',
'__init__',
'__init_subclass__',
'__int__',
'__invert__',
'__ior__',
'__ipow__',
'__irshift__',
'__isub__',
'__iter__',
'__itruediv__',
'__ixor__',
'__le__',
'__len__',
'__lshift__',
'__lt__',
'__matmul__',
'__mod__',
'__mul__',
'__ne__',
'__neg__',
'__new__',
'__or__',
'__pos__',
'__pow__',
'__radd__',
'__rand__',
'__rdivmod__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__rfloordiv__',
'__rlshift__',
'__rmatmul__',
'__rmod__',
'__rmul__',
'__ror__',
'__rpow__',
'__rrshift__',
'__rshift__',
'__rsub__',
'__rtruediv__',
'__rxor__',
'__setattr__',
'__setitem__',
'__setstate__',
'__sizeof__',
'__str__',
'__sub__',
'__subclasshook__',
'__truediv__',
'__xor__',
'all',
'any',
'argmax',
'argmin',
'argpartition',
'argsort',
'astype',
'base',
'byteswap',
'choose',
'clip',
'compress',
'conj',
'conjugate',
'copy',
'ctypes',
'cumprod',
'cumsum',
'data',
'diagonal',
'dot',
'dtype',
'dump',
'dumps',
'fill',
'flags',
'flat',
'flatten',
'getfield',
'imag',
'item',
'itemset',
'itemsize',
'max',
'mean',
'min',
'nbytes',
'ndim',
'newbyteorder',
'nonzero',
'partition',
'prod',
'ptp',
'put',
'ravel',
'real',
'repeat',
'reshape',
'resize',
'round',
'searchsorted',
'setfield',
'setflags',
'shape',
'size',
'sort',
'squeeze',
'std',
'strides',
'sum',
'swapaxes',
'take',
'tobytes',
'tofile',
'tolist',
'tostring',
'trace',
'transpose',
'var',
'view']
このリストの中にtranspose()
やT
があるのが確認できる。この中でよく使うのがshape
であり,行と列の数を確認する場合に有用なデータ属性である。
m3.shape
(2, 3)
データ属性には()
がない。一方,メソッド属性は()
が必要となる。言い換えると,メソッドの()
は「実行する」ということを意味する。
次に,抽出した行または列をリストに変換するメソッドについて説明する。
r0 = mat[0,:]
type(r0)
numpy.ndarray
r0
のデータ型はNumPy
のndarray
である。これをリストに変換するためにtolist()
というメソッドを使う。
r0_list = r0.tolist()
r0_list
[1, 2, 3, 4]
type(r0_list)
list
よく使うNumPy関数#
ルート \(\left(\sqrt x\right)\)
np.sqrt(4)
2.0
sqrt
は square root の略。
底が\(e\)の指数関数(\(e^x\))
np.exp(10)
22026.465794806718
exp
は exponentiation の略
自然対数(\(\log_ex\)または\(\ln x\))
np.log(10)
2.302585092994046
0
が\(N\)個のarray
を作る。
np.zeros(N)
np.zeros(10)
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
1
(float)が\(N\)個のarray
を作る。
np.ones(N)
np.ones(10)
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
\(a\)から\(b\)までの区間を等間隔に割った\(N\)個の数字を返す。
np.linspace(a,b,N)
np.linspace(0,1,5)
array([0. , 0.25, 0.5 , 0.75, 1. ])
\(a\)から\(b\)までの区間で\(m\)ステップずつ増加し等間隔に割った数字を返す(\(b\)は含まない)。
np.arange(a,b,m)
m = 1
の場合,組み込み関数のrange(a,b)
と同じ数字を生成するが,返り値がarray
であることが異なる。
np.arange(5,10,0.5)
array([5. , 5.5, 6. , 6.5, 7. , 7.5, 8. , 8.5, 9. , 9.5])
標本平均
x
が数字のarray
やリストの場合
np.mean(x)
\(=\bar{x}=\frac{1}{n}\sum_{i=1}^{n}x_i\)
xx = [1,2,3,4,5,6]
np.mean(xx)
3.5
標本中央値
x
が数字のarray
やリストの場合
np.median(x)
np.median(xx)
3.5
標本分散
x
が数字のarray
やリストの場合
np.var(x, ddof=0)
\(=s_x^2=\dfrac{1}{n-\text{ddof}}\sum_{i=1}^n\left(x_i-\bar{x}\right)^2\)(ddof=0
がデフォルト)
(注意)計量経済学で習う分散の不偏推定量はddof=1
が必要!
np.var(xx,ddof=1)
3.5
標本標準偏差
x
が数字のarray
やリストの場合
np.std(x, ddof=0)
\(=s_x=\sqrt{s_x^2}\) (ddof=0
がデフォルト)
(注意)標本標準偏差の場合,必ずしもddof=1
により不偏推定量とはならないが,通常ddof=1
を用いる。
np.std(xx,ddof=1)
1.8708286933869707
標本共分散
2次元以上のarray
やリストの場合
np.cov(xy, ddof=0)
\(=c_{xy}=\dfrac{1}{n-\text{ddof}}\sum_{i=1}^n(x_i-\bar{x})(y_i-\bar{y})\)(ddof=0
がデフォルト)
(注意1)計量経済学で習う分散の不偏推定量はddof=1
が必要!
下の計算結果
\(c_{xy}=-0.6\)
\(s_x^2=3.5\) ([1,2,3,4,5,6]の分散)
\(s_y^2=4.4\) ([1,6,2,5,3,1]の分散)
x = [1,2,3,4,5,6]
y = [1,6,2,5,3,1]
cov_xy = np.cov([x,y],ddof=1)
cov_xy
array([[ 3.5, -0.6],
[-0.6, 4.4]])
ここでそれぞれの数字は次を表している。
-0.6:
x
とy
の共分散であり,次のように値を抽出できる。`cov_xy[0,1]` もしくは `cov_xy[1,0]`
3.5:
x
の分散であり,次のように値を抽出できる。`cov_xy[0,0]`
4.4:
y
の分散であり,次のように値を抽出できる。`cov_xy[1,1]`
標本相関係数
2次元以上のarray
やリストの場合
np.corrcoef(xy)
\(=r_{xy}=\dfrac{c_{xy}}{s_x\cdot s_y}\)
(注意)ddof
の影響はない。
下の計算結果
\(r_{xy}=-0.152...\)
\(r_{xx}=r_{yy}=1\)
corr_xy = np.corrcoef([x,y])
corr_xy
array([[ 1. , -0.15289416],
[-0.15289416, 1. ]])
ここでそれぞれの数字は次を表している。
-0.15289416:
x
とy
の相関係数であり,次のように値を抽出できる。`corr_xy[0,1]` もしくは `corr_xy[1,0]`
1.0:
x
とx
の相関関係であり,次のように値を抽出できる。`corr_xy[0,0]`
1.0:
y
とy
の相関係数であり,次のように値を抽出できる。`corr_xy[1,1]`
array vs list#
ここではlist
とNumPy
のarray
の重要な違いについて説明する。
次のリストのそれぞれの要素に10
を足したいとしよう。
list0 = [1.0, 2.0, 3.0, 4.0, 5.0]
for
ループを使うと次のようになる。
list1 = []
for i in list0:
list1.append(i + 10)
list1
[11.0, 12.0, 13.0, 14.0, 15.0]
もう1つの方法として内包標記(list comprehension)がある。
list2 = [i + 10 for i in list0]
list2
[11.0, 12.0, 13.0, 14.0, 15.0]
どちらの方法を使ったとしても複雑さが残る。また次のコードでは10
を最後に追加するだけである。
list0 + [10]
[1.0, 2.0, 3.0, 4.0, 5.0, 10]
より簡単なコードで実行できれば良いが,以下のコードではエラーが発生する。
list0 + 10
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[55], line 1
----> 1 list0 + 10
TypeError: can only concatenate list (not "int") to list
これを実現するのがNumPy
のarray
である。
まずarray
を作成する。
arr0 = np.array(list0)
arr0
array([1., 2., 3., 4., 5.])
arr0 + 10
array([11., 12., 13., 14., 15.])
この機能はベクトル演算(Vectorization)と呼ばれ、ループを使わずに個々の要素に直接働きかけ計算している。上記のコードは次の計算を行っている。
arr0 + np.array([10]*5)
array([11., 12., 13., 14., 15.])
裏でarr0
の長さに合わせて10
を「拡張」し計算している。この機能により、より高速な計算が可能となるばかりか、より短いコードでそれを実現できる。+
, -
, *
, **
や他の関数にも同様に使うことができる。以下で例を挙げる。
arr0 - 5
array([-4., -3., -2., -1., 0.])
arr0 * 10
array([10., 20., 30., 40., 50.])
arr0 ** 2
array([ 1., 4., 9., 16., 25.])
np.sqrt(arr0)
array([1. , 1.41421356, 1.73205081, 2. , 2.23606798])
np.log(arr0)
array([0. , 0.69314718, 1.09861229, 1.38629436, 1.60943791])
次の計算も可能である。
y = arr0 * 2 + np.sqrt(arr0) + 10
y
array([13. , 15.41421356, 17.73205081, 20. , 22.23606798])
この機能はNumPy
の行列でも有効である。
mat0 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
mat0
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
mat0 * 10
array([[10, 20, 30],
[40, 50, 60],
[70, 80, 90]])
np.sqrt(mat0)
array([[1. , 1.41421356, 1.73205081],
[2. , 2.23606798, 2.44948974],
[2.64575131, 2.82842712, 3. ]])
np.log(mat0)
array([[0. , 0.69314718, 1.09861229],
[1.38629436, 1.60943791, 1.79175947],
[1.94591015, 2.07944154, 2.19722458]])