从数据字典创建累积百分比

时间:2012-02-01 17:46:09

标签: python

给出一个字典(或计数器)的计数数据,如下所示:

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}

3 个答案:

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