python histogram one-liner
编写计算柱状图的python程序有很多方法。
我所说的柱状图,是指一个计算
1 2 3 | >>> L = 'abracadabra' >>> histogram(L) {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2} |
编写此函数的一种方法是:
1 2 3 4 5 6 7 8 | def histogram(L): d = {} for x in L: if x in d: d[x] += 1 else: d[x] = 1 return d |
号
写这个函数有更简洁的方法吗?
如果我们有关于python的字典理解,我们可以写:
1 | >>> { x: L.count(x) for x in set(L) } |
但是由于python 2.6没有它们,我们必须写:
1 | >>> dict([(x, L.count(x)) for x in set(L)]) |
。
尽管这种方法可能是可读的,但它是不有效的:l被遍历多次。此外,对于单寿命生成器,这不起作用;对于迭代器生成器,该函数应该同样有效,例如:
1 2 3 | def gen(L): for x in L: yield x |
我们可以尝试使用
1 | >>> reduce(lambda d,x: dict(d, x=d.get(x,0)+1), L, {}) # wrong! |
。
哎呀,这不管用:密钥名是
我的结尾是:
1 | >>> reduce(lambda d,x: dict(d.items() + [(x, d.get(x, 0)+1)]), L, {}) |
。
(在python 3中,我们必须编写
请用更好、更易读的一行字打败我!;)
python 3.x有
python 2.7和3.x也有一个计数器类,它可以完全满足您的需要:
1 2 | from collections import Counter cnt = Counter("abracadabra") |
在python 2.6或更早版本中,我个人会使用一个defaultdict并用两行来完成它:
1 2 | d = defaultdict(int) for x in xs: d[x] += 1 |
号
这是一种干净、高效、脓毒症,对大多数人来说,这比任何涉及到
为OneLines导入模块有点欺骗,所以这里有一个OneLiner,它是O(N),至少可以追溯到python2.4。
1 2 3 | >>> 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} |
。
如果你认为
1 2 3 | >>> 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} |
:)
1 2 3 | import pandas as pd pd.Series(list(L)).value_counts() |
。
1 | $d{$_} += 1 for split //, 'abracadabra'; |
。
对于Python2.7,您可以使用下面的小列表理解:
1 2 | v = list('abracadabra') print {x: v.count(x) for x in set(v)} |
一个可以追溯到2.3(比Timmerman的稍短,我认为可读性更强):
1 2 3 4 5 | 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} |
。
我需要一个柱状图实现来在Python2.2到2.7版本中工作,并得出了以下结论:
1 2 3 4 5 | >>> 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} |
我被伊莱·考特赖特的一篇不守规矩的口述所启发。这些是在python 2.5中引入的,因此不能使用。但是可以使用dict.setdefault(key,default)来模拟它们。
这基本上是相同的事情,gnibble正在做,但我必须先写这之前,我可以完全理解他的lambda函数。
你使用
1 2 | >>> reduce(lambda d, x: dict(d, **{x: d.get(x, 0) + 1}), L, {}) {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2} |
。
当然,这并不能打败现有的解决方案(也不能在速度上,也不能在脓毒症上),但作为交换,你已经得到了一个很好的、纯功能的代码片段。顺便说一句,如果python有一个方法
有一段时间,任何使用
1 2 3 4 5 | >>> 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。