Todas las combinaciones de valores de dos listas que representan una característica determinada

Tengo tres listas:

a = [0,1,2] b = [3,4,5] c = [aab, abb, aaa] 

¿Cómo crear todas las combinaciones de tres elementos? Donde las secuencias de la lista c te dicen qué lista se puede usar para elegir números para un lugar determinado en una secuencia de salida determinada

Por ejemplo (pseudocódigo):

 for i=0 in range(len(c)): print: [0,1,3] [0,1,4] ... [0,2,5] ... [1,2,4] [1,2,5] 

Y lo mismo para el rest de los índices i . Donde los valores en sublistas individuales no se pueden repetir. Estaré muy agradecido por cualquier consejo.

Related of "Todas las combinaciones de valores de dos listas que representan una característica determinada"

Esta función del generador manejará las cadenas de la plantilla ‘ab’ con las a y las b en cualquier orden, y las listas de salida no contendrán elementos repetidos si las listas a y b están separadas. Usamos itertools.combinations para generar combinaciones del orden requerido, y combinamos las combinaciones a y b usando itertools.product . Los conseguimos en el orden correcto al convertir cada combinación b en un iterador y seleccionamos del iterador correcto a través de un diccionario.

 from itertools import combinations, product def groups(a, b, c): for pat in c: acombo = combinations(a, pat.count('a')) bcombo = combinations(b, pat.count('b')) for ta, tb in product(acombo, bcombo): d = {'a': iter(ta), 'b': iter(tb)} yield [next(d[k]) for k in pat] # tests a = [0,1,2] b = [3,4,5] templates = ['aab', 'abb', 'aaa'], ['aba'], ['bab'] for c in templates: print('c', c) for i, t in enumerate(groups(a, b, c), 1): print(i, t) print() 

salida

 c ['aab', 'abb', 'aaa'] 1 [0, 1, 3] 2 [0, 1, 4] 3 [0, 1, 5] 4 [0, 2, 3] 5 [0, 2, 4] 6 [0, 2, 5] 7 [1, 2, 3] 8 [1, 2, 4] 9 [1, 2, 5] 10 [0, 3, 4] 11 [0, 3, 5] 12 [0, 4, 5] 13 [1, 3, 4] 14 [1, 3, 5] 15 [1, 4, 5] 16 [2, 3, 4] 17 [2, 3, 5] 18 [2, 4, 5] 19 [0, 1, 2] c ['aba'] 1 [0, 3, 1] 2 [0, 4, 1] 3 [0, 5, 1] 4 [0, 3, 2] 5 [0, 4, 2] 6 [0, 5, 2] 7 [1, 3, 2] 8 [1, 4, 2] 9 [1, 5, 2] c ['bab'] 1 [3, 0, 4] 2 [3, 0, 5] 3 [4, 0, 5] 4 [3, 1, 4] 5 [3, 1, 5] 6 [4, 1, 5] 7 [3, 2, 4] 8 [3, 2, 5] 9 [4, 2, 5] 

Debo mencionar que aunque las combinations devuelven iteradores, y el product felizmente toma a los iteradores como argumentos, tiene que hacer listas de los iteradores porque tiene que correr sobre el contenido del iterador varias veces. Así que si el número de combinaciones es enorme, esto puede consumir una buena cantidad de RAM.


Si quieres permutaciones en lugar de combinaciones, eso es fácil. Simplemente lo llamamos itertools.permutations lugar de itertools.combinations .

 from itertools import permutations, product def groups(a, b, c): for pat in c: acombo = permutations(a, pat.count('a')) bcombo = permutations(b, pat.count('b')) for ta, tb in product(acombo, bcombo): d = {'a': iter(ta), 'b': iter(tb)} yield [next(d[k]) for k in pat] # tests a = [0,1,2] b = [3,4,5] templates = ['aaa'], ['abb'] for c in templates: print('c', c) for i, t in enumerate(groups(a, b, c), 1): print(i, t) print() 

salida

  c ['aaa'] 1 [0, 1, 2] 2 [0, 2, 1] 3 [1, 0, 2] 4 [1, 2, 0] 5 [2, 0, 1] 6 [2, 1, 0] c ['abb'] 1 [0, 3, 4] 2 [0, 3, 5] 3 [0, 4, 3] 4 [0, 4, 5] 5 [0, 5, 3] 6 [0, 5, 4] 7 [1, 3, 4] 8 [1, 3, 5] 9 [1, 4, 3] 10 [1, 4, 5] 11 [1, 5, 3] 12 [1, 5, 4] 13 [2, 3, 4] 14 [2, 3, 5] 15 [2, 4, 3] 16 [2, 4, 5] 17 [2, 5, 3] 18 [2, 5, 4] 

Finalmente, aquí hay una versión que maneja cualquier cantidad de listas y cadenas de plantillas de cualquier longitud. Solo acepta una única cadena de plantilla por llamada, pero eso no debería ser un problema. También puede elegir si desea generar permutaciones o combinaciones a través de una palabra clave opcional arg.

 from itertools import permutations, combinations, product def groups(sources, template, mode='P'): func = permutations if mode == 'P' else combinations keys = sources.keys() combos = [func(sources[k], template.count(k)) for k in keys] for t in product(*combos): d = {k: iter(v) for k, v in zip(keys, t)} yield [next(d[k]) for k in template] # tests sources = { 'a': [0, 1, 2], 'b': [3, 4, 5], 'c': [6, 7, 8], } templates = 'aa', 'abc', 'abba', 'cab' for template in templates: print('\ntemplate', template) for i, t in enumerate(groups(sources, template, mode='C'), 1): print(i, t) 

