如何计算物品的数量,但保持它们出现的顺序?

问题描述 投票:3回答:3

例如,我需要计算一个单词出现在列表中的次数,不是按频率排序,而是按照单词出现的顺序排序,即插入顺序。

from collections import Counter

words = ['oranges', 'apples', 'apples', 'bananas', 'kiwis', 'kiwis', 'apples']

c = Counter(words)

print(c)

所以而不是:{'apples': 3, 'kiwis': 2, 'bananas': 1, 'oranges': 1}

我宁愿得到:{'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}

我真的不需要这种Counter方法,任何可以产生正确结果的方法对我来说都没问题。

python python-3.x dictionary counter ordereddictionary
3个回答
10
投票

你可以使用使用recipecollections.Countercollections.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)]))

2
投票

在Python 3.6+上,dict现在将维护插入顺序。

所以你可以这样做:

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}

不幸的是,Python 3.6和3.7中的Counter不显示它维护的插入顺序;相反,__repr__ sorts the return最受欢迎。

但你可以使用相同的OrderedDict recipe,但只需使用Python 3.6+ dict:

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})

或者,由于Counter是dict的子类,它在Python 3.6+中保持顺序,你可以通过在计数器上调用__repr__或者将计数器变回.items()来避免使用Counter的dict

>>> c=Counter(words)

该计数器的这个表示按最常见的元素排序,并使用计数器__repr__方法:

>>> c
Counter({'apples': 3, 'kiwis': 2, 'oranges': 1, 'bananas': 1})

此演示文稿是遇到的或插入顺序:

>>> c.items()
dict_items([('oranges', 1), ('apples', 3), ('bananas', 1), ('kiwis', 2)])

要么,

>>> dict(c)
{'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}

1
投票

在Python 3.6中,字典是按顺序排序的,但这是一个实现细节。

在Python 3.7+中,插入顺序是有保证的,可以依赖。有关详细信息,请参阅Are dictionaries ordered in Python 3.6+?

因此,根据您的Python版本,您可能希望按原样使用Counter,而不创建OrderedCounter中描述的documentation类。这是因为Counterdict的子类,即issubclass(Counter, dict)返回True,因此继承了dict的插入排序行为。

字符串表示

值得注意的是Counter的字符串表示,如repr方法中所定义的,has not been updated反映3.6 / 3.7的变化,即print(Counter(some_iterable))仍然返回最大计数下降的项目。您可以通过list(Counter(some_iterable))轻松返回插入订单。

以下是一些演示行为的示例:

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

例外

如果Counter可用的附加或覆盖方法对您很重要,则不应使用常规OrderedCounter。特别值得注意的是

  1. OrderedDictOrderedCounter提供popitemmove_to_end方法。
  2. OrderedCounter对象之间的等式测试是对顺序敏感的,并且实现为list(oc1.items()) == list(oc2.items())

例如,相等测试将产生不同的结果:

Counter('xy') == Counter('yx')                # True
OrderedCounter('xy') == OrderedCounter('yx')  # False
© www.soinside.com 2019 - 2024. All rights reserved.