【问题标题】:Get all subsets of a Python dictionary with restrictions获取有限制的 Python 字典的所有子集
【发布时间】:2017-06-06 05:22:52
【问题描述】:

我有以下字典:

intervals = {'param1': [0, 1],
             'param2_hi': [4, 5, 6, 7, 8, 9],
             'param2_lo': [0, 1, 2, 3, 4, 5],
             'param3_hi': [9, 10, 11, 12, 13, 14, 15],
             'param3_lo': [5, 6, 7, 8, 9, 10],
             'param4': [0, 1],
             'param5_hi': [4, 5, 6, 7, 8, 9],
             'param5_lo': [0, 1, 2, 3, 4, 5]}

我将如何创建这本字典的所有可能子集,其中我必须为 'paramx' 的每个数字 x 恰好有一个?这意味着我只能有一个用于 param1(_lo 或 _hi)的条目,一个用于 param2(_lo 或 _hi)的条目,依此类推,并且每个子集必须包含每个没有 _lo 或 _hi 的 paramx。

这里有两个可能的子集:

subset_one = {'param1': [0, 1],
              'param2_hi': [4, 5, 6, 7, 8, 9],
              'param3_hi': [9, 10, 11, 12, 13, 14, 15],
              'param4': [0, 1],
              'param5_hi': [4, 5, 6, 7, 8, 9]}

subset_two = {'param1': [0, 1],
              'param2_lo': [0, 1, 2, 3, 4, 5],
              'param3_lo': [5, 6, 7, 8, 9, 10],
              'param4': [0, 1],
              'param5_lo': [0, 1, 2, 3, 4, 5]}

# Example of mixed '_hi' and '_lo' intervals
subset_three = {'param1': [0, 1],
                'param2_lo': [0, 1, 2, 3, 4, 5],
                'param3_hi': [9, 10, 11, 12, 13, 14, 15],
                'param4': [0, 1],
                'param5_lo': [0, 1, 2, 3, 4, 5]}

...

注意:我想保留键值对。

编辑:添加了 subset_three 以显示混合 '_lo' 和 '_hi' 间隔的可能性。

【问题讨论】:

  • 这些是您仅有的钥匙吗?
  • 这是一个有限的参数列表吗?这些是他们的真实姓名吗?他们总是以递增顺序加上数字后缀吗?
  • 可能还有更多(取决于我的程序的不同运行),但键的形式总是 paramx、paramx_lo、paramx_hi,其中 x 是某个整数。
  • @zwer,后缀整数总是递增的。数字之间不会有跳过。
  • @ToddYoung - Jared Goguen 咬了我一口 - 对我的评论稍作修正,他的解决方案应该完全符合您的需要。

标签: python dictionary combinations


【解决方案1】:

在最简单的情况下,您可以使用以下 dict 推导:

subset_1 = {k:v for k,v in intervals.items() if k.endswith('_hi') or not k.endswith('_lo')}
subset_2 = {k:v for k,v in intervals.items() if k.endswith('_lo') or not k.endswith('_hi')}

print(subset_1)
print(subset_2)

输出:

{'param3_hi': [9, 10, 11, 12, 13, 14, 15], 'param1': [0, 1], 'param2_hi': [4, 5, 6, 7, 8, 9], 'param4': [0, 1], 'param5_hi': [4, 5, 6, 7, 8, 9]}
{'param2_lo': [0, 1, 2, 3, 4, 5], 'param1': [0, 1], 'param3_lo': [5, 6, 7, 8, 9, 10], 'param4': [0, 1], 'param5_lo': [0, 1, 2, 3, 4, 5]}

【讨论】:

  • 可能有高值和低值的某种组合。只是我必须拥有每个编号参数之一。
  • @ToddYoung,你能用扩展和详细的条件和所需的输出来更新你的问题吗?
  • 刚刚添加了“subset_three”,这是一个我们可以混合使用“_lo”和“_hi”区间的示例。
【解决方案2】:

我认为collections.defaultdictitertools.product 在这里都很有用。首先,构建一个池,根据键的开头对键进行排序。然后,构造这些 bin 的笛卡尔积。

from collections import defaultdict
from itertools import product

pool = defaultdict(list)
for key in intervals:
    base = key.split('_')[0]
    pool[base].append(key)

subsets = [{key: intervals[key] for key in keys} for keys in product(*pool.values())]

# {'param2_lo': [0, 1, 2, 3, 4, 5], ... , 'param1': [0, 1]}
# {'param5_lo': [0, 1, 2, 3, 4, 5], ..., 'param2_hi': [4, 5, 6, 7, 8, 9]}
# and so on...

【讨论】:

  • itertools.product() 将返回一个带有元组元素的迭代器,由于intervals 持有单独的键,因此您的字典理解将失败。您需要将其双循环为:subsets = [{key: intervals[key] for key in keys} for keys in product(*pool.values())]
