Konwertuj listę słowników do pand DataFrame

656

Mam listę takich słowników:

[{'points': 50, 'time': '5:00', 'year': 2010}, 
{'points': 25, 'time': '6:00', 'month': "february"}, 
{'points':90, 'time': '9:00', 'month': 'january'}, 
{'points_h1':20, 'month': 'june'}]

I chcę zmienić to w pandy DataFrame taką:

      month  points  points_h1  time  year
0       NaN      50        NaN  5:00  2010
1  february      25        NaN  6:00   NaN
2   january      90        NaN  9:00   NaN
3      june     NaN         20   NaN   NaN

Uwaga: kolejność kolumn nie ma znaczenia.

Jak mogę zmienić listę słowników w pandę DataFrame, jak pokazano powyżej?

appleLover
źródło

Odpowiedzi:

949

Załóżmy, że dmasz listę dykt, po prostu:

pd.DataFrame(d)
joris
źródło
3
Jak można wykorzystać jedną z par klucz / wartość jako indeks (np. Czas)?
CatsLoveJazz
6
@CatsLoveJazz Możesz po prostu zrobić df = df.set_index('time')później
joris
1
@CatsLoveJazz Nie, nie jest to możliwe przy konwersji z nagrania.
joris
6
Począwszy od Pandas 0.19.2, nie ma o tym wzmianki w dokumentacji, a przynajmniej nie w dokumentach dlapandas.DataFrame
Leo Alekseyev
1
Pamiętaj, że w przypadku zagnieżdżonego słownika '{"":{"...używasz metody json_normalize, zapoznaj się ze szczegółową odpowiedzią @ cs95
Lorenz
136

Jak przekonwertować listę słowników na pandę DataFrame?

Pozostałe odpowiedzi są poprawne, ale niewiele wyjaśniono w kategoriach zalet i ograniczeń tych metod. Celem tego postu będzie pokazanie przykładów tych metod w różnych sytuacjach, omówienie, kiedy stosować (a kiedy nie) i zaproponowanie alternatyw.


DataFrame(), DataFrame.from_records()i.from_dict()

W zależności od struktury i formatu danych istnieją sytuacje, w których albo wszystkie trzy metody działają, albo niektóre działają lepiej niż inne, albo niektóre wcale nie działają.

Rozważ bardzo wymyślony przykład.

np.random.seed(0)
data = pd.DataFrame(
    np.random.choice(10, (3, 4)), columns=list('ABCD')).to_dict('r')

print(data)
[{'A': 5, 'B': 0, 'C': 3, 'D': 3},
 {'A': 7, 'B': 9, 'C': 3, 'D': 5},
 {'A': 2, 'B': 4, 'C': 7, 'D': 6}]

Ta lista składa się z „zapisów” z każdym kluczem. To najprostszy przypadek, jaki można napotkać.

# The following methods all produce the same output.
pd.DataFrame(data)
pd.DataFrame.from_dict(data)
pd.DataFrame.from_records(data)

   A  B  C  D
0  5  0  3  3
1  7  9  3  5
2  2  4  7  6

Słowo na temat orientacji słownika: orient='index'/'columns'

Zanim przejdziesz dalej, ważne jest, aby wprowadzić rozróżnienie między różnymi typami orientacji słownikowych i wspierać pandy. Istnieją dwa podstawowe typy: „kolumny” i „indeks”.

orient='columns'
Słowniki o orientacji „kolumny” będą miały klucze odpowiadające kolumnom w równoważnej ramce danych.

Na przykład datapowyżej znajduje się w orientacji „kolumny”.

data_c = [
 {'A': 5, 'B': 0, 'C': 3, 'D': 3},
 {'A': 7, 'B': 9, 'C': 3, 'D': 5},
 {'A': 2, 'B': 4, 'C': 7, 'D': 6}]

pd.DataFrame.from_dict(data_c, orient='columns')

   A  B  C  D
0  5  0  3  3
1  7  9  3  5
2  2  4  7  6

