The Right Way ™ para crear un marco de datos
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.
Aquí está mi consejo: espere hasta estar seguro de tener todos los datos con los que necesita trabajar. Use una lista para recopilar sus datos, luego inicialice un DataFrame cuando esté listo.
data = []
for a, b, c in some_function_that_yields_data():
data.append([a, b, c])
df = pd.DataFrame(data, columns=['A', 'B', 'C'])
Siempre es más barato agregar a una lista y crear un DataFrame de una vez que crear un DataFrame vacío (o uno de los NaN) y agregarlo una y otra vez. Las listas también ocupan menos memoria y son una estructura de datos mucho más liviana para trabajar , agregar y eliminar (si es necesario).
La otra ventaja de este método dtypes
se infiere automáticamente (en lugar de asignarlos object
a todos).
La última ventaja es que a RangeIndex
se crea automáticamente para sus datos , por lo que es una cosa menos de qué preocuparse (eche un vistazo a los pobres append
y los loc
métodos a continuación, verá elementos en ambos que requieren el manejo adecuado del índice).
Cosas que NO debes hacer
append
o concat
dentro de un bucle
Aquí está el error más grande que he visto de los 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
u concat
operación que tiene. Combine esto con un bucle y tendrá una operación de complejidad cuadrática . Desde la df.append
página del documento :
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.
El otro error asociado df.append
es que los usuarios tienden a olvidar que agregar no es una función in situ , por lo que el resultado debe asignarse de nuevo. También debes 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 algo bueno, porque los pandas no pueden vectorizar las operaciones en esas columnas. Deberá hacer esto para solucionarlo:
df.infer_objects().dtypes
A int64
B float64
C object
dtype: object
loc
dentro de un bucle
También he visto que se loc
solía 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á creando un DataFrame de NaNs, y todas las advertencias asociadas con él.
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 columnas de objetos, como los demás.
df.dtypes
A object # you DON'T want this
B object
C object
dtype: object
Anexar todavía tiene todos los problemas como 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
El cronometraje de estos métodos es la forma más rápida de ver cuánto difieren en términos de su memoria y utilidad.
Código de referencia para referencia.