Jaccard係数

概要

文系プログラマが数式アレルギーを治す為に、中学〜高校レベルの知識でもわかる数式を、プログラムのコードに直す作業を黙々と続ける企画。

ソースコードはJuliaを利用。徐々に改善はされているけどまだまだPythonに勝てる日は遠いJulia。

今回のテーマはJaccard距離。「あなたにピッタリの異性を紹介します」みたいなサービスでも使えそうなJaccard距離。

@CretedDate 2015/07/11
@Versions Julia0.3.10

数式

式はWikipediaのJaccard indexを参考にした。

https://en.wikipedia.org/wiki/Jaccard_index

Jaccard similarity(類似)の式はこんな感じ。

J ( A , B ) = A B A B

# 自分用メモ
$ J( A, B ) = \frac { \mid A \cap B \mid } { \mid A \cup B \mid  } $

上の式では同一であれば1に、似ていないものであれば0に近づく。

これに対してJaccard距離ということになると、似ているほど0に近くなって欲しいので、1-類似度ということになる。

その式がこんな感じになるらしい。

1 - J ( A , B ) = A B - A B A B

# 自分用メモ
$ 1 - J( A, B ) = \frac { \mid A \cup B \mid - \mid A \cap B \mid } { \mid A \cup B \mid  } $

なんかよくわからないけど、感じ的にbitをandしてorすればいいのかな。

数式の解説

∪とか∩はベン図とか集合の話とかで出てくるヤツ。

名前はUnionとIntercsection。日本語だと和集合と交差?

コードに直す

10011011010101010101という2つの2進数があったとする。この2つの距離を求めてみる。

# 2つの2進数があったとさ
a = parseint("1001101101",2)
b = parseint("0101010101",2)

この2つの単純なAND/ORならこうなる。

sim(x, y) = count_ones(x & y) / count_ones(x | y)
sim(a, b )
  #=> 0.375

こんな感じでsimilarityが計算された。

distanceを出す場合もやってみる。

dist(x, y) = ( count_ones( x | y ) - count_ones(x & y) ) / count_ones(x | y)
dist(a, b)
  #=> 0.625

ちゃんと 1 - sim(a, b) の値が出た。

Setでやってみる

上の例はbinary numberをただAND/ORしたけど、普通はSet同士の距離とか測ったりするらしい。

試しに下記3つのSetで距離を取ってみる。

# 奇数のSet
odd = Set( 1, 3, 5, 7, 9 )
# 偶数のSet
even = Set( 2, 4, 6, 8, 10 )
# 両方混じったSet
each = Set( 1, 2, 3, 4, 5 )

当然ながら奇数と偶数では距離が遠い(1), 奇数と奇偶双方混じったSetでは距離が中間になることになる。

Setにはunion, intersectなどの関数が用意されているけど、Juliaさんなので ∪ と ∩ にもaliasが貼ってある。

というわけで、こんな風に書ける。

dist( x, y ) = (length(x ∪ y) - length(x ∩ y)) / length(x ∪ y)
dist( odd, even )
  #=> 1.0
dist( odd, each )
  #=> 0.5714285714285714
dist( even, each )
  #=> 0.75

oddとevenは1に、odd, even, each(eachは奇数の方が多い)では、odd-each間の方が近くなっている。

結果が正しいか確認する

自前で書いただけだと合ってるかどうか怪しいので、scipy先生で答え合わせする。

from scipy.spatial.distance import jaccard

# odd - each距離
jaccard( [ 1, 0, 3, 0, 5, 0, 7, 0, 9 ], [ 1, 2, 3, 4, 5, 0, 0, 0, 0 ] )
  #=> 0.5714285714285714

# even - each距離
jaccard( [ 0, 2, 0, 4, 0, 6, 0, 8, 0, 10 ], [ 1, 2, 3, 4, 5, 0, 0, 0, 0, 0 ] )
  #=> 0.75

同じ答えになった。