Uwaga: Jeśli używasz pd.DataFrame.from_records, zakłada się, że orientacja to „kolumny” (nie można określić inaczej), a słowniki zostaną odpowiednio załadowane.

orient='index'
Przy takim ukierunkowaniu zakłada się, że klucze odpowiadają wartościom indeksu. Tego rodzaju dane najlepiej nadają się pd.DataFrame.from_dict.

data_i ={
 0: {'A': 5, 'B': 0, 'C': 3, 'D': 3},
 1: {'A': 7, 'B': 9, 'C': 3, 'D': 5},
 2: {'A': 2, 'B': 4, 'C': 7, 'D': 6}}

pd.DataFrame.from_dict(data_i, orient='index')

   A  B  C  D
0  5  0  3  3
1  7  9  3  5
2  2  4  7  6

Ten przypadek nie jest rozpatrywany w PO, ale nadal warto o nim wiedzieć.

Ustawianie indeksu niestandardowego

Jeśli potrzebujesz wynikowego indeksu w wynikowej ramce danych, możesz ustawić go za pomocą index=...argumentu.

pd.DataFrame(data, index=['a', 'b', 'c'])
# pd.DataFrame.from_records(data, index=['a', 'b', 'c'])

   A  B  C  D
a  5  0  3  3
b  7  9  3  5
c  2  4  7  6

To nie jest obsługiwane przez pd.DataFrame.from_dict.

Radzenie sobie z brakującymi kluczami / kolumnami

Wszystkie metody działają od razu po wyjęciu z pudełka podczas obsługi słowników z brakującymi wartościami kluczy / kolumn. Na przykład,

data2 = [
     {'A': 5, 'C': 3, 'D': 3},
     {'A': 7, 'B': 9, 'F': 5},
     {'B': 4, 'C': 7, 'E': 6}]

# The methods below all produce the same output.
pd.DataFrame(data2)
pd.DataFrame.from_dict(data2)
pd.DataFrame.from_records(data2)

     A    B    C    D    E    F
0  5.0  NaN  3.0  3.0  NaN  NaN
1  7.0  9.0  NaN  NaN  NaN  5.0
2  NaN  4.0  7.0  NaN  6.0  NaN

Czytanie podzbioru kolumn

„Co jeśli nie chcę czytać w każdej kolumnie”? Możesz to łatwo określić za pomocą columns=...parametru.

Na przykład z powyższego słownika data2, jeśli chcesz czytać tylko kolumny „A”, „D” i „F”, możesz to zrobić, przekazując listę:

pd.DataFrame(data2, columns=['A', 'D', 'F'])
# pd.DataFrame.from_records(data2, columns=['A', 'D', 'F'])

     A    D    F
0  5.0  3.0  NaN
1  7.0  NaN  5.0
2  NaN  NaN  NaN

Nie jest to obsługiwane przez pd.DataFrame.from_dictdomyślny orient „kolumny”.

pd.DataFrame.from_dict(data2, orient='columns', columns=['A', 'B'])

ValueError: cannot use columns parameter with orient='columns'

Czytanie podzbioru wierszy

Nie jest obsługiwany przez każdy z tych metod bezpośrednio . Będziesz musiał wykonać iterację swoich danych i wykonać odwrotne usuwanie w miejscu podczas iteracji. Na przykład, aby wyodrębnić tylko 0 th i 2 nd wiersze z data2powyżej, można użyć:

rows_to_select = {0, 2}
for i in reversed(range(len(data2))):
    if i not in rows_to_select:
        del data2[i]

pd.DataFrame(data2)
# pd.DataFrame.from_dict(data2)
# pd.DataFrame.from_records(data2)

     A    B  C    D    E
0  5.0  NaN  3  3.0  NaN
1  NaN  4.0  7  NaN  6.0

Panacea: json_normalizedla danych zagnieżdżonych

Silną, niezawodną alternatywą dla metod opisanych powyżej jest json_normalizefunkcja, która działa z listami słowników (rekordów), a ponadto może obsługiwać zagnieżdżone słowniki.

pd.io.json.json_normalize(data)

   A  B  C  D
0  5  0  3  3
1  7  9  3  5
2  2  4  7  6

