2017年1月16日月曜日

Sklearnを使ってみる2

以前、といってもかなり前になってしまいますが、『Sklearnを使ってみる1』という投稿で、『実践 機械学習システム』という本を読み進めようとしていましたが、かなり間があいてしまいましたが、続きをやっていこうと思います。


前回、とりあえず以下のコードが動きました。
from matplotlib import pyplot as plt
from sklearn.datasets import load_iris
import numpy as np

data = load_iris()

features=data["data"]
feature_names=data["feature_names"]
target=data["target"]
target_names=data["target_names"]
labels=target_names[target]

for t,marker,c in zip (xrange(3),">ox","rgb"):

 plt.scatter(features[target == t,0],features[target == t,1],marker=marker,c=c)


とりあえず、この先
for t,marker,c in zip (xrange(3),">ox","rgb"):

 plt.scatter(features[target == t,0],features[target == t,1],marker=marker,c=c)


の部分は使わないので、コメントアウトするなり消すなりしておきます。
from matplotlib import pyplot as plt
from sklearn.datasets import load_iris
import numpy as np

data = load_iris()

features=data["data"]
feature_names=data["feature_names"]
target=data["target"]
target_names=data["target_names"]
labels=target_names[target]

#for t,marker,c in zip (xrange(3),">ox","rgb"):

 #plt.scatter(features[target == t,0],features[target == t,1],marker=marker,c=c)


で代わりにコードを継ぎ足して以下のようにします。
from matplotlib import pyplot as plt
from sklearn.datasets import load_iris
import numpy as np

data = load_iris()

features=data["data"]
feature_names=data["feature_names"]
target=data["target"]
target_names=data["target_names"]
labels=target_names[target]

#for t,marker,c in zip (xrange(3),">ox","rgb"):

 #plt.scatter(features[target == t,0],features[target == t,1],marker=marker,c=c)


###追加した部分
plength=features[:,2]
is_setosa=(labels=='setosa')

max_setosa=plength[is_setosa].max()
min_non_setosa=plength[~is_setosa].min()

print("Maximum of setosa:{0}".format(max_setosa))
print("Minimum of others:{0}".format(min_non_setosa))



一行一行説明していきます。

plengthの行

まず、plengthの行は、生データから花弁の長さ(plength)の値だけからなる新しい配列を作成しています。

前回、Irisデータセットは辞書型配列になっていて、その"data"要素には生データの一覧がarray型で格納されているということを確認しました。

それで、そのdata要素をfeaturesという変数に格納しました。(なので当然featuresにも生データが入っている。)

featuresの中は特徴量がarray型としてデータ別に入っているわけです。各要素の3番目の要素(インデックスとしては2番目)には花弁の長さの値が入っているので、インデックス2番目だけをすべて抽出して、plengthという変数に配列として格納しますよ、という意味。

厳密に言えば、plengthの型はarray型です。気になるようであれば、print(type(plength))として確認する。

plengthの部分だけ確認するのであれば、print(plength)を一番最後に追記しておけば、本当かどうか確かめられる。


from matplotlib import pyplot as plt
from sklearn.datasets import load_iris
import numpy as np

data = load_iris()

features=data["data"]
feature_names=data["feature_names"]
target=data["target"]
target_names=data["target_names"]
labels=target_names[target]

#for t,marker,c in zip (xrange(3),">ox","rgb"):

 #plt.scatter(features[target == t,0],features[target == t,1],marker=marker,c=c)


###追加した部分
plength=features[:,2]
is_setosa=(labels=='setosa')

max_setosa=plength[is_setosa].max()
min_non_setosa=plength[~is_setosa].min()

#print("Maximum of setosa:{0}".format(max_setosa))
#print("Minimum of others:{0}".format(min_non_setosa))

print(plength)
print(type(plength))



