Python中的组合和乘积函数

3

我有一个数据框的列列表:

L=[AA ,  AS  ,  AD  , BB  , BC  , CD ,  CF,CG ]

我需要所有项目的组合,没有特定的顺序。

但是,在每个组合中,我只能有一个以A开头的名称,但我可以拥有多个或没有以C开头的名称。

关于B,我必须至少有1个,但也可以有更多。

因此,我需要所有这些组合:

A=[AA,AS,AD] #only one of these
B=[BB,BC]  #at least one of these
all_others=[CD,CF,CG]  #All, 1, 2 or none of these

目前我有这段代码:

from itertools import product

for choices in product(['AA','AS','AD',None],['BB', 'BC', None], ['CD','CF', None],):
    print(' '.join(column for column in choices if column))

这个代码是可行的,但是它只允许一个以C开头的值,而我想要生成所有以C开头的组合。有没有人看到我可以做出的好修改?

总结一下; 我需要列表中名称的所有组合。有一个规则,即您不能拥有以A开头的多个变量和以B开头的多个变量。


很难理解你的输出应该是什么。 - vishes_shell
嘿,应该是列表中所有名称的组合。有一个规则,就是不能有超过1个以A开头的变量和超过1个以B开头的变量。这样更清楚吗? - fred.schwartz
3个回答

2

尝试使用以下代码替代您的for循环:

for choices in itertools.product(['AA','AS','AD',None],['BB', 'BC', None],[' '.join(k) for j in list(itertools.combinations(['CD','CF'],i) for i in range(3)) for k in j]):
    # do what you need

使用print(' '.join(column for column in choices if column))输出选择的结果为:
AA BB
AA BB CD
AA BB CF
AA BB CD CF
AA BC
AA BC CD
AA BC CF
AA BC CD CF
AA
AA CD
AA CF
AA CD CF
AS BB
AS BB CD
AS BB CF
AS BB CD CF
AS BC
AS BC CD
AS BC CF
AS BC CD CF
AS
AS CD
AS CF
AS CD CF
AD BB
AD BB CD
AD BB CF
AD BB CD CF
AD BC
AD BC CD
AD BC CF
AD BC CD CF
AD
AD CD
AD CF
AD CD CF
BB
BB CD
BB CF
BB CD CF
BC
BC CD
BC CF
BC CD CF

CD
CF
CD CF

我建议您将None替换为''或删除它们。

太棒了!!!这就是我想要的东西。棒极了。非常感谢。 - fred.schwartz
如果您发现这个答案正确,请考虑点赞并接受它。不用谢。 - Mehrdad Pedramfar
@fred.schwartz,我在那里添加了一个小更新,请查看。 - Mehrdad Pedramfar

1
当然,表达
all_others=[CD,CF,CG]  #All, 1, 2 or none of these

把它分解为:
all_others=[CD]  #one or none of these
all_others=[CF]  #one or none of these
all_others=[CG]  #one or none of these

然后你的代码变成了。
from itertools import product

for choices in product(['AA','AS','AD',None],['BB', 'BC', None], ['CD', None], ['CF', None], ['CG', None],):
    print(' '.join(column for column in choices if column))

这个处理特定的例子。 然而,如果您有多个以C开头的项目,则可以按以下方式更系统地处理它们:
from itertools import product

for choices in product(['AA','AS','AD',None],['BB', 'BC', None], *product(['CD', 'CF', 'CG'], [None]),):
    print(' '.join(column for column in choices if column))

为了解释正在发生的事情,将['CD', 'CF', 'CG'][None]相乘会产生一个包含的迭代器。
('CD', None), ('CF', None), ('CG', None)

这些正是我们希望传递给product的参数。 *运算符将迭代器内部的元素转换为函数参数。 因此,上述两个代码片段是等效的。

1
谢谢,有更有效的方法吗?那个变量列表可能会变得非常大,我不确定我能够把它们全部打出来。 - fred.schwartz
如果我想更改B的条件,以确保至少有1个B存在但可以有更多,应该如何操作? - fred.schwartz

1
这里有一种更加健壮/通用的方法来完成你想要的事情。我先定义一个辅助函数:
from itertools import combinations, chain, product

def subsets_of_length(s, lengths):
    return chain.from_iterable(combinations(s,l) for l in lengths)

它产生以下输出:

>>>> list(subsets_of_length(['a','b','c'], range(2,4)))
[('a', 'b'), ('a', 'c'), ('b', 'c'), ('a', 'b', 'c')]

>>>> list(subsets_of_length(['d','e'], range(0,2)))
[(), ('d',), ('e',)]

现在我们想要将两个或多个子集合并如下。
>>>> for choices in product(
         subsets_of_length(['a','b','c'], range(2,4)),
         subsets_of_length(['d','e'], range(0,2)),
     ):
         print(' '.join(str(subset) for subset in choices))

('a', 'b') ()
('a', 'b') ('d',)
('a', 'b') ('e',)
('a', 'c') ()
('a', 'c') ('d',)
('a', 'c') ('e',)
('b', 'c') ()
('b', 'c') ('d',)
('b', 'c') ('e',)
('a', 'b', 'c') ()
('a', 'b', 'c') ('d',)
('a', 'b', 'c') ('e',)

但是我们想要将这些元组链接在一起。因此,我们应该这样做:
>>>> for choices in map(chain.from_iterable,product(
         subsets_of_length(['a','b','c'], range(2,4)),
         subsets_of_length(['d','e'], range(0,2)),
     )):
         print(' '.join(column for column in choices if column))

a b
a b d
a b e
a c
a c d
a c e
b c
b c d
b c e
a b c
a b c d
a b c e

您编辑后问题的代码如下:
for choices in map(chain.from_iterable,product(
    subsets_of_length(['AA','AS','AD'], [1]),       #only one of these
    subsets_of_length(['BB','BC'], [1,2]),          #at least one of these
    subsets_of_length(['CD','CF','CG'], [0,1,2,3]), #All, 1, 2 or none of these
)):
    print(' '.join(column for column in choices if column))

1
谢谢Ben,非常友善。我会尝试理解/应用你的代码,并告诉你它是否有效。看起来非常有前途。干杯! - fred.schwartz
工作得非常好。非常令人印象深刻。 - fred.schwartz
在最后一个问题上,我可以写吗? - fred.schwartz
subsets_of_length(['CD','CF','CG'], [0:3])? - fred.schwartz
1
太好了,它起作用了! 我想我明白您想要使用选择器的意图。但似乎不适用于在这里选择长度,因为长度只是整数范围。如果您真的想要,可以编写range(100)[0:3]。但这等效于range(3),它会给出[0,1,2]。请注意,由于Python的索引约定,我们缺少末尾的3,因此需要使用range(4)。这就是为什么对于小列表,我更喜欢将它们显式地写出来。 - Ben Mares

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接