matplotlib.patches – 図形の描画

概要

matplotlib.patchesパッケージに様々な図形クラスが準備されていて、Axesadd_patch()メソッドでそれらのオブジェクトを加えていく。

各種図形

以下の点は各図形において共通

  • ほとんどの図形は引数xyで基準点のx座標とy座標をタプルで与える
  • edgecolor/ecで外枠の色、facecolor/fcで塗りつぶし色を指定する
  • fill=True/Falseで塗りつぶしの有無を指定する
  • angleで傾きの角度を指定できる図形がある
Circle(xy[, radius=5])
中心点を指定して円を描く。
Ellipse(xy, width, height[, angle])
中心点と幅・高さを指定して楕円を描く。
Rectangle(xy, width, height[, angle])
左下の点と幅・高さを指定して楕円を描く。
CirclePolygon(x, y, rasius=5, resolution=20)
多角形を描画。辺/頂点の数をresolutionで指定する。
Polygon(xy, closed=True)
複数の点を指定して図形を描画する。xyはNx2配列(xy座標を要素とした2次元配列)。closedをFalseに指定すると図形の最初の点と最後の点を結ばない。
Arc(xy, width, height[, angle, theta1, theta2])
楕円の一部の弧を描く。扇形に中を塗りつぶすことはできない。
Wedge(center, r, theta1, theta2[, width=None])
円の一部を切出した図形を描く。widhを指定すると中心からその長さだけ除かれて描かれる。
Arrow(x, y, dx, dy[, width, ...])
矢印を描画する。
FancyArrow(x, y, dx, dy[, width, ...])
鏃を片側だけにしたり、鏃の大きさや形を設定したりできる。

 

ndarray.reshape – 配列の形状変更

基本

配列の形状変更は、reshape()メソッドで行う。reshape()メソッドは、元の配列を破壊せず新たな配列を生成する。

具体のいろいろな使い方は、ndarray.reshapeの使い方を参照

以下の例では6個の要素の1次元配列を2×3の2次元配列に変更し、それをさらに3 ×2の2次元配列に変更している。要素は常に行を上から、各行の列要素を左からネストした形で埋めていく。

暗黙指定

サイズ変更の際、ある次元の要素数を-1とすると、他の要素数に合わせて適切に設定してくれる。

以下の例では2×3×2の3次元配列をつくり、それを3×2×2に変形しているが、2次元目を-1として1次元目と3次元目から設定させている。

この方法は、たとえば行ベクトルの配列を列ベクトルに変換するときに使われる。以下の例では1次元の配列をつくり、それを列ベクトルとするのに、列数を1で固定し、行数を-1として算出させている。

1次元化するときの注意

多次元配列や列ベクトルを1次元化するとき、行数を1、列数を-1で暗黙指定すると求める1次元配列を1つだけ含む2次元の配列になる。こうなってしまのはreshape()の引数で1行×n列の2次元で指定したため。

そこで、size属性で1つの整数だけを指定すると、1次元でその要素数の配列になってくれる。

さらには、引数を-1のみで指定すると、配列のサイズを適当に持ってきて適用してくれる。

これは列ベクトルを行ベクトル化するときのほか、pyplotで複数のAxesインスタンスを行×列の形で受け取った時に、全てのインスタンスに同じ設定を適用したいときなどに1次元化してループで回す、といったようなことにも使える。

 

ndarray – 配列の次元・形状・サイズ

ndim属性~配列の次元

ndim属性は配列の次元を整数で返す。

1次元配列を1つだけ要素に持つ配列や列ベクトルの次元が2となっている点に注意。とにかく[]のネストの数だと考えればよい。

shape属性~配列の形状

shape属性は配列の形状を返す。

1次元1行の単純な配列のときにはshapeが(1, n)とならないのが気になるがこれは結果が常にタプルで返されるためで、1次元とわかっているときには1つの整数が返ってくると考えてよい。

ndim=2となる形状の場合にはタプルも2要素となって、shape=(行数, 列数)となる。より多次元の場合、外側の次元の要素数からの順番になる。

size属性~配列のサイズ

size属性で得られる配列のサイズは配列の要素数。

 

Python – __file__

osパッケージのgetcwd()を使ってカレントディレクトリーを取得しようとして、Atomからの直接実行などのときにうまくいかなかった。

