简体   繁体   English

使用组合迭代多个字典

[英]Iterate over multiple dictionaries with combinations

I have an increasing number of n_term dictionaries, where the values of E need to be combined in a specific way.我有越来越多的 n_term 字典,其中E的值需要以特定方式组合。 The dictionaries have the form:字典具有以下形式:

one_terms = {'i': {'E':1},
             'j': {'E':2},
             ...
             'n': {'E':some_int}}
   
two_terms = {'i:j': {'E':12},
             'i:k': {'E':13},
             ...
             'm:n': {'E':some_int}}

three_terms = {'i:j:k': {'E':111},
               'i:j:l': {'E':112},
               ...
               'm:n:o': {'E':some_int}}

The numbers inside are placeholders.里面的数字是占位符。 In reality these numbers come from computationally expensive calculations.实际上,这些数字来自计算量大的计算。 The number of dictionaries holding n-terms (ijkl...n) can also become large.包含 n 项 (ijkl...n) 的字典数量也可能会变得很大。 For instance, this can go up to a fifty_term dictionary.例如,这可以 go 最多五十个字典。

The intention is to combine all the corresponding terms of a n_term dictionary and append that value to the appropriate dictionary.目的是将 n_term 字典的所有相应术语和 append 该值组合到适当的字典中。 An example explains this better:一个例子更好地解释了这一点:

the task is relatively simple for a low number of terms (2 or 3), and can be done without an invoking combinations.对于少量的术语(2 或 3),该任务相对简单,并且可以在没有调用组合的情况下完成。 For example, for the two_term dictionary:例如,对于 two_term 字典:

for key, value in two_terms.items():
    i = one_terms[str(key).split(':')[0]]['E']
    j = one_terms[str(key).split(':')[1]]['E']

    ij = 0 -i -j
    two_terms[key]['new_value'] = ij

Gives the solution I desire.给出我想要的解决方案。 And for the three_terms dictionary.而对于three_terms 字典。