pd.io.json.json_normalize(data2)

     A    B  C    D    E
0  5.0  NaN  3  3.0  NaN
1  NaN  4.0  7  NaN  6.0

Ponownie pamiętaj, że przekazywane dane json_normalizemuszą mieć format listy słowników (rekordów).

Jak wspomniano, json_normalizemoże również obsługiwać zagnieżdżone słowniki. Oto przykład zaczerpnięty z dokumentacji.

data_nested = [
  {'counties': [{'name': 'Dade', 'population': 12345},
                {'name': 'Broward', 'population': 40000},
                {'name': 'Palm Beach', 'population': 60000}],
   'info': {'governor': 'Rick Scott'},
   'shortname': 'FL',
   'state': 'Florida'},
  {'counties': [{'name': 'Summit', 'population': 1234},
                {'name': 'Cuyahoga', 'population': 1337}],
   'info': {'governor': 'John Kasich'},
   'shortname': 'OH',
   'state': 'Ohio'}
]

pd.io.json.json_normalize(data_nested, 
                          record_path='counties', 
                          meta=['state', 'shortname', ['info', 'governor']])

         name  population    state shortname info.governor
0        Dade       12345  Florida        FL    Rick Scott
1     Broward       40000  Florida        FL    Rick Scott
2  Palm Beach       60000  Florida        FL    Rick Scott
3      Summit        1234     Ohio        OH   John Kasich
4    Cuyahoga        1337     Ohio        OH   John Kasich

Więcej informacji na temat argumentów metai znajduje record_pathsię w dokumentacji.


Zreasumowanie

Oto tabela wszystkich metod omówionych powyżej, wraz z obsługiwanymi funkcjami / funkcjonalnością.

wprowadź opis zdjęcia tutaj

* Użyj, orient='columns'a następnie transponuj, aby uzyskać taki sam efekt jak orient='index'.

cs95
źródło
8
Łał! Ok, to wraz ze scaleniem wpisu SO należą do interfejsu API. Powinieneś przyczynić się do dokumentacji pand, jeśli jeszcze tego nie zrobiłeś. Ted Petrou właśnie opublikował artykuł na LinkedIn o popularności pand na Stack Overflow i wspomina, że ​​brak dobrej dokumentacji przyczynia się do zwiększenia liczby pytań tutaj.
Scott Boston,
2
@ScottBoston Masz całkowitą rację, słyszałem już tyle razy, że wiem, że jest to coś, o czym powinienem poważniej przemyśleć. Myślę, że dokumentacja może być świetnym sposobem na pomoc użytkownikom, bardziej niż publikowanie pytań, które dotrą tylko do części tej samej grupy odbiorców.
cs95,
1
to ładna odpowiedź, myślę, że nadszedł czas, abyśmy ponownie odpowiedzieli na te często zadawane pytania w najnowszej wersji pand :-)
YOBEN_S,
3
@ely: w każdym razie nigdy nie jest to powód, aby nie pisać odpowiedzi . Każda odpowiedź może stać się nieaktualna, na to właśnie głosujemy, i istnieją tutaj różne perspektywy i różne cele, i zawsze warto mieć różne sposoby wyjaśniania tego samego.
Martijn Pieters
1
@MartijnPieters Pytam i nie zgadzam się z twoim ostatnim stwierdzeniem, ale ogólnie zgadzam się z tobą. Nie zawsze wartość dodana jest zestawiania różnych odpowiedzi na to samo pytanie, zwłaszcza jeśli niektóre z nich są aktualizacjami lub różnicami warunkowymi na podstawie innych odpowiedzi. W najgorszych przypadkach odpowiedzi te mogą być destrukcyjne dla wartości, gdy zostaną zestawione razem (w przeciwieństwie do użycia bardziej zaktualizowanej odpowiedzi do zwykłej edycji starszej odpowiedzi w bardziej poprawny stan). Ale znowu w dużej mierze się z tobą zgadzam.
ely
83

W pandach 16.2 musiałem zrobić, pd.DataFrame.from_records(d)żeby to zadziałało.

