これは何かと言うと・・・
最近、ソフトウェアエンジニアやインフラエンジニアの方が機械学習に関わる機会が増えているようです。データ分析というと伝統的にRが有名ですが、プログラミングのバックグランドがある人には、Pythonの方がなじみやすいのでは? と思いつつ、Pythonの基礎知識を前提に、NumPy/pandasと言ったデータ分析ライブラリーの使い方を紹介してみます。パート1は、IPythonの使い方とNumPyによるベクトル/行列の計算です。
ちなみに、このエントリーは、筆者の覚書を兼ねているので、随時、加筆修正される可能性があります。
前提環境
Pythonに加えて、下記のライブラリー、ツールを使用します。
・NumPy : ベクトルや行列計算を行うためのライブラリー
・SciPy : 科学計算用のライブラリー
・pandas : Rに類似のデータフレームを提供するライブラリー
・matplotlib : グラフ描画ライブラリー
・IPython : 対話的にPythonコマンドを実行するツール
Enthought CanopyやAnacondaなど、機械学習用の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 9月 8 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()(行列の積)などの関数が適用できます。
一例として、次の連立一次方程式を考えてみます。
これは、行列で次のように書きなおして、解くことができます。
同じ計算を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])