给出一个字典(或计数器)的计数数据,如下所示:
d={'dan':7, 'mike':2, 'john':3}
和我想要包含累积百分比的新词典“d_cump”
d_cump={'mike':16, 'john':41, 'dan':100}
编辑:应该澄清顺序对我的输入集无关紧要,这就是我使用字典或计数器的原因。在计算累积百分比时,顺序确实很重要,所以我需要对该操作的数据进行排序,一旦我有每个名称的累积百分比,然后我再次将它放回字典中,如果我正在查看单个顺序则无关紧要值。
从d到d_cump的最优雅/ pythonic方式是什么?
这是我看起来有点笨拙的事情:
from numpy import cumsum
d={'dan':7, 'mike':2, 'john':3}
sorted_keys = sorted(d,key=lambda x: d[x])
perc = [x*100/sum(d.values()) for x in cumsum([ d[x] for x in sorted_keys ])]
d_cump=dict(zip(sorted_keys,perc))
>>> d_cump
{'mike': 16, 'john': 41, 'dan': 100}
答案 0 :(得分:2)
考虑到原始字典的顺序是任意的,很难说累积百分比是如何有价值的。
那就是说,我就是这样做的:
from numpy import cumsum
from operator import itemgetter
d={'dan':7, 'mike':2, 'john':3}
#unzip keys from values in a sorted order
keys, values = zip(*sorted(d.items(), key=itemgetter(1)))
total = sum(values)
# calculate cumsum and zip with keys into new dict
d_cump = dict(zip(keys, (100*subtotal/total for subtotal in cumsum(values))))
请注意,结果没有特殊顺序,因为字典不是有序的:
{'dan': 100, 'john': 41, 'mike': 16}
答案 1 :(得分:0)
因为你无论如何都在使用numpy,你可以绕过/简化列表推导:
>>> from numpy import cumsum
>>> d={'dan':7, 'mike':2, 'john':3}
>>> sorted_keys = sorted(d,key=d.get)
>>> z = cumsum(sorted(d.values())) # or z = cumsum([d[k] for k in sorted_keys])
>>> d2 = dict(zip(sorted_keys, 100.0*z/z[-1]))
>>> d2
{'mike': 16, 'john': 41, 'dan': 100}
但正如其他地方所述,以这种方式使用字典感觉很奇怪。
答案 2 :(得分:0)
计算累积值?对我来说听起来像个折叠!
d = {'dan':7, 'mike':2, 'john':3}
denominator = float(sum(d.viewvalues()))
data = ((k,(v/denominator)) for (k, v) in sorted(d.viewitems(), key = lambda (k,v):v))
import functional
f = lambda (k,v), l : [(k, v+l[0][1])]+l
functional.foldr(f, [(None,0)], [('a', 1), ('b', 2), ('c', 3)])
#=>[('a', 6), ('b', 5), ('c', 3), (None, 0)]
d_cump = { k:v for (k,v) in functional.foldr(f, [(None,0)], data) if k is not None }
功能不是内置包。您也可以重新进行f
jig右键折叠,因此如果您愿意,可以降低标准。
正如您所看到的,这不会短得多,但它利用序列解构来避免分裂/压缩,并且它使用生成器作为中间data
,这避免了构建列表。
如果要进一步减少对象创建,可以使用此替代函数修改传入的初始列表(但必须使用愚蠢的技巧返回适当的值,因为list.append
返回None
})。
uni = lambda x:x
ff = lambda (k,v), l : uni(l) if l.insert(0, (k, v+l[0][1])) is None else uni(l)
顺便说一句,使用ireduce
(从此页面http://www.ibm.com/developerworks/linux/library/l-cpyiter/index.html)左侧折叠很容易,因为它消除了列表构造:
ff = lambda (l, ll), (k,v), : (k, v+ll)
g = ireduce(ff, data, (None, 0))
tuple(g)
#=>(('mike', 0.16666666666666666), ('john', 0.41666666666666663), ('dan', 1.0))
def ireduce(func, iterable, init=None):
if init is None:
iterable = iter(iterable)
curr = iterable.next()
else:
curr = init
for x in iterable:
curr = func(curr, x)
yield curr
这很有吸引力,因为初始值不包括在内,因为生成器很懒,所以特别适合链接。
请注意,上面的ireduce
相当于:
def ireduce(func, iterable, init=None):
from functional import scanl
if init is None: init = next(iterable)
sg = scanl(func, init, iterable)
next(sg)
return sg