Inconsistent sort with sort ()

I have the following function for counting words from a string and highlighting the top "n":

Function

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

Thus, it is sorted by origin and linked in alphabetical order. The following examples:

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

works (shows [('cat', 4), ('mat', 4), ('bat', 3)])

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

does not work (shows [('butter', 2), ('a', 1), ('bitter', 1)], but should have bettyinstead bitter, since it is tied be...up to bi...)

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

works (shows [('butter', 2), ('a', 1), ('betty', 1), ('bitter', 1), ('but', 1), ('of', 1)]from bettyto bitteras intended)

What could be the reason (word length maybe?) And how can I fix it?

+4
source share
3 answers

3 , , , .

most_common() , heapq ( , n , ):

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)

Python 2 , , iteritems(), items() .

Counter.most_common(), . , heapq , O (NlogK), O (NlogN) ( 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):

>>> 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
+3

sort, most_common. Counter -, , , . most_common(n), n , , , !

- most_common :

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

You can fix this by doing .most_common(), then sorting and then slicing the result, instead of giving nin most_common:

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]
+1
source

Source: https://habr.com/ru/post/1659063/


All Articles