for key, value in three_terms.items():
    i = one_terms[str(key).split(':')[0]]['E']
    j = one_terms[str(key).split(':')[1]]['E']
    k = one_terms[str(key).split(':')[2]]['E']

    ij = two_terms[(str(key).split(':')[0] + ':' + two_terms[(str(key).split(':')[1])]['E']
    ik = two_terms[(str(key).split(':')[0] + ':' + two_terms[(str(key).split(':')[2])]['E']
    jk = two_terms[(str(key).split(':')[1] + ':' + two_terms[(str(key).split(':')[2])]['E']

    ijk = 0 -ij -ik -jk -i -j -k
    three_terms[key]['new_value'] = ijk

Also gives the desired solution.也给出了所需的解决方案。 Note that I have used the split() function to go through the different key combinations (ij, ik, etc).请注意,我通过不同的组合键(ij、ik 等)使用了split() function 到 go。

However, for an increasing number of n_terms this method becomes impractical.然而,对于越来越多的 n_terms,这种方法变得不切实际。 I need an iterative solution.我需要一个迭代解决方案。


My attempt.我的尝试。

I have attempted to use the itertools.combinations library so that I can iteratively go through all combinations.我试图使用itertools.combinations库,以便我可以通过所有组合迭代 go。

for C in range(1, len(dictionary):
    for S in itertools.combinations(dictionary, C):

However, this simply provides a tuple which becomes difficult to manipulate into something of the form然而,这只是提供了一个元组,它变得难以操纵成某种形式

n = n_terms[(str(key).split(':')[A] + ':' +\
             str(key).split(':')[B] + ':' +\
             ... 
             str(key).split(':')[Z])]['E']

Where A, B,...Z would be combinations.其中A, B,...Z将是组合。

In the two_term case , the solution might take the form:two_term case ,解决方案可能采用以下形式:

for key, value in two_term.items():
    n_list
    for i in range (0, 2):
        n_list.append(one_terms[str(key).split(':')[i]])

    two_body[key]['E'] = sum(n_list)

Though I am struggling to extend this method.虽然我正在努力扩展这种方法。

If you have a large (or even variable) number of these n-term dictionaries, it's a good idea to store them in a separate data structure, eg a list where the list index refers to the "n" in n-term:如果您有大量(甚至可变)这些 n-term 字典,最好将它们存储在单独的数据结构中,例如列表索引引用 n-term 中的“n”的列表:

terms = [
    None,  # zero terms (placeholder to align the n-term index later on)
    {'i': {'E': 1}, 'j': {'E': 2}, 'k': {'E': 3}},  # one terms
    {'i:j': {'E': 12}, 'i:k': {'E': 13}, 'j:k': {'E': 14}},  # two terms
    {'i:j:k': {'E': 111}}  # three terms
    # and so on
]

Then you can loop over these dicts and for each n-terms dictionary you can build combinations of keys for each of the r-terms dicts where r < n :然后,您可以遍历这些字典,并且对于每个 n-terms 字典,您可以为r < n的每个 r-terms 字典构建键组合:

import itertools as it

for n in range(2, len(terms)):
    for key in terms[n]:
        new_value = 0
        parts = key.split(':')
        for r in range(1, n):
            new_value -= sum(terms[r][':'.join(x)]['E'] for x in it.combinations(parts, r=r))
        terms[n][key]['new_value'] = new_value

For the general case, you would probably want a nested loop, as you already noticed.对于一般情况,您可能需要一个嵌套循环,正如您已经注意到的那样。 Do not be afraid of tuples.不要害怕元组。 A data structure with an indeterminate length is exactly what you need to generalize.具有不确定长度的数据结构正是您需要概括的。 Instead, be afraid of enumerating all the combinations in your local namespace, as that hinders generalization.相反,不要枚举本地命名空间中的所有组合,因为这会妨碍泛化。

The first problem is that all your dictionaries have a hard-coded human-readable names.第一个问题是您的所有字典都有硬编码的人类可读名称。 That is hard to access in a general purpose loop without using additional data-structures.如果不使用额外的数据结构,这很难在通用循环中访问。 I would recommend grouping them into a list, such that element N of the list is the dictionary with N + 1 terms:我建议将它们分组到一个列表中,这样列表的元素N就是具有N + 1术语的字典:

all_terms = [one_terms, two_terms, three_terms, ..., n_terms]

You call str(key).split(':') for each term, rather than once.您为每个术语调用str(key).split(':') ,而不是一次。 Aside from being inefficient, you are not using the result to determine how many elements you actually have.除了效率低下之外,您还没有使用结果来确定您实际拥有多少元素。 So let's start with that:所以让我们从那个开始:

z = 2  # 2 chosen arbitrarily. Make it an input
for key, value in all_terms[z].items():
    names = str(key).split(':')

Now len(names) tells you just how deep you need to go, and all_terms lets you actually do it.现在len(names)告诉您 go 需要多深,而all_terms让您真正做到这一点。 So let's put that all together, along with some combinations:因此,让我们将所有内容以及一些组合放在一起:

    new_value = 0
    for n in range(len(names) - 1):
        for combo in itertools.combinations(names, n + 1):
            new_value -= all_terms[n][':'.join(combo)]['E']
    all_terms[z][key]['new_value'] = new_value

You can put the whole thing into a function that accepts z (the level you want to process) as an input, or just processes the last available element in all_terms , so you can keep expanding it iteratively.您可以将整个内容放入接受z (您要处理的级别)作为输入的 function 中,或者只处理all_terms中的最后一个可用元素,因此您可以继续迭代扩展它。 Keep in mind that this will become mind-numbingly slow as your list expands and the number of combinations does too:请记住,随着列表的扩展以及组合的数量,这将变得非常缓慢:

TL;DR TL;博士

def expand_terms(terms, level=None):
    if level is None:
        level = len(terms) - 1
    for key, value in terms[level].items():
        names = str(key).split(':')
        new_value = 0
        for n in range(len(names) - 1):
            for combo in itertools.combinations(names, n + 1):
                new_value -= terms[n][':'.join(names)]['E']
        terms[level][key]['new_value'] = new_value

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

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