以前、Pythonで類似度を計算する方法を書きましたが、これをJavaScriptに書き換えてみました。
Pythonの方が書き慣れてしまったこともありますが、JavaScriptも便利な記法が増えていて、移植に時間がかかりました。
コード
function calc_tf(term, terms) {
ct = 0;
for (i in terms) {
w = terms[i];
if (w == term) {
ct += 1;
}
}
return ct / terms.length;
}
ct = 0;
for (i in terms) {
w = terms[i];
if (w == term) {
ct += 1;
}
}
return ct / terms.length;
}
function calc_idf(term, texts) {
ct = 0;
for (i in texts) {
terms = texts[i];
if (terms.includes(term)) {
ct += 1;
}
}
if (ct == 0) {
return 0;
}
result = Math.log(texts.length / ct) + 1;
return result;
}
ct = 0;
for (i in texts) {
terms = texts[i];
if (terms.includes(term)) {
ct += 1;
}
}
if (ct == 0) {
return 0;
}
result = Math.log(texts.length / ct) + 1;
return result;
}
function calc_tfidf(word, words, texts) {
tr = calc_tf(word, words);
idf = calc_idf(word, texts);
return tr * idf
}
tr = calc_tf(word, words);
idf = calc_idf(word, texts);
return tr * idf
}
function cosine_similarity(vector0, vector1) {
// 類似度
mykeys = Object.keys(vector0).concat(Object.keys(vector1));
mykeys = Array.from(new Set(mykeys));
a = [];
for (i in mykeys) {
k = mykeys[i];
if (k in vector0) {
a[k] = vector0[k];
} else {
a[k] = 0;
}
}
b = [];
for (i in mykeys) {
k = mykeys[i];
if (k in vector1) {
b[k] = vector1[k];
} else {
b[k] = 0;
}
}
x = 0;
y = 0;
z = 0;
for (k in a) {
u = a[k];
v = b[k];
x += u * v
y += u ** 2
z += v ** 2
}
if (y == 0 || z == 0) {
return 0;
}
return x / (y ** (1 / 2)) / (z ** (1 / 2));
}
// 類似度
mykeys = Object.keys(vector0).concat(Object.keys(vector1));
mykeys = Array.from(new Set(mykeys));
a = [];
for (i in mykeys) {
k = mykeys[i];
if (k in vector0) {
a[k] = vector0[k];
} else {
a[k] = 0;
}
}
b = [];
for (i in mykeys) {
k = mykeys[i];
if (k in vector1) {
b[k] = vector1[k];
} else {
b[k] = 0;
}
}
x = 0;
y = 0;
z = 0;
for (k in a) {
u = a[k];
v = b[k];
x += u * v
y += u ** 2
z += v ** 2
}
if (y == 0 || z == 0) {
return 0;
}
return x / (y ** (1 / 2)) / (z ** (1 / 2));
}
function calc_vector_tfidf(target_terms, texts) {
v = [];
for (i in target_terms) {
target = target_terms[i];
v[target] = calc_tfidf(target, target_terms, texts);
}
return v;
}
v = [];
for (i in target_terms) {
target = target_terms[i];
v[target] = calc_tfidf(target, target_terms, texts);
}
return v;
}
function get_similar_tfidf(target_terms, texts) {
vector_target = calc_vector_tfidf(target_terms, texts);
results = [];
for (i in texts) {
text = texts[i];
vector_one = calc_vector_tfidf(text, texts);
result = cosine_similarity(vector_target, vector_one);
results.push([result, text]);
}
results.sort(function(a, b) {
return b[0] - a[0];
});
return results;
}
vector_target = calc_vector_tfidf(target_terms, texts);
results = [];
for (i in texts) {
text = texts[i];
vector_one = calc_vector_tfidf(text, texts);
result = cosine_similarity(vector_target, vector_one);
results.push([result, text]);
}
results.sort(function(a, b) {
return b[0] - a[0];
});
return results;
}
テスト
target_terms = ["りんご", "みかん"];
texts = [
["りんご", "みかん"],
["りんご", "みかん", "みかん"],
["りんご", "みかん", "ばなな"],
["りんご", "ぶどう", "ばなな"],
["すいか", "ばなな"],
];
results = get_similar_tfidf(target_terms, texts)
console.log(results);
texts = [
["りんご", "みかん"],
["りんご", "みかん", "みかん"],
["りんご", "みかん", "ばなな"],
["りんご", "ぶどう", "ばなな"],
["すいか", "ばなな"],
];
results = get_similar_tfidf(target_terms, texts)
console.log(results);
元となる配列
[“りんご”, “みかん”]
結果
1.00, [‘りんご’, ‘みかん’]
0.96, [‘りんご’, ‘みかん’, ‘みかん’]
0.79, [‘りんご’, ‘みかん’, ‘ばなな’]
0.24, [‘りんご’, ‘ぶどう’, ‘ばなな’]
0.00, [‘すいか’, ‘ばなな’]
「りんご みかん」の場合は1.00となり、「すいか ばなな」の場合は0.00となります。
その他の結果も妥当であると思われます。
コメント
[…] JavaScriptで類似度を計算する方法を書きました。 これを使って複数の文から類似度の高い文を検索する方法を考えます。 […]