Converter lista de dicionários em um DataFrame do pandas

657

Eu tenho uma lista de dicionários como este:

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

E eu quero transformar isso em pandas DataFrameassim:

      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

Nota: A ordem das colunas não importa.

Como transformar a lista de dicionários em um DataFrame do pandas, como mostrado acima?

appleLover
fonte

Respostas:

951

Suponha que dseja sua lista de ditados, simplesmente:

pd.DataFrame(d)
joris
fonte
3
Como alguém pode usar um dos pares de chave / valor como índice (por exemplo, tempo)?
CatsLoveJazz
6
@CatsLoveJazz Você pode fazer df = df.set_index('time')depois
joris
1
@CatsLoveJazz Não, isso não é possível ao converter de um ditado.
Joris
6
A partir do Pandas 0.19.2, não há menção a isso na documentação, pelo menos não na documentação de #pandas.DataFrame
Leo Alekseyev
1
Lembre-se de que, para um dicionário aninhado, '{"":{"...você usa a abordagem json_normalize, veja a resposta detalhada de @ cs95
Lorenz
136

Como converter uma lista de dicionários em um DataFrame do pandas?

As outras respostas estão corretas, mas pouco foi explicado em termos de vantagens e limitações desses métodos. O objetivo deste post será mostrar exemplos desses métodos em diferentes situações, discutir quando usar (e quando não usar) e sugerir alternativas.


DataFrame(),, DataFrame.from_records()e.from_dict()

Dependendo da estrutura e formato dos seus dados, há situações em que todos os três métodos funcionam, ou alguns funcionam melhor que outros, ou alguns não funcionam.

Considere um exemplo muito artificial.

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

Esta lista consiste em "registros" com todas as chaves presentes. Este é o caso mais simples que você pode encontrar.

# 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

Palavra no dicionário Orientações: orient='index'/'columns'

Antes de continuar, é importante fazer a distinção entre os diferentes tipos de orientações do dicionário e apoiar com os pandas. Existem dois tipos principais: "colunas" e "índice".

orient='columns'
Dicionários com a orientação "colunas" terão suas chaves correspondentes a colunas no DataFrame equivalente.

Por exemplo, dataacima está no oriente "colunas".

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

Nota: Se você estiver usando pd.DataFrame.from_records, a orientação será assumida como "colunas" (você não pode especificar o contrário) e os dicionários serão carregados de acordo.

orient='index'
Nesse sentido, presume-se que as chaves correspondam aos valores do índice. Esse tipo de dados é mais adequado 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

Esse caso não é considerado no OP, mas ainda é útil saber.

Definir índice personalizado

Se você precisar de um índice personalizado no DataFrame resultante, poderá configurá-lo usando o index=...argumento

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

Isso não é suportado por pd.DataFrame.from_dict.

Lidando com chaves / colunas ausentes

Todos os métodos funcionam imediatamente ao manipular dicionários com valores de chaves / colunas ausentes. Por exemplo,

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

Subconjunto de colunas de leitura

"E se eu não quiser ler em todas as colunas"? Você pode especificar isso facilmente usando o columns=...parâmetro

Por exemplo, no dicionário de exemplo data2acima, se você quiser ler apenas as colunas "A ',' D 'e' F ', poderá fazê-lo passando uma lista:

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

Isso não é suportado pelas pd.DataFrame.from_dict"colunas" orientais padrão.

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

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

Subconjunto de linhas de leitura

Não é suportado por nenhum desses métodos diretamente . Você precisará iterar sobre seus dados e executar uma exclusão reversa no local enquanto itera. Por exemplo, para extrair apenas a 0 º e 2 nd linhas de data2acima, você pode usar:

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

Panaceia: json_normalizepara dados aninhados

Uma alternativa forte e robusta aos métodos descritos acima é a json_normalizefunção que trabalha com listas de dicionários (registros) e, além disso, também pode lidar com dicionários aninhados.

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

Novamente, lembre-se de que os dados transmitidos json_normalizeprecisam estar no formato de lista de dicionários (registros).

Como mencionado, json_normalizetambém pode manipular dicionários aninhados. Aqui está um exemplo retirado da documentação.

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

