2016-10-27 5 views
4

私は、文字列から単語をカウントし、一番上の「n」を抽出するために、以下の機能を持っている:不整合()

def count_words(s, n): 
"""Return the n most frequently occuring words in s.""" 

    #Split words into list 
    wordlist = s.split() 

    #Count words 
    counts = Counter(wordlist) 

    #Get top n words 
    top_n = counts.most_common(n) 

    #Sort by first element, if tie by second 
    top_n.sort(key=lambda x: (-x[1], x[0])) 

    return top_n 

機能だからのoccuranceでと結ばれている場合ソート、アルファベット順。 以下の例:

print count_words("cat bat mat cat cat mat mat mat bat bat cat", 3)

作品(番組を[('cat', 4), ('mat', 4), ('bat', 3)]

print count_words("betty bought a bit of butter but the butter was bitter", 3)

(番組[('butter', 2), ('a', 1), ('bitter', 1)]動作しませんが、それとしてbetty代わりのbitter彼らは結び付けられているを持っている必要がありますし、 be...bi...より前)

print count_words("betty bought a bit of butter but the butter was bitter", 6)

(?多分ワード長)ことを引き起こす可能性は何

(意図したとおりにbitterbetty[('butter', 2), ('a', 1), ('betty', 1), ('bitter', 1), ('but', 1), ('of', 1)]を示し)を動作し、どのように私はそれを修正するだろうか?

+0

をあなたは( '.most_commonをすることによってそれを修正できます) 'に' most_common'を 'n 'するのではなく、結果をソートしてスライスします。 – L3viathan

答えて

3

あなたがトップ3を求めている、とあなたがアイテムを選んだことができる前にこのようにあなたがデータのカットあなたの特定の並べ替え順序で。

most_common()事前ソート再度並べ替えを持っているのではなく、(n実際のバケット数よりも小さいを提供)、カスタム基準によってソートするheapqを使用します。Pythonの2オン

import heapq 

def count_words(s, n): 
    """Return the n most frequently occuring words in s.""" 
    counts = Counter(s.split()) 
    key = lambda kv: (-kv[1], kv[0]) 
    if n >= len(counts): 
     return sorted(counts.items(), key=key) 
    return heapq.nsmallest(n, counts.items(), key=key) 

、あなた上記の呼び出しにitems()ではなくiteritems()を使用することをお勧めします。

これにより、Counter.most_common() methodが更新されたキーで再作成されます。元のようにheapqを使用すると、O(NlogN)ではなくO(NlogK)のパフォーマンスにバインドされます(Nバケットの数、Kは見たい上位の要素数)。

デモ:

>>> count_words("cat bat mat cat cat mat mat mat bat bat cat", 3) 
[('cat', 4), ('mat', 4), ('bat', 3)] 
>>> count_words("betty bought a bit of butter but the butter was bitter", 3) 
[('butter', 2), ('a', 1), ('betty', 1)] 
>>> count_words("betty bought a bit of butter but the butter was bitter", 6) 
[('butter', 2), ('a', 1), ('betty', 1), ('bit', 1), ('bitter', 1), ('bought', 1)] 

とPython 3.6のクイック性能比較(。0B1):あなたが代わりにmost_commonnを与えるのは、仕分けして、結果をスライスし、.most_common()を行うことによってそれを修正することができ

>>> from collections import Counter 
>>> from heapq import nsmallest 
>>> from random import choice, randrange 
>>> from timeit import timeit 
>>> from string import ascii_letters 
>>> sentence = ' '.join([''.join([choice(ascii_letters) for _ in range(randrange(3, 15))]) for _ in range(1000)]) 
>>> counts = Counter(sentence) # count letters 
>>> len(counts) 
53 
>>> key = lambda kv: (-kv[1], kv[0]) 
>>> timeit('sorted(counts.items(), key=key)[:3]', 'from __main__ import counts, key', number=100000) 
2.119404911005404 
>>> timeit('nsmallest(3, counts.items(), key=key)', 'from __main__ import counts, nsmallest, key', number=100000) 
1.9657367869949667 
>>> counts = Counter(sentence.split()) # count words 
>>> len(counts) 
1000 
>>> timeit('sorted(counts.items(), key=key)[:3]', 'from __main__ import counts, key', number=10000) # note, 10 times fewer 
6.689963405995513 
>>> timeit('nsmallest(3, counts.items(), key=key)', 'from __main__ import counts, nsmallest, key', number=10000) 
2.902360848005628 
10

問題はsortではなく、most_commonです。 Counterはハッシュテーブルとして実装されているため、使用する順序はで、任意のです。 most_common(n)を尋ねると、最も一般的な言葉がnで返されます。もし結びつきがあれば、返すものはどれかを決めるだけです!

これを解決する最も簡単な方法は、ちょうどmost_commonの使用を避けて直接リストを使用することです:

top_n = sorted(counts.items(), key=lambda x: (-x[1], x[0]))[:n] 
+0

'sorted()'は使わないでください。バケツの数が多い場合、これはかなりの時間を無駄にする。 'heapq.nsmallest()'を使い、O(NlogN)ではなくO(NlogK)のステップだけを実行してください。 –

+0

@MartijnPieters私はそれとheapqについてよく知っています。しかし、実際には重要ではないと思う。なぜなら、それは対数ファクターの変化であり、指数関数的に大きなデータが必要となるからだ。指数関数的に大きいデータでは、とにかく問題がたくさんある。特に、カウンター 'ハッシュテーブルはかなりのスペースを無駄にしている... – Bakuriu

+0

あなたは本当に巨大なハッシュを必要としない違いを参照してください。 [このクイックタイミングテスト](https://gist.github.com/mjpieters/3831d567f8ec4e0adc4c0fbc830f9326)を参照してください。それは手紙の数のために53の中でトップ3を選んでいます、そして、すでにheapqが勝ちます。ヒープは*遅くなることはありません*。一意の1000語に上げると、その差は顕著です。 –

1

def count_words(s, n): 
    """Return the n most frequently occuring words in s.""" 

    #Split words into list 
    wordlist = s.split() 

    #Count words 
    counts = Counter(wordlist) 

    #Sort by frequency 
    top = counts.most_common() 

    #Sort by first element, if tie by second 
    top.sort(key=lambda x: (-x[1], x[0])) 

    return top[:n] 
+0

これは 'most_common()'に含まれる最適化を完全に取り除きます。 'most_common()'にソートを行ってから、ほとんどのものを破棄する必要はありません。 –

+0

良い点、ヒープが良い方法ですが、これが問題の理解に役立つと思います。 – L3viathan

関連する問題