235

Other than doing list comprehensions of reversed list comprehension, is there a pythonic way to sort Counter by value? If so, it is faster than this:

>>> from collections import Counter
>>> x = Counter({'a':5, 'b':3, 'c':7})
>>> sorted(x)
['a', 'b', 'c']
>>> sorted(x.items())
[('a', 5), ('b', 3), ('c', 7)]
>>> [(l,k) for k,l in sorted([(j,i) for i,j in x.items()])]
[('b', 3), ('a', 5), ('c', 7)]
>>> [(l,k) for k,l in sorted([(j,i) for i,j in x.items()], reverse=True)]
[('c', 7), ('a', 5), ('b', 3)

4 Answers 4

430

Use the Counter.most_common() method, it'll sort the items for you:

>>> from collections import Counter
>>> x = Counter({'a':5, 'b':3, 'c':7})
>>> x.most_common()
[('c', 7), ('a', 5), ('b', 3)]

It'll do so in the most efficient manner possible; if you ask for a Top N instead of all values, a heapq is used instead of a straight sort:

>>> x.most_common(1)
[('c', 7)]

Outside of counters, sorting can always be adjusted based on a key function; .sort() and sorted() both take callable that lets you specify a value on which to sort the input sequence; sorted(x, key=x.get, reverse=True) would give you the same sorting as x.most_common(), but only return the keys, for example:

>>> sorted(x, key=x.get, reverse=True)
['c', 'a', 'b']

or you can sort on only the value given (key, value) pairs:

>>> sorted(x.items(), key=lambda pair: pair[1], reverse=True)
[('c', 7), ('a', 5), ('b', 3)]

See the Python sorting howto for more information.

Sign up to request clarification or add additional context in comments.

Comments

55

A rather nice addition to @MartijnPieters answer is to get back a dictionary sorted by occurrence since Collections.most_common only returns a tuple. I often couple this with a json output for handy log files:

from collections import Counter, OrderedDict

x = Counter({'a':5, 'b':3, 'c':7})
y = OrderedDict(x.most_common())

With the output:

OrderedDict([('c', 7), ('a', 5), ('b', 3)])
{
  "c": 7, 
  "a": 5, 
  "b": 3
}

3 Comments

Starting from Python 3.7 (3.6 for CPython) there is no need for OrderedDict any more, because dict now keeps the insertion order. So it's simply y = dict(x.most_common())
@WalterTross Just to be clear, even in Python 3.7+, OrderedDict offers functionality that dict doesn't, especially regarding comparisons. For example, OrderedDict([('a', 1), ('b', 2)]) == OrderedDict([('b', 2), ('a', 1)]) evaluates to False, whereas {'a': 1, 'b': 2} == {'b': 2, 'a': 1} evaluates to True.
OrderedDict isn't the slightest bit more useful here compared to a plain dict. That includes the == check. Here the non-ordered dict does the right thing. Insertion order is that's desired in this instance. Sometimes yes, you want the order to change the result of the comparison. I don't think the comment you replied to suggested OrderedDict wasn't needed for anything just that it wasn't needed here, and it's not. BTW JSON is effectively insertion-ordered even though it isn't in the spec.
26

Yes:

>>> from collections import Counter
>>> x = Counter({'a':5, 'b':3, 'c':7})

Using the sorted keyword key and a lambda function:

>>> sorted(x.items(), key=lambda i: i[1])
[('b', 3), ('a', 5), ('c', 7)]
>>> sorted(x.items(), key=lambda i: i[1], reverse=True)
[('c', 7), ('a', 5), ('b', 3)]

This works for all dictionaries. However Counter has a special function which already gives you the sorted items (from most frequent, to least frequent). It's called most_common():

>>> x.most_common()
[('c', 7), ('a', 5), ('b', 3)]
>>> list(reversed(x.most_common()))  # in order of least to most
[('b', 3), ('a', 5), ('c', 7)]

You can also specify how many items you want to see:

>>> x.most_common(2)  # specify number you want
[('c', 7), ('a', 5)]

2 Comments

Another way to reverse sort is to set the key function to lamda i: -i[1]
I had forgotten the .items() which then gave me TypeError: bad operand type for unary -: 'str'. It is just that you need the items() to make it read as a pair so that k[1] is found as the second item of each pair which can be reverse sorted with -k[1] because it is a number. You would not be able to do -k[0] since k[0] is a string.
11

More general sorted, where the key keyword defines the sorting method, minus before numerical type indicates descending:

>>> x = Counter({'a':5, 'b':3, 'c':7})
>>> sorted(x.items(), key=lambda k: -k[1])  # Ascending
[('c', 7), ('a', 5), ('b', 3)]

1 Comment

The key keyword defines the sorting method, minus before numerical type indicate descending

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.