これを実行すれば、
[ 1.4  1.4  1.3  1.5  1.4  1.7  1.4  1.5  1.4  1.5  1.5  1.6  1.4  1.1  1.2
  1.5  1.3  1.4  1.7  1.5  1.7  1.5  1.   1.7  1.9  1.6  1.6  1.5  1.4  1.6
  1.6  1.5  1.5  1.4  1.5  1.2  1.3  1.5  1.3  1.5  1.3  1.3  1.3  1.6  1.9
  1.4  1.6  1.4  1.5  1.4  4.7  4.5  4.9  4.   4.6  4.5  4.7  3.3  4.6  3.9
  3.5  4.2  4.   4.7  3.6  4.4  4.5  4.1  4.5  3.9  4.8  4.   4.9  4.7  4.3
  4.4  4.8  5.   4.5  3.5  3.8  3.7  3.9  5.1  4.5  4.5  4.7  4.4  4.1  4.
  4.4  4.6  4.   3.3  4.2  4.2  4.2  4.3  3.   4.1  6.   5.1  5.9  5.6  5.8
  6.6  4.5  6.3  5.8  6.1  5.1  5.3  5.5  5.   5.1  5.3  5.5  6.7  6.9  5.
  5.7  4.9  6.7  4.9  5.7  6.   4.8  4.9  5.6  5.8  6.1  6.4  5.6  5.1  5.6
  6.1  5.6  5.5  4.8  5.4  5.6  5.1  5.1  5.9  5.7  5.2  5.   5.2  5.4  5.1]

こうなる。

is_setosaの行

次にis_setosaの行。
まさにリスト内包表記を使っている例。
前回でlabelという変数に、どのデータがどの花なのかという名前の対応リストを格納しました。

(label=='setosa')で、labelの中の名前の一覧と比較してsetosaに一致していればTrueを、そうでなければFalseを返してTrueかFalseから成る配列を生成しis_setosaに格納します。

もっとスマートにいえばブーリアン配列を生成するということです。

確認するには、例のごとくprint(is_setosa)を追記して実行すればいいです。面倒なのと、若干くどい気もするので、ここは省略。


max_setosaの行

次にmax_setosaの行へ行きます。

plength[is_setosa]の意味ですが、ブールインデックス参照という書き方です。

is_setosaがTrue or Falseからなる配列であることを上で書きました。そういうブーリアン配列をインデックスとして代入すると、plengthの要素の中からブーリアン配列のTrueに対応している要素だけを抽出して新しい配列を作成します。

Trueになっているのはsetosaのところでしたので、setosaに対応している花弁の長さだけが抽出できます。

plength[is_setosa]という名前の新しい配列なので、それにmax()メソッドを使っている。

つまり、setosaの花弁の長さの中で一番値が大きいものを、max_setosaに代入。

min_non_setosaの行

この行も、先ほど使ったブールインデックス参照が使われてます。

今度はsetosa以外のものの最小値を求めています。

setosa以外、ということはis_setosaにおいてはFalseにあたるものです。

Falseのままではブールインデックス参照ができないので、「~」演算子を使ってビット反転させis_setosaのTrueはFalseに、FalseはTrueにしてして代入しています。

printの行

最後は、処理というよりは、単純にpythonの文法ですが、一応自分なりに説明しておきます。

これは文字列に対するformatメソッドです。
調べてみると、formatメソッドにもいろいろ描き方のバリエーションがあるようですが、おそらく基本的な描き方は以下の形式かと思われます。

'文字列'.format(要素0,要素1,・・・)
ドットより前の文字列の中に、formatに引数として渡されている要素のインデックスの番号を代わりに代入して、最終的な出力文字列の体裁だけ整えるイメージです。

上記の"Maximum of setosa:{0}".format(max_setosa)とかであれば、この場合はformatメソッドに渡している要素が一つだけなので、それがインデックス0番目ということになりますが、それを「Maximum of setosa:{0}」という文字列の{0}をmax_setosaで置き換えたものが、結果としてできる文字列です。それをprintしている。
{数字}のところが、その番号に対応しているインデックスの要素で置きかわるという意味です。
詳しくは、下でまとめてあるリンクや本を参照。


で結局のところ、
from matplotlib import pyplot as plt
from sklearn.datasets import load_iris
import numpy as np

data = load_iris()

features=data["data"]
feature_names=data["feature_names"]
target=data["target"]
target_names=data["target_names"]
labels=target_names[target]


#for t,marker,c in zip (range(3),'>ox','rgb'):
#    
#    plt.scatter(features[target == t,0],features[target == t,1],marker=marker,c=c)
#
#plt.show()


plength=features[:,2]
is_setosa=(labels=='setosa')

max_setosa=plength[is_setosa].max()
min_non_setosa=plength[~is_setosa].min()

print('Maximum of setosa:{0}'.format(max_setosa))
print('Minimum of others:{0}'.format(min_non_setosa))

を実行すると、
hiroshi-no-MacBook-Air:Pro-tr hiroshi$ python3 others.py
Maximum of setosa:1.9
Minimum of others:3.0

という感じになります。
参考にしたページや文献・書籍
NumPy配列のブールインデックス参照 | hydroculのメモ
Python 3 の文字列フォーマット formatメソッド | TM Life
『実践 機械学習システム』(オライリー)