salida

 template aa 1 [0, 1] 2 [0, 2] 3 [1, 2] template abc 1 [0, 3, 6] 2 [0, 3, 7] 3 [0, 3, 8] 4 [0, 4, 6] 5 [0, 4, 7] 6 [0, 4, 8] 7 [0, 5, 6] 8 [0, 5, 7] 9 [0, 5, 8] 10 [1, 3, 6] 11 [1, 3, 7] 12 [1, 3, 8] 13 [1, 4, 6] 14 [1, 4, 7] 15 [1, 4, 8] 16 [1, 5, 6] 17 [1, 5, 7] 18 [1, 5, 8] 19 [2, 3, 6] 20 [2, 3, 7] 21 [2, 3, 8] 22 [2, 4, 6] 23 [2, 4, 7] 24 [2, 4, 8] 25 [2, 5, 6] 26 [2, 5, 7] 27 [2, 5, 8] template abba 1 [0, 3, 4, 1] 2 [0, 3, 5, 1] 3 [0, 4, 5, 1] 4 [0, 3, 4, 2] 5 [0, 3, 5, 2] 6 [0, 4, 5, 2] 7 [1, 3, 4, 2] 8 [1, 3, 5, 2] 9 [1, 4, 5, 2] template cab 1 [6, 0, 3] 2 [7, 0, 3] 3 [8, 0, 3] 4 [6, 0, 4] 5 [7, 0, 4] 6 [8, 0, 4] 7 [6, 0, 5] 8 [7, 0, 5] 9 [8, 0, 5] 10 [6, 1, 3] 11 [7, 1, 3] 12 [8, 1, 3] 13 [6, 1, 4] 14 [7, 1, 4] 15 [8, 1, 4] 16 [6, 1, 5] 17 [7, 1, 5] 18 [8, 1, 5] 19 [6, 2, 3] 20 [7, 2, 3] 21 [8, 2, 3] 22 [6, 2, 4] 23 [7, 2, 4] 24 [8, 2, 4] 25 [6, 2, 5] 26 [7, 2, 5] 27 [8, 2, 5] 
 from itertools import product, chain setups = ['aab', 'abb', 'aaa'] sources = { 'a': [0,1,2], 'b': [3,4,5] } combinations = (product(*map(sources.get, setup)) for setup in setups) 

combinations es un iterador perezoso nested (es decir, no hay nada almacenado en la memoria y calculado todavía). Si quieres conseguir un iterador de listas.

 combinations = map(list, (product(*map(sources.get, setup)) for setup in setups)) 

O quizás quieras aplanar el resultado:

 combinations = chain.from_iterable(product(*map(sources.get, setup)) for setup in setups) 

Si lo comprendo correctamente, puede lograr el objective con un diccionario de contabilidad, la correspondencia de un personaje como "a" a un nombre de variable a .

 from collections import defaultdict a = [0,1,2] b = [3,4,5] c = ["aab", "abb", "aaa"] d = {"a": a, "b": b} d2 = defaultdict(list) for seq in c: l = [] for idx, v in enumerate(seq): l.append(d[v][idx]) print(l) d2[seq].append(l) # Out: #[0, 1, 5] #[0, 4, 5] #[0, 1, 2] print(d2) # defaultdict(, {'aab': [[0, 1, 5]], 'abb': [[0, 4, 5]], 'aaa': [[0, 1, 2]]}) 

Coloque las listas en un diccionario para que pueda acceder a ellas con cadenas.
Usa los caracteres en cada secuencia para determinar qué listas usar.
Use itertools.product para obtener las combinaciones .

 import itertools, collections from pprint import pprint d = {'a':[0,1,2], 'b':[3,4,5]} c = ['aab', 'abb', 'aaa'] def f(t): t = collections.Counter(t) return max(t.values()) < 2 for seq in c: data = (d[char] for char in seq) print(f'sequence: {seq}') pprint(list(filter(f, itertools.product(*data)))) print('***************************') 

Resultado para la secuencia 'abb' :

 sequence: abb [(0, 3, 4), (0, 3, 5), (0, 4, 3), (0, 4, 5), (0, 5, 3), (0, 5, 4), (1, 3, 4), (1, 3, 5), (1, 4, 3), (1, 4, 5), (1, 5, 3), (1, 5, 4), (2, 3, 4), (2, 3, 5), (2, 4, 3), (2, 4, 5), (2, 5, 3), (2, 5, 4)] 

Editar para filtrar tuplas con duplicados


Me gusta la idea de un dict de llamada que se puede utilizar con el mapa. Podría ser utilizado aquí.

 class CallDict(dict): def __call__(self, key): return self[key] #self.get(key) e = CallDict([('a',[0,1,2]), ('b',[3,4,5])]) for seq in c: data = map(e, seq) print(f'sequence: {seq}') for thing in filter(f, itertools.product(*data)): print(thing) print('***************************') 

No pude evitarlo, aquí hay una versión genérica de la solución / respuesta de @ PM2Ring. En lugar de filtrar elementos no deseados, no los produce en primer lugar.

 d = {'a':[0,1,2], 'b':[3,4,5]} c = ['aab', 'abb', 'aaa', 'aba'] def g(d, c): for seq in c: print(f'sequence: {seq}') counts = collections.Counter(seq) ## data = (itertools.combinations(d[key],r) for key, r in counts.items()) data = (itertools.permutations(d[key],r) for key, r in counts.items()) for thing in itertools.product(*data): q = {key:iter(other) for key, other in zip(counts, thing)} yield [next(q[k]) for k in seq] for t in g(d, c): print(t) 

Parece que estás buscando una manera de llamar programáticamente itertools.product

 from itertools import product d = {'a': [0,1,2], 'b': [3,4,5]} c = ['aab', 'abb', 'aaa'] for s in c: print(list(product(*[d[x] for x in s])))