[ACCEPTED]-How to get count dict of items but maintain the order in which they appear?-ordereddictionary
You can use the recipe that uses collections.Counter
and collections.OrderedDict
:
from collections import Counter, OrderedDict
class OrderedCounter(Counter, OrderedDict):
'Counter that remembers the order elements are first encountered'
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))
def __reduce__(self):
return self.__class__, (OrderedDict(self),)
words = ["oranges", "apples", "apples", "bananas", "kiwis", "kiwis", "apples"]
c = OrderedCounter(words)
print(c)
# OrderedCounter(OrderedDict([('oranges', 1), ('apples', 3), ('bananas', 1), ('kiwis', 2)]))
0
On Python 3.6+, dict
will now maintain insertion 14 order.
So you can do:
words = ["oranges", "apples", "apples", "bananas", "kiwis", "kiwis", "apples"]
counter={}
for w in words: counter[w]=counter.get(w, 0)+1
>>> counter
{'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}
Unfortunately, the Counter 13 in Python 3.6 and 3.7 does not display the 12 insertion order that it maintains; instead, __repr__
sorts the return by 11 the most to least common.
But you can use 10 the same OrderedDict recipe but just use the Python 9 3.6+ dict instead:
from collections import Counter
class OrderedCounter(Counter, dict):
'Counter that remembers the order elements are first encountered'
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, dict(self))
def __reduce__(self):
return self.__class__, (dict(self),)
>>> OrderedCounter(words)
OrderedCounter({'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2})
Or, since Counter is a 8 subclass of dict
that maintains order in Python 7 3.6+, you can just avoid using Counter's 6 __repr__
by either calling .items()
on the counter or turning 5 the counter back into a dict
:
>>> c=Counter(words)
This presentation 4 of that Counter is sorted by most common 3 element to least and uses Counters __repr__
method:
>>> c
Counter({'apples': 3, 'kiwis': 2, 'oranges': 1, 'bananas': 1})
This 2 presentation is as encountered, or insertion 1 order:
>>> c.items()
dict_items([('oranges', 1), ('apples', 3), ('bananas', 1), ('kiwis', 2)])
Or,
>>> dict(c)
{'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}
In Python 3.6, dictionaries are insertion ordered, but 19 this is an implementation detail.
In Python 3.7+, insertion 18 order is guaranteed and can be relied upon. See 17 Are dictionaries ordered in Python 3.6+? for more details.
So, depending on your 16 Python version, you may wish to just use 15 Counter
as is, without creating an OrderedCounter
class as described 14 in the documentation. This works because Counter
is a subclass 13 of dict
, i.e. issubclass(Counter, dict)
returns True
, and therefore inherits 12 the insertion ordering behaviour of dict
.
String representation
It 11 is worth noting the the string representation 10 for Counter
, as defined in the repr
method, has not been updated to reflect 9 the change in 3.6 / 3.7, i.e. print(Counter(some_iterable))
still returns 8 items from largest counts descending. You 7 can trivially return the insertion order 6 via list(Counter(some_iterable))
.
Here are some examples demonstrating 5 the behaviour:
x = 'xyyxy'
print(Counter(x)) # Counter({'y': 3, 'x': 2}), i.e. most common first
print(list(Counter(x))) # ['x', 'y'], i.e. insertion ordered
print(OrderedCounter(x)) # OC(OD([('x', 2), ('y', 3)])), i.e. insertion ordered
Exceptions
You should not use a regular 4 Counter
if additional or overwritten methods available 3 to OrderedCounter
are important to you. Of particular 2 note:
OrderedDict
and consequentlyOrderedCounter
offerpopitem
andmove_to_end
methods.- Equality tests between
OrderedCounter
objects are order-sensitive and are implemented aslist(oc1.items()) == list(oc2.items())
.
For example, equality tests will yield 1 different results:
Counter('xy') == Counter('yx') # True
OrderedCounter('xy') == OrderedCounter('yx') # False
Explained in comments
text_list = ['oranges', 'apples', 'apples', 'bananas', 'kiwis', 'kiwis', 'apples']
# create empty dictionary
freq_dict = {}
# loop through text and count words
for word in text_list:
# set the default value to 0
freq_dict.setdefault(word, 0)
# increment the value by 1
freq_dict[word] += 1
print(freq_dict )
{'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}
[Program finished]
0
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.