diff -r e6cd37d4dad8 Doc/library/collections.rst --- a/Doc/library/collections.rst Fri Jan 18 00:10:37 2013 -0500 +++ b/Doc/library/collections.rst Fri Jan 18 12:19:49 2013 +0000 @@ -220,6 +220,13 @@ [('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631), ('you', 554), ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)] + >>> # Find the ten least common words in Hamlet + >>> import re + >>> words = re.findall('\w+', open('hamlet.txt').read().lower()) + >>> Counter(words).least_common(10) + [('forgone', 1), ('lunacies', 1), ('doubts', 1), ('conjuring', 1), ('homage', 1), + ('appetite', 1), ('evermore', 1), ('hate', 1), ('patches', 1), ('remiss', 1)] + .. class:: Counter([iterable-or-mapping]) A :class:`Counter` is a :class:`dict` subclass for counting hashable objects. @@ -274,6 +281,16 @@ >>> Counter('abracadabra').most_common(3) [('a', 5), ('r', 2), ('b', 2)] + + .. method:: least_common([n]) + + Return a list of the *n* least common elements and their counts from the + least common to the most. If *n* is not specified, :func:`least_common` + returns *all* elements in the counter. Elements with equal counts are + ordered arbitrarily: + + >>> Counter('abracadabra').least_common(3) + [('c', 1), ('d', 1), ('r', 2)] .. method:: subtract([iterable-or-mapping]) @@ -313,6 +330,7 @@ c.items() # convert to a list of (elem, cnt) pairs Counter(dict(list_of_pairs)) # convert from a list of (elem, cnt) pairs c.most_common()[:-n:-1] # n least common elements + c.least_common()[:n:1] # n most common elements +c # remove zero and negative counts Several mathematical operations are provided for combining :class:`Counter` @@ -358,6 +376,8 @@ * The :meth:`most_common` method requires only that the values be orderable. + * The :meth:`least_common` method requires only that the values be orderable. + * For in-place operations such as ``c[key] += 1``, the value type need only support addition and subtraction. So fractions, floats, and decimals would work and negative values are supported. The same is also true for diff -r e6cd37d4dad8 Doc/whatsnew/2.7.rst --- a/Doc/whatsnew/2.7.rst Fri Jan 18 00:10:37 2013 -0500 +++ b/Doc/whatsnew/2.7.rst Fri Jan 18 12:19:49 2013 +0000 @@ -1022,7 +1022,10 @@ There are three additional :class:`~collections.Counter` methods. :meth:`~collections.Counter.most_common` returns the N most common - elements and their counts. :meth:`~collections.Counter.elements` + elements and their counts. + :meth:`~collections.Counter.least_common` returns the N least common + elements and their counts. + :meth:`~collections.Counter.elements` returns an iterator over the contained elements, repeating each element as many times as its count. :meth:`~collections.Counter.subtract` takes an iterable and @@ -1032,6 +1035,8 @@ >>> c.most_common(5) [(' ', 6), ('e', 5), ('s', 3), ('a', 2), ('i', 2)] + >>> c.least_common(5) + [('g', 1), ('f', 1), ('m', 1), ('o', 1), ('n', 1)] >>> c.elements() -> 'a', 'a', ' ', ' ', ' ', ' ', ' ', ' ', 'e', 'e', 'e', 'e', 'e', 'g', 'f', 'i', 'i', diff -r e6cd37d4dad8 Lib/collections/__init__.py --- a/Lib/collections/__init__.py Fri Jan 18 00:10:37 2013 -0500 +++ b/Lib/collections/__init__.py Fri Jan 18 12:19:49 2013 +0000 @@ -409,6 +409,8 @@ >>> c.most_common(3) # three most common elements [('a', 5), ('b', 4), ('c', 3)] + >>> c.least_common(3) # three least common elements + [('e', 1), ('d', 2), ('c', 3)] >>> sorted(c) # list all unique elements ['a', 'b', 'c', 'd', 'e'] >>> ''.join(sorted(c.elements())) # list elements with repetitions @@ -442,6 +444,9 @@ >>> c['b'] -= 2 # reduce the count of 'b' by two >>> c.most_common() # 'b' is still in, but its count is zero [('a', 3), ('c', 1), ('b', 0)] + >>> c.least_common() # 'b' is still in and has now replaced 'c' + [('b', 0), ('c', 1), ('a', 3)] # in the first position as its value + # is now zero ''' # References: @@ -483,6 +488,19 @@ return sorted(self.items(), key=_itemgetter(1), reverse=True) return _heapq.nlargest(n, self.items(), key=_itemgetter(1)) + def least_common(self, n=None): + '''List the n least common elements and their counts from the least + common to the most. If n is None, then list all element counts. + + >>> Counter('abcdeabcdabcaba').least_common(3) + [('e', 1), ('d', 2), ('c', 3)] + + ''' + # Emulate Bag.sortedByCount from Smalltalk + if n is None: + return sorted(self.items(), key=_itemgetter(1), reverse=True) + return _heapq.nsmallest(n, self.items(), key=_itemgetter(1)) + def elements(self): '''Iterator over elements repeating each as many times as its count. diff -r e6cd37d4dad8 Lib/test/test_collections.py --- a/Lib/test/test_collections.py Fri Jan 18 00:10:37 2013 -0500 +++ b/Lib/test/test_collections.py Fri Jan 18 12:19:49 2013 +0000 @@ -875,9 +875,14 @@ self.assertEqual(c, dict(a=3, b=2, c=1)) self.assertEqual(repr(c), "Counter({'a': 3, 'b': 2, 'c': 1})") self.assertEqual(c.most_common(), [('a', 3), ('b', 2), ('c', 1)]) + self.assertEqual(c.least_common(), [('c', 1), ('b', 2), ('a', 3)]) + self.assertEqual(c.most_common()[:-4:-1], c.least_common(3)) + self.assertEqual(c.least_common()[:3:1], c.most_common(3)) for i in range(5): self.assertEqual(c.most_common(i), [('a', 3), ('b', 2), ('c', 1)][:i]) + self.assertEqual(c.least_common(i), + [('c', 1), ('b', 2), ('a', 3)][:i]) self.assertEqual(''.join(sorted(c.elements())), 'aaabbc') c['a'] += 1 # increment an existing value c['b'] -= 2 # sub existing value to zero