Załóżmy, że mam zagnieżdżony słownik „user_dict” o strukturze:
- Poziom 1: UserId (długa liczba całkowita)
- Poziom 2: Kategoria (ciąg znaków)
- Poziom 3: różne atrybuty (liczby zmiennoprzecinkowe, liczby wewnętrzne itp.)
Na przykład wpis w tym słowniku wyglądałby tak:
user_dict[12] = {
"Category 1": {"att_1": 1,
"att_2": "whatever"},
"Category 2": {"att_1": 23,
"att_2": "another"}}
każdy element w user_dict
ma taką samą strukturę i user_dict
zawiera dużą liczbę elementów, które chcę przekazać do pandy DataFrame, konstruując serię z atrybutów. W tym przypadku przydatny byłby indeks hierarchiczny.
W szczególności, moje pytanie brzmi: czy istnieje sposób, aby pomóc konstruktorowi DataFrame zrozumieć, że seria powinna być zbudowana z wartości „poziomu 3” w słowniku?
Jeśli spróbuję czegoś takiego:
df = pandas.DataFrame(users_summary)
Elementy na „poziomie 1” (UserId's) są traktowane jako kolumny, co jest przeciwieństwem tego, co chcę osiągnąć (mieć UserId jako indeks).
Wiem, że mógłbym skonstruować serię po iteracji po wpisach w słowniku, ale jeśli istnieje bardziej bezpośredni sposób, byłoby to bardzo przydatne. Podobnym pytaniem byłoby pytanie, czy możliwe jest skonstruowanie pandy DataFrame z obiektów json wymienionych w pliku.
źródło
Odpowiedzi:
MultiIndex pandy składa się z listy krotek. Zatem najbardziej naturalnym podejściem byłoby przekształcenie dyktu wejściowego, tak aby jego klucze były krotkami odpowiadającymi wymaganym wartościom z wieloma indeksami. Następnie możesz po prostu skonstruować swoją ramkę danych za pomocą
pd.DataFrame.from_dict
, używając opcjiorient='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
Alternatywnym podejściem byłoby zbudowanie ramki danych poprzez konkatenację składowych ramek danych:
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
źródło
pd.concat
akceptuje słownik. Mając to na uwadze, możliwe jest ulepszenie obecnie przyjętej odpowiedzi pod względem prostoty i wydajności poprzez użycie rozumienia słownikowego do zbudowania słownika mapującego klucze do podramek.pd.concat({k: pd.DataFrame(v).T for k, v in user_dict.items()}, axis=0)
Lub,
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
źródło
12:{cat1:{cat11:{att1:val1,att2:val2}}}
. Innymi słowy: jak ktoś mógłby uogólnić rozwiązanie na nieistotną liczbę kategorii?json_normalize
. Mam inną odpowiedź, która pokazuje, jak to działa.v
na przykład jest pojedynczą liczbą całkowitą. Czy znasz alternatywę w takim przypadku?Więc użyłem pętli for również do iteracji w słowniku, ale jedna rzecz, która działa znacznie szybciej, to konwersja do panelu, a następnie do ramki danych. Powiedzmy, że masz słownik 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}}}
Komenda
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
gdzie pd.Panel (d) [item] daje ramkę danych
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
Następnie możesz nacisnąć polecenie to_frame (), aby przekształcić je w ramkę danych. Używam reset_index również do przekształcenia osi głównej i pomocniczej w kolumny, zamiast mieć je jako indeksy.
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
Wreszcie, jeśli nie podoba ci się wygląd ramki, możesz użyć funkcji transpozycji panelu, aby zmienić wygląd przed wywołaniem to_frame (), zobacz dokumentację tutaj http://pandas.pydata.org/pandas-docs/dev/generated /pandas.Panel.transpose.html
Jako przykład
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
Mam nadzieję że to pomoże.
źródło
W przypadku, gdy ktoś chce pobrać ramkę danych w „długim formacie” (wartości liści mają ten sam typ) bez multiindeksu, możesz to zrobić:
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
(Wiem, że pierwotne pytanie prawdopodobnie chce (I.) mieć Poziomy 1 i 2 jako multiindeks, a Poziom 3 jako kolumny, a (II.) Pyta o inne sposoby niż iteracja wartości w dyktandzie. Mam jednak nadzieję, że ta odpowiedź jest nadal aktualna i użyteczne (I.): dla ludzi takich jak ja, którzy próbowali znaleźć sposób, aby uzyskać zagnieżdżony dykt w tym kształcie, a Google zwraca tylko to pytanie i (II.): ponieważ inne odpowiedzi również obejmują pewną iterację i znajduję to podejście elastyczne i łatwe do odczytania; nie jestem jednak pewien wydajności).
źródło
Opierając się na zweryfikowanej odpowiedzi, dla mnie zadziałało najlepiej:
ab = pd.concat({k: pd.DataFrame(v).T for k, v in data.items()}, axis=0) ab.T
źródło