词袋表示法问题

3
基本上,我有一个包含所有词汇的字典作为键,值都为0。
为了将文档处理成词袋表示,我曾经使用适当的IEqualityComparer复制该字典,并简单地检查字典是否包含文档中的每个单词并增加其键。
为了获取词袋表示的数组,我只需使用ToArray方法即可。
这似乎很好用,但我刚刚被告知,字典不能保证相同的键顺序,因此生成的数组可能以不同的顺序表示单词,使其无用。
解决这个问题的我的当前想法是将词语字典的所有键复制到ArrayList中,创建正确大小的数组,然后使用数组列表的indexOf方法填充数组。
所以我的问题是,有没有更好的方法来解决这个问题?我的方法似乎有点粗糙... 我会不会因为IEqualityComparer而遇到问题?

1
作为一个问题的兴趣,你为什么想要转换成数组? - Mitch Wheat
2
你能提供一些示例代码和数据来更好地说明你正在尝试使用词袋模型做什么吗? - G-Wiz
我想将它转换为数组,以便稍后可以使用余弦相似度。词袋模型是指通过单词的频率来表示文档,而忽略其顺序和其他属性。 - brokencoding
3
确实,而且“单词袋”这个术语也暗示着一种无序关系,所以要么是要求有问题,要么是术语使用不当。 - Aaronaught
除非你需要将不同类型的对象放入单个容器中(例如,arr [0]是int,arr [1]是Page,arr [2]是string等),否则永远不要使用ArrayList。相反,使用带类型参数的泛型列表。 - 3Dave
6个回答

4

让我看看是否理解了问题。您有两个文档D1和D2,每个文档包含从已知词汇{W1,W2... Wn}中抽取的单词序列。您希望获得两个映射,指示每个文档中每个单词的出现次数。因此,对于D1,您可能会有以下结果:

W1 --> 0
W2 --> 1
W3 --> 4

提示D1可能是"W3 W2 W3 W3 W3"。也许D2是"W2 W1 W2",因此它的映射是

W1 --> 1
W2 --> 2
W3 --> 0

你希望获取映射关系并确定向量[0, 1, 4]和[1, 2, 0],然后计算这两个向量之间的角度,以确定两个文档的相似度或差异性。
你的问题在于字典不能保证按任何特定顺序枚举键/值对。
好的,那就给它们排序。
vector1 = (from pair in map1 orderby pair.Key select pair.Value).ToArray();
vector2 = (from pair in map2 orderby pair.Key select pair.Value).ToArray();

并且你完成了。这解决了你的问题吗?还是我误解了情境?

这是一种情况,但是对于一个有 2 万个单词的字典来说,每次转换文档都进行排序是否计算量过大? - brokencoding
1
Eric,我不明白“W2 W1 W2”如何对应第二个映射。 - spender
@brokencoding:如果你只是比较两个文档,那么为什么要在包含所有20k单词的字典上执行此操作?文档有多大?你只需要包括出现在至少一个文档中的单词计数。 - Aaronaught
不仅仅是两个文档,假设我必须像你的示例一样将一个文档目录表示为向量,然后我可以通过相似性对它们进行聚类。 - brokencoding
@brokencoding:所以你基本上是在尝试对整个文档目录进行某种程度的自然语言处理...而你担心的是单个排序的性能?推测显然不能替代分析,但我有一种感觉,无论你做什么后处理,更不用说打开和读取文件所涉及的I/O,都将成为关键路径。你可以轻松并行化排序。如果你有疑问,请测试它。 - Aaronaught
显示剩余3条评论

2

如果我理解正确,您想通过单词频率拆分文档。

您可以对文档运行正则表达式以拆分单词:


如果我理解正确,你想按单词频率拆分一个文档。
你可以使用正则表达式将文档拆分成单词:
var words=Regex
    .Matches(input,@"\w+")
    .Cast<Match>()
    .Where(m=>m.Success)
    .Select(m=>m.Value);

制作频率图:
var map=words.GroupBy(w=>w).Select(g=>new{word=g.Key,freqency=g.Count()});

如果需要的话,GroupBy方法有多种重载形式,可以提供替代的IEqualityComparer。

根据您的评论,要创建仅频率的相应序列:

map.Select(a=>a.frequency)

这个序列的顺序将与上面的map序列完全相同。这有帮助吗?

1

还有一个OrderedDictionary

表示一组可通过键或索引访问的键/值对集合。


@gWiz: 这不是必需的要求。 - Mitch Wheat
@gWiz - 这里有一些版本可供使用:http://www.codeproject.com/KB/recipes/GenericOrderedDictionary.aspx - Nick Craver
我想将它转换为数组以便能够稍后使用余弦相似性。 - brokencoding
真实的,这不是一个明确的要求。我只是注意到他提到了“字典”,这是通用的。 - G-Wiz

0

可能会有类似这样的解决方案,虽然它确实很丑陋,但我相信它与您所建议的类似。GetWordCount() 完成了工作。

class WordCounter {

public Dictionary dictionary = new Dictionary();

    public void CountWords(string text)
    {
        if (text != null && text != string.Empty)
        {
            text = text.ToLower();
            string[] words = text.Split(' ');
            if (dictionary.ContainsKey(words[0]))
            {
                if (text.Length > words[0].Length)
                {
                    text = text.Substring(words[0].Length + 1);
                    CountWords(text);
                }

            }
            else
            {
                int count = words.Count(
                    delegate(string s)
                    {
                        if (s == words[0]) { return true; }
                        else { return false; }
                    });
                dictionary.Add(words[0], count);
                if (text.Length > words[0].Length)
                {
                    text = text.Substring(words[0].Length + 1);
                    CountWords(text);
                }

            }
        }
    }

    public int[] GetWordCount(string text)
    { 
        CountWords(text);
        return dictionary.Values.ToArray<int>();
    }


}

不,我没有解析文本的问题,我的想法是像这样表示文本:Text = "猫 狗 狼 猫 马 狗";我会有这样的字典: [猫, 2] [狗, 2] [狼, 1] [马, 1]而单词袋表示法将简单地为: [2 2 1 1]但是,如果字典没有保持顺序,我可能会得到类似 [2 1 2 1] 的东西,这就失败了其目的。 - brokencoding

0

这对您有帮助吗:

SortedDictionary<string, int> dic = new SortedDictionary<string, int>();

            for (int i = 0; i < 10; i++)
            {
                if (dic.ContainsKey("Word" + i))
                    dic["Word" + i]++;
                else
                    dic.Add("Word" + i, 0);
            }

            //to get the array of words:
            List<string> wordsList = new List<string>(dic.Keys);
            string[] wordsArr = wordsList.ToArray();

            //to get the array of values
            List<int> valuesList = new List<int>(dic.Values);
            int[] valuesArr = valuesList.ToArray();

0

如果你只是想计算余弦相似度,那么你不需要将数据转换为长度为20,000的数组,特别是考虑到数据很可能是稀疏的,大多数条目都是零。

在处理文件时,将文件输出数据存储到以单词为键的字典中。然后,为了计算点积和大小,你需要遍历完整的单词列表中的单词,在每个文件输出数据中查找单词,并使用找到的值(如果存在)和零(如果不存在)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接