Pythonでサンプル間の類似度を求める(ユークリッド距離)

こんにちは

まだちょっと寒いですね

最近、機械学習の勉強を基礎の「き」の字からはじめることにしました


とうことで、忘れないようにメモしがてら頭の中をもう一度整理しようということで書き連ねていきます

今日のテーマは統計の基礎の基礎、ユークリッド距離についてです

サンプルデータのセットにポケモンの能力値を使いました

ポケモンは小学校以降やらなくなっちゃいましたね、、、最後にやったポケモンはエメラルドだったっけ、、、今何世代目なんだろうか

今の子供はひみつきちとかダートじてんしゃとか知らないんだろうか、、、



とまぁ雑談はそれまでとして

ポケモン種族値リスト/ランキング(ORAS・XY対応版) - ポケモン徹底攻略

からPythonスクレイピングしてデータをとってきて(スクレイピングに関してはまた記事あげれたらなぁと思います)

とってきたデータを少しいじって

ラルトス': {'sum': 198, 'speed': 40, 'attack': 25, 'HP': 28, 'special_defence': 35, 'special_attack'
: 45, 'defence': 25}, 'マッスグマ': {'sum': 420, 'speed': 100, 'attack': 70, 'HP': 78, 'special_defe
nce': 61, 'special_attack': 50, 'defence': 61}, 'メタモン': {'sum': 288, 'speed': 48, 'attack': 48,
'HP': 48, 'special_defence': 48, 'special_attack': 48, 'defence': 48}, 'ドードー': {'sum': 310, 'speed
': 75, 'attack': 85, 'HP': 35, 'special_defence': 35, 'special_attack': 35, 'defence': 45}, 'チョボ
マキ': {'sum': 305, 'speed': 25, 'attack': 40, 'HP': 50, 'special_defence': 65, 'special_attack': 40
, 'defence': 85}, 'ゲンシカイオーガ': {'sum': 770, 'speed': 90, 'attack': 150, 'HP': 100, 'special_de
fence': 160, 'special_attack': 180, 'defence': 90}, 'ケムッソ': {'sum': 195, 'speed': 20, 'attack':
45, 'HP': 45, 'special_defence': 30, 'special_attack': 20, 'defence': 35}}

(一部分)

こんな感じでデータセットを作りました。さて、本題入っていきましょう

ユークリッド距離


ユークリッド距離とは、いわばぼくらの考える「2点間の距離」と同じと考えていいと思います

高校でならった座標平面上の距離もユークリッド距離です

この数値が高いほど、2つのサンプルは似ていないということになります

点Pと点Qとの距離は
f:id:ryo37-antsdsr:20160330073804p:plain
という式になります

ようは、共通要素の差分を2乗して合計して平方根をとりましょうってことです

ってことで関数を作ります

# prefsはデータセット
def calcEuclidianDistance(prefs,sample1,sample2):
    from math import sqrt
    sameItem = {}
    for item in prefs[sample1]:
        if item in prefs[sample2]: sameItem[item] = 1
    if len(sameItem) == 0: return 0
    return sqrt(sum([pow(prefs[sample1][item]-prefs[sample2][item],2)for item in sameItem]))

sameItemにはsample1とsample2の共通要素の名前が入っています
(今回のデータの性質上ではあまり意味がない)

数的な処理を行っているのは最後の一行のみです

それぞれの共通要素について、差分をとり、pow関数で2乗してリストに代入、そのリストにsum関数をかけて出した合計にsqrt関数で平方根を算出しています

いや~内包表記って便利ですね

では、関数を呼び出し、出力するコードです

# pokemonsはデータセット
print(calcEuclidianDistance(pokemons,'ピカチュウ','ライチュウ'))

実行結果です

179.4435844492636

ちょっと似てなさすぎじゃね??(データの加工で思い当たる節があるからそこが原因かな?)

ちなみに

print(calcEuclidianDistance(pokemons,'ピカチュウ','ピカチュウ'))

とするとあたりまえですけど実行結果は

0.0

となります

アプリケーションによっては、例えばサンプル間の距離が近いほど高い数値をだしたい!

となるときがあると思います

そんなときは関数の最終行で

return 1/(1+(sqrt(sum([pow(prefs[sample1][item]-prefs[sample2][item],2)for item in sameItem]))))

として、算出したユークリッド距離に1を足して(0除算エラー回避)、逆数をとれば実現できます




ブログ書くのもいい勉強になるけど時間使うなぁ、、、以上!!