简体   繁体   English

使用dicts进行动态编程-Python

[英]Dynamic programming with dicts - python

I have a list of dict s; 我有一个listdict秒; I get the indices and get the combination of indices using itertools.combinations . 我得到索引,并使用itertools.combinations获得索引的组合。

list_of_dict = [{1: 'a', 2:'b', 3: 'c', 4: 'd'}, 
                {1: 'b', 2:'b', 3: 'a', 4: 'd'},
                {1: 'a', 2:'c', 3: 'd', 4: 'd'},
                {1: 'c', 2:'a', 3: 'd', 4: 'b'}]
indices = [0, 1, 2, 3]
combinations = [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3), 
                (0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3), 
                (0, 1, 2, 3)]

Now I define a merge function that takes 2 dict s implements my logic of merge and returns a single dict . 现在,我定义一个merge函数,该函数采用2个dict实现我的合并逻辑并返回一个dict I perform merge on all elements of combinations . 我对combinations所有元素执行合并。

The calls to merge happen as follows: merge的调用发生如下:

candidates = {}
for p in combinations:
    n = 1
    temp_p = None
    c = None
    while (n < (len(p) - 2)):
        c = tuple(list(p)[0:-n]) 
        if c in candidates:
            temp_p = candidates[c]
            break
        else: 
            n += 1
    if temp_p:
        p1 = temp_p
        p2 = p[len(c)]
        candidate[p] = merge(list_of_dict[p1], list_of_dict[p2])
    else:
        candidates[p] = merge(list_of_dict[p[0]], list_of_dict[p[1]])

Logic: As we can see, I can reuse the output of merge(0, 1) when I calculate merge(0, 1, 2) ; 逻辑:正如我们所看到的,当我计算merge(0, 1, 2) merge(0, 1)时,我可以重用merge(0, 1)的输出; ie merge(0, 1, 2) = merge( merge(0,1), 2) . merge(0, 1, 2) = merge( merge(0,1), 2) Similarly for merge(0, 1, 2, 3) I can reuse merge(0, 1, 2) . 类似地,对于merge(0, 1, 2, 3)我可以重用merge(0, 1, 2)

Questions: 问题:

  1. I am not completely sure if the above chunk of code does exactly what is described in "Logic". 我不完全确定上面的代码是否完全符合“逻辑”中的描述。
  2. Is there an elegant and efficient way to do the above? 是否有一种优雅而有效的方法来完成上述任务?

Edit: Merge function does the following. 编辑:合并功能执行以下操作。 If d1[k] == d2[k] it adds d1[k] to the new dict to be returned else adds None. 如果d1 [k] == d2 [k],则将d1 [k]添加到要返回的新字典中,否则添加“无”。 So from the example given: 因此,从给出的示例中:

merge(0,1) should return : {1: None, 2: 'b', 3: None, 4: 'd'} merge(0,1)应该返回: {1: None, 2: 'b', 3: None, 4: 'd'}

and

merge(0, 1, 2) becomes merge merge({1: None, 2: 'b', 3: None, 4: 'd'}, {1: 'a', 2:'c', 3: 'd', 4: 'd'}) and should return {1: None, 2: None, 3: None, 4: 'd'} merge(0, 1, 2)变为合并merge({1: None, 2: 'b', 3: None, 4: 'd'}, {1: 'a', 2:'c', 3: 'd', 4: 'd'})并应返回{1: None, 2: None, 3: None, 4: 'd'}

This process continues till all combination of dicts are merged. 这个过程一直持续到所有字典组合都合并为止。

The question is especially important when we don't know the initial number of such dictionaries presented to the algorithm. 当我们不知道向算法显示的此类字典的初始数量时,该问题尤为重要。

This code will fit all your requirements: 此代码将满足您的所有要求:

from collections import defaultdict

def merge(d1, d2, *dicts):
    new_dict = defaultdict()
    new_dict.update(d1.copy())

    for key, value in d2.items():
        if value != new_dict[key]:
            new_dict[key] = None

    if dicts:
        new_dict = merge(new_dict, *dicts)

    return new_dict


for comb in combinations:
    print(merge(*map(list_of_dict.__getitem__, comb)))

First of all, I left passing dictionaries to merge , because dict is mutable object, so in this case only its reference in memory is passed to function. 首先,我将传递的字典留给merge ,因为dict是可变对象,因此在这种情况下,只有它在内存中的引用才传递给函数。 You can easily check it with id function. 您可以使用id函数轻松检查它。 So, since this solution is more readable than gathering dictionaries from list by indexes - it's more pythonic. 因此,由于此解决方案比按索引从列表中收集字典更具可读性-因此它更具Python风格。

Secondary, I used copy of first dictionary passed to merge , so original one is left unmodified for other combinations. 其次,我使用了传递给merge的第一个词典的copy ,因此对于其他组合,原始的未修改。

Also as you can see, instead of dict I used defaultdict from collections package, so if your d1 and d2 have different keys - you can skip checking if key exist in new_dict . 同样如您所见,我不是使用dict而是使用collections包中的defaultdict ,因此,如果您的d1d2具有不同的键-您可以跳过检查键是否存在于new_dict

And last one: map(list_of_dict.__getitem__, comb) - this will return generator object ( for python 3.x ) with items from list_of_dict , which indexes in comb . 最后一个: map(list_of_dict.__getitem__, comb) -这将返回带有list_of_dict项目的生成器对象(对于python 3.x),该对象在comb进行索引。 Wildcard before ( * ) map means unpack as arguments , so, without actually creating new list or tuple ( and unpack it afterwards ) - it just yields items ( in our case references to dictionaries in list ) as merge arguments. (*) map之前的通配符意味着将其作为参数解包 ,因此,无需实际创建新的列表或元组(之后再对其进行解包)-它仅将项(在我们的示例中为引用list中的字典的引用)作为merge参数。

Thats it. 而已。

Provided code will print this: 提供的代码将打印以下内容:

defaultdict(None, {1: None, 2: 'b', 3: None, 4: 'd'})
defaultdict(None, {1: 'a', 2: None, 3: None, 4: 'd'})
defaultdict(None, {1: None, 2: None, 3: None, 4: None})
defaultdict(None, {1: None, 2: None, 3: None, 4: 'd'})
defaultdict(None, {1: None, 2: None, 3: None, 4: None})
defaultdict(None, {1: None, 2: None, 3: 'd', 4: None})
defaultdict(None, {1: None, 2: None, 3: None, 4: 'd'})
defaultdict(None, {1: None, 2: None, 3: None, 4: None})
defaultdict(None, {1: None, 2: None, 3: None, 4: None})
defaultdict(None, {1: None, 2: None, 3: None, 4: None})
defaultdict(None, {1: None, 2: None, 3: None, 4: None})

Also, i've tested this code on list of 20 dictionaries ( each dictionary len = 10000 ) and i've got next timings: 另外,我已经在20个字典的列表中测试了此代码(每个字典len = 10000),并且我有下一个计时:

  1. generating list of dicts: 15.96887145599976 生成字典列表: 15.96887145599976
  2. apply merge to each indexes combination ( 1012 in total ): 15.630234674000349 将合并应用于每个索引组合(总计1012个): 15.630234674000349

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

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