如何按输入顺序获取Python Counter输出的排序结果?

8

我一直在努力获取计数(频率)并制作图表表示。

我正在使用Python的Collections中的Counter类。我希望Counter的输出按照第一个对象的顺序排序。

例如:

offset=['a','b','c','a','b','b','b','c','c','c','c','c']
counts = Counter(offset)
print counts

输出结果为:

Counter({'c': 6, 'b': 4, 'a': 2})

我希望输出的结果如下,按照首先到达的对象的顺序:

Counter({'a': 2, 'b': 4, 'c': 6})

这可行吗?

谢谢


请注意,从Python 3.7开始,Counter保证保留插入顺序。 - rerx
5个回答

10

通过(2行长)多重继承实现的OrderedCounter

在Raymond Hettinger精彩演讲超越考虑中,我们可以使用多重继承:

from collections import OrderedDict, Counter

class OrderedCounter(Counter, OrderedDict):
    pass

并使用它:

>>> counter = OrderedCounter("abracadabra")
>>> for key, value in counter.items():
...    print key, value
a 5
b 2
r 2
c 1
d 1

这就是你完成任务所需要的全部。

修复有点混乱的表示

让我们进行更多的测试,看看我们得到了什么“结果”:

>>> counter = OrderedCounter("cbaaa")
>>> counter
OrderedCounter({'a': 3, 'c': 1, 'b': 1})

哦- 看起来不对,期望的顺序应该是 "c","b","a"。让我们通过打印键和值来测试它:

>>> for key, value in counter.items():
...    print key, value
c 1
b 1
a 3

这看起来是正确的(并且完全符合在您的代码中使用的要求)。

事实证明,我们创建的类只是产生了一种有点令人困惑的表示形式。

这可以修复:

class OrderedCounter(Counter, OrderedDict):
    def __repr__(self):
        return "%s(%r)" % (self.__class__.__name__, OrderedDict(self))

当使用时:

>>> counter = OrderedCounter("cbaaa")
>>> counter
OrderedCounter({'c': 1, 'b': 1, 'a': 1})

来自Raymond Hettinger演讲的完整版本(添加了pickling)

演讲中提供的完整版本添加了一个方法__reduce__,允许正确地进行pickling对象。

from collections import OrderedDict, Counter

class OrderedCounter(Counter, OrderedDict):
    """Counter that remembers the order elements are first seen"""
    def __repr__(self):
        return "%s(%r)" % (self.__class__.__name__, OrderedDict(self))

    def __reduce__(self):
        return self.__class__, (OrderedDict(self),)

无论如何,在大多数情况下,您只需使用最简单版本的OrderedCounter类即可完成。


2

编辑

抱歉,我误解了。试试这个。 我使用队列概念来实现。

q = []
ret = []
for i in offset:
  if q.count(i)==0: q.insert(i, 0)
while len(q):
  item = q.pop()
  ret.append((item, d.get(item)))
print(ret)

1
这如何保证输出的顺序与首次计数对象的顺序相同? - Reblochon Masque
抱歉,我误解了关于订购的事情。请再次检查一下。 - Hexoul

1
Python中的字典只是哈希表,因此它们没有任何排序方式。您永远无法打印出排序列表。但是,您可以将它们转换为元组列表并对其进行排序。
from collections import Counter
import operator
offset=['a','b','c','a','b','b','b','c','c','c','c','c']
counts = Counter(offset)
print(sorted(counts.items(), key=operator.itemgetter(0)))
#[('a', 2), ('b', 4), ('c', 6)]

对计数器输出进行排序并不能保证元素的顺序与第一个对象出现的顺序相同。 - Reblochon Masque
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Keatinge

1
你需要以“老派”的方式使用有序字典来完成,该字典保证保留元素最初插入的顺序:
from collections import OrderedDict

offset = ['a','b','c','a','b','b','b','c','c','c','c','c']
counts = OrderedDict()
for elt in offset:
    try:
        counts[elt] += 1
    except KeyError:
        counts[elt] = 1

print(counts)

Result:

OrderedDict([('a', 2), ('b', 4), ('c', 6)])

这并不是“老派”的方式。如果没有有序字典,你还能用什么更好的方法呢? - Ozgur Vatansever
加一,因为这是唯一符合首次计数元素顺序的答案。 - Ozgur Vatansever
1
呵呵,谢谢 - 我所说的“老式方法”是指不使用像计数器这样的高阶函数来完成。 - Reblochon Masque

1
您可以使用这样的方法论:

(保留HTML,不做解释)
from collections import Counter
offset=['a','b','c','a','b','b','b','c','c','c','c','c']
counts = Counter(offset)

for letter in offset:
    if letter in counts:
        print (letter + ": " + str(counts[letter]))
        counts.pop(letter)

这的作用如下所示:

...

>>> ================================ RESTART ================================
>>> 
a: 2
b: 4
c: 6

你可以将这些键值对存储在一个列表中,而不是直接打印输出。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接