類似係数(Jaccard/Simpson/Dice)をPythonで求める

語が2つ与えられた時に、どのくらい似ているのか計量評価したいといった目的のために類似指数というのが存在します。

今回は、よく知られていて、かつ、実装の簡単な

  • Jaccard 係数
  • Simpson 係数
  • Dice 係数

を Python で実装します。

これら3つの係数は、0から1までの値を取り、1に近づくほど類似し、0に近づくほど類似していないことを表します。

Jaccard 係数

Jaccard index, Jaccard similarity coefficient などとも呼ばれます。

次の式で表されます。

|X∩Y| / |X∪Y|
  • xとYが完全一致

の場合に1となります。

def jaccard(x, y):
    """
    Jaccard index
    Jaccard similarity coefficient
    https://en.wikipedia.org/wiki/Jaccard_index
    """
    x = frozenset(x)
    y = frozenset(y)
    return len(x & y) / float(len(x | y))

Simpson 係数

overlap coefficient, Szymkiewicz-Simpson coefficient などとも呼ばれます。

次の式で表されます。

|X∩Y| / min(|X|, |Y|)
  • XとYが完全一致
  • XがYの部分集合(またはその逆)

の場合に1となります。

def overlap(x, y):
    """
    overlap coefficient
    Szymkiewicz-Simpson coefficient)
    https://en.wikipedia.org/wiki/Overlap_coefficient
    """
    x = frozenset(x)
    y = frozenset(y)
    return len(x & y) / float(min(map(len, (x, y))))

Dice 係数

Dice’s coefficient/Sørensen-Dice coefficient などとも呼ばれます。
次の式で表されます。

2 * |X∩Y| / (|X| + |Y|)

xとYが完全一致

の場合に1となります。

三角不等式を満たさないため、

def dice(x, y):
    """
    Dice's coefficient
    Sørensen-Dice coefficient
    https://en.wikipedia.org/wiki/Dice%27s_coefficient
    """
    x = frozenset(x)
    y = frozenset(y)
    return 2 * len(x & y) / float(sum(map(len, (x, y))))

計算してみる

  • night
  • nacht
  • nuit

の3語に対して、各組み合わせの類似係数を求めてみましょう。

#!/usr/bin/env python
# vim: set fileencoding=utf8 :

def jaccard(x, y):
    """
    Jaccard index
    Jaccard similarity coefficient
    https://en.wikipedia.org/wiki/Jaccard_index
    """
    x = frozenset(x)
    y = frozenset(y)
    return len(x & y) / float(len(x | y))

def overlap(x, y):
    """
    overlap coefficient
    Szymkiewicz-Simpson coefficient)
    https://en.wikipedia.org/wiki/Overlap_coefficient
    """
    x = frozenset(x)
    y = frozenset(y)
    return len(x & y) / float(min(map(len, (x, y))))

def dice(x, y):
    """
    Dice's coefficient
    Sørensen-Dice coefficient
    https://en.wikipedia.org/wiki/Dice%27s_coefficient
    """
    x = frozenset(x)
    y = frozenset(y)
    return 2 * len(x & y) / float(sum(map(len, (x, y))))

a = 'night'
b = 'nacht'
c = 'nuit'

for x, y in ((a,b), (a,c), (b, c)):
    print "similarity between [%s] and [%s]" % (x, y)
    print "Jaccard:", jaccard(x, y)
    print "overlap:", overlap(x, y)
    print "dice:", dice(x, y)

実行してみると、次のような結果となりました。

similarity between [night] and [nacht]
Jaccard: 0.428571428571
overlap: 0.6
dice: 0.6
similarity between [night] and [nuit]
Jaccard: 0.5
overlap: 0.75
dice: 0.666666666667
similarity between [nacht] and [nuit]
Jaccard: 0.285714285714
overlap: 0.5
dice: 0.444444444444

その他

今回は類似指数を「語」を対象にしましたが、2つのベクトル間の類似性を求めるなど、広い意味での類似性の抽出に利用出来ます。

関連リンク

Leave a comment