es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Combinación de diccionarios anidados con longitudes arbitrarias en Python.

Estoy buscando una función que tome un diccionario anidado y produzca las combinaciones/producto de los valores.

Mi consulta es similar al problema especificado aquí, pero no puedo adaptar las respuestas para que se ajusten a mis necesidades:
https://stackoverflow.com/questions/50606454/cartesian-product-of-nested-dictionaries-of-lists

Deseo tener una entrada como esta:

d = {
  "country": [1, 2],
  "health_state": [
    {"healthy": [1]},
    {"breast cancer": {"stage": [1, 2]}}
  ]
}

Producir una salida como sigue:

[
{{"country":1},{"health state":{"healthy":1}}},
{{"country":2},{"health state":{"healthy":1}}},
{{"country":1},{"health state":{"breast cancer":{"stage":1}}}},
{{"country":1},{"health state":{"breast cancer":{"stage":2}}}},
{{"country":2},{"health state":{"breast cancer":{"stage":1}}}},
{{"country":2},{"health state":{"breast cancer":{"stage":2}}}}
]

En este ejemplo, la salida es una lista de ‘estados’ que una persona puede ocupar.

  • Cualquier dos elementos de una lista (entrada) no deben estar en el mismo elemento de la lista devuelta (salida), por ejemplo, alguien no puede estar en el país 1 y país 2 simultáneamente.
  • Todas las claves en un diccionario (entrada) deben ser devueltas en el mismo elemento de la lista (salida), por ejemplo, alguien está en el país 1 y también en un estado de salud. Si ese estado de salud es ‘cáncer de mama’, también está en la etapa 1 o en la etapa 2.

Puedo imaginar una solución que requiere muchos bucles for, verificando si los elementos son diccionarios, listas o ninguno, pero esto parece ineficiente, especialmente para diccionarios profundamente anidados. Sospecho que hay una solución más elegante utilizando itertools.product y recursión, tal vez?

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Para obtener la salida deseada a partir del diccionario anidado de entrada, necesitamos realizar un producto cartesiano de todos los valores en cada clave del diccionario (excluyendo cualquier diccionario anidado). Para los diccionarios anidados, necesitamos llamar a la función de producto cartesiano de forma recursiva hasta que alcancemos el nivel más interno.

    Aquí hay una función que logra esta tarea:

    import itertools
    
    def dict_product(d):
        # crear una lista de claves y valores que no son diccionarios
        keys = []
        vals = []
        for k, v in d.items():
            if isinstance(v, dict):
                keys.append(k)
            else:
                vals.append([(k, x) for x in v])
    
        # utilizar itertools.product para generar el producto de todos los valores no diccionarios
        prod = [dict(p) for p in itertools.product(*vals)]
    
        # llamar recursivamente a dict_product para generar el producto de todos los diccionarios anidados
        for k in keys:
            vals = []
            for v in d[k].values():
                if isinstance(v, dict):
                    vals.append(dict_product(v))
                else:
                    vals.append([(k, v)])
            prod = [dict(list(p.items()) + list(q.items())) for p in prod for q in itertools.product(*vals)]
    
        return prod
    

    Para usar esta función con el diccionario de entrada d, simplemente llamamos dict_product(d).

    Esto devolverá la salida siguiente:

    [{'country': 1, 'health_state': {'healthy': 1}},
     {'country': 2, 'health_state': {'healthy': 1}},
     {'country': 1, 'health_state': {'breast cancer': {'stage': 1}}},
     {'country': 1, 'health_state': {'breast cancer': {'stage': 2}}},
     {'country': 2, 'health_state': {'breast cancer': {'stage': 1}}},
     {'country': 2, 'health_state': {'breast cancer': {'stage': 2}}}]
    

    Esta salida coincide con la salida deseada especificada en la pregunta.

Comments are closed.