Para mais informações sobre os argumentos metae record_path, consulte a documentação.


Resumindo

Aqui está uma tabela de todos os métodos discutidos acima, juntamente com os recursos / funcionalidades suportados.

insira a descrição da imagem aqui

* Use orient='columns'e transponha para obter o mesmo efeito que orient='index'.

cs95
fonte
8
Woah! Ok, isso junto com a postagem Mesclando SO pertence à API. Você deve contribuir com as documentações dos pandas, se ainda não o fez. Ted Petrou acabou de publicar um artigo no LinkedIn sobre a popularidade dos pandas no Stack Overflow e menciona que a falta de boa documentação contribui para o volume de perguntas aqui.
Scott Boston
2
@ScottBoston Você está absolutamente certo, já ouvi isso várias vezes agora que sei que é algo que devo pensar mais seriamente. Acho que a documentação pode ser uma ótima maneira de ajudar os usuários, mais do que postar perguntas que atingiriam apenas uma fração do mesmo público.
cs95
1
é resposta bom, eu acho que é hora de re-pé-no aqueles pergunta comum com a versão mais atual pandas :-)
YOBEN_S
3
@ely: isso nunca é motivo para não escrever respostas aqui, de qualquer maneira . Qualquer resposta pode ficar desatualizada, é para isso que votamos e existem diferentes perspectivas e objetivos diferentes aqui, e sempre é valioso ter maneiras diferentes de explicar a mesma coisa.
Martijn Pieters
1
@MartijnPieters Eu questiono e discordo de sua última afirmação, mas no geral eu concordo com você. Nem sempre é aditivo de valor coletar respostas diferentes para a mesma pergunta, especialmente se algumas das respostas são atualizações ou diferenças condicionais com base em outras respostas. Nos piores casos, essas respostas podem ser destrutivas quando agrupadas (em vez de usar a resposta mais atualizada para simplesmente editar a resposta mais antiga em um estado mais correto). Mas, novamente, concordo amplamente com você.
Ely
83

No pandas 16.2, eu tinha que fazer pd.DataFrame.from_records(d)para que isso funcionasse.

szeitlin
fonte
1
A coisa boa sobre esta abordagem é que ela também trabalha comdeque
MBZ
3
funciona bem com pandas 0.17.1com solução @joris
Anton Protopopov
2
Usinig 0.14.1 e solução @joris não funcionou, mas este fez
mchen
13
Em 0.18.1, é preciso usar from_recordsse os dicionários não tiverem todas as mesmas chaves.
fredcallaway
23

Você também pode usar pd.DataFrame.from_dict(d)como:

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
fonte
A pergunta é sobre a construção de um quadro de dados a partir de uma lista de dicts, não de um único, dictcomo você supôs em sua resposta.
a_guest
@a_guest verifique a resposta atualizada. Não estou assumindo.
shivsn
2

Eu sei que algumas pessoas vão se deparar com isso e não encontrar nada aqui ajuda. A maneira mais fácil de encontrar é assim:

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)

Espero que isso ajude alguém!

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

e chamada simples:

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

print(pd)
Günel
fonte
0

Pyhton3: A maioria das soluções listadas anteriormente funciona. No entanto, há casos em que o número da linha do quadro de dados não é necessário e cada linha (registro) deve ser gravada individualmente.

O método a seguir é útil nesse caso.

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
fonte
0

Para converter uma lista de dicionários em um DataFrame do pandas, você pode usar "anexar":

Temos um dicionário chamado dice dic tem 30 itens de lista ( list1, list2..., list30)

  1. Etapa 1: defina uma variável para manter seu resultado (ex: total_df )
  2. step2: inicialize total_dfcomlist1
  3. passo 3: use "for loop" para anexar todas as listas a 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
fonte
Qual é o benefício dessa abordagem sobre as abordagens descrito por @ cs95 em sua resposta detalhada de dois anos a respeito DataFrame(), DataFrame.from_records()e .from_dict()?
Jeremy Caney
Eu testei todos os métodos acima para um dicionário que possui 30 listas, só recebi a resposta usando a função Anexar.
Armin Ahmadi Nasab