めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

機械学習のためのPython/NumPy/pandasチュートリアル(パート1)

これは何かと言うと・・・

最近、ソフトウェアエンジニアやインフラエンジニアの方が機械学習に関わる機会が増えているようです。データ分析というと伝統的にRが有名ですが、プログラミングのバックグランドがある人には、Pythonの方がなじみやすいのでは? と思いつつ、Pythonの基礎知識を前提に、NumPy/pandasと言ったデータ分析ライブラリーの使い方を紹介してみます。パート1は、IPythonの使い方とNumPyによるベクトル/行列の計算です。

ちなみに、このエントリーは、筆者の覚書を兼ねているので、随時、加筆修正される可能性があります。

前提環境

Pythonに加えて、下記のライブラリー、ツールを使用します。

・NumPy : ベクトルや行列計算を行うためのライブラリー
・SciPy : 科学計算用のライブラリー
・pandas : Rに類似のデータフレームを提供するライブラリー
・matplotlib : グラフ描画ライブラリー
・IPython : 対話的にPythonコマンドを実行するツール

Enthought CanopyAnacondaなど、機械学習用のPythonディストリビューションを使うと、これらをまとめてインストールすることができます。Canopyのインストール手順は、下記の資料に記載してあります。

ソフトウェアエンジニアのための「機械学習理論」入門・ハンズオン演習ガイド

ここでは扱いませんが、機械学習ライブラリーscikit-learnもインストールしたい場合は、Anacondaの方がよいかも知れません。(Enthought Canopyは有償版のみにscikit-learnが入っています。)

IPython

IPythonを使うと、対話的にpythonコマンドを実行しながらデータ分析を進めることができます。OSコマンドを実行することもできるので、viを立ち上げてスクリプトを作成・編集した後、IPythonから実行することも可能です。

ここでは、下記の初期設定スクリプトを用意して、IPython起動時に、いくつかのライブラリーを自動でインポートしておきます。

~/.ipython/profile_default/startup/00-setup.py

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from pandas import Series, DataFrame

関数電卓として利用する

まずは、ipythonコマンドでIPythonを起動すると、下記のように数値計算ができます。**は冪乗を表します。また、直前の結果は、_で参照できます。

In [1]: 2*(1+3)
Out[1]: 8

In [2]: 2**10
Out[2]: 1024

In [3]: _ * 2
Out[3]: 2048

小数点以下のない数値は、整数型とみなされるので注意が必要です。実数値として計算する際は、小数点以下を明示するか、float()で実数型を指定します。

In [4]: 1/2
Out[4]: 0

In [5]: 1.0/2
Out[5]: 0.5

In [6]: float(1)/2
Out[6]: 0.5

NumPyが提供する各種関数や定数値を利用することもできます。

In [7]: np.pi
Out[7]: 3.141592653589793

In [8]: np.e
Out[8]: 2.718281828459045

In [9]: np.sin(np.pi/4)
Out[9]: 0.70710678118654746

In [10]: np.sqrt(2)
Out[10]: 1.4142135623730951

これらの関数は、リスト(もしくは、array)を代入するとそれぞれの要素を代入した結果のarrayが返ります。

In [11]: np.sqrt([0,1,2,3])
Out[11]: array([ 0.        ,  1.        ,  1.41421356,  1.73205081])

OSコマンドを実行する

ls, cd, pwdなどの基本コマンドは、IPythonのプロンプトから直接実行することができます。その他のOSコマンドは、「!<コマンド>」で実行します。TABによるファイル名補完、[↑][↓]によるコマンドヒストリーの呼び出しも可能です。また、「%run」コマンドでファイルに保存したスクリプトを実行することができます。

In [5]: !vi test.py      # viエディターでtest.pyを作成

In [6]: cat test.py
for i in range(5):
    print i**2

In [7]: ls -l test.py
-rw-rw-r--. 1 canopy canopy 35  98 16:19 2015 test.py

In [8]: %run test.py
0
1
4
9
16

また、面白い機能として、「%paste」コマンドを実行するとクリップボードにあるスクリプトをペーストして実行します。([Ctrl]+[V]でペーストした場合に発生する、インデントなどの問題が回避できます。)ブログ記事などにあるスクリプトをコピペで実行する際に便利です。

NumPyで行列計算

行列/ベクトルの計算を行うには、NumPyのarrayオブジェクト(ndarray)を使用します。Pythonネイティブの多次元リストで行列を表現することも可能ですが、この場合は、行列の積など、行列に固有の計算ができません。arrayオブジェクトに対しては、np.linalg.inv()(逆行列)、np.dot()(行列の積)などの関数が適用できます。

一例として、次の連立一次方程式を考えてみます。

2x+y=5
x+5y=7

これは、行列で次のように書きなおして、解くことができます。

