Python 一行代码绘制直方图

50 投票
9 回答
56840 浏览
提问于 2025-04-15 22:55

有很多种方法可以用Python写一个计算直方图的程序。

这里的直方图是指一个函数,它会统计某些对象在一个可迭代的集合中出现的次数,并把这些次数以字典的形式输出。比如:

>>> L = 'abracadabra'
>>> histogram(L)
{'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}

写这个函数的一种方法是:

def histogram(L):
    d = {}
    for x in L:
        if x in d:
            d[x] += 1
        else:
            d[x] = 1
    return d

有没有更简洁的方法来写这个函数呢?

如果Python支持字典推导式,我们可以这样写:

>>> { x: L.count(x) for x in set(L) }

但是因为Python 2.6没有这个功能,所以我们得写成:

>>> dict([(x, L.count(x)) for x in set(L)])

虽然这种写法可能比较容易理解,但效率不高:列表L会被多次遍历。此外,这种方法不适用于单次生成器;这个函数应该同样适用于像这样的迭代器生成器:

def gen(L):
    for x in L:
        yield x

我们可能会尝试使用reduce函数(愿它安息):

>>> reduce(lambda d,x: dict(d, x=d.get(x,0)+1), L, {}) # wrong!

哎呀,这个不行:键名是'x',而不是x。 :(

最后我写成了:

>>> reduce(lambda d,x: dict(d.items() + [(x, d.get(x, 0)+1)]), L, {})

(在Python 3中,我们需要写list(d.items())而不是d.items(),但这是假设的,因为那里没有reduce。)

请给我一个更好、更易读的一行代码! ;)

9 个回答

7
import pandas as pd

pd.Series(list(L)).value_counts()

当然可以!请把你想要翻译的内容发给我,我会帮你把它变得更简单易懂。

8

为了写一行代码而导入模块有点不太公平,所以这里有一个一行代码的写法,它的效率是O(n),并且至少在Python2.4版本中可以使用。

>>> f=lambda s,d={}:([d.__setitem__(i,d.get(i,0)+1) for i in s],d)[-1]
>>> f("ABRACADABRA")
{'A': 5, 'R': 2, 'B': 2, 'C': 1, 'D': 1}

如果你觉得__方法有点不太正经,你也可以选择这样做。

>>> f=lambda s,d=lambda:0:vars(([setattr(d,i,getattr(d,i,0)+1) for i in s],d)[-1])
>>> f("ABRACADABRA")
{'A': 5, 'R': 2, 'B': 2, 'C': 1, 'D': 1}

:)

78

在Python 3.x中确实有reduce这个功能,不过你需要先用from functools import reduce来引入它。此外,Python 3.x还支持“字典推导式”,它的语法和你例子中的完全一样。

Python 2.7和3.x也有一个叫做Counter的类,正好能满足你的需求:

from collections import Counter
cnt = Counter("abracadabra")

在Python 2.6或更早的版本中,我个人会使用defaultdict,这样可以用两行代码完成:

d = defaultdict(int)
for x in xs: d[x] += 1

这样写既简洁又高效,更符合Python的风格,而且比用reduce的方式更容易让大多数人理解。

撰写回答