¿Crear un Pandas DataFrame vacío y luego llenarlo?

647

Estoy comenzando con los documentos de Pandas DataFrame aquí: http://pandas.pydata.org/pandas-docs/stable/dsintro.html

Me gustaría llenar iterativamente el DataFrame con valores en un tipo de cálculo de serie temporal. Básicamente, me gustaría inicializar el DataFrame con las columnas A, B y las filas de marca de tiempo, todas 0 o todas NaN.

Luego agregaría valores iniciales y revisaría estos datos calculando la nueva fila de la fila anterior, digamos más row[A][t] = row[A][t-1]+1o menos.

Actualmente estoy usando el código de la siguiente manera, pero siento que es algo feo y debe haber una manera de hacer esto con un DataFrame directamente, o simplemente una mejor manera en general. Nota: estoy 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
3
  • 42
    ¡Nunca crezca un DataFrame! Siempre es más barato agregarlo a una lista de Python y luego convertirlo en un DataFrame al final, tanto en términos de memoria como de rendimiento.
    cs95
    29 feb.20 a las 12:04
  • @ cs95 ¿Qué es funcionalmente diferente entre .appenden pd y agregar una lista? Sé que .appenden pandas copia todo el conjunto de datos a un nuevo objeto ´, ¿las pitones anexar funcionan de manera diferente?
    Lamma
    3 abr.20 a las 9:16
  • 3
    @Lamma, encuentre los detalles en mi respuesta a continuación. Cuando se agrega a df, se crea un nuevo DataFrame cada vez en la memoria en lugar de usar el existente, lo que francamente es un desperdicio.
    cs95
    5 de junio de 2020 a las 2:38
516

¡NUNCA haga crecer un DataFrame!

TLDR; (just read the bold text)

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: acumule datos en una lista, no en un DataFrame.

Use una lista para recopilar sus datos, luego inicialice un DataFrame cuando esté listo. El formato de lista de listas o de lista de dictados funcionará, pd.DataFrameacepta ambos.

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

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

Ventajas de este enfoque:

  1. 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 los NaN) y agregarlo una y otra vez.

  2. 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).

  3. dtypesse infieren automáticamente (en lugar de asignarlos objecta todos).

  4. A RangeIndexse crea automáticamente para sus datos , en lugar de tener que ocuparse de asignar el índice correcto a la fila que está agregando en cada iteración.

Si aún no está convencido, esto también se menciona en la documentación :

Iteratively appending rows to a DataFrame can be more computationally intensive than a single concatenate. A better solution is to append those rows to a list and then concatenate the list with the original DataFrame all at once.

Pero, ¿qué pasa si mi función devuelve DataFrames más pequeños que necesito combinar en un DataFrame grande?

Eso está bien, aún puede hacer esto en tiempo lineal aumentando o creando una lista de Python de DataFrames más pequeños, luego llamando pd.concat.

small_dfs = []
for small_df in some_function_that_yields_dataframes():
    small_dfs.append(small_df)

large_df = pd.concat(small_dfs, ignore_index=True)

o, de forma más concisa:

large_df = pd.concat(
    list(some_function_that_yields_dataframes()), ignore_index=True)


Estas opciones son horribles

appendo concatdentro de un bucle

Aquí está el mayor error que he visto en 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 appendu concatoperación que tiene. Combine esto con un bucle y tendrá una operación de complejidad cuadrática .

El otro error asociado df.appendes 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 dtypes:

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 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 locusa 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 appendy 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 é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

La adición 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

Medir el tiempo de estos métodos es la forma más rápida de ver cuánto se diferencian en términos de memoria y utilidad.

ingrese la descripción de la imagen aquí

Código de evaluación comparativa para referencia.

6
  • 15
    Esto está literalmente en la documentación. "Agregar filas iterativamente 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 sola vez". pandas.pydata.org/pandas-docs/version/0.21/generated/… 11/08/19 a las 0:06
  • También "Nota Vale la pena señalar que concat () (y por lo tanto append ()) hace una copia completa de los datos, y que la reutilización constante de esta función puede generar un impacto significativo en el rendimiento. Si necesita utilizar la operación en varios conjuntos de datos, utilice una lista de comprensión ". pandas.pydata.org/pandas-docs/stable/user_guide/… 11/08/19 a las 0:07
  • Entonces, ¿qué hago cuando mis datos "entran" como listas 1d una a la vez y cada una representa una columna en un marco de datos? ¿Cómo los agrego antes de convertirlos en un marco de datos? Parece que list1.apped(list2)inserta una lista dentro de otra lista en lugar de agregar una columna. Gracias 11 mar.20 a las 19:59
  • 1
    @Confounded Ese es un problema diferente al que se pregunta aquí, pero debería estar bien asignar una columna a la vez a un marco de datos vacío. El problema surge con la adición sucesiva de filas.
    cs95
    16 de enero a las 7:34
  • 3
    respuesta asombrosa! 23 de febrero a las 10:57
