首页 > 解决方案 > 独特产品与 itertools 的组合

问题描述

我有一个嵌套列表,想制作两个项目的产品。

test = [[('juice', 'NOUN'), ('orange', 'FLAVOR')], 
        [('juice', 'NOUN'), ('orange', 'FLAVOR'), ('lemon', 'FLAVOR')],
        [('orange', 'FLAVOR'), ('chip', 'NOUN')]]

我期望的是这样的:

[(('juice', 'NOUN'), ('lemon', 'FLAVOR')), 
 (('juice', 'NOUN'), ('chip', 'NOUN')),
 (('orange', 'FLAVOR'), ('lemon', 'FLAVOR')),
 (('orange', 'FLAVOR'), ('chip', 'NOUN')),
 (('lemon', 'FLAVOR'), ('chip', 'NOUN'))]

也就是说,我想获得跨列表的排列,但仅限于独特的项目。我更喜欢使用itertools. 以前,我试过list(itertools.product(*test))但我意识到它会产生嵌套列表长度的乘积......

我当前的代码:

unique_list = list(set(itertools.chain(*test)))
list(itertools.combinations(unique_list, 2))

我的思考过程是先获取嵌套列表中的唯一项,所以嵌套列表将是 [[('juice', 'NOUN'), ('orange', 'FLAVOR')], [('lemon', 'FLAVOR')], [('chip', 'NOUN')]]然后使用itertools.combinations来置换。然而,它会在列表中排列(即果汁和橙子一起出现),这是我不希望在我的结果中出现的。

标签: pythonlistpermutationitertools

解决方案


这可以满足您的要求,而无需将原始列表的大小固定为 3:

输入:

test = [[('juice', 'NOUN'), ('orange', 'FLAVOR')], 
        [('juice', 'NOUN'), ('orange', 'FLAVOR'), ('lemon', 'FLAVOR')],
        [('juice', 'NOUN'), ('chip', 'NOUN')]]

首先,重新格式化输入以删除重复项(见注 1):

test = [[x for x in sublist if x not in sum(test[:i], [])] for i, sublist in enumerate(test)]

最后,得到组合乘积

from itertools import combinations, product

for c in combinations(test, 2):
    for x in product(*c):
        print(x)

产生:

(('juice', 'NOUN'), ('lemon', 'FLAVOR'))
(('orange', 'FLAVOR'), ('lemon', 'FLAVOR'))
(('juice', 'NOUN'), ('chip', 'NOUN'))
(('orange', 'FLAVOR'), ('chip', 'NOUN'))
(('lemon', 'FLAVOR'), ('chip', 'NOUN'))

  1. 如果在任何先前的子列表中看到内部元组,则删除它们。这里的魔术是通过sum(test[:i], [])将所有先前的子列表“添加”在一起以仅执行一次成员资格检查来完成的。

上面还有一个列表理解版本,用于紧凑性和样式点:

res = [x for c in combinations(test, 2) for x in product(*c)]

推荐阅读