我想迭代列表的子序列,但我有一个由外部函数定义的唯一性概念,我想忽略在函数下具有相同值的多个元素的组合。
例如,我有一个名单列表,我想迭代这三个名称的所有组合,这样所有三个名字都以不同的字母开头。以下代码实现了这一点:
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个名字的所有组合慢。有没有一种方法实际上会更快?要创建一个迭代器,排除我想要首先跳过的那些?
答案 0 :(得分:0)
是的,我能想到的一个解决方案需要创建一个字典,将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']})
答案 1 :(得分:0)
您可以使用itertools
中的combinations
,groupby
和product
来获取以下内容:
[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
,它将返回包含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