Supposons que j'ai un dictionnaire imbriqué 'user_dict' avec la structure:
- Niveau 1: UserId (Long Integer)
- Niveau 2: Catégorie (chaîne)
- Niveau 3: Attributs assortis (flottants, entiers, etc.)
Par exemple, une entrée de ce dictionnaire serait:
user_dict[12] = {
"Category 1": {"att_1": 1,
"att_2": "whatever"},
"Category 2": {"att_1": 23,
"att_2": "another"}}
chaque élément de user_dict
a la même structure et user_dict
contient un grand nombre d'éléments que je veux nourrir à un pandas DataFrame, en construisant la série à partir des attributs. Dans ce cas, un index hiérarchique serait utile à cette fin.
Plus précisément, ma question est de savoir s'il existe un moyen d'aider le constructeur DataFrame à comprendre que la série doit être construite à partir des valeurs du «niveau 3» dans le dictionnaire?
Si j'essaye quelque chose comme:
df = pandas.DataFrame(users_summary)
Les éléments du "niveau 1" (les UserId) sont considérés comme des colonnes, ce qui est l'opposé de ce que je veux réaliser (avoir UserId comme index).
Je sais que je pourrais construire la série après avoir parcouru les entrées du dictionnaire, mais s'il existe un moyen plus direct, ce serait très utile. Une question similaire serait de demander s'il est possible de construire un pandas DataFrame à partir d'objets json répertoriés dans un fichier.
la source
Réponses:
Un pandas MultiIndex se compose d'une liste de tuples. L'approche la plus naturelle serait donc de remodeler votre dict d'entrée pour que ses clés soient des tuples correspondant aux valeurs multi-index dont vous avez besoin. Ensuite, vous pouvez simplement construire votre dataframe en utilisant
pd.DataFrame.from_dict
, en utilisant l'optionorient='index'
:user_dict = {12: {'Category 1': {'att_1': 1, 'att_2': 'whatever'}, 'Category 2': {'att_1': 23, 'att_2': 'another'}}, 15: {'Category 1': {'att_1': 10, 'att_2': 'foo'}, 'Category 2': {'att_1': 30, 'att_2': 'bar'}}} pd.DataFrame.from_dict({(i,j): user_dict[i][j] for i in user_dict.keys() for j in user_dict[i].keys()}, orient='index') att_1 att_2 12 Category 1 1 whatever Category 2 23 another 15 Category 1 10 foo Category 2 30 bar
Une approche alternative consisterait à créer votre dataframe en concaténant les dataframes des composants:
user_ids = [] frames = [] for user_id, d in user_dict.iteritems(): user_ids.append(user_id) frames.append(pd.DataFrame.from_dict(d, orient='index')) pd.concat(frames, keys=user_ids) att_1 att_2 12 Category 1 1 whatever Category 2 23 another 15 Category 1 10 foo Category 2 30 bar
la source
pd.concat
accepte un dictionnaire. Dans cet esprit, il est possible d'améliorer la réponse actuellement acceptée en termes de simplicité et de performance en utilisant une compréhension de dictionnaire pour construire un dictionnaire mappant des clés à des sous-trames.pd.concat({k: pd.DataFrame(v).T for k, v in user_dict.items()}, axis=0)
Ou,
pd.concat({ k: pd.DataFrame.from_dict(v, 'index') for k, v in user_dict.items() }, axis=0)
att_1 att_2 12 Category 1 1 whatever Category 2 23 another 15 Category 1 10 foo Category 2 30 bar
la source
12:{cat1:{cat11:{att1:val1,att2:val2}}}
. En d'autres termes: comment quelqu'un généraliserait-il la solution à un nombre non pertinent de catégories?json_normalize
. J'ai une autre réponse qui montre comment cela fonctionne.v
s'agit d'un seul entier par exemple. Connaissez-vous une alternative dans ce cas?J'avais donc l'habitude d'utiliser une boucle for pour parcourir le dictionnaire également, mais une chose que j'ai trouvée qui fonctionne beaucoup plus rapidement est de convertir en panneau puis en dataframe. Disons que vous avez un dictionnaire d
import pandas as pd d {'RAY Index': {datetime.date(2014, 11, 3): {'PX_LAST': 1199.46, 'PX_OPEN': 1200.14}, datetime.date(2014, 11, 4): {'PX_LAST': 1195.323, 'PX_OPEN': 1197.69}, datetime.date(2014, 11, 5): {'PX_LAST': 1200.936, 'PX_OPEN': 1195.32}, datetime.date(2014, 11, 6): {'PX_LAST': 1206.061, 'PX_OPEN': 1200.62}}, 'SPX Index': {datetime.date(2014, 11, 3): {'PX_LAST': 2017.81, 'PX_OPEN': 2018.21}, datetime.date(2014, 11, 4): {'PX_LAST': 2012.1, 'PX_OPEN': 2015.81}, datetime.date(2014, 11, 5): {'PX_LAST': 2023.57, 'PX_OPEN': 2015.29}, datetime.date(2014, 11, 6): {'PX_LAST': 2031.21, 'PX_OPEN': 2023.33}}}
La commande
pd.Panel(d) <class 'pandas.core.panel.Panel'> Dimensions: 2 (items) x 2 (major_axis) x 4 (minor_axis) Items axis: RAY Index to SPX Index Major_axis axis: PX_LAST to PX_OPEN Minor_axis axis: 2014-11-03 to 2014-11-06
où pd.Panel (d) [item] donne une trame de données
pd.Panel(d)['SPX Index'] 2014-11-03 2014-11-04 2014-11-05 2014-11-06 PX_LAST 2017.81 2012.10 2023.57 2031.21 PX_OPEN 2018.21 2015.81 2015.29 2023.33
Vous pouvez ensuite appuyer sur la commande to_frame () pour le transformer en dataframe. J'utilise aussi reset_index pour transformer les axes majeur et mineur en colonnes plutôt que de les avoir comme index.
pd.Panel(d).to_frame().reset_index() major minor RAY Index SPX Index PX_LAST 2014-11-03 1199.460 2017.81 PX_LAST 2014-11-04 1195.323 2012.10 PX_LAST 2014-11-05 1200.936 2023.57 PX_LAST 2014-11-06 1206.061 2031.21 PX_OPEN 2014-11-03 1200.140 2018.21 PX_OPEN 2014-11-04 1197.690 2015.81 PX_OPEN 2014-11-05 1195.320 2015.29 PX_OPEN 2014-11-06 1200.620 2023.33
Enfin, si vous n'aimez pas l'apparence du cadre, vous pouvez utiliser la fonction de transposition du panneau pour changer l'apparence avant d'appeler to_frame () voir la documentation ici http://pandas.pydata.org/pandas-docs/dev/generated /pandas.Panel.transpose.html
Juste à titre d'exemple
pd.Panel(d).transpose(2,0,1).to_frame().reset_index() major minor 2014-11-03 2014-11-04 2014-11-05 2014-11-06 RAY Index PX_LAST 1199.46 1195.323 1200.936 1206.061 RAY Index PX_OPEN 1200.14 1197.690 1195.320 1200.620 SPX Index PX_LAST 2017.81 2012.100 2023.570 2031.210 SPX Index PX_OPEN 2018.21 2015.810 2015.290 2023.330
J'espère que cela t'aides.
la source
Si quelqu'un souhaite obtenir la trame de données dans un "format long" (les valeurs de feuille ont le même type) sans multi-index, vous pouvez le faire:
pd.DataFrame.from_records( [ (level1, level2, level3, leaf) for level1, level2_dict in user_dict.items() for level2, level3_dict in level2_dict.items() for level3, leaf in level3_dict.items() ], columns=['UserId', 'Category', 'Attribute', 'value'] ) UserId Category Attribute value 0 12 Category 1 att_1 1 1 12 Category 1 att_2 whatever 2 12 Category 2 att_1 23 3 12 Category 2 att_2 another 4 15 Category 1 att_1 10 5 15 Category 1 att_2 foo 6 15 Category 2 att_1 30 7 15 Category 2 att_2 bar
(Je sais que la question d'origine veut probablement que (I.) ait les niveaux 1 et 2 comme multi-index et le niveau 3 comme colonnes et (II.) Demande d'autres moyens que l'itération sur les valeurs dans le dict. Mais j'espère que cette réponse est toujours pertinente et utile (I.): aux personnes comme moi qui ont essayé de trouver un moyen d'obtenir le dict imbriqué dans cette forme et Google ne renvoie que cette question et (II.): parce que d'autres réponses impliquent également une itération et je trouve ceci approche flexible et facile à lire; pas sûr de la performance, cependant.)
la source
En s'appuyant sur une réponse vérifiée, pour moi, cela a fonctionné le mieux:
ab = pd.concat({k: pd.DataFrame(v).T for k, v in data.items()}, axis=0) ab.T
la source