Supposons que vous ayez un dictionnaire comme:
{'a': 1,
'c': {'a': 2,
'b': {'x': 5,
'y' : 10}},
'd': [1, 2, 3]}
Comment pourriez-vous l'aplatir en quelque chose comme:
{'a': 1,
'c_a': 2,
'c_b_x': 5,
'c_b_y': 10,
'd': [1, 2, 3]}
python
dictionary
A Timmes
la source
la source
Réponses:
En gros, de la même façon que vous aplatiriez une liste imbriquée, il vous suffit de faire le travail supplémentaire pour itérer le dict par clé / valeur, créer de nouvelles clés pour votre nouveau dictionnaire et créer le dictionnaire à l'étape finale.
la source
isinstance
par untry..except
bloc, cela fonctionnera pour tout mappage, même s'il n'est pas dérivé dedict
.collections.MutableMapping
le rendre plus générique. Mais pour Python <2.6,try..except
c'est probablement la meilleure option.if isinstance(v, collections.MutableMapping):
pourif v and isinstance(v, collections.MutableMapping):
new_key = parent_key + sep + k if parent_key else k
suppose que les clés sont toujours des chaînes, sinon cela augmenteraTypeError: cannot concatenate 'str' and [other] objects
. Cependant, vous pouvez résoudre ce problème en contraignant simplementk
string (str(k)
), ou en concaténant des clés dans un tuple au lieu d'une chaîne (les tuples peuvent également être des clés dict).L'affiche originale doit prendre en compte deux grandes considérations:
{'a_b':{'c':1}, 'a':{'b_c':2}}
entraînerait{'a_b_c':???}
. La solution ci-dessous élude le problème en renvoyant un itérable de paires.joinedKey = '_'.join(*keys)
, cela vous coûtera O (N ^ 2) temps d'exécution. Cependant, si vous êtes prêt à le direnextKey = previousKey+'_'+thisKey
, cela vous donne du temps O (N). La solution ci-dessous vous permet de faire les deux (puisque vous pouvez simplement concaténer toutes les clés, puis les post-traiter).( La performance est peu probable un problème, mais je vais élaborer sur le deuxième point au cas où quelqu'un d' autre soins. Dans la mise en œuvre cela, il y a de nombreux choix dangereux Si vous le faites de manière récursive et le rendement et re-rendement, ou quoi que ce soit équivalent qui touche nœuds plus d'une fois ( ce qui est assez facile à faire par hasard), vous êtes en train de faire potentiellement O (N ^ 2) travail plutôt que O (N). en effet , vous êtes peut - être une clé calcules
a
puisa_1
alorsa_1_i
..., puis le calcula
alorsa_1
alorsa_1_ii
..., mais en réalité, vous ne devriez pas avoir à calculer àa_1
nouveau. Même si vous ne le recalculez pas, le redonner (une approche "niveau par niveau") est tout aussi mauvais. Un bon exemple est penser à la performance sur{1:{1:{1:{1:...(N times)...{1:SOME_LARGE_DICTIONARY_OF_SIZE_N}...}}}}
)Voici une fonction que j'ai écrite
flattenDict(d, join=..., lift=...)
qui peut être adaptée à de nombreuses fins et qui peut faire ce que vous voulez. Malheureusement, il est assez difficile de créer une version paresseuse de cette fonction sans encourir les pénalités de performances ci-dessus (de nombreuses versions intégrées de python comme chain.from_iterable ne sont pas réellement efficaces, ce que je n'ai réalisé qu'après des tests approfondis de trois versions différentes de ce code avant de me décider celui-là).Pour mieux comprendre ce qui se passe, vous trouverez ci-dessous un diagramme pour ceux qui ne connaissent pas
reduce
(à gauche), autrement connu sous le nom de «repli à gauche». Parfois, il est dessiné avec une valeur initiale à la place de k0 (ne fait pas partie de la liste, passé dans la fonction). VoiciJ
notrejoin
fonction. Nous prétraitons chaque k n aveclift(k)
.C'est en fait la même chose que
functools.reduce
, mais où notre fonction le fait à tous les chemins de clé de l'arbre.Démonstration (que je mettrais autrement dans docstring):
Performance:
... soupir, ne pense pas que c'est de ma faute ...
[note historique sans importance en raison de problèmes de modération]
Concernant le prétendu duplicata de Flatten un dictionnaire de dictionnaires (2 niveaux de profondeur) de listes en Python :
La solution de cette question peut être mise en œuvre en fonction de celle-ci en faisant
sorted( sum(flatten(...),[]) )
. L'inverse n'est pas possible: s'il est vrai que les valeurs deflatten(...)
peuvent être récupérées à partir du prétendu doublon en mappant un accumulateur d'ordre supérieur, on ne peut pas récupérer les clés. (modifier: Il s'avère également que la question du prétendu propriétaire en double est complètement différente, en ce qu'elle ne traite que des dictionnaires exactement à 2 niveaux de profondeur, bien qu'une des réponses sur cette page donne une solution générale.)la source
Ou si vous utilisez déjà des pandas, vous pouvez le faire
json_normalize()
comme ceci:Production:
la source
Si vous utilisez,
pandas
il y a une fonction cachée danspandas.io.json._normalize
1 appeléenested_to_record
qui fait exactement cela.1 Dans les versions pandas
0.24.x
et les anciennes utilisationspandas.io.json.normalize
(sans le_
)la source
from pandas.io.json._normalize import nested_to_record
. Notez le trait de soulignement (_
) avantnormalize
.0.25.x
, j'ai mis à jour la réponse. :)Voici une sorte d'implémentation "fonctionnelle", "one-liner". Il est récursif et basé sur une expression conditionnelle et une compréhension de dict.
Tester:
la source
('hgf',2)
la 2ème clé dans vos lancers de testTypeError
+
opérateur. Pour toute autre chose, vous devrez vous adapterprefix + separator + k
à l'appel de fonction approprié pour composer les objets.{'a_b':{'c':1}, 'a':{'b_c':2}}
{'name': 'Steven', 'children': [{'name': 'Jessica', 'children': []}, {'name': 'George', 'children': []}]}
Code:
Résultats:
J'utilise python3.2, mise à jour pour votre version de python.
la source
lkey=''
dans votre définition de fonction plutôt que lors de l'appel de la fonction. Voir d'autres réponses à ce sujet.Que diriez-vous d'une solution fonctionnelle et performante en Python3.5?
C'est encore plus performant:
Utilisé:
la source
reduce
c'est génial au cas où vous auriez besoin de réduire les dictionnaires. J'ai mis à jour la réponse. Devrait avoir l'air un peu plus pythonique maintenant.Cela n'est pas limité aux dictionnaires, mais à tous les types de mappage qui implémentent .items (). De plus, c'est plus rapide car cela évite une condition if. Néanmoins, les crédits vont à Imran:
la source
d
n'est pas undict
type de mappage personnalisé mais qui n'implémente pasitems
, votre fonction échouerait immédiatement. Donc, cela ne fonctionne pas pour tous les types de mappage mais seulement ceux qui implémententitems()
.items
? Je serais curieux d'en voir un.Ma solution Python 3.3 utilisant des générateurs:
la source
En utilisant la récursivité, en la gardant simple et lisible par l'homme:
L'appel est simple:
ou
si nous voulons changer le séparateur par défaut.
Une petite panne:
Lorsque la fonction est appelée pour la première fois, elle est appelée uniquement en passant le que
dictionary
nous voulons aplatir. Leaccumulator
paramètre est là pour prendre en charge la récursivité, que nous verrons plus tard. Donc, nous instancionsaccumulator
dans un dictionnaire vide où nous mettrons toutes les valeurs imbriquées de l'originaldictionary
.Lorsque nous parcourons les valeurs du dictionnaire, nous construisons une clé pour chaque valeur. L'
parent_key
argument seraNone
pour le premier appel, tandis que pour chaque dictionnaire imbriqué, il contiendra la clé pointant vers lui, donc nous ajoutons cette clé.Dans le cas où la valeur vers laquelle pointe
v
la clék
est un dictionnaire, la fonction s'appelle elle-même, en passant le dictionnaire imbriqué, leaccumulator
(qui est passé par référence, donc toutes les modifications qui y sont apportées sont effectuées sur la même instance) et la clék
afin que nous peut construire la clé concaténée. Remarquez lacontinue
déclaration. Nous voulons sauter la ligne suivante, en dehors duif
bloc, afin que le dictionnaire imbriqué ne se retrouve pas dans laaccumulator
clé underk
.Alors, que faisons-nous si la valeur
v
n'est pas un dictionnaire? Mettez-le simplement inchangé dans le fichieraccumulator
.Une fois que nous avons terminé, nous retournons simplement le
accumulator
, en laissant l'dictionary
argument d' origine intact.REMARQUE
Cela fonctionnera uniquement avec les dictionnaires qui ont des chaînes comme clés. Il fonctionnera avec des objets hachables implémentant la
__repr__
méthode, mais produira des résultats indésirables.la source
Fonction simple pour aplatir les dictionnaires imbriqués. Pour Python 3, remplacez
.iteritems()
par.items()
L'idée / exigence était: Obtenir des dictionnaires plats sans garder les clés parentes.
Exemple d'utilisation:
La conservation des clés parentales est également simple.
la source
Ceci est similaire à la réponse d'imran et de ralu. Il n'utilise pas de générateur, mais utilise à la place la récursivité avec une fermeture:
la source
_flatten_dict
n'est jamais renvoyée et ne devrait jamais l'être. Elle peut peut-être être appelée à la place une sous - fonction ou une fonction incluse .La solution de Davoud est très agréable mais ne donne pas de résultats satisfaisants lorsque le dict imbriqué contient également des listes de dictionnaires, mais son code doit être adapté pour ce cas:
la source
type([])
pour éviter un appel de fonction pour chaque élément dudict
.isinstance(v, list)
placeLes réponses ci-dessus fonctionnent vraiment bien. Je pensais juste que j'ajouterais la fonction non aplatie que j'ai écrite:
Remarque: cela ne tient pas compte de '_' déjà présent dans les clés, tout comme les homologues aplatis.
la source
Voici un algorithme pour un remplacement sur place élégant. Testé avec Python 2.7 et Python 3.5. Utilisation du caractère point comme séparateur.
Exemple:
Production:
J'ai publié ce code ici avec la
unflatten_json
fonction de correspondance .la source
Si vous souhaitez mettre à plat un dictionnaire imbriqué et que toutes les clés uniques sont répertoriées, voici la solution:
la source
la source
la source
Je pensais à une sous-classe de UserDict pour aplatir automatiquement les touches.
Les avantages que les clés peuvent être ajoutées à la volée, ou en utilisant l'instanciation de dict standard, sans surprise:
la source
Utilisation de générateurs:
la source
type(i).__name__=='dict'
pourrait être remplacé partype(i) is dict
ou peut-être même mieuxisinstance(d, dict)
(ouMapping
/MutableMapping
).Utilisation de dict.popitem () dans une récursivité simple de type liste imbriquée:
la source
Pas exactement ce que l'OP a demandé, mais beaucoup de gens viennent ici à la recherche de moyens d'aplatir les données JSON imbriquées du monde réel qui peuvent avoir des objets et des tableaux json à clé-valeur imbriqués et des objets json dans les tableaux, etc. JSON n'inclut pas les tuples, nous n'avons donc pas à nous en préoccuper.
J'ai trouvé une implémentation du commentaire d' inclusion de liste par @roneo à la réponse publiée par @Imran :
https://github.com/ScriptSmith/socialreaper/blob/master/socialreaper/tools.py#L8
Essaye-le:
Et cela fait le travail dont j'ai besoin: je lance n'importe quel json compliqué et cela l'aplatit pour moi.
Tous les crédits à https://github.com/ScriptSmith .
la source
En fait, j'ai récemment écrit un paquet appelé cherrypicker pour traiter exactement ce genre de chose puisque je devais le faire si souvent!
Je pense que le code suivant vous donnerait exactement ce que vous recherchez:
Vous pouvez installer le package avec:
... et il y a plus de documents et de conseils sur https://cherrypicker.readthedocs.io .
D' autres méthodes peuvent être plus rapides, mais la priorité de ce paquet est de rendre ces tâches faciles . Si vous avez une longue liste d'objets à aplatir, vous pouvez également dire à CherryPicker d'utiliser le traitement parallèle pour accélérer les choses.
la source
Je préfère toujours accéder aux
dict
objets via.items()
, donc pour aplatir les dicts, j'utilise le générateur récursif suivantflat_items(d)
. Si vous aimez en avoir àdict
nouveau, enveloppez-le simplement comme ceci:flat = dict(flat_items(d))
la source
Variation de ce Dictionnaires imbriqués Flatten, compression des clés avec max_level et réducteur personnalisé.
la source
Si les fonctions récursives ne vous dérangent pas, voici une solution. J'ai également pris la liberté d'inclure un paramètre d' exclusion au cas où vous souhaiteriez conserver une ou plusieurs valeurs.
Code:
Usage:
Production:
la source
J'ai essayé certaines des solutions de cette page - mais pas toutes - mais celles que j'ai essayées n'ont pas réussi à gérer la liste imbriquée de dict.
Considérez un dict comme celui-ci:
Voici ma solution improvisée:
qui produit:
Une solution de fortune et ce n'est pas parfait.
REMARQUE:
il ne garde pas de dictionnaires vides tels que la
address: {}
paire k / v.il n'aplatira pas les dicts dans les tuples imbriqués - bien qu'il soit facile à ajouter en utilisant le fait que les tuples python agissent de la même manière que les listes.
la source
Il suffit de l'utiliser
python-benedict
, c'est une sous-classe de dict qui offre de nombreuses fonctionnalités, dont uneflatten
méthode. Il est possible de l'installer en utilisant pip:pip install python-benedict
https://github.com/fabiocaccamo/python-benedict#flatten
la source