【解决方案3】:

我相信您希望itertools.product 应用于您的参数中的五个选择集:

choices = [
    ['param1'],
    ['param2_lo', 'param2_hi'],
    ['param3_lo', 'param3_hi'],
    ['param4'],
    ['param5_lo', 'param5_hi']
]

for permute in itertools.product(*choices):
    ....

这足以让你继续前进吗?

【讨论】:

  • 我认为你需要使用(*choices)
【解决方案4】:

我认为最好的工具是 itertools.product,一个解决方案是:

intervals = {'param1': [0, 1],
             'param2_hi': [4, 5, 6, 7, 8, 9],
             'param2_lo': [0, 1, 2, 3, 4, 5],
             'param3_hi': [9, 10, 11, 12, 13, 14, 15],
             'param3_lo': [5, 6, 7, 8, 9, 10],
             'param4': [0, 1],
             'param5_hi': [4, 5, 6, 7, 8, 9],
             'param5_lo': [0, 1, 2, 3, 4, 5]}


import itertools

def get_subsets(intervals):

   params_list = set(key.split('_')[0] for key in intervals.keys())

   list_keys = [[x for x in intervals.keys() if x.startswith(param)] for param in params_list]

   subsets = [{x : intervals[x] for x in sublist} for sublist in itertools.product(*list_keys)]

   return subsets

get_subsets(intervals)

# [{'param2_lo': [0, 1, 2, 3, 4, 5], 'param3_hi': [9, 10, 11, 12, 13, 14, 15], 'param5_lo': [0, 1, 2, 3, 4, 5], 'param1': [0, 1], 'param4': [0, 1]}, {'param2_lo': [0, 1, 2, 3, 4, 5], 'param3_hi': [9, 10, 11, 12, 13, 14, 15], 'param1': [0, 1], 'param4': [0, 1], 'param5_hi': [4, 5, 6, 7, 8, 9]}, ...

【讨论】:

    【解决方案5】:

    您可以像此示例一样使用itertools 模块中的groupbyproduct 来列出所有可能的组合:

    from itertools import groupby, product
    
    intervals = {'param1': [0, 1],
                 'param2_hi': [4, 5, 6, 7, 8, 9],
                 'param2_lo': [0, 1, 2, 3, 4, 5],
                 'param3_hi': [9, 10, 11, 12, 13, 14, 15],
                 'param3_lo': [5, 6, 7, 8, 9, 10],
                 'param4': [0, 1],
                 'param5_hi': [4, 5, 6, 7, 8, 9],
                 'param5_lo': [0, 1, 2, 3, 4, 5]}
    
    
    sub = []
    
    for _,v in groupby(sorted(intervals.keys()), lambda x: x[5]):
        # Or:
        # sub.append(list(v))
        sub.append(sorted(list(v)))
    
    
    for k in product(*sub):
        print({j:intervals[j] for j in k})
        print("------")
    

    输出:

    {'param1': [0, 1], 'param3_hi': [9, 10, 11, 12, 13, 14, 15], 'param2_hi': [4, 5, 6, 7, 8, 9], 'param5_hi': [4, 5, 6, 7, 8, 9], 'param4': [0, 1]}
    ------
    {'param1': [0, 1], 'param3_hi': [9, 10, 11, 12, 13, 14, 15], 'param2_hi': [4, 5, 6, 7, 8, 9], 'param5_lo': [0, 1, 2, 3, 4, 5], 'param4': [0, 1]}
    ------
    {'param1': [0, 1], 'param2_hi': [4, 5, 6, 7, 8, 9], 'param5_hi': [4, 5, 6, 7, 8, 9], 'param3_lo': [5, 6, 7, 8, 9, 10], 'param4': [0, 1]}
    ------
    {'param1': [0, 1], 'param2_hi': [4, 5, 6, 7, 8, 9], 'param3_lo': [5, 6, 7, 8, 9, 10], 'param5_lo': [0, 1, 2, 3, 4, 5], 'param4': [0, 1]}
    ------
    ....
    ------
    
    {'param4': [0, 1], 'param1': [0, 1], 'param5_hi': [4, 5, 6, 7, 8, 9], 'param3_lo': [5, 6, 7, 8, 9, 10], 'param2_lo': [0, 1, 2, 3, 4, 5]}
    ------
    {'param4': [0, 1], 'param1': [0, 1], 'param5_lo': [0, 1, 2, 3, 4, 5], 'param3_lo': [5, 6, 7, 8, 9, 10], 'param2_lo': [0, 1, 2, 3, 4, 5]}
    ------
    

    【讨论】:

      猜你喜欢
      • 2011-04-26
      • 2019-08-09
      • 2016-05-27
      • 2021-03-30
      • 2020-02-06
      • 1970-01-01
      • 2020-11-03
      • 2020-10-13
      • 1970-01-01
      相关资源
      最近更新 更多