2014-04-01 61 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'是如此之快。謝謝你告訴我。無論如何,我上傳更快的一個。:) –

相關問題