407

Aquí hay un par de sugerencias:

Utilizar date_rangepara 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 NaNs) simplemente escribiendo:

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

Para hacer este tipo de cálculos para los datos, use 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
7
  • 2
    pd.date_range () no me funciona. Intenté con DateRange (del autocompletado de eclipse), pero eso funciona con cadenas como formato de fecha, ¿verdad? Sin embargo, el enfoque general funciona (cambié el índice a otra cosa). 15/12/12 a las 8:42
  • 3
    date_range es una función de fábrica para crear índices de fecha y hora y era una característica nueva en 0.8.0 , definitivamente recomendaría actualizar a la última versión estable (0.9.1), hay muchas correcciones de errores y nuevas características. :) 15/12/12 a las 9:52
  • 1
    Noté que hay un error tipográfico en el ejemplo de la declaración de importación. Dice: import datatimeDebería decir: import datetimeEsa puede ser la causa de su dificultad.
    user2899462
    20/10/2013 a las 6:17
  • 31
    En mi experiencia, crear un marco de datos del tamaño necesario lleno de NaN y luego llenarlo con valores es mucho más lento que crear un marco de datos con dimensiones indexx 0( columns = []) y adjuntar una columna en cada vuelta de un bucle. Me refiero df[col_name] = pandas.Series([...])a un bucle que recorre los nombres de las columnas. En el primer caso, no solo la asignación de memoria lleva tiempo, sino que reemplazar los NaN con nuevos valores parece extremadamente lento.
    deeenes
    03/03/15 a las 16:33
  • 5
    @deeenes definitivamente. esta respuesta probablemente debería aclararlo: muy rara vez (si es que alguna vez) desea crear un marco de datos vacío (de NaN). 3 mar.15 a las 17:33
209

Si simplemente desea crear un marco de datos vacío y llenarlo con algunos marcos de datos entrantes más tarde, 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 newDF con datos de oldDF.

Si tengo que seguir agregando nuevos datos en este nuevo DF de más de un archivo antiguo, solo uso un bucle for para iterar sobre pandas.DataFrame.append ()

4
  • 18
    Tenga en cuenta que append(y de manera similar concat) copia el conjunto de datos completo en un nuevo objeto cada vez, por lo tanto, la iteración y la adición pueden causar y causarán un impacto importante en el rendimiento. para obtener más información, consulte: pandas.pydata.org/pandas-docs/stable/merging.html 18 de septiembre de 2017 a las 12:21
  • 4
    @MoustafaAAtta ¿Cuáles son las alternativas para agregar datos iterativamente al marco de datos? 13/08/18 a las 11:24
  • 2
    @MoustafaAAtta ¿Es Fred la respuesta en esta publicación: stackoverflow.com/questions/10715965/… mejor en este punto de vista? 13/08/18 a las 11:29
  • @MoustafaAAtta tal vez pueda agregar solo filas a un marco de datos, aún creará un nuevo objeto, pero para conjuntos de datos más pequeños, podría ser útil. pandas.pydata.org/pandas-docs/stable/user_guide/… 28/01/20 a las 21:28
155

Inicializar el marco vacío con nombres de columna

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 desee 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 está agregando filas dentro de un bucle, considere los problemas de rendimiento. Aproximadamente para 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 adelgazamiento dentro de un bucle grande (digamos registros de 10M‌ más o menos), es mejor usar una mezcla de estos dos; llene un marco de datos con iloc hasta que el tamaño sea de alrededor de 1000, luego añádalo al marco de datos original y vacíe el marco de datos temporal. Esto aumentaría su rendimiento alrededor de 10 veces.

1
  • my_df = my_df.append(my_df2)no funciona para mí a menos que lo especifique ignore_index=True. 1/06/20 a las 16:12
0

Suponga un marco de datos con 19 filas

index=range(0,19)
index

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

Manteniendo la Columna A como una constante

test['A']=10

Mantener la columna b como una variable dada por un bucle

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

Puede reemplazar la primera x pd.Series([x], index = [x])por cualquier valor

0

Esta es mi forma de hacer un marco de datos dinámico a partir de varias listas con un bucle

x = [1,2,3,4,5,6,7,8]
y = [22,12,34,22,65,24,12,11]
z = ['as','ss','wa', 'ss','er','fd','ga','mf']
names = ['Bob', 'Liz', 'chop']

un bucle

def dataF(x,y,z,names):
    res = []

    for t in zip(x,y,z):
        res.append(t)

    return pd.DataFrame(res,columns=names)

Resultado

dataF(x,y,z,names)

ingrese la descripción de la imagen aquí