Supongamos que tiene un diccionario como:
{'a': 1,
'c': {'a': 2,
'b': {'x': 5,
'y' : 10}},
'd': [1, 2, 3]}
¿Cómo harías para aplanar eso en algo como:
{'a': 1,
'c_a': 2,
'c_b_x': 5,
'c_b_y': 10,
'd': [1, 2, 3]}
python
dictionary
Un timmes
fuente
fuente
Respuestas:
Básicamente, de la misma manera que aplanaría una lista anidada, solo tiene que hacer el trabajo adicional para iterar el dict por clave / valor, crear nuevas claves para su nuevo diccionario y crear el diccionario en el paso final.
fuente
isinstance
con untry..except
bloque, esto funcionará para cualquier asignación, incluso si no se deriva dedict
.collections.MutableMapping
hacerlo más genérico. Pero para Python <2.6,try..except
es probablemente la mejor opción.if isinstance(v, collections.MutableMapping):
aif v and isinstance(v, collections.MutableMapping):
new_key = parent_key + sep + k if parent_key else k
supone que las teclas son siempre cadenas, de lo contrario se elevaráTypeError: cannot concatenate 'str' and [other] objects
. Sin embargo, puede solucionarlo simplemente coaccionandok
a string (str(k)
), o concatenando claves en una tupla en lugar de una cadena (las tuplas también pueden ser claves dictadas).Hay dos grandes consideraciones que el póster original debe tener en cuenta:
{'a_b':{'c':1}, 'a':{'b_c':2}}
resultaría en{'a_b_c':???}
. La siguiente solución evade el problema devolviendo un iterable de pares.joinedKey = '_'.join(*keys)
, eso te costará O (N ^ 2) tiempo de ejecución. Sin embargo, si estás dispuesto a decirnextKey = previousKey+'_'+thisKey
, eso te da tiempo O (N). La solución a continuación le permite hacer ambas cosas (ya que podría simplemente concatenar todas las claves y luego procesarlas posteriormente).(Es probable que el rendimiento no sea un problema, pero explicaré el segundo punto en caso de que a alguien más le importe: al implementar esto, hay numerosas opciones peligrosas. Si lo hace de forma recursiva y cede y vuelve a ceder, o cualquier cosa equivalente que toque nodos más de una vez (lo cual es bastante fácil de hacer accidentalmente), está haciendo un trabajo potencialmente O (N ^ 2) en lugar de O (N). Esto se debe a que tal vez está calculando una clave
a
ya_1
luegoa_1_i
..., y luego calculandoa
entoncesa_1
entoncesa_1_ii
..., pero realmente no debería tener que calcular dea_1
nuevo. Incluso si no lo está recalculando, volver a cederlo (un enfoque 'nivel por nivel') es igual de malo. Un buen ejemplo es pensar en el rendimiento en{1:{1:{1:{1:...(N times)...{1:SOME_LARGE_DICTIONARY_OF_SIZE_N}...}}}}
)A continuación hay una función que escribí
flattenDict(d, join=..., lift=...)
que se puede adaptar a muchos propósitos y puede hacer lo que quiera. Lamentablemente, es bastante difícil hacer una versión perezosa de esta función sin incurrir en las penalizaciones de rendimiento anteriores (muchas construcciones de python como chain.from_iterable no son realmente eficientes, de lo que solo me di cuenta después de probar exhaustivamente tres versiones diferentes de este código antes de establecerme éste).Para comprender mejor lo que está sucediendo, a continuación hay un diagrama para aquellos que no están familiarizados con
reduce
(izquierda), también conocido como "doblar a la izquierda". A veces se dibuja con un valor inicial en lugar de k0 (no forma parte de la lista, se pasa a la función). Aquí,J
es nuestrajoin
función. Preprocesamos cada k n conlift(k)
.De hecho, esto es lo mismo que
functools.reduce
, pero nuestra función hace esto en todas las rutas de teclas del árbol.Demostración (que de lo contrario pondría en docstring):
Actuación:
... suspiro, no pienses que es mi culpa ...
[nota histórica sin importancia debido a problemas de moderación]
Con respecto al supuesto duplicado de Flatten, un diccionario de diccionarios (2 niveles de profundidad) de listas en Python :
La solución de esa pregunta se puede implementar en términos de esta haciendo
sorted( sum(flatten(...),[]) )
. Lo contrario no es posible: si bien es cierto que los valores deflatten(...)
se pueden recuperar del supuesto duplicado mapeando un acumulador de orden superior, no se pueden recuperar las claves. (editar: también resulta que la supuesta pregunta del propietario duplicado es completamente diferente, ya que solo trata con diccionarios con una profundidad de 2 niveles, aunque una de las respuestas en esa página ofrece una solución general).fuente
O si ya está usando pandas, puede hacerlo
json_normalize()
así:Salida:
fuente
Si está utilizando,
pandas
hay una función oculta enpandas.io.json._normalize
1 llamadanested_to_record
que hace esto exactamente.1 En versiones de pandas
0.24.x
y uso anteriorpandas.io.json.normalize
(sin el_
)fuente
from pandas.io.json._normalize import nested_to_record
. Observe el guión bajo (_
) antesnormalize
.0.25.x
, he actualizado la respuesta. :)Aquí hay una especie de implementación "funcional", de "línea única". Es recursivo y se basa en una expresión condicional y una comprensión dictada.
Prueba:
fuente
('hgf',2)
la segunda clave en tus lanzamientos de pruebaTypeError
+
operador. Para cualquier otra cosa, deberá adaptarseprefix + separator + k
a la llamada de función adecuada para componer los objetos.{'a_b':{'c':1}, 'a':{'b_c':2}}
{'name': 'Steven', 'children': [{'name': 'Jessica', 'children': []}, {'name': 'George', 'children': []}]}
Código:
Resultados:
Estoy usando python3.2, actualización para su versión de python.
fuente
lkey=''
en la definición de su función en lugar de cuando llame a la función. Ver otras respuestas al respecto.¿Qué tal una solución funcional y eficiente en Python3.5?
Esto es aún más eficaz:
En uso:
fuente
reduce
es genial en caso de que necesite reducir los diccionarios. Actualicé la respuesta. Debería verse un poco más pitón ahora.Esto no está restringido a los diccionarios, sino a todos los tipos de mapeo que implementan .items (). Además es más rápido ya que evita una condición if. Sin embargo, los créditos van a Imran:
fuente
d
no se trata de undict
tipo de mapeo personalizado que no se implementaitems
, su función fallará en ese momento. Por lo tanto, no funciona para todos los tipos de mapeo, sino solo para aquellos que lo implementanitems()
.items
? Me daría curiosidad ver uno.Mi solución Python 3.3 usando generadores:
fuente
Utilizando la recursividad, manteniéndola simple y legible por humanos:
La llamada es simple:
o
si queremos cambiar el separador predeterminado
Un pequeño desglose:
Cuando la función se llama por primera vez, se llama solo pasando el
dictionary
que queremos aplanar. Elaccumulator
parámetro está aquí para admitir la recursividad, que veremos más adelante. Entonces, instanciamosaccumulator
a un diccionario vacío donde colocaremos todos los valores anidados del originaldictionary
.A medida que iteramos sobre los valores del diccionario, construimos una clave para cada valor. El
parent_key
argumento seráNone
para la primera llamada, mientras que para cada diccionario anidado, contendrá la clave que lo señala, por lo que anteponemos esa clave.En caso de que el valor
v
alk
que apunta la clave sea un diccionario, la función se llama a sí misma, pasando el diccionario anidado, elaccumulator
(que se pasa por referencia, por lo que todos los cambios realizados en él se hacen en la misma instancia) y la clavek
para que podamos puede construir la clave concatenada. Observe lacontinue
declaración. Queremos omitir la siguiente línea, fuera delif
bloque, para que el diccionario anidado no termine en laaccumulator
tecla debajok
.Entonces, ¿qué hacemos en caso de que el valor
v
no sea un diccionario? Solo ponlo sin cambios dentro delaccumulator
.Una vez que terminamos, simplemente devolvemos el
accumulator
, dejandodictionary
intacto el argumento original .NOTA
Esto funcionará solo con diccionarios que tengan cadenas como claves. Funcionará con objetos hashables implementando el
__repr__
método, pero producirá resultados no deseados.fuente
Función simple para aplanar diccionarios anidados. Para Python 3, reemplace
.iteritems()
con.items()
La idea / requisito era: obtener diccionarios planos sin guardar las claves principales.
Ejemplo de uso:
Mantener las claves primarias también es simple.
fuente
Esto es similar a la respuesta de imran y ralu. No utiliza un generador, sino que emplea la recursión con un cierre:
fuente
_flatten_dict
nunca se devuelve, ni se espera que se devuelva alguna vez. Tal vez se pueda referir a ella como una subfunción o una función cerrada .La solución de Davoud es muy buena, pero no da resultados satisfactorios cuando el dictado anidado también contiene listas de dictados, pero su código debe adaptarse para ese caso:
fuente
type([])
para evitar una llamada de función para cada elemento deldict
.isinstance(v, list)
en su lugarLas respuestas anteriores funcionan realmente bien. Solo pensé que agregaría la función unflatten que escribí:
Nota: Esto no tiene en cuenta '_' que ya está presente en las teclas, al igual que las contrapartidas aplanados.
fuente
Aquí hay un algoritmo para un reemplazo elegante en el lugar. Probado con Python 2.7 y Python 3.5. Usando el carácter de punto como separador.
Ejemplo:
Salida:
Publiqué este código aquí junto con la
unflatten_json
función de coincidencia .fuente
Si desea aplanar el diccionario anidado y desea una lista de claves única, esta es la solución:
fuente
fuente
fuente
Estaba pensando en una subclase de UserDict para aplanar las teclas automáticamente.
Las ventajas es que las claves se pueden agregar sobre la marcha, o utilizando la instancia estándar dict, sin sorpresa:
fuente
Usando generadores:
fuente
type(i).__name__=='dict'
podría ser reemplazado contype(i) is dict
o quizás incluso mejorisinstance(d, dict)
(oMapping
/MutableMapping
).Usando dict.popitem () en una recursión directa similar a una lista anidada:
fuente
No es exactamente lo que pidió el OP, pero mucha gente viene aquí buscando formas de aplanar datos JSON anidados en el mundo real que pueden tener objetos y matrices json de valor clave anidado y objetos json dentro de las matrices, etc. JSON no incluye tuplas, por lo que no tenemos que preocuparnos por ellas.
Encontré una implementación del comentario de inclusión de lista de @roneo a la respuesta publicada por @Imran :
https://github.com/ScriptSmith/socialreaper/blob/master/socialreaper/tools.py#L8
Pruébalo:
Y eso hace el trabajo que necesito hacer: arrojo cualquier json complicado a esto y lo aplana para mí.
Todos los créditos a https://github.com/ScriptSmith .
fuente
¡De hecho, escribí un paquete llamado cherrypicker recientemente para tratar este tipo de cosas exactamente ya que tenía que hacerlo con tanta frecuencia!
Creo que el siguiente código te daría exactamente lo que buscas:
Puede instalar el paquete con:
... y hay más documentos y orientación en https://cherrypicker.readthedocs.io .
Otros métodos pueden ser más rápido, pero la prioridad de este paquete es hacer este tipo de tareas fáciles . Sin embargo, si tiene una gran lista de objetos para aplanar, también puede decirle a CherryPicker que use el procesamiento paralelo para acelerar las cosas.
fuente
Siempre prefiero acceder a los
dict
objetos a través de.items()
, por lo que para aplanar dictos utilizo el siguiente generador recursivoflat_items(d)
. Si desea tener dedict
nuevo, simplemente envuélvalo así:flat = dict(flat_items(d))
fuente
Variación de este Flatten diccionarios anidados, comprimiendo claves con max_level y reductor personalizado.
fuente
Si no le importa las funciones recursivas, aquí hay una solución. También me he tomado la libertad de incluir una exclusión parámetro de en caso de que desee mantener uno o más valores.
Código:
Uso:
Salida:
fuente
Probé algunas de las soluciones en esta página, aunque no todas, pero las que probé no pudieron manejar la lista anidada de dict.
Considere un dict como este:
Aquí está mi solución improvisada:
que produce:
Una solución improvisada y no es perfecta.
NOTA:
no mantiene dictos vacíos como el
address: {}
par k / v.no aplanará los dictados en tuplas anidadas, aunque sería fácil agregarlas usando el hecho de que las tuplas de pitón actúan de manera similar a las listas.
fuente
Simplemente use
python-benedict
, es una subclase dict que ofrece muchas características, incluido unflatten
método. Es posible instalarlo usando pip:pip install python-benedict
https://github.com/fabiocaccamo/python-benedict#flatten
fuente