繁体   English   中英

使用较少的循环比较字典

[英]Comparing dictionaries with fewer loops

我有一本字典,其中包含字典作为其值。 以下是我的字典的结构

myD = {'key1': {'x' : 123, 'y' : 432},
       'key2': {'x' : 456, 'y' : 565},
       'key3': {'x' : 789, 'y' : 420},
       ...}

我需要比较这个字典的值(你可以看到我在每个值中都有类似的字典结构)并生成以下输出。 策略遍历值字段中的每个字典,并选择给定键的最小值并将其插入到新字典中。 例如,如果我们从值字典中考虑x键,则它最少为123 所以我的新词典应该有x:123

my_newD =  {'x' : 123, 'y' : 420, ...}

我可以使用3 for循环来实现这一点,但有没有任何优雅的方法来做更少的for循环?

我想出了两个循环:

my_newD = {key: min(item[key] for item in myD.values()) 
           for key in  next(iter(myD.values()))}

编辑:根据@Andriy Makukha的建议,删除了对外循环中'key1'显式依赖

编辑2:用next(iter(myD.values()))替换myD[list(myD.keys())[0]]
不确定哪一个不那么神秘,但使用next并且iter看起来效率更高。
感谢Leo K指出它!

这是使用collections.defaultdict的O(n)解决方案:

from collections import defaultdict

myD = {'key1': {'x' : 123, 'y' : 432},
       'key2': {'x' : 456, 'y' : 565},
       'key3': {'x' : 789, 'y' : 420}}

# initialise defaultdict of lists
d = defaultdict(list)

# iterate input dictionary and add values to lists
for v1 in myD.values():
    for k2, v2 in v1.items():
        d[k2].append(v2)

# calculate minimum
res = {k: min(v) for k, v in d.items()}

print(res)

{'x': 123, 'y': 420}

两个循环:

myD = {'key1': {'x' : 123, 'y' : 432},
       'key2': {'x' : 456, 'y' : 565},
       'key3': {'x' : 789, 'y' : 420}}

resD = {}
for key in myD:
    subD = myD[key]
    for k in subD:
        resD[k] = min(resD[k], subD[k]) if k in resD else subD[k]

print (resD)

输出:

{'x': 123, 'y': 420}

pandas另一种可能性(在优雅方面更好):

import pandas as pd
my_newD = pd.DataFrame(myD).min(axis=1).to_dict()

这是我在O(n)中的解决方案

>>> from itertools import accumulate
>>>  min_t = lambda *t: [min(r) for r in zip(*t)]
>>> *_, min_vals = accumulate([v.values() for k,v in myD.items()], min_t)
>>> keys = next(iter(myD.values())).keys()
>>> dict(zip(keys, vals))
{'x': 123, 'y': 420}
>>> 

说明

>>> from itertools import accumulate
>>> 
>>> myD = {'key1': {'x': 123, 'y': 432}, 'key2': {'x': 456, 'y': 565}, 'key3': {'x': 789, 'y': 420}}
>>> 
>>> # Define a func to find min of tuples
>>> def min_t(*t):
...     return [min(r) for r in zip(*t)]
... 
>>> # Build the tuple 
>>> t = (v.values() for k,v in myD.items())
>>> *_, min_vals = accumulate(t, min_t)
>>> min_vals
[123, 420]
>>> 
>>> keys = next(iter(myD.values())).keys()
>>> keys
dict_keys(['x', 'y'])
>>> 
>>> 
>>> dict(zip(keys, vals))
{'x': 123, 'y': 420}
>>> 

或者在单行中

>>> from itertools import accumulate
>>> from collections import deque
>>> dict(zip(next(iter(myD.values())).keys(), deque(accumulate((v.values() for k,v in myD.items()), lambda *t: [min(r) for r in zip(*t)]), maxlen=1).pop()))
{'x': 123, 'y': 420}
flat_d = [v[1] for v in myD.items()]

{"x":min([v["x"] for v in flat_d ]),"y":min([v["y"] for v in flat_d ])}

此动态代码适用于任意数量的变量 其复杂度为O(M * N),其中M =找到最小值的变量数,N = myD的键myD

# here, M = 3, N = 3
myD = {'key1': {'x' : 123, 'y' : 432, 'z': 100},
       'key2': {'x' : 456, 'y' : 565, 'z': 99},
       'key3': {'x' : 789, 'y' : 420, 'z': 250}}
firstKey = "key1"

# assume firstKey has all the minimum entries
# use it as a base to compare all other values to
minD = {}
for (k,v) in myD[firstKey].items():
    minD[k] = v

items = myD.items()

#  find minimum of variable x, then y, then z
for variable in minD:
    print "Finding minimum of " + variable

    for key, dictionary in items:
        keyVal = dictionary[variable]
        if minD[variable] > keyVal:
            minD[variable] = keyVal

print minD # {'y': 420, 'x': 123, 'z': 99}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM