pythonで「君とはベクトルが合わない」を数値で出そう

#ベクトルが合わない？
趣味、思考、方向性が違うってときに、君とはベクトルが合わないわーとかで一言で片付けられません？
そう言われるとなんかちょっとイラってします・・

そんなベクトルが合うとか合わないとかいう曖昧な表現を、数値で正確に表す方法として”コサイン類似度”というものがあるので紹介します！
(( かんたんに言えば数Ⅱで習う内積の公式を使った類似度計算です ))

#コサイン類似度とは
コサイン類似度の話をする前にまず内積の復習をしますね
##内積

![コサイン類似度.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486822/6571bc8e-5c97-dc35-abc2-9d809b605c0f.jpeg)


α = (a,b)
β = (c,d)
α,βをベクトルとすると、これらの内積は

α・β = ac + bd

このα,βの内積は、この２つのベクトルの間の角度をθとすると

α・β = |α||β|cosθ

っていう超有名な関係式が成り立ちます
(ちなみに、|α| = √(a^2 + b^2), |β| = √(c^2 + d^2) ですね！）

##コサイン類似度

この関係式をcosθ基準に考えると

cosθ = (α・β)/|α||β|


<img width="538" alt="IMG_0144.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486822/105fa58f-f359-da6d-a004-19777b962d18.jpeg">


θは２つのベクトルの間の角度だから、２つのベクトルが近いとθ→０に近づき、cosθ→１に近づく。

α、βの要素がすべて**正**なら　0 ≦ cosθ ≦ 1

**つまりα君とβ君のベクトルが合う(近い)ときcosθ ＝１、合わない(遠い)ときcosθ ＝ ０**

---

#Pythonで書いてみる

####環境

ubuntu 18.04
python3

---
分かりやすいようにコサイン類似度を返すモジュール作ってimportして使います！
まずモジュール制作


```python:module_cos.py

import math
import numpy as np

def calc_cos(list1, list2):
    list1 = np.array(list1) # list ではなく np.array を使う
    list2 = np.array(list2)
    sum1 = sum(list1**2) # ループを使う必要がない（速い）
    sum2 = sum(list2**2)
    length1 = math.sqrt(sum1)
    length2 = math.sqrt(sum2)
    inner_product = sum(list1*list2) # ループを使う必要がない（速い）
    if (length1 != 0 and length2 != 0):
        cos = inner_product/(length1*length2)
    else:
        cos = 0
    return cos

```

簡単に説明すると
sum は、ベクトルの各要素の２乗を足したやつ
length は、sumの平方根　つまり|sum|ですね

length = 0 だと分母が0になって発散しちゃうんで、その時だけ例外処理してます

このモジュールを使うときは同じフォルダに置いて、import module_cos で呼んで、module_cos.cos(list1,list2)　みたいに使います
(返り値はコサイン類似度)

---

###例えば、音楽の嗜好ベクトルの類似度を求めてみよう

個人的に音楽を聞くのが趣味なので、音楽のベクトルの類似性を考えてみよう！

架空の６人を創造します

1 = 好き
0 = そこまで

|           |   A  |   B   |   C   |   D   |   E   |   F   |
|:----------|-----:|------:|------:|------:|------:|------:|
|  クリープハイプ     |   1   |   1    |  0     |   1    |   0    |   1    |
| RADWIMPS    |  1    |    1   |    0   |    1   |    1   |   0    |
|  Mr.Children   |   0   |    0   |   1    |    0   |    1   |   1    |
| GreeeeN        |    0  |    0   |   1    |    0   |    1   |   0    |
| サカナクション      |   0   |   1    |   1    |   0    |   0    |   0    |
| ONE OK ROCK   |   0   |    1   |    1   |     1  |     0  |   0    |
| あいみょん   |   1   |    1   |    0   |     1  |     1  |   1    |
| BUMP OF CHICKEN   |   1   |    0   |    1   |     1  |     1  |   0    |

---

さて、A〜Fまでの中で音楽性のベクトルが合うor合わないを求めます。

```python:cos_sim.py
import module_cos
import numpy as np

A = [1,1,0,0,0,0,1,1]
B = [1,1,0,0,1,1,1,0]
C = [0,0,1,1,1,1,0,1]
D = [1,1,0,0,0,1,1,1]
E = [0,1,1,1,0,0,1,1]
F = [1,0,1,0,0,0,1,0]
people = [A,B,C,D,E,F]
tag = ["A","B","C","D","E","F"]


chemistry_list = []
cos_list = [] # cos だけを記録するリスト
n = len(people)
for i in range(n-1):
    for j in range(i+1, n):
        cos0 = module_cos.calc_cos(people[i], people[j])
        temp = tag[i]+"と"+tag[j]+"："+str(cos0)
        chemistry_list.append(temp)
        cos_list.append(cos0)
        print(temp)
cos_list = np.array(cos_list)
max2 = cos_list.argmax() # 最大値は何番目の要素かを求める
min2 = cos_list.argmin() # 最小値は何番目の要素かを求める
print("\nベクトルが最も合うのは", chemistry_list[max2])
print("ベクトルが最も合わないのは", chemistry_list[min2])

```
---
結果↓
AとB：0.6708203932499369
AとC：0.22360679774997896
AとD：0.8944271909999159
AとE：0.6708203932499369
AとF：0.5773502691896258
BとC：0.3999999999999999
BとD：0.7999999999999998
BとE：0.3999999999999999
BとF：0.5163977794943222
CとD：0.3999999999999999
CとE：0.5999999999999999
CとF：0.2581988897471611
DとE：0.5999999999999999
DとF：0.5163977794943222
EとF：0.5163977794943222

ベクトルが最も合うのは AとD：0.8944271909999159
ベクトルが最も合わないのは AとC：0.22360679774997896

---

実際のところ、
A君「C君とは**ベクトル**合わないわー」
　　　　　　↓↓↓↓
A君「C君とは**コサイン類似度が0.223だから**合わないわー」

って言ったらまあ多分嫌われます()