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 è dtypes
automaticamente dedotto (piuttosto che assegnarlo object
a tutti).
L'ultimo vantaggio è che a RangeIndex
viene creato automaticamente per i tuoi dati , quindi è una cosa in meno di cui preoccuparsi (dai un'occhiata ai metodi poveri append
e di loc
seguito, vedrai elementi in entrambi che richiedono una gestione appropriata dell'indice).
Cose che NON dovresti fare
append
o concat
all'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 append
o concat
operazioni che avete. Abbina questo ad un loop e avrai un'operazione di complessità quadratica . Dalla df.append
pagina 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 loc
usato 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 append
e 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à.

Codice di riferimento per riferimento.