【问题标题】:Comparing dictionaries with fewer loops比较具有较少循环的字典
【发布时间】:2018-11-30 22:58:16
【问题描述】:

我有一本字典,它的值是字典。以下是我的字典的结构

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

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

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

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

【问题讨论】:

    标签: python dictionary


    【解决方案1】:

    这是一个使用 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}
    

    【讨论】:

    • 两个嵌套的,O(N) ?请解释一下,我可以使用一些理解:)
    • 这是 O(n) 因为您只迭代子字典的每个分支一次。 不可能在不迭代所有数据至少一次的情况下计算结果。
    • 是的,没错。仍然是 O(N^2) 但我同意这是最小的
    • @Attersson,我的n 是子词典项目的数量。你的n 是什么?如果您的 n 是外部键的数量,我认为您误读了问题。
    • 对。由于这本字典是结构化的,我们可以将 N =“元素数”和 M“元素宽度”命名为 O(M*N)。无论如何,说得对。
    【解决方案2】:

    我想出了两个循环:

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

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

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

    【讨论】:

    • 这个解决方案比我的更快。即使它假设主字典中的所有字典都具有相同的键并且需要存在 'key1'
    • my_newD = {key: min(item[key] for item in myD.values()) for key in myD[myD.keys()[0]]} 这样key1 不会被硬编码。
    • @Andriy Makukha,是的,这样会更好。
    • 这个解决方案的复杂度与我的相同,即 O(m*n),但假设密钥是从 key1 知道的,在这种情况下似乎是正确的。因此,我认为这是最好的答案。
    • 好吧,我的解决方案也是 O(m*n)。为什么大家都忽略了第一个正确答案?去图:)
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      两个循环:

      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}
      

      【讨论】:

        【解决方案5】:

        这是我在 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}
        

        【讨论】:

          【解决方案6】:
          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 ])}
          

          【讨论】:

            【解决方案7】:

            此动态代码适用于任意数量的变量。它的复杂度是 O(M*N),其中 M=要找到最小值的变量数,N=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}
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-02-03
              • 2019-03-25
              • 2012-04-04
              • 2017-08-13
              • 2021-01-11
              • 2021-04-23
              • 1970-01-01
              相关资源
              最近更新 更多