2011-03-08 34 views
30

Estoy tratando de escribir algún código para probar el producto cartesiano de un conjunto de parámetros de entrada.Producto cartesiano de un diccionario de listas

He visto itertools, pero su función product no es exactamente lo que quiero. ¿Hay una manera simple y obvia de tomar un diccionario con un número arbitrario de claves y un número arbitrario de elementos en cada valor, y luego generar un diccionario con la siguiente permutación?

de entrada:

options = {"number": [1,2,3], "color": ["orange","blue"] } 
print list(my_product(options)) 

Ejemplo de salida:

[ {"number": 1, "color": "orange"}, 
    {"number": 1, "color": "blue"}, 
    {"number": 2, "color": "orange"}, 
    {"number": 2, "color": "blue"}, 
    {"number": 3, "color": "orange"}, 
    {"number": 3, "color": "blue"} 
] 
+0

Estoy bastante seguro de que no necesita ninguna biblioteca para hacer esto, pero no conozco Python lo suficiente como para responder. Supongo que las listas de comprensión son el truco. –

+0

Pregunto si existe un generador ya hecho que se pueda adaptar fácilmente para hacer algo como esto. Las comprensiones de listas no son relevantes. –

Respuesta

29

Ok, gracias a @dfan por decirme que estaba buscando en el lugar equivocado. Tengo ahora:

def my_product(dicts): 
    return (dict(izip(dicts, x)) for x in product(*dicts.itervalues())) 
+2

¿El hecho de que las entradas del diccionario se almacenen desordenadas afecta esto de todos modos? – Phani

+1

Este es un código muy claro para generar rápidamente casos de prueba de unidad (estilo de conjunto de validación cruzada) – gaborous

+0

Para usuarios de Python 3. Tengo una versión actualizada [aquí] (http://stackoverflow.com/a/40623158/621449) – Tarrasch

5

Por cierto, esto no es una permutación. Una permutación es una reorganización de una lista. Esta es una enumeración de posibles selecciones de listas.

Editar: después de recordar que se llama un producto cartesiano, me encontré con esto:

import itertools 
options = {"number": [1,2,3], "color": ["orange","blue"] } 
product = [x for x in apply(itertools.product, options.values())] 
print [dict(zip(options.keys(), p)) for p in product] 
+0

Estaba tratando de explicar por qué buscar "permutaciones" no ayudaba. Recordé lo que realmente es esto: es un producto cartesiano. Comenzaría mirando itertools.product(). – dfan

+0

Sí, hecho, y gracias por el puntero. Pero aún así, bienvenido a Stack Overflow: una respuesta debería ser una que realmente brinde una respuesta a la pregunta. Esto pertenece a un comentario sobre la pregunta. –

+0

@ user470379 no realmente, la versión original no indicaba el producto cartesiano –

2
# I would like to do 
keys,values = options.keys(), options.values() 
# but I am not sure that the keys and values would always 
# be returned in the same relative order. Comments? 
keys = [] 
values = [] 
for k,v in options.iteritems(): 
    keys.append(k) 
    values.append(v) 

import itertools 
opts = [dict(zip(keys,items)) for items in itertools.product(*values)] 

resultados en

opts = [ 
    {'color': 'orange', 'number': 1}, 
    {'color': 'orange', 'number': 2}, 
    {'color': 'orange', 'number': 3}, 
    {'color': 'blue', 'number': 1}, 
    {'color': 'blue', 'number': 2}, 
    {'color': 'blue', 'number': 3} 
] 
+2

Creo que Python garantiza que las claves() y los valores() y su iter * correspondiente volverán en el mismo orden. Consulte http://docs.python.org/library/stdtypes.html#dict.items –

+0

@Seth: ¡excelente! Gracias, eso me ha estado molestando por un tiempo. –

+0

eres bienvenido. Es muy útil, y especialmente para este caso. Si revisas mi respuesta, puedes ver que los métodos iterkeys/itervalues ​​también te ahorrarán la creación de un montón de temporales. –

9

Python 3 versión de Seth's answer.

import itertools 

def dict_product(dicts): 
    """ 
    >>> list(dict_product(dict(number=[1,2], character='ab'))) 
    [{'character': 'a', 'number': 1}, 
    {'character': 'a', 'number': 2}, 
    {'character': 'b', 'number': 1}, 
    {'character': 'b', 'number': 2}] 
    """ 
    return (dict(zip(dicts, x)) for x in itertools.product(*dicts.values())) 
Cuestiones relacionadas