2012-11-26 11 views
35

Supponiamo che ho un dizionario nidificato 'user_dict' con struttura:Construct panda dataframe da elementi in dizionario nidificato

Livello 1: UserId (Intero lungo)

Livello 2: Categoria (String)

Livello 3: attributi assortiti (galleggianti, interi, ecc ..)

Ad esempio, una voce di questo dizionario sarebbe:

user_dict[12] = { 
    "Category 1": {"att_1": 1, 
        "att_2": "whatever"}, 
    "Category 2": {"att_1": 23, 
        "att_2": "another"}} 

ciascuna voce in "user_dict" ha la stessa struttura e "user_dict" contiene un gran numero di elementi che voglio per alimentare ad un dataframe panda, costruendo la serie dal attributi. In questo caso un indice gerarchico sarebbe utile allo scopo.

In particolare, la mia domanda è se esiste un modo per aiutare il costruttore DataFrame a capire che la serie deve essere costruita dai valori del "livello 3" nel dizionario?

se provo qualcosa di simile:

df = pandas.DataFrame(users_summary) 

Le voci nel "livello 1" (l'utente id) sono presi come colonne, che è l'opposto di ciò che voglio raggiungere (avere id utente di come indice).

So che potrei costruire la serie dopo aver eseguito un'iterazione sulle voci del dizionario, ma se c'è un modo più diretto questo sarebbe molto utile. Una domanda simile si chiederebbe se sia possibile costruire un DataFrame panda da oggetti json elencati in un file.

risposta

51

Un pda MultiIndex consiste in un elenco di tuple. Quindi l'approccio più naturale sarebbe quello di rimodellare l'input in modo che le sue chiavi siano tuple corrispondenti ai valori multi-indice richiesti. Poi si può solo costruire il dataframe utilizzando pd.DataFrame.from_dict, utilizzando l'opzione orient='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 

Un approccio alternativo sarebbe quello di costruire la vostra dataframe fino concatenando le dataframes componenti:

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 
+1

Esiste un modo ragionevole per generalizzare questo funzionamento con elenchi di profondità arbitrari? per esempio. elenca una profondità arbitraria, in cui alcuni rami possono essere più corti di altri e viene utilizzato un Nessuno o un nan quando i rami più brevi non raggiungono la fine? – naught101

+3

Hai guardato il supporto di pandas json (io strumenti) e la normalizzazione? http://pandas.pydata.org/pandas-docs/dev/io.html#normalization –

+0

Salvami la vita !!!!!!!!!! Impara molto !! grazie – Wen

10

Così ho usato per usare un ciclo for per iterare attraverso il dizionario, ma una cosa che ho trovato che funziona molto più velocemente è la conversione in un pannello e quindi in un dataframe. Diciamo che avete un dizionario 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}}} 

Il comando

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 

dove pd.Panel (d) [voce] produce un dataframe

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 

È possibile poi ha colpito il comando to_frame() per trasformarlo in un dataframe. Uso anche reset_index per trasformare l'asse maggiore e minore in colonne piuttosto che averli come indici.

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 

Infine, se non ti piace il modo in cui il telaio sembra è possibile utilizzare la funzione di trasposizione del pannello per modificare l'aspetto prima di chiamare to_frame() vedere la documentazione qui http://pandas.pydata.org/pandas-docs/dev/generated/pandas.Panel.transpose.html

A titolo di esempio

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 

Spero che questo aiuti.

+0

Una soluzione così elegante! – vk1011

+0

Questo è stato più facile da capire. Grazie. – Moondra