Criando um DataFrame Pandas vazio e preenchendo-o?

461

Estou começando nos documentos do DataFrame do pandas aqui: http://pandas.pydata.org/pandas-docs/stable/dsintro.html

Gostaria de preencher iterativamente o DataFrame com valores em um tipo de cálculo de série temporal. Então, basicamente, eu gostaria de inicializar o DataFrame com as colunas A, B e as linhas de registro de data e hora, todas as 0 ou todas as NaN.

Eu adicionaria valores iniciais e examinaria esses dados calculando a nova linha da linha antes, digamos row[A][t] = row[A][t-1]+1assim.

Atualmente, estou usando o código abaixo, mas acho que é meio feio e deve haver uma maneira de fazer isso com um DataFrame diretamente, ou apenas um modo melhor em geral. Nota: Estou usando o Python 2.7.

import datetime as dt
import pandas as pd
import scipy as s

if __name__ == '__main__':
    base = dt.datetime.today().date()
    dates = [ base - dt.timedelta(days=x) for x in range(0,10) ]
    dates.sort()

    valdict = {}
    symbols = ['A','B', 'C']
    for symb in symbols:
        valdict[symb] = pd.Series( s.zeros( len(dates)), dates )

    for thedate in dates:
        if thedate > dates[0]:
            for symb in valdict:
                valdict[symb][thedate] = 1+valdict[symb][thedate - dt.timedelta(days=1)]

    print valdict
Matthias Kauer
fonte
6
Nunca aumente um DataFrame! É sempre mais barato anexar a uma lista python e convertê-la em um DataFrame no final, tanto em termos de memória quanto de desempenho.
cs95 29/02
@ cs95 Qual é a diferença funcional entre .appendpd e anexar uma lista? Eu sei que .appendnos pandas copia todo o conjunto de dados para um novo objeto; os pythons acrescentam funcionam de maneira diferente?
Lamma 03/04
@ Lamma, por favor, encontre detalhes na minha resposta abaixo. Ao anexar ao df, um novo DataFrame é criado sempre na memória, em vez de usar o existente, o que é francamente um desperdício.
cs95 5/06

Respostas:

330

Aqui estão algumas sugestões:

Use date_rangepara o índice:

import datetime
import pandas as pd
import numpy as np

todays_date = datetime.datetime.now().date()
index = pd.date_range(todays_date-datetime.timedelta(10), periods=10, freq='D')

columns = ['A','B', 'C']

Nota: poderíamos criar um DataFrame ( NaNs) vazio simplesmente escrevendo:

df_ = pd.DataFrame(index=index, columns=columns)
df_ = df_.fillna(0) # with 0s rather than NaNs

Para fazer esses tipos de cálculos para os dados, use uma matriz numpy:

data = np.array([np.arange(10)]*3).T

Portanto, podemos criar o DataFrame:

In [10]: df = pd.DataFrame(data, index=index, columns=columns)

In [11]: df
Out[11]: 
            A  B  C
2012-11-29  0  0  0
2012-11-30  1  1  1
2012-12-01  2  2  2
2012-12-02  3  3  3
2012-12-03  4  4  4
2012-12-04  5  5  5
2012-12-05  6  6  6
2012-12-06  7  7  7
2012-12-07  8  8  8
2012-12-08  9  9  9
Andy Hayden
fonte
2
pd.date_range () não funciona para mim. Eu tentei com DateRange (do preenchimento automático do eclipse), mas isso funciona com strings como formato de data, certo? A abordagem geral funciona (eu mudei o índice para outra coisa).
Matthias Kauer
2
date_range é uma função de fábrica para a criação de índices de data e hora e era um novo recurso na 0.8.0 , eu recomendaria definitivamente atualizar para a versão estável mais recente (0.9.1); existem muitas correções de bugs e novos recursos. :)
Andy Hayden
26
Nas minhas experiências, criar um quadro de dados do tamanho necessário preenchido com NaNs e depois preencher valores é muito mais lento do que criar um quadro de dados com indexx 0dimensões ( columns = []) e anexar uma coluna a cada volta de um loop. Quero dizer df[col_name] = pandas.Series([...])em um loop percorrendo os nomes das colunas. No primeiro caso, não apenas a alocação de memória leva tempo, mas a substituição de NaNs por novos valores parece extremamente lenta.
deeenes 3/03/2015
5
@deeenes definitivamente. essa resposta provavelmente deve esclarecer isso - você raramente (se alguma vez) deseja criar um Dataframe vazio (de NaNs).
Andy Hayden
1
De acordo com esta resposta stackoverflow.com/a/30267881/2302569 Você precisa atribuir o resultado de fillna, ou passar param inplace = True
JayJay
169