{\mathbf M} = \begin{pmatrix}
2 & 1 \\ 1 & 5
\end{pmatrix}
,\,\,\,\,
{\mathbf x} = \begin{pmatrix}
x \\
y
\end{pmatrix}
,\,\,\,\,
{\mathbf c} = \begin{pmatrix}
5 \\
7
\end{pmatrix}

\mathbf{Mx}={\mathbf c}

{\mathbf x} = {\mathbf M}^{-1}{\mathbf c}

同じ計算をIPythonから行うと次のようになります。まず、行列/ベクトルは、多次元リストで表現したものをnp.array()に渡して作成します。

In [1]: m = np.array([[2,1],[1,5]])

In [2]: c = np.array([[5],[7]])

値を表示すると、行列/ベクトルとして整形されて表示されます。

In [3]: m
Out[3]: 
array([[2, 1],
       [1, 5]])

In [4]: c
Out[4]: 
array([[5],
       [7]])

先ほど紹介した方法で、行列計算を行います。

In [5]: x = np.dot(np.linalg.inv(m), c)

In [6]: x
Out[6]: 
array([[ 2.],
       [ 1.]])

その他には、np.zeros()、np.ones()を用いると、全成分が0、もしくは、1の配列が生成できます。行列サイズを表すタプル(y,x)を引数として渡します。

In [2]: np.zeros((3,3))
Out[2]: 
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])

In [3]: np.ones((2,3))
Out[3]: 
array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])

arrayオブジェクトは、reshape()メソッドで縦横のサイズを変更することができます。また、Tメソッドは転置行列を表します。

In [4]: a = np.array([1,2,3,4,5,6])

In [5]: a
Out[5]: array([1, 2, 3, 4, 5, 6])

In [6]: b = a.reshape((2,3))

In [7]: b
Out[7]: 
array([[1, 2, 3],
       [4, 5, 6]])

In [8]: c = b.reshape((3,2))

In [9]: c
Out[9]: 
array([[1, 2],
       [3, 4],
       [5, 6]])

In [10]: d = c.T

In [11]: d
Out[11]: 
array([[1, 3, 5],
       [2, 4, 6]])

np.arange()を用いると、一定幅で変化する数列(等差数列)が生成できます。

In [12]: np.arange(9)
Out[12]: array([0, 1, 2, 3, 4, 5, 6, 7, 8])

In [13]: np.arange(1,10)
Out[13]: array([1, 2, 3, 4, 5, 6, 7, 8, 9])

In [14]: np.arange(1.0,10.0)
Out[14]: array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])

In [15]: np.arange(1,1.9,0.1)
Out[15]: array([ 1. ,  1.1,  1.2,  1.3,  1.4,  1.5,  1.6,  1.7,  1.8])

また、np.linspace()を用いると、指定の区間を指定数に分割した数列が生成されます。関数のグラフを描画する際に、評価ポイントを指定するのに便利です。

In [16]: np.linspace(0,1,10)   # 区間 [0, 1] から10個の値を取得
Out[16]: 
array([ 0.        ,  0.11111111,  0.22222222,  0.33333333,  0.44444444,
        0.55555556,  0.66666667,  0.77777778,  0.88888889,  1.        ])

In [17]: np.sin(np.linspace(0,0.5*np.pi,10)) # 区間 [0, π/2] の10箇所で sin(x) を評価
Out[17]: 
array([ 0.        ,  0.17364818,  0.34202014,  0.5       ,  0.64278761,
        0.76604444,  0.8660254 ,  0.93969262,  0.98480775,  1.        ])

vstack()とhstack()は、それぞれ、2つの配列を縦、または、横に結合します。

In [18]: a = np.ones(9).reshape((3,3))

In [19]: b = a*2

In [20]: a
Out[20]: 
array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])

In [21]: b
Out[21]: 
array([[ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.]])

In [22]: np.vstack((a,b))
Out[22]: 
array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.],
       [ 1.,  1.,  1.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.]])

In [23]: np.hstack((a,b))
Out[23]: 
array([[ 1.,  1.,  1.,  2.,  2.,  2.],
       [ 1.,  1.,  1.,  2.,  2.,  2.],
       [ 1.,  1.,  1.,  2.,  2.,  2.]])


縦方向、横方向での合計/平均なども可能です。

In [23]: a = np.arange(12).reshape((3,4))

In [24]: a
Out[24]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

In [25]: np.sum(a, axis=0)
Out[25]: array([12, 15, 18, 21])

In [26]: np.sum(a, axis=1)
Out[26]: array([ 6, 22, 38])

In [28]: np.mean(a, axis=0)
Out[28]: array([ 4.,  5.,  6.,  7.])

In [29]: np.mean(a, axis=1)
Out[29]: array([ 1.5,  5.5,  9.5])