Python直方图一行

编写计算直方图的Python程序有很多种方法。

通过直方图,我的意思是一个函数,可以计算iterable中对象的出现次数,并输出字典中的计数。 例如:

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

有没有更简洁的方式来编写这个function?

如果我们有Python的字典parsing,我们可以写:

 >>> { 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函数(RIP):

 >>> 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() ,但是它是d.items()的,因为在那里没有reduce

请用更好,更具可读性的单行纸打败我! ;)

Python 3.x确实reduce ,你只需from functools import reduce做一个。 它也有“字典理解”,在你的例子中有正确的语法。

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 

这是干净的,高效的,Pythonic,对于大多数人来说,比任何涉及reduce事情都容易理解。

导入模块的内容是非常不好的,所以这里有一个O(n)的oneliner,至less可以在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} 

🙂

 $d{$_} += 1 for split //, 'abracadabra'; 

对于Python 2.7,你可以使用这个小列表理解:

 v = list('abracadabra') print {x: v.count(x) for x in set(v)} 
 import pandas as pd pd.Series(list(L)).value_counts() 

一个回到2.3(比Timmerman稍短,我认为更可读):

 L = 'abracadabra' hist = {} for x in L: hist[x] = hist.pop(x,0) + 1 print hist {'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1} 

有一段时间,任何使用itertools东西都是由Pythonic定义的。 尽pipe如此,这在不透明的一面是有点儿的:

 >>> from itertools import groupby >>> grouplen = lambda grp : sum(1 for i in grp) >>> hist = dict((a[0], grouplen(a[1])) for a in groupby(sorted("ABRACADABRA"))) >>> print hist {'A': 5, 'R': 2, 'C': 1, 'B': 2, 'D': 1} 

我目前正在运行Python 2.5.4。

你使用reduce单线程几乎没问题,你只需稍微调整一下即可:

 >>> reduce(lambda d, x: dict(d, **{x: d.get(x, 0) + 1}), L, {}) {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2} 

当然,这不会殴打就地解决scheme(速度和pythonicity),但作为交换,你已经得到了一个不错的纯粹的function片段。 顺便说一句,如果Python有一个方法dict.merge() ,这将会更漂亮dict.merge()

我需要一个直方图实现工作在Python 2.2到2.7,并提出这样的:

 >>> L = 'abracadabra' >>> hist = {} >>> for x in L: hist[x] = hist.setdefault(x,0)+1 >>> print hist {'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1} 

我受到Eli Courtwright的defaultdict的启发。 这些是在Python 2.5中引入的,所以不能使用。 但是它们可以用dict.setdefault(key,default)来模拟。

这与gibibbler基本上是一样的,但是在我完全理解他的lambda函数之前,我必须先写这个。