このような場合、__file__で実行ファイルの位置を得ることができる。また、os.path.dirname(__file__)でファイル名を除いたパスを、os.path.basename(__file__)でファイル名のみを得ることができる。

実行方法によってはエラーとなる。

Atomから実行した場合

コマンドラインから実行ファイル(__file__.py)のあるディレクトリーに移動し、直接ファイル名をタイプした場合

コマンドラインから実行ファイル(__file__.py)のあるディレクトリーに移動し、”python 実行ファイル名”で実行した場合

上記と同じだが、実行ファイル名を相対パスで指定した場合

 

 

Boston house‐pricesデータセット

概要

Boston house-pricesデータセットは、カーネギーメロン大学のStatLibライブラリーから取得したもので、持家の価格とその持家が属する地域に関する指標からなる。

ボストンの各地域にある506の持家の価格の中央値に対して、その地域の犯罪発生率やNOx濃度など13の指標が得られる。

ここではPythonのscikit-learnにあるbostonデータの使い方をまとめる。

データの取得とデータ構造

Pythonで扱う場合、scikit-learndatasetsモジュールにあるload_breast_cancer()でデータを取得できる。データはBunchクラスのオブジェクト。

データセットの構造は辞書型で、506の地域に関する13の特徴量と、当該地域における持家住宅の1000ドル単位の価格などのデータ。

データのキーは以下のようになっている。

データの内容

'data'~特徴量データセット

506の地域における13の指標を特徴量として格納した2次元配列。列のインデックスが特徴量の番号に対応している。

'target'~住宅価格

506の地域における持家住宅の1000ドル単位の価格中央値

'feature_names'~特徴名

13種類の特徴量の名称。

  1. CRIM:町ごとの人口当たり犯罪率
  2. ZN:25,000平方フィート以上の区画の住居用途地区比率
  3. INDUS:町ごとの小売り以外の産業用途地区比率
  4. CHAS:チャールズ川に関するダミー変数(1:川沿い、0:それ以外)
  5. NOX:NOx濃度(10ppm単位)
  6. RM:1戸あたり部屋数
  7. AGE:1940年より前に建てられた持家物件の比率
  8. DIS:ボストンの5つの職業紹介所への重みづけ平均距離
  9. RAD:放射道路へのアクセス性
  10. TAX:10,000ドルあたりの固定資産税総額
  11. PTRATIO:生徒対教師の比率
  12. B:1000(Bk – 0.63)^2(Bkは待ちにおける黒人比率)
  13. LSTAT:下位層の人口比率(%)

'filename'~ファイル名

CSVファイルのフルパス名が示されている。1行目にはデータ数、特徴量数が並んでおり、2行目に13の特徴量とターゲットの住宅価格、その後に506行のレコードに対する13列の特徴量と1列のターゲットデータが格納されている。このファイルにはDESCRに当たるデータは格納されていない。

'DESCR'~データセットの説明

データセットの説明。print(breast_ds_dataset['DESCR'])のようにprint文で整形表示される。

  • レコード数506個
  • 属性は、13の数値/カテゴリー属性と、通常はターゲットに用いられる中央値

データの利用

データの取得方法

