繁体   English   中英

根据关键功能排除包含重复项的组合

[英]Excluding combinations with duplicates based on a key function

我想迭代列表的子序列,但我有一个由外部函数定义的唯一性概念,我想忽略在函数下具有相同值的多个元素的组合。

例如,我有一个名称列表,我想迭代这三个名称的所有组合,以便所有三个名称以不同的字母开头。 以下代码完成此操作:

import itertools

names = ["Anabel",
         "Alison",
         "Avery",
         "Abigail",
         "Aimee",
         "Alice",
         "Bethany",
         "Beatrice",
         "Claudia",
         "Carolyn",
         "Diane",
         "Dana"]

f = lambda x : x[0]

for i in itertools.combinations(names, 3):
    if ((f(i[0]) != f(i[1])) and
        (f(i[0]) != f(i[2])) and
        (f(i[1]) != f(i[2]))):
        print i

我实际上在这里做的是迭代3个名称的所有可能组合,并丢弃那些没有的名称,这当然比迭代3个名称的所有组合慢。 有没有一种方法实际上会更快? 要创建一个迭代器,排除我想要首先跳过的那些?

是的,有可能,我能想到的一个解决方案需要创建一个字典,将f(value)和实际名称分组为字典。 我将在这里使用iteration_utilities.groupedby ,但是使用collections.defaultdict也很容易自己做(我会在答案的底部显示)。

>>> from iteration_utilities import groupedby 
>>> equivalent = groupedby(names, f)
>>> equivalent
{'A': ['Anabel', 'Alison', 'Avery', 'Abigail', 'Aimee', 'Alice'],
 'B': ['Bethany', 'Beatrice'],
 'C': ['Claudia', 'Carolyn'],
 'D': ['Diane', 'Dana']}

然后迭代该字典中(排序)键的组合,然后使用itertools.product对每个前缀的所有名称进行迭代:

import itertools

for comb in itertools.combinations(sorted(equivalent), 3):
    for uniquecomb in itertools.product(*[equivalent[i] for i in comb]):
        print(uniquecomb)

使用已sorted ,因为否则在运行之间出现的顺序不是确定的。


为了表明这更快,我使用了以下设置:

def unique_combs(names, f):
    equivalent = groupedby(names, f)

    for comb in itertools.combinations(sorted(equivalent), 3):
        for uniquecomb in itertools.product(*[equivalent[i] for i in comb]):
            pass

def unique_combs_original(names, f):
    for i in itertools.combinations(names, 3):
        if ((f(i[0]) != f(i[1])) and
                (f(i[0]) != f(i[2])) and
                (f(i[1]) != f(i[2]))):
            pass

names = ["Anabel", "Alison", "Avery", "Abigail", "Aimee", "Alice",
         "Bethany", "Beatrice",
         "Claudia", "Carolyn",
         "Diane", "Dana"]

f = lambda x : x[0]

%timeit unique_combs(names, f)           # 10000 loops, best of 3: 59.4 µs per loop
%timeit unique_combs_original(names, f)  # 1000 loops, best of 3: 417 µs per loop

但是如果有很多待丢弃的组合,它也可以更好地扩展:

names = names * 10  # more duplicates

%timeit unique_combs(names, f)           # 100 loops, best of 3: 9.74 ms per loop
%timeit unique_combs_original(names, f)  # 1 loop, best of 3: 577 ms per loop

我提到defaultdict而不是groupedby ,因为completness它可以像这样创建:

from collections import defaultdict

>>> names = ["Anabel", "Alison", "Avery", "Abigail", "Aimee", "Alice",
...          "Bethany", "Beatrice",
...         "Claudia", "Carolyn",
...         "Diane", "Dana"]

>>> equivalent = defaultdict(list)
>>> for name in names:
...     equivalent[f(name)].append(name)

>>> equivalent
defaultdict(list,
            {'A': ['Anabel', 'Alison', 'Avery', 'Abigail', 'Aimee', 'Alice'],
             'B': ['Bethany', 'Beatrice'],
             'C': ['Claudia', 'Carolyn'],
             'D': ['Diane', 'Dana']})

您可以使用itertools combinationsgroupbyproduct来跟随单行:

[p for c in combinations((tuple(g) for _, g in groupby(names, lambda x: x[0])), 3) 
       for p in product(*c)]

在上面的groupby组中,名称基于第一个字母并返回(key, group)元组的可迭代。 每个group本身都是一个可迭代的,然后转换为元组,以便可以多次迭代。 请注意,这假定以相同字母开头的名称在列表中彼此相邻。 如果不是这种情况,您可以在使用groupby之前对名称进行排序。

这是groupby本身的一个例子:

>>> groups = list(tuple(g) for _, g in groupby(names, lambda x: x[0]))
>>> groups
[('Anabel', 'Alison', 'Avery', 'Abigail', 'Aimee', 'Alice'), ('Bethany', 'Beatrice'), ('Claudia', 'Carolyn'), ('Diane', 'Dana')]

接下来,结果组被赋予combinations ,这些combinations将返回组中包含3个元素的所有子序列:

>>> combs = list(combinations(groups, 3))
>>> combs
[(('Anabel', 'Alison', 'Avery', 'Abigail', 'Aimee', 'Alice'), ('Bethany', 'Beatrice'), ('Claudia', 'Carolyn')), (('Anabel', 'Alison', 'Avery', 'Abigail', 'Aimee', 'Alice'), ('Bethany', 'Beatrice'), ('Diane', 'Dana')), (('Anabel', 'Alison', 'Avery', 'Abigail', 'Aimee', 'Alice'), ('Claudia', 'Carolyn'), ('Diane', 'Dana')), (('Bethany', 'Beatrice'), ('Claudia', 'Carolyn'), ('Diane', 'Dana'))]

最后,每个组合在解包并传递给product ,将产生对给定组的笛卡尔乘积:

>>> result = list(p for c in combs for p in product(*c))
>>> result[0]
('Anabel', 'Bethany', 'Claudia')
>>> len(result)
80

暂无
暂无

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

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