Перетворити список словників у пандус DataFrame


656

У мене є список таких словників:

[{'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'}]

І я хочу перетворити це на DataFrameтакі панди :

      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

Примітка: Порядок стовпців значення не має.

Як я можу перетворити список словників у панди DataFrame, як показано вище?

Відповіді:


949

Припустимо, dце ваш список диктів, просто:

pd.DataFrame(d)

3
Як можна використовувати одну із клавіш / значень пар як індекс (наприклад, час)?
CatsLoveJazz

6
@CatsLoveJazz Ви можете просто зробити це df = df.set_index('time')згодом
joris

1
@CatsLoveJazz Ні, це неможливо при перетворенні з диктату.
joris

6
Станом на Pandas 0.19.2 про це в документації не згадується, принаймні не в документах дляpandas.DataFrame
Лев Алексєєв

1
Зверніть увагу, що для вкладеного словника '{"":{"...ви використовуєте підхід json_normalize, див. Детальну відповідь @ cs95
Lorenz

136

Як перетворити список словників у пандус DataFrame?

Інші відповіді є правильними, але мало що було пояснено з точки зору переваг та обмежень цих методів. Метою цієї публікації буде показати приклади цих методів у різних ситуаціях, обговорити, коли їх використовувати (а коли не використовувати) та запропонувати альтернативи.


DataFrame(), DataFrame.from_records()І.from_dict()

Залежно від структури та формату ваших даних, бувають ситуації, коли або всі три методи працюють, або деякі працюють краще, ніж інші, або деякі взагалі не працюють.

Розглянемо дуже надуманий приклад.

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}]

Цей список складається з "записів" з усіма присутніми клавішами. Це найпростіший випадок, з яким ви могли зіткнутися.

# 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

Слово про орієнтації на словник: orient='index'/'columns'

Перш ніж продовжувати, важливо зробити відмінність між різними типами орієнтацій на словники та підтримати пандами. Існує два основних типи: "стовпці" та "індекс".

orient='columns'
Словники з орієнтацією "стовпці" матимуть свої ключі, що відповідають стовпцям у еквівалентній DataFrame.

Наприклад, dataвище в орієнтації «стовпчики».

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

Примітка. Якщо ви користуєтесь pd.DataFrame.from_records, орієнтація вважається "стовпчиками" (не можна вказати інше), і словники будуть завантажені відповідно.

orient='index'
При такому орієнтації вважається, що ключі відповідають значенням індексу. Цей вид даних найкраще підходить 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

Цей випадок не розглядається в ОП, але все ж корисно знати.

Встановлення спеціального індексу

Якщо вам потрібен спеціальний індекс на результуючій DataFrame, ви можете встановити його за допомогою index=...аргументу.

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

Це не підтримується pd.DataFrame.from_dict.

Робота зі відсутніми ключами / стовпцями

Усі методи спрацьовують під час роботи зі словниками із відсутніми значеннями ключів / стовпців. Наприклад,

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

Читання підмножини стовпців

"Що робити, якщо я не хочу читати в кожній колонці"? Ви можете легко вказати це за допомогою columns=...параметра.

Наприклад, із прикладу словника data2вище, якщо ви хочете читати лише стовпці "A", "D" і "F", ви можете зробити це, передавши список:

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

Це не підтримується, pd.DataFrame.from_dictякщо орієнтовані "стовпці" за замовчуванням.

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

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

Читання підмножини рядків

Не підтримується жодним із цих методів безпосередньо . Вам доведеться перебирати дані та виконувати зворотне видалення на місці під час ітерації. Наприклад, для витягування лише 0- го та 2- го рядків data2зверху, ви можете використовувати:

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

Панацея: json_normalizeдля вкладених даних

Сильною, надійною альтернативою описаним вище методам є json_normalizeфункція, яка працює зі списками словників (записів), а крім того може також обробляти вкладені словники.

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

Знову ж таки, майте на увазі, що передані дані json_normalizeповинні бути у форматі списків словників (записів).

Як було сказано, json_normalizeтакож можна обробляти вкладені словники. Ось приклад, взятий з документації.

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

Для отримання додаткової інформації про metaта record_pathаргументи, ознайомтеся з документацією.


Узагальнення

Ось таблиця всіх описаних вище методів, а також підтримувані функції / функціональність.

введіть тут опис зображення

* Використовуйте orient='columns'та переміщуйте, щоб отримати той же ефект, що і orient='index'.


8
Вуа! Гаразд, це разом із об'єднанням "SO" належать до API. Ви повинні внести свій внесок у документацію про панди, якщо ви цього ще не зробили. Тед Петру щойно опублікував статтю LinkedIn про популярність панд на Stack Overflow та згадує, що відсутність гарної документації сприяє обсягу питань тут.
Скотт Бостон

2
@ScottBoston Ви абсолютно праві, я чув, що достатньо разів, коли я знаю, що це те, що я повинен серйозніше задуматися. Я думаю, що документація може стати чудовим способом допомоги користувачам, більше ніж публікація питань, які охоплювали б лише частину тієї ж аудиторії.
cs95

1
це гарна відповідь, я думаю, що настав час, щоб ми переглянули це поширене питання під найновішою версією панди :-)
YOBEN_S

3
@ely: що ніколи не причина , щоб не писати відповіді тут, у всякому разі . Будь-яка відповідь може застаріти, саме за це ми і голосуємо, і тут існують різні перспективи та різні цілі, і завжди важливо мати різні способи пояснення одного і того ж.
Martijn Pieters

1
@MartijnPieters Я сумніваюся і не згоден з вашим останнім твердженням, але в цілому я з вами згоден. Не завжди ціннісна добавка поєднує різні відповіді на одне і те ж питання разом, особливо якщо частина відповідей є оновленнями або умовними відмінностями на основі інших відповідей. У гірших випадках ці відповіді можуть бути руйнівними за значенням, коли вони складаються разом (на відміну від використання більш оновленої відповіді для простого редагування старішої відповіді в більш правильний стан). Але знову ж таки, я багато в чому згоден з вами.
ely

83

У пандах 16.2 мені довелося це зробити, pd.DataFrame.from_records(d)щоб це спрацювало.


1
хороша річ у цьому підході, що він також працює зdeque
MBZ

3
прекрасно працює з пандами 0.17.1з рішенням @joris
Антон Протопопов,

2
Usinig 0.14.1 і рішення @joris 'не працює , але це зробив
mchen

13
В 0.18.1, треба використовувати, from_recordsякщо у всіх словниках немає однакових ключів.
fredcallaway

23

Ви також можете використовувати pd.DataFrame.from_dict(d)як:

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

Йдеться про побудову кадру даних з списку в dictсек, а не з одного , dictяк ви припустити , в своїй відповіді.
a_guest

@a_guest перевірте оновлену відповідь. Я не припускаю.
shivsn

2

Я знаю, що декілька людей натраплять на це, і нічого тут не допомагає. Найпростіший спосіб я знайшов це зробити так:

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)

Сподіваюся, це комусь допоможе!


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'}]

і простий дзвінок:

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

print(pd)

0

Pyhton3: Більшість перерахованих раніше рішень працює. Однак є випадки, коли номер_ рядка фрейму даних не потрібен і кожен рядок (запис) повинен писатися окремо.

Наступний метод корисний у такому випадку.

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])

0

Для перетворення списку словників у панди DataFrame ви можете використовувати "додати":

У нас є словник називається dicі DIC має 30 елементів списку ( list1, list2..., list30)

  1. step1: визначте змінну для збереження результату (наприклад: total_df )
  2. крок2: ініціалізувати total_df зlist1
  3. step3: використовувати "для циклу", щоб додати всі списки до 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)])

Що таке перевага такого підходу над підходами , викладене на @ cs95 в їх детальному два роки старого відповіді щодо DataFrame(), DataFrame.from_records()і .from_dict()?
Джеремі Кейні

Я перевірив усі вищевказані методи для словника, який містить 30 списків, відповідь я отримав лише за допомогою функції Додавання.
Армін Ахмаді Насаб
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.