Se você simplesmente deseja criar um quadro de dados vazio e preenchê-lo com alguns quadros de dados recebidos posteriormente, tente o seguinte:

newDF = pd.DataFrame() #creates a new dataframe that's empty
newDF = newDF.append(oldDF, ignore_index = True) # ignoring index is optional
# try printing some data from newDF
print newDF.head() #again optional 

Neste exemplo, estou usando este documento do pandas para criar um novo quadro de dados e, em seguida, usando o acréscimo para gravar no newDF com dados do oldDF.

Se eu tiver que continuar anexando novos dados a este newDF de mais de um oldDFs, apenas uso um loop for para iterar sobre pandas.DataFrame.append ()

geekidharsh
fonte
14
Observe que append(e da mesma forma concat) copia o conjunto de dados completo para um novo objeto toda vez; portanto, a iteração e o acréscimo podem e causarão um grande impacto no desempenho. para obter mais informações, consulte: pandas.pydata.org/pandas-docs/stable/merging.html
MoustafaAAtta
4
@MoustafaAAtta Quais são as alternativas para acrescentar dados iterativamente ao quadro de dados?
precisa saber é o seguinte
2
@MoustafaAAtta Fred respondeu neste post: stackoverflow.com/questions/10715965/… melhor neste ponto de vista?
precisa saber é o seguinte
@MoustafaAAtta, talvez você possa anexar apenas linhas a um quadro de dados, ele ainda criará um novo objeto, mas para conjuntos de dados menores, pode ser útil. pandas.pydata.org/pandas-docs/stable/user_guide/…
geekidharsh 28/01
135

A maneira certa de criar um DataFrame

TLDR; (basta ler o texto em negrito)

A maioria das respostas aqui mostrará como criar um DataFrame vazio e preenchê-lo, mas ninguém lhe dirá que é uma coisa ruim a se fazer.

Aqui está o meu conselho: espere até ter certeza de que possui todos os dados com os quais precisa trabalhar. Use uma lista para coletar seus dados e, em seguida, inicialize um DataFrame quando estiver pronto.

data = []
for a, b, c in some_function_that_yields_data():
    data.append([a, b, c])

df = pd.DataFrame(data, columns=['A', 'B', 'C'])

É sempre mais barato anexar a uma lista e criar um DataFrame de uma só vez do que criar um DataFrame vazio (ou um dos NaNs) e anexá-lo repetidamente. As listas também ocupam menos memória e são uma estrutura de dados muito mais leve para trabalhar , anexar e remover (se necessário).

A outra vantagem desse método é dtypesinferida automaticamente (em vez de atribuir objecta todos eles).

A última vantagem é que a RangeIndexé criada automaticamente para seus dados , portanto, é uma coisa a menos com que se preocupar (dê uma olhada nos métodos appende nos locmétodos abaixo, você verá elementos nos dois que exigem o tratamento adequado do índice).


Coisas que você NÃO deve fazer

appendou concatdentro de um loop

Aqui está o maior erro que eu já vi dos iniciantes:

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df = df.append({'A': i, 'B': b, 'C': c}, ignore_index=True) # yuck
    # or similarly,
    # df = pd.concat([df, pd.Series({'A': i, 'B': b, 'C': c})], ignore_index=True)

A memória é re-alocados para cada appendou concatoperação que você tem. Junte isso a um loop e você terá uma operação de complexidade quadrática . Na df.appendpágina do documento :

O acréscimo de linhas de forma iterativa a um DataFrame pode ser mais intensivo em termos computacionais do que uma única concatenação. Uma solução melhor é anexar essas linhas a uma lista e concatenar a lista com o DataFrame original de uma só vez.

O outro erro associado df.appendé que os usuários tendem a esquecer o acréscimo não é uma função no local , portanto, o resultado deve ser atribuído novamente. Você também precisa se preocupar com os tipos:

df = pd.DataFrame(columns=['A', 'B', 'C'])
df = df.append({'A': 1, 'B': 12.3, 'C': 'xyz'}, ignore_index=True)

df.dtypes
A     object   # yuck!
B    float64
C     object
dtype: object

Lidar com colunas de objetos nunca é uma coisa boa, porque os pandas não podem vetorizar operações nessas colunas. Você precisará fazer isso para corrigi-lo:

df.infer_objects().dtypes
A      int64
B    float64
C     object
dtype: object

