Python 一行代码绘制直方图
有很多种方法可以用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
的方式更容易让大多数人理解。