bostonデータセットから各データを取り出すのに、以下の2つの方法がある。

  • 辞書のキーを使って呼び出す(例:boston['DESCR']
  • キーの文字列をプロパティーに指定する(例:boston.DESCR

全レコードの特徴量データの取得

'data'から、506のレコードに関する13の特徴量が506行13列の2次元配列で得られる。13の特徴量は’feature_names’の13の特徴名に対応している。

特定の特徴量のデータのみ取得

特定の特徴量に関する全レコードのデータを取り出すときにはX[:, n]の形で指定する。

 

Pyplot – グラフの標準色

Pyplotのグラフを描くときに標準で使われる色を直接指定する方法。色名に"tab:blue"のように指定する。

waveデータセット – knn

概要

k-最近傍回帰の例として、scikit-learnのwaveデータKNeighborsRegressorを適用してみた結果。

近傍点数とクラス分類の挙動

訓練データとして10個のwaveデータを訓練データとして与え、2つのテストデータの予測するのに、近傍点数を1, 2, 3と変えた場合の様子を見てみる。

近傍点数=1の場合

2つのテストデータの特徴量の値に最も近い特徴量を持つ訓練データが選ばれ、その属性値がそのままテストデータの属性値となっている。

近傍点数=2の場合

テストデータの特徴量に最も近い方から1番目、2番目の特徴量を持つ訓練データが選ばれ、それらの属性値の平均がテストデータの属性値となっている。

近傍点数=3の場合

同様に、テストデータの特徴量に最も近い3つの訓練データの属性の平均がテストデータの属性値となっている。

実行コード

上記の計算のコードは以下の通り。

knnの精度

O’Reillyの”Pythonではじめる機械学習”中、KNeighborsRegressorのwaveデータに対する精度が計算されている。40サンプルのwaveデータを発生させ訓練データとテストデータに分け、テストデータに対するR2スコアが0.83となることが示されている。実際に計算してみると、確かに同じ値となる。

これを見ると比較的高い精度のように見えるが、train_test_split()の引数random_stateを変化させてみると以下のように精度はばらつく。乱数系列が異なると精度が0.3未満の場合もあるが、全体としてみると0.6~0.7あたりとなりそうである。

ためしにmake_wave(n_samples=1000)としてみると、結果は以下の通りとなり、精度は0.67程度(平均は0.677)と一定してくる。

予測カーブ

訓練データが少ない場合

40個のwaveデータに対して、n_neighborsを変化させたときの予測カーブを見てみる。

  • n_neighbors=1の時は、全ての訓練データを通るような線となる
  • n_neighborsが多くなるほど滑らかになる
  • n_neighborsがかなり大きくなると水平に近くなる
  • n_neighborsが訓練データ数と同じになると、予測線は水平になる(任意の特徴量に対して、全ての点の平均を計算しているため)

訓練データが多い場合

今度はwaveデータでn_samples=200と数を多くしてみる。データ数を多くするとその名の通り、上下に波打ちながら増加している様子が見られる。これに対してn_neighborsを変化させたのが以下の図。

n_neighbors=10~20あたりで滑らかに、かつ波打つ状況が曲線で再現されている。

n_samples=300として訓練データに200を振り分け、n_neighborsを変化させたときのスコアは以下の通り。n_neighbors=20あたりで精度が最もよさそうである。

あるデータが得られたとき、その科学的なメカニズムは置いておいて、とりあえずデータから予測値を再現したいときにはそれなりに使えるかもしれない。

 

pyplot – zorder~グラフの描画順

pyplotでグラフを描画する際、点よりも線の方が上になって見栄えが悪い・・・といった場合に、どのグラフから上にするかという指定が必要になる。

グラフ描画の優先性はplot()scatter()などのグラフメソッドの引数にzorderを指定して実現できる。zorderに指定した値がより大きいグラフの方が上のレイヤーになる。指定できる値は正負の実数。

左のグラフは後から実行しているscatterplotの下に表示されている。

右のグラフではzorderを指定しており、scatterの方が値が大きいため上のレイヤーに表示されている。

 

 

forgeデータセット – knn

概要

ここでは、Pythonのscikit-learnパッケージのKNeighborsClassifierクラスにmglearnパッケージのforgeデータを適用してknnの挙動を確認する。

近傍点数を変化させたときのクラス分類の挙動や学習率曲線についてみていく。

近傍点数によるクラス分類の挙動

近傍点数=1の場合

データセットとしてmglearnで提供されているforgeデータを用いて、近傍点数=1とした場合の、3つのテストデータのクラス判定を以下に示す。各テストデータに対して最も距離(この場合はユークリッド距離)が近い点1つが定まり、その点のクラステストデータのクラスとして決定している。

なお、いろいろなところで見かけるforgeデータセットの散布図は当該データセットの特徴量0(横軸)と特徴量1(縦軸)の最小値と最大値に合わせて表示しており、軸目盛の比率が等しくない。ここでは、距離計算に視覚上の齟齬が生じないように、縦軸と横軸の比率を同じとしている。

後の計算のために、このグラフ描画のコードを以下に示す。

概要は以下の通り。

  • 5行目でforgeデータセットを準備
  • 7行目で近傍点数を1で指定してクラス分類器を構築
  • 8行目で訓練データとしてforgeデータを与える
  • 12行目で3つのテストデータを準備
  • 13行目でテストデータに対する近傍点のインデックスとテストデータまでの距離を獲得
  • 14行目でテストデータのクラスを決定
  • 18-19行目で訓練データの散布図を描画
  • 23行目で、テストデータとそのクラス決定結果、クラス決定に用いられた点群のインデックス、テストデータと各点の距離を並行してループ
    • 24行目でテストデータの座標を出力
    • 25行目でテストデータを描画
    • 26行目のループで、テストデータごとの近傍点に関する処理を実行
      • 27行目でテストデータと近傍点の間に直線を描画
      • 28行目で近傍点とテストデータからの距離を出力

出力結果は以下の通りで、各予測点に対して近傍点が1つ決定されている。

近傍点数=3の場合

先の例で、コードの7行目で近傍点=3で指定してクラス分類器を構築する。

一般にknnでは、テストデータに対して複数の近傍点を指定する場合、各近傍点のクラスのうち最も多いものをテストデータのクラスとする(多数決)。

近傍点数=2の場合

テストデータのクラスを近傍点のクラスの多数決で求めるとすると、近傍点数が偶数の時の処理が問題になる。KNeighborsClassifierの場合、偶数でクラス分類が拮抗する場合は、クラス番号が最も小さいものに割り当てられるらしい。実際、n_neighbors=2としたときの3つのテストデータのうち中央の点(10.0, 3.0)については、赤い点(10.24, 2.45)~class-1~距離0.5952の方が青い点(9.5017, 1.9382)~class-0~距離1.1729よりも距離は近いがクラス番号が0である青い点のクラスで判定されている。

偶数の点で多数決で拮抗した場合には、最も近い点のクラスで決定する、平均距離が近い方のクラスで決定するといった方法が考えられるが、この場合は必ず番号が小さなクラスが選ばれるため、若干結果に偏りがでやすいのでは、と考える。

決定境界

近傍点の数を変えた時の決定境界の変化を確認する。k近傍法はscikit-learnのKNeighborsClassifierクラスを利用する。

近傍点の数を1, 2, 3, …と変化させたときの決定境界の変化は以下の通り。

近傍点数が少ないときは訓練データにフィットするよう決定境界が複雑になるが、近傍点数が多いと決定境界は滑らかになる。特に近傍点数が訓練データの点数に等しいとき、全訓練データの多数決でクラス決定され、全領域で判定結果が同じとなる(この場合は近傍点数26が偶数なので、クラス番号の小さいclass-0で決定されている)。

この図を描画したコードを以下に示す。

  • 7行目、引数で与えたAxesに対して決定境界を描く関数を定義
    • 18行目、決定境界をcontourf()を利用して描いている
  • 21行目、引数で与えたAxesに対してクラスごとに色分けした散布図を描く関数を定義
  • 54行目、2次元配列のAxes1次元配列として扱っている

waveデータセット

概要

waveデータセットは、”Pythonではじめる機械学習”(O’REILLY)中で用いられる架空のデータセットである。

その内容は、引数n_samplesで指定した個数の点について1つの特徴量とターゲットの値を持ち、回帰を扱うのに適している。

利用方法

mglearnパッケージから、たとえば以下のように利用する。

実行するとdeprecatedの警告が出るが、放置してもよいらしい。

内容

waveデータの特徴は以下の通り。

  • 引数のn_samplesには任意の整数を指定できる
  • 特徴量(x座標の値)は決まっている
    • n_samplesが増えてもx0, x1, …の値は変わらない
    • x0, x1, …は実行のたびに同じパターン
  • ターゲットの値(y座標の値)は変化するが実行ごとに同じ
    • n_samplesが変わると同じx0, x1, …の値に対するy0, y1, …の値は変化する
    • y0, y1, …は実行のたびに同じパターン

このことを、n_samplesの値を変化させたときのX, yの内容で確認してみる。

このコードは何度実行しても同じ値を返す。x座標のパターンが変わっていないこと、y座標のパターンは実行のたびに変化していることがわかる。ただし異なるn_sampleに対して、同じxに対するyの値は大きくは変化していない。

なお、n_samplesが6の時のxの最後の値とその1つ前の値がかなり近く、対応するyの値も近い。n_samplesが1の時と3の時に、先頭のXとyの値が殆ど等しい。

以上のことから、waveデータセットはXについては毎回同じ系列でランダムな値を返し、yはXに対して一定の計算値に毎回同じ系列の乱数で擾乱を加えていると想像される。

最後に、n_samplesを多くしたときの結果を見てみると明らかに線形で上昇しつつ波打っているのがわかる。おそらくy=a \sin b x + cのような式に擾乱を与えていると思われる。

手法の適用