szeitlin
źródło
1
dobrą rzeczą w tym podejściu jest to, że działa również zdeque
MBZ
3
działa dobrze z 0.17.1pandami z rozwiązaniem @joris
Anton Protopopov
2
Rozwiązanie Usinig 0.14.1 i @joris nie działało, ale tak się
stało
13
W 0.18.1należy użyć, from_recordsjeśli wszystkie słowniki nie mają takich samych kluczy.
fredcallaway
23

Możesz także użyć pd.DataFrame.from_dict(d)jako:

In [8]: d = [{'points': 50, 'time': '5:00', 'year': 2010}, 
   ...: {'points': 25, 'time': '6:00', 'month': "february"}, 
   ...: {'points':90, 'time': '9:00', 'month': 'january'}, 
   ...: {'points_h1':20, 'month': 'june'}]

In [12]: pd.DataFrame.from_dict(d)
Out[12]: 
      month  points  points_h1  time    year
0       NaN    50.0        NaN  5:00  2010.0
1  february    25.0        NaN  6:00     NaN
2   january    90.0        NaN  9:00     NaN
3      june     NaN       20.0   NaN     NaN
Shivsn
źródło
Pytanie jest o konstruowaniu ramki danych z listy od dicts, a nie z jednego dict, jak zakłada się w swojej odpowiedzi.
a_guest
@ a_guest sprawdź zaktualizowaną odpowiedź. Nie zakładam.
shivsn
2

Wiem, że kilka osób się z tym spotka i nic tu nie pomoże. Najłatwiejszy sposób, jaki to zrobiłem, to:

dict_count = len(dict_list)
df = pd.DataFrame(dict_list[0], index=[0])
for i in range(1,dict_count-1):
    df = df.append(dict_list[i], ignore_index=True)

Mam nadzieję, że to komuś pomoże!

scottapotamus
źródło
1
list=[{'points': 50, 'time': '5:00', 'year': 2010}, 
{'points': 25, 'time': '6:00', 'month': "february"}, 
{'points':90, 'time': '9:00', 'month': 'january'}, 
{'points_h1':20, 'month': 'june'}]

i proste połączenie:

pd=DataFrame.from_dict(list, orient='columns', dtype=None)

print(pd)
Günel
źródło
0

Pyhton3: Większość wymienionych wcześniej rozwiązań działa. Są jednak przypadki, w których numer wiersza ramki danych nie jest wymagany, a każdy wiersz (rekord) musi być zapisany osobno.

W takim przypadku przydatna jest następująca metoda.

import csv

my file= 'C:\Users\John\Desktop\export_dataframe.csv'

records_to_save = data2 #used as in the thread. 


colnames = list[records_to_save[0].keys()] 
# remember colnames is a list of all keys. All values are written corresponding
# to the keys and "None" is specified in case of missing value 

with open(myfile, 'w', newline="",encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(colnames)
    for d in records_to_save:
        writer.writerow([d.get(r, "None") for r in colnames])
Soum
źródło
0

Aby przekonwertować listę słowników na pandę DataFrame, możesz użyć „append”:

Mamy słownik nazywa dici DIC posiada 30 Pozycji ( list1, list2, ..., list30)

  1. krok 1: zdefiniuj zmienną, aby zachować swój wynik (np .: total_df )
  2. krok 2: zainicjuj za total_dfpomocąlist1
  3. krok 3: użyj „for loop”, aby dołączyć wszystkie listy do total_df
total_df=list1
nums=Series(np.arange(start=2, stop=31))
for num in nums:
    total_df=total_df.append(dic['list'+str(num)])
Armin Ahmadi Nasab
źródło
Co jest zaletą tego podejścia na podejściach przedstawione przez @ CS95 w ich szczegółowej dwulatka odpowiedzi dotyczące DataFrame(), DataFrame.from_records()i .from_dict()?
Jeremy Caney
Przetestowałem wszystkie powyższe metody dla słownika, który ma 30 list, odpowiedź uzyskałem tylko za pomocą funkcji Append.
Armin Ahmadi Nasab