如何统计包含特定单词的文档数量?
假设我有一个字典,内容大概是这样的:
docDict = {"alpha": ["a", "b", "c", "a", "b"], "bravo": ["b", "c", "d", "c", "d"]}
我想做的事情是计算“文档频率”:假设每个字典里的项都是一个文档,我有一个特定的词,那么有多少个文档包含这个词呢?
我看到很多帖子在告诉我怎么计算频率,但这里有个特别的地方,如果字母"a"
在文档"alpha"
中出现了两次,我只需要计数为1。所以字母"a"
的“频率”应该是1,而"c"
的频率应该是2。
我知道我可以遍历整个文档字典,找到词的时候就加计数。或者我可以先把每个文档里的词变成唯一的,然后把所有文档合并再统计这个词。
但我觉得还有更好的方法,更有效的方法。有没有什么想法呢?
顺便问一下,有没有办法保持字典的结构?在这个例子中,我想得到的结果是{"alpha": {'c': 2, 'b': 2, 'a': 1}, "bravo": {'c': 2, 'b': 2, 'd': 1}
更新
如果我这里只有一个列表(类似于[["a", "b", "c", "a", "b"], ["b", "c", "d", "c", "d"]]
),我该如何得到像[[1, 2, 2, 0], [0, 2, 2, 1]]
这样的结果列表呢?
我完全没有头绪。关键是要展开每个列表,并确保词的顺序。有什么想法吗?
4 个回答
你可以使用集合(set)来统一一个文档中的所有字符。然后只需用 Counter()
来统计它们的出现次数。
from collections import Counter
docDict = {"alpha": ["a", "b", "c", "a", "b"], "bravo": ["b", "c", "d", "c", "d"]}
result = reduce(lambda x, y: x + Counter(set(y)), docDict.itervalues(), Counter([]))
docDict = {"alpha": ["a", "b", "c", "a", "b"], "bravo": ["b", "c", "d", "c", "d"]}
revDict = {v : sum(1 for l in docDict.values() if v in l)
for v in set(x for y in docDict.values() for x in y) }
print revDict
结果是:
{'a': 1, 'c': 2, 'b': 2, 'd': 1}
这不是特别的方式,挺普通的。
from collections import defaultdict
docDict = {"alpha": ["a", "b", "c", "a", "b"], "bravo": ["b", "c", "d", "c", "d"]}
result = defaultdict(set)
for k, vlist in docDict.items():
for v in vlist:
result[v].add(k)
#Now the result looks like this.
#{'a': set(['alpha']), 'c': set(['alpha', 'bravo']), 'b': set(['alpha', 'bravo']), 'd': set(['bravo'])})
print dict(zip(result.keys(), map(lambda x:len(x), result.values())))
#{'a': 1, 'c': 2, 'b': 2, 'd': 1}
更新
还有一种方法……就是简单地计数。并且改用了迭代器,所以比上面的代码更快。
from collections import defaultdict
def func3(docDict):
result = defaultdict(int)
for vlist in docDict.itervalues():
for i in set(vlist):
result[i] += 1
return dict(result)
我建议你使用第二种方法,也就是用collections.Counter
和set
。
>>> from collections import Counter
>>> sum((Counter(set(x)) for x in docDict.itervalues()), Counter())
Counter({'c': 2, 'b': 2, 'a': 1, 'd': 1})
更新 1:
>>> c = sum((Counter(set(x)) for x in docDict.itervalues()), Counter())
>>> {k: {k1:c[k1] for k1 in set(v)} for k, v in docDict.iteritems()}
{'alpha': {'a': 1, 'c': 2, 'b': 2}, 'bravo': {'c': 2, 'b': 2, 'd': 1}}
更新 2:
如果你担心性能问题,那就不要把Counter
和sum
一起用,这里有另一种做法。需要注意的是,我和@user2931409的回答不同,我并没有把单词存储在内存中只是为了获取它们的长度,所以这种方法在内存使用上更高效,但比他们的答案稍微慢一点。
result = Counter()
for v in docDict.itervalues():
result.update(set(v))
return result
时间比较:
def func1():
#http://stackoverflow.com/a/22787509/846892
result = defaultdict(set)
for k, vlist in docDict.items():
for v in vlist:
result[v].add(k)
return dict(zip(result.keys(), map(lambda x:len(x), result.values())))
def func2():
result = Counter()
for v in docDict.itervalues():
result.update(set(v))
return result
In [94]: docDict = {''.join(random.choice(lis) for _ in xrange(8)): random.sample(lis, 25)
...: for _ in xrange(70000)}
In [95]: %timeit func1(docDict)
1 loops, best of 3: 380 ms per loop
In [96]: %timeit func2(docDict)
1 loops, best of 3: 591 ms per loop
In [97]: docDict = {''.join(random.choice(lis) for _ in xrange(8)): random.sample(lis, 25)
...: for _ in xrange(10**5)}
In [98]: %timeit func1(docDict)
1 loops, best of 3: 529 ms per loop
In [99]: %timeit func2(docDict)
1 loops, best of 3: 848 ms per loop
In [101]: func1(docDict) == func2(docDict)
Out[101]: True