Creare un DataFrame Pandas vuoto, quindi riempirlo?


463

Sto iniziando dai documenti Panda DataFrame qui: http://pandas.pydata.org/pandas-docs/stable/dsintro.html

Vorrei riempire iterativamente il DataFrame con i valori in un tipo di calcolo di serie temporali. Quindi, fondamentalmente, vorrei inizializzare DataFrame con le colonne A, B e le righe timestamp, tutte 0 o tutte NaN.

Quindi aggiungerei i valori iniziali e ripasserei questi dati calcolando la nuova riga dalla riga precedente, diciamo row[A][t] = row[A][t-1]+1o giù di lì.

Attualmente sto usando il codice come di seguito, ma penso che sia un po 'brutto e ci deve essere un modo per farlo direttamente con un DataFrame, o semplicemente un modo migliore in generale. Nota: sto usando 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

6
Non crescere mai un DataFrame! È sempre più economico aggiungere a un elenco Python e poi convertirlo in un DataFrame alla fine, sia in termini di memoria che di prestazioni.
cs95,

@ cs95 Qual è la differenza funzionale tra .appendin pd e l'aggiunta di un elenco? So che .appendin Panda copia l'intero set di dati in un nuovo oggetto ´, append pythons funziona diversamente?
Lamma,

@Lamma, puoi trovare i dettagli nella mia risposta di seguito. Quando si aggiunge a df, un nuovo DataFrame viene creato ogni volta in memoria invece di utilizzare quello esistente, il che è francamente uno spreco.
cs95,

Risposte:


330

Ecco un paio di suggerimenti:

Utilizzare date_rangeper l'indice:

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: potremmo creare un DataFrame vuoto (con NaN) semplicemente scrivendo:

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

Per eseguire questo tipo di calcoli per i dati, utilizzare una matrice numpy:

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

Quindi possiamo creare il 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

2
pd.date_range () non funziona per me. Ho provato con DateRange (dal completamento automatico di eclipse), ma funziona con le stringhe come formato data, giusto? L'approccio generale funziona comunque (ho cambiato l'indice in qualcos'altro).
Matthias Kauer,

2
date_range è una funzione di fabbrica per la creazione di indici datetime ed era una nuova funzionalità in 0.8.0 , consiglierei sicuramente l'aggiornamento all'ultima versione stabile (0.9.1) ci sono molte correzioni di bug e nuove funzionalità. :)
Andy Hayden,

26
Nelle mie esperienze, creare un frame di dati della dimensione necessaria riempito con NaN e quindi riempire di valori è molto più lento rispetto alla creazione di un frame di dati con dimensioni indexx 0( columns = []) e al collegamento di una colonna a ogni giro di un ciclo. Voglio dire df[col_name] = pandas.Series([...])in un ciclo che scorre i nomi delle colonne. Nel primo caso, non solo l'allocazione della memoria richiede tempo, ma la sostituzione di NaN con nuovi valori sembra estremamente lenta.
Deeenes

5
@deeenes sicuramente. questa risposta dovrebbe probabilmente renderlo più chiaro - molto raramente (se mai) vuoi creare un Dataframe vuoto (di NaN).
Andy Hayden,

1
In base a questa risposta stackoverflow.com/a/30267881/2302569 È necessario assegnare il risultato di fillna o passare param inplace = True
JayJay

169

Se vuoi semplicemente creare un frame di dati vuoto e riempirlo con alcuni frame di dati in arrivo in un secondo momento, prova questo:

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 

In questo esempio sto usando questo panda doc per creare un nuovo frame di dati e quindi usando append per scrivere nel newDF con i dati di oldDF.

Se devo continuare ad aggiungere nuovi dati a questo nuovo DF da più di un vecchio DF, uso semplicemente un ciclo for per scorrere su pandas.DataFrame.append ()


14
Si noti che append(e allo stesso modo concat) viene copiato il set di dati completo in un nuovo oggetto ogni volta, pertanto l'iterazione e l'aggiunta possono causare un grave calo delle prestazioni. per maggiori informazioni consultare: pandas.pydata.org/pandas-docs/stable/merging.html
MoustafaAtta

4
@MoustafaAAtta Quali sono le alternative per aggiungere iterativamente i dati al dataframe?
Mystery Acquista il

2
@MoustafaAAtta Fred risponde in questo post: stackoverflow.com/questions/10715965/… meglio sotto questo punto di vista?
Mystery Acquista il

@MoustafaAAtta puoi forse aggiungere solo righe a un frame di dati, creerà comunque un nuovo oggetto ma per set di dati più piccoli potrebbe essere utile. pandas.pydata.org/pandas-docs/stable/user_guide/…
geekidharsh

136

The Right Way ™ per creare un DataFrame

TLDR; (basta leggere il testo in grassetto)

La maggior parte delle risposte qui ti dirà come creare un DataFrame vuoto e compilarlo, ma nessuno ti dirà che è una brutta cosa da fare.

Ecco il mio consiglio: attendi fino a quando non sei sicuro di avere tutti i dati di cui hai bisogno per lavorare. Utilizzare un elenco per raccogliere i dati, quindi inizializzare un DataFrame quando si è pronti.

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 più economico aggiungere a un elenco e creare un DataFrame in una volta, piuttosto che creare un DataFrame vuoto (o uno dei NaN) e aggiungerlo più e più volte. Gli elenchi occupano anche meno memoria e sono una struttura di dati molto più leggera con cui lavorare , aggiungere e rimuovere (se necessario).

