Конвертировать список словарей в панду DataFrame


658

У меня есть список словарей, как это:

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

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

Как я могу превратить список словарей в pandas DataFrame, как показано выше?

Ответы:


951

Предположим, dэто ваш список диктов, просто:

pd.DataFrame(d)

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

6
@CatsLoveJazz Вы можете просто сделать df = df.set_index('time')потом
Joris

1
@CatsLoveJazz Нет, это невозможно при конвертации из dict.
Йорис

6
Начиная с Pandas 0.19.2, об этом нет упоминания в документации, по крайней мере, в документации дляpandas.DataFrame
Лев Алексеев,

1
Имейте в виду, что для вложенного словаря '{"":{"...вы используете подход json_normalize, см. Подробный ответ @ cs95
Лоренц

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'
Словари с ориентацией «столбцы» будут иметь ключи, соответствующие столбцам в эквивалентном кадре данных.

Например, 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: это никогда не причина, чтобы не писать ответы здесь, в любом случае . Любой ответ может устареть, вот за что мы голосуем, и здесь существуют разные точки зрения и разные цели, и всегда полезно иметь разные способы объяснения одного и того же.
Мартин Питерс

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 'не сработало, но это
сработало

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 проверьте обновленный ответ. Я не предполагаю.
Шивсн

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: большинство решений, перечисленных ранее, работают. Однако есть случаи, когда row_number элемента данных не требуется, и каждая строка (запись) должна записываться индивидуально.

Следующий метод полезен в этом случае.

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 для панд вы можете использовать «append»:

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

  1. step1: определить переменную для хранения вашего результата (например: total_df)
  2. шаг 2: инициализировать total_dfсlist1
  3. шаг 3: использовать «цикл» для добавления всех списков к 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.