Crear un Pandas DataFrame vacío y luego llenarlo
Estoy empezando por la documentación de pandas DataFrame aquí: Introducción a las estructuras de datos
Me gustaría llenar de forma iterativa el DataFrame con valores en un tipo de cálculo de serie temporal. Me gustaría inicializar el DataFrame con las columnas A, B y filas de marca de tiempo, todas 0 o todas NaN.
Luego agregaría valores iniciales y repasaría estos datos calculando la nueva fila a partir de la fila anterior, digamos más row[A][t] = row[A][t-1]+1
o menos.
Actualmente estoy usando el código que se muestra a continuación, pero siento que es un poco feo y debe haber una manera de hacer esto directamente con un DataFrame o simplemente una mejor manera en general.
import pandas as pd
import datetime as dt
import scipy as s
base = dt.datetime.today().date()
dates = [ base - dt.timedelta(days=x) for x in range(9, -1, -1) ]
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)]
¡NUNCA haga crecer un DataFrame en filas!
TLDR; (solo lea el texto en negrita)
La mayoría de las respuestas aquí le dirán cómo crear un DataFrame vacío y completarlo, pero nadie le dirá que es algo malo.
Este es mi consejo: acumule datos en una lista, no en un DataFrame.
Utilice una lista para recopilar sus datos y luego inicialice un DataFrame cuando esté listo. Funcionará un formato de lista de listas o de lista de dictadospd.DataFrame
, acepta ambos.
data = []
for row in some_function_that_yields_data():
data.append(row)
df = pd.DataFrame(data)
pd.DataFrame
convierte la lista de filas (donde cada fila es un valor escalar) en un DataFrame. Si su función produce DataFrame
s en su lugar, llame a pd.concat
.
Ventajas de este enfoque:
Siempre es más barato agregar a una lista y crear un DataFrame de una sola vez que crear un DataFrame vacío (o uno de NaN) y agregarlo una y otra vez.
Las listas también ocupan menos memoria y son una estructura de datos mucho más liviana con la que trabajar , agregar y eliminar (si es necesario).
dtypes
se infieren automáticamente (en lugar de asignarlosobject
a todos).A
RangeIndex
se crea automáticamente para sus datos , en lugar de tener que preocuparse de asignar el índice correcto a la fila que está agregando en cada iteración.
Si aún no estás convencido, esto también se menciona en la documentación :
Agregar filas de forma iterativa a un DataFrame puede ser más intensivo desde el punto de vista computacional que una sola concatenación. Una mejor solución es agregar esas filas a una lista y luego concatenar la lista con el DataFrame original de una vez.
pandas >= 2.0 Actualización: ¡ append
ha sido eliminada!
DataFrame.append
quedó obsoleto en la versión 1.4 y se eliminó por completo de la API de pandas en la versión 2.0.
Consulte los documentos sobre desaprobaciones , así como este problema de github que originalmente propuso su desaprobación.
Estas opciones son horribles
append
o concat
dentro de un bucle
Este es el mayor error que he visto en principiantes:
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 se reasigna para cada append
operación concat
que tenga. Combine esto con un bucle y tendrá una operación de complejidad cuadrática .
El otro error asociado df.append
es que los usuarios tienden a olvidar que agregar no es una función local , por lo que el resultado debe asignarse nuevamente. También tienes que preocuparte por los 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
Tratar con columnas de objetos nunca es bueno, porque los pandas no pueden vectorizar operaciones en esas columnas. Deberá llamar al infer_objects()
método para solucionarlo:
df.infer_objects().dtypes
A int64
B float64
C object
dtype: object
loc
dentro de un bucle
También he visto loc
que se usa para agregar a un DataFrame que se creó vacío:
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, no ha asignado previamente la cantidad de memoria que necesita cada vez, por lo que la memoria vuelve a crecer cada vez que crea una nueva fila . Es tan malo como append
y aún más feo.
Marco de datos vacío de NaN
Y luego, está la creación de un DataFrame de NaN y todas las advertencias asociadas con el mismo.
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 de object
columnas, como los demás.
df.dtypes
A object # you DON'T want this
B object
C object
dtype: object
Agregar todavía tiene todos los problemas que los métodos anteriores.
for i, (a, b, c) in enumerate(some_function_that_yields_data()):
df.iloc[i] = [a, b, c]
La prueba está en el pudín
Cronometrar estos métodos es la forma más rápida de ver en qué se diferencian en términos de memoria y utilidad.
Código de evaluación comparativa como referencia.
Aquí hay un par de sugerencias:
Utilice date_range
para el í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: podríamos crear un DataFrame vacío (con NaN
s) simplemente escribiendo:
df_ = pd.DataFrame(index=index, columns=columns)
df_ = df_.fillna(0) # With 0s rather than NaNs
Para realizar este tipo de cálculos para los datos, utilice una matriz NumPy :
data = np.array([np.arange(10)]*3).T
Por lo tanto podemos crear el 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
Si simplemente desea crear un marco de datos vacío y llenarlo con algunos marcos de datos entrantes más adelante, intente esto:
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
En este ejemplo, estoy usando este documento de pandas para crear un nuevo marco de datos y luego uso append para escribir en el nuevo DF con datos del antiguo DF.
Si tengo que seguir agregando nuevos datos a este nuevo DF de más de un antiguo DF, simplemente uso un bucle for para iterar sobre pandas.DataFrame.append()
Nota: append()
está en desuso desde la versión 1.4.0. Usar concat()
.
Inicializar marco vacío con nombres de columnas
import pandas as pd
col_names = ['A', 'B', 'C']
my_df = pd.DataFrame(columns = col_names)
my_df
Agregar un nuevo registro a un marco
my_df.loc[len(my_df)] = [2, 4, 5]
También es posible que desees pasar un diccionario:
my_dic = {'A':2, 'B':4, 'C':5}
my_df.loc[len(my_df)] = my_dic
Agregue otro marco a su marco existente
col_names = ['A', 'B', 'C']
my_df2 = pd.DataFrame(columns = col_names)
my_df = my_df.append(my_df2)
Consideraciones de rendimiento
Si agrega filas dentro de un bucle, considere los problemas de rendimiento. Alrededor de los primeros 1000 registros, el rendimiento de "my_df.loc" es mejor, pero gradualmente se vuelve más lento al aumentar el número de registros en el bucle.
Si planea hacer cosas dentro de un bucle grande (por ejemplo, 10 millones de registros aproximadamente), es mejor que utilice una combinación de estos dos; llene un marco de datos con iloc hasta que el tamaño sea de alrededor de 1000, luego agréguelo al marco de datos original y vacíe el marco de datos temporal. Esto aumentaría su rendimiento aproximadamente 10 veces.
Simplemente:
import numpy as np
import pandas as pd
df=pd.DataFrame(np.zeros([rows,columns])
Luego llénalo.