2014-04-01 62 views
2

假设我有这样一个字典:如何计算包含特定单词的文档?

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]]

我不知道。重点是扩大每个清单并确保条款的顺序。思考?

+1

我不明白你的目标。你想要统计一个文档中的字符/单词的次数?但你不想要实际的数量?为什么''''''''有'1'和''''''有'2'的计数?这对我来说没有意义。 – CoryKramer

+0

@Cyber​​我想统计有多少文档包含这个词。在我的例子中,只有'''alpha''包含''a“',所以它应该是1(尽管在”alpha“中是两次),而”c“是在”“alpha”和“布拉沃“,所以它是2(尽管总共有3个”C“)。 – Melkor

回答

6

我会用你的第二种方式使用collections.Counterset

>>> 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:

如果性能是一个关注的则不要使用sum使用Counter,这里另一种方式来做到这一点。请注意,不像@ 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 
+0

谢谢!我从来没有想过它可以如此紧凑和优雅! – Melkor

+0

但是...有什么办法可以保留字典的结构吗?喜欢......'{'alpha':{'c':2,'b':2,'a':1},'bravo':{'c':2,'b':2,'d' :1}}' – Melkor

+0

@Melkor检查更新后的答案,您需要额外的循环。 –

1
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} 
1

您可以使用一套统一字符的单个文件内。然后简单地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([])) 
2

这不是特别的,很普通的方式。

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) 
+0

我不得不说这真的很神奇。处理超过3千行的超过70,000个单词只需要2秒钟!至于保留字典的结构,我只是创建一个新的字典,并迭代原字典来映射这个计数器的结果。仍然非常快。 – Melkor

+0

@Melkor:我不知道'set'函数和'for-loop'是如此之快。谢谢你告诉我。无论如何,我上传更快的一个。:) –

相关问题