L'altro vantaggio di questo metodo è dtypesautomaticamente dedotto (piuttosto che assegnarlo objecta tutti).

L'ultimo vantaggio è che a RangeIndexviene creato automaticamente per i tuoi dati , quindi è una cosa in meno di cui preoccuparsi (dai un'occhiata ai metodi poveri appende di locseguito, vedrai elementi in entrambi che richiedono una gestione appropriata dell'indice).


Cose che NON dovresti fare

appendo concatall'interno di un ciclo

Ecco l'errore più grande che ho visto dai principianti:

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)

La memoria viene ri-assegnato per ogni appendo concatoperazioni che avete. Abbina questo ad un loop e avrai un'operazione di complessità quadratica . Dalla df.appendpagina del documento :

L'aggiunta facoltativa di righe a un DataFrame può essere più intensiva dal punto di vista computazionale rispetto a un singolo concatenato. Una soluzione migliore è aggiungere quelle righe a un elenco e quindi concatenare l'elenco con il DataFrame originale tutto in una volta.

L'altro errore associato df.appendè che gli utenti tendono a dimenticare che append non è una funzione sul posto , quindi il risultato deve essere assegnato nuovamente. Devi anche preoccuparti dei tipi:

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

Trattare con le colonne di oggetti non è mai una buona cosa, perché i panda non possono vettorializzare le operazioni su quelle colonne. Dovrai farlo per risolverlo:

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

loc all'interno di un ciclo

Ho anche visto locusato per aggiungere a un DataFrame che è stato creato vuoto:

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

Come in precedenza, non è stata pre-allocata la quantità di memoria necessaria ogni volta, quindi la memoria viene ricresciuta ogni volta che si crea una nuova riga . È altrettanto brutto appende persino più brutto.

DataFrame vuoto di NaNs

E poi, sta creando un DataFrame di NaN e tutte le avvertenze ad esso associate.

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

Crea un DataFrame di colonne di oggetti, come gli altri.

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

L'aggiunta ha ancora tutti i problemi come i metodi sopra.

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

La prova è nel budino

Il tempismo di questi metodi è il modo più veloce per vedere quanto differiscono in termini di memoria e utilità.

inserisci qui la descrizione dell'immagine

Codice di riferimento per riferimento.


6
L'elenco append dovrebbe essere il modo migliore per questo tipo di domanda
YOBEN_S

9
Questo deve essere votato un milione di volte in più. Non crescere mai un frame di dati!
Buggy

3
@ user3293236 Peccato che tu debba iniziare dal basso ogni volta che rispondi a una vecchia domanda;)
cs95

2
Questa è una delle cose che odio di più. Quelle molte volte vedi il 𝒓𝒆𝒂𝒍 𝒄𝒐𝒓𝒓𝒆𝒄𝒕 𝒂𝒏𝒔𝒘𝒆𝒓 che rimane da qualche parte in basso con pochi voti e mai accettato. Mi manca il codice con 𝚍𝚏 = 𝚙𝚍.𝙳𝚊𝚝𝚊𝙵𝚛𝚊𝚖𝚎 ([]) per creare un frame di dati panda vuoto. Valorizzare questa risposta. Ottima spiegazione, @ cs95!
Jonathan,

1
Questo è letteralmente nella documentazione. "L'aggiunta interattiva di righe a un DataFrame può essere più intensiva dal punto di vista computazionale rispetto a un singolo concatenato. Una soluzione migliore è quella di aggiungere quelle righe a un elenco e quindi concatenare l'elenco con il DataFrame originale tutto in una volta." pandas.pydata.org/pandas-docs/version/0.21/generated/...
endolith

132

Inizializza frame vuoto con nomi di colonna

import pandas as pd

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

Aggiungi un nuovo record a un frame

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

Potresti anche voler passare un dizionario:

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

Aggiungi un altro frame al tuo frame esistente

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

Considerazioni sulle prestazioni

Se si aggiungono righe all'interno di un ciclo, considerare i problemi di prestazioni. Per circa i primi 1000 record le prestazioni di "my_df.loc" sono migliori, ma gradualmente diventano più lente aumentando il numero di record nel ciclo.

Se hai intenzione di fare le cose all'interno di un grande loop (diciamo circa 10 milioni di record), stai meglio usando una combinazione di questi due; riempire un frame di dati con iloc fino a quando la dimensione non raggiunge circa 1000, quindi aggiungerlo al frame di dati originale e svuotare il frame di dati temporaneo. Ciò aumenterebbe le prestazioni di circa 10 volte.


my_df = my_df.append(my_df2)non funziona per me a meno che non specifichi ignore_index=True.
Nasif Imtiaz Ohi,

0

Assumi un frame di dati con 19 righe

index=range(0,19)
index

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

Mantenere la colonna A come costante

test['A']=10

Mantenere la colonna b come variabile data da un ciclo

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

È possibile sostituire il primo x in pd.Series([x], index = [x])con qualsiasi valore

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.