loc dentro de um loop

Eu também vi locusado para acrescentar a um DataFrame que foi criado vazio:

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df.loc[len(df)] = [a, b, c]

Como antes, você não pré-alocou a quantidade de memória necessária a cada vez; portanto, a memória é re-crescida cada vez que você cria uma nova linha . É tão ruim quanto append, e ainda mais feio.

DataFrame vazio de NaNs

E então, está criando um DataFrame de NaNs, e todas as advertências associadas a ele.

df = pd.DataFrame(columns=['A', 'B', 'C'], index=range(5))
df
     A    B    C
0  NaN  NaN  NaN
1  NaN  NaN  NaN
2  NaN  NaN  NaN
3  NaN  NaN  NaN
4  NaN  NaN  NaN

Ele cria um DataFrame de colunas de objeto, como as outras.

df.dtypes
A    object  # you DON'T want this
B    object
C    object
dtype: object

O anexo ainda tem todos os problemas, conforme os métodos acima.

for i, (a, b, c) in enumerate(some_function_that_yields_data()):
    df.iloc[i] = [a, b, c]

A prova está no pudim

A temporização desses métodos é a maneira mais rápida de ver o quanto eles diferem em termos de memória e utilidade.

insira a descrição da imagem aqui

Código de benchmarking para referência.

cs95
fonte
6
Lista de acréscimo deve ser a melhor maneira para este tipo de pergunta
YOBEN_S
9
Isso precisa ser votado um milhão de vezes mais. Nunca aumente um quadro de dados!
Buggy
3
@ user3293236 Pena que você tem que começar de baixo cada vez que você responder a uma pergunta de idade;)
cs95
2
Esta é uma das coisas que eu mais odeio. Essas muitas vezes você vê o 𝒓𝒆𝒂𝒍 𝒄𝒐𝒓𝒓𝒆𝒄𝒕 𝒂𝒏𝒔𝒘𝒆𝒓 que fica em algum lugar com poucos votos e nunca aceito. Sinto falta do código com 𝚍𝚏 = 𝚙𝚍.𝙳𝚊𝚝𝚊𝙵𝚛𝚊𝚖𝚎 ([]) para criar um quadro de dados vazio do pandas. Promovendo esta resposta. Excelente explicação, @ cs95!
jonathan
1
Isso está literalmente na documentação. "Anexar linhas iterativamente a um DataFrame pode ser mais intensivo em termos computacionais do que uma única concatenada. Uma solução melhor é anexar essas linhas a uma lista e concatenar a lista com o DataFrame original de uma só vez." pandas.pydata.org/pandas-docs/version/0.21/generated/...
endolith
132

Inicializar quadro vazio com nomes de colunas

import pandas as pd

col_names =  ['A', 'B', 'C']
my_df  = pd.DataFrame(columns = col_names)
my_df

Adicionar um novo registro a um quadro

my_df.loc[len(my_df)] = [2, 4, 5]

Você também pode querer passar um dicionário:

my_dic = {'A':2, 'B':4, 'C':5}
my_df.loc[len(my_df)] = my_dic 

Anexe outro quadro ao seu quadro existente

col_names =  ['A', 'B', 'C']
my_df2  = pd.DataFrame(columns = col_names)
my_df = my_df.append(my_df2)

Considerações de desempenho

Se você estiver adicionando linhas dentro de um loop, considere problemas de desempenho. Nos primeiros 1000 registros, o desempenho "my_df.loc" é melhor, mas gradualmente se torna mais lento, aumentando o número de registros no loop.

Se você planeja fazer finas dentro de um loop grande (digamos 10M‌ de registros), é melhor usar uma mistura dessas duas; preencha um quadro de dados com iloc até que o tamanho fique em torno de 1000, depois anexe-o ao quadro de dados original e esvazie o quadro de dados temporário. Isso aumentaria seu desempenho em cerca de 10 vezes.

Afshin Amiri
fonte
my_df = my_df.append(my_df2)não funciona para mim, a menos que eu especifique ignore_index=True.
Nasif Imtiaz Ohi 01/06
0

Suponha um quadro de dados com 19 linhas

index=range(0,19)
index

columns=['A']
test = pd.DataFrame(index=index, columns=columns)

Manter a coluna A como constante

test['A']=10

Mantendo a coluna b como uma variável dada por um loop

for x in range(0,19):
    test.loc[[x], 'b'] = pd.Series([x], index = [x])

Você pode substituir o primeiro x pd.Series([x], index = [x])por qualquer valor

Ajay Ohri
fonte