余弦相似文档距离

时间:2015-03-27 15:15:37

标签: java cosine-similarity

我有两份文件,我被要求计算文件中每个单词出现的频率。例如,在doc1和doc2中,单词“CAT”每次出现两次,然后它总共出现了4次,我需要计算它出现的频率。

通过过去三晚的谷歌搜索,我找到了一种称为余弦相似度的优秀算法。我现在明白它是如何工作的。

但我不知道如何在Java中实现它。我应该如何将单词转换为向量?

假设我的输入是“多少木头夹头的土拨鼠可以扔掉木头”我怎么能将这些单词转换成n向量空间?我首先创建一个单词数组,然后使用count变量遍历数组,看看这个单词出现了多少次?但那不就意味着我们至少需要n个计数变量吗?

非常感谢你帮我解决这个问题

3 个答案:

答案 0 :(得分:1)

将结果保持为Map<String, Integer>,并使用String#split()将输入分隔为单词。

在将文本读入字符串后,您只需要一行代码:

Map<String, Integer> frequencies = Arrays
    .stream(text.toLowerCase().split("[^a-z']+"))
    .collect(Collectors.groupingBy(s -> s, Collectors.counting());

答案 1 :(得分:1)

我正在看麻省理工学院的精彩视频系列:Models of Computation, Document Distance。我在那里发现了这个问题。

所以我写了一个Java代码来查找两个文档之间的距离,其中文档不过是用空格分隔的单词。

import java.util.HashMap;
import java.util.Scanner;

public class document_distance {

//print the string array made from document
public static void printDoc(String[] doc) {
    System.out.println("=====printing doc words ====");
    int len = doc.length;
    for( int i=0; i<len; i++ )  {
        System.out.print(doc[i]+" ");
    }
    System.out.println();
}

public static void printMap(HashMap<String, Integer> dict) {
    System.out.println("=====printing dictionary (key,value) ====");
    for(String key: dict.keySet()) {
        System.out.println(key+" ->"+dict.get(key));
    }
}

public static void main(String[] args) {

    Scanner sc = new Scanner(System.in);
    String doc1[] = sc.nextLine().split(" ");
    String doc2[] = sc.nextLine().split(" ");

    //print both documents to verify that they are saved correctly!
    printDoc(doc1);
    printDoc(doc2);

    //create two dictionaries with keys as words and values as count of that word!
    HashMap<String, Integer> dict1 = new HashMap<String, Integer>();
    HashMap<String, Integer> dict2 = new HashMap<String, Integer>();

    //update counts for doc1 both dictionaries
    for(int i=0; i<doc1.length ;i++) {
        if(!dict1.containsKey(doc1[i])) { //word is not in dict1 yet
            dict1.put(doc1[i], 1);
        }
        else if(dict1.containsKey(doc1[i])) { //word is in dict1 
            dict1.put(doc1[i], dict1.get(doc1[i]) + 1);
        }

        if(!dict2.containsKey(doc1[i])) { //word is not in dict2 yet
            dict2.put(doc1[i], 0);
        }


    }

    //update counts for doc1 both dictionaries
    for(int i=0; i<doc2.length ;i++) {
        if(!dict2.containsKey(doc2[i])) { //word is not in dict2 yet
            dict2.put(doc2[i], 1);
        }
        else if(dict2.containsKey(doc2[i])) { //word is in dict2
            dict2.put(doc2[i], dict2.get(doc2[i]) + 1);
        }

        if(!dict1.containsKey(doc2[i])) { //word is not in dict1
            dict1.put(doc2[i], 0);
        }


    }
    //print dictionaries
    printMap(dict1);
    printMap(dict2);

    int dotProduct =0;
    int doc1sq = 0;
    int doc2sq = 0;
    for(int i=0; i<doc1.length ;i++) {
        dotProduct = dotProduct + (dict1.get(doc1[i])) * (dict2.get(doc1[i]));
        doc1sq = doc1sq +  (dict1.get(doc1[i])) *  (dict1.get(doc1[i]));
        doc2sq = doc2sq +  (dict2.get(doc1[i])) *  (dict2.get(doc1[i]));    
    }

    double similarity = dotProduct / Math.sqrt(doc1sq*doc2sq);
    System.out.print("similarity = "+ similarity);

}

}

答案 2 :(得分:0)

是的,没错。如果你想考虑每个单词的频率,你需要与两个文档中的唯一单词一样多的组件。

在Java中执行此操作的一种简单方法是使用HashMap键和String值生成Integer。只需浏览文档中显示的单词列表,然后在HashMap中的相应条目中添加一个单词。最后,您将获得计数值作为键的单词。确保在添加一个条目时,如果条目不存在,则将其初始化为1。

伪代码中的更多细节:

for word in doc1
    if (!vector1.has(word)) {vector1.put(word, 0);}
    if (!vector2.has(word)) {vector2.put(word, 0);}
    vector1.put(word, vector1.get(word) + 1);
done
same loop for doc2, with the last line changed to vector2

现在,您有两个带有与键相同的单词的向量,并在各个文档中计数。然后你可以用任何一个来走过这些词:

dotp = 0; v1sq = 0; v2sq = 0
for word in vector1
    dotp = dotp + vector1.get(word) * vector2.get(word)
    v1sq = v1sq + vector1.get(word) * the-same-thing
    v2sq = the-same-same-thing
done
similarity = dotp / sqrt(v1sq * v2sq)

你有!只需弄清楚Java部分。