¿Crear un marco de datos de Pandas vacío y luego llenarlo?

463

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 columnas A, B y 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 un poco 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
Matthias Kauer
fuente
66
¡Nunca crezca un DataFrame! Siempre es más barato agregar a una lista de Python y luego convertirlo en un DataFrame al final, tanto en términos de memoria como de rendimiento.
cs95
@ 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 agregadas funcionan de manera diferente?
Lamma
@Lamma, encuentre 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 es francamente un desperdicio.
cs95

Respuestas:

330

Aquí hay un par de sugerencias:

Uso 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
Andy Hayden
fuente
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).
Matthias Kauer
2
date_range es una función de fábrica para crear índices de fecha y hora y era una nueva característica 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. :)
Andy Hayden
26
En mi experiencia, crear un marco de datos del tamaño necesario lleno de NaNs, y luego rellenar con valores es mucho más lento que crear un marco de datos con indexx 0dimensiones ( columns = []), y adjuntar una columna en cada vuelta de un ciclo. Quiero decir df[col_name] = pandas.Series([...])en un bucle iterando a través de los nombres de columna. En el primer caso, no solo la asignación de memoria lleva tiempo, sino que reemplazar NaNs con nuevos valores parece extremadamente lento.
deeenes
55
@deeenes definitivamente. esta respuesta probablemente debería aclararlo: muy rara vez (si alguna vez) desea crear un Marco de datos vacío (de NaNs).
Andy Hayden
1
Según esta respuesta stackoverflow.com/a/30267881/2302569 Debe asignar el resultado de fillna, o pasar el parámetro inplace = True
JayJay
169

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 estoy usando append para escribir en newDF con datos de oldDF.

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

geekidharsh
fuente
14
Tenga en cuenta que append(y de manera similar concat) copia el conjunto de datos completo a un nuevo objeto cada vez, por lo tanto, iterar y agregar puede causar un gran impacto en el rendimiento. Para obtener más información, consulte: pandas.pydata.org/pandas-docs/stable/merging.html
MoustafaAAtta
44
@MoustafaAAtta ¿Cuáles son las alternativas para agregar datos de forma iterativa al marco de datos?
MysteryGuy
2
@MoustafaAAtta ¿Es Fred la respuesta en esta publicación: stackoverflow.com/questions/10715965/... mejor en este punto de vista?
MysteryGuy
@MoustafaAAtta quizás 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/…
geekidharsh
136

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 dtypesse infiere automáticamente (en lugar de asignarlos objecta todos).

La última ventaja es que a RangeIndexse crea automáticamente para sus datos , por lo que es una cosa menos de qué preocuparse (eche un vistazo a los pobres appendy los locmétodos a continuación, verá elementos en ambos que requieren el manejo adecuado del índice).


Cosas que NO debes hacer

appendo concatdentro 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 appendu concatoperación que tiene. Combine esto con un bucle y tendrá una operación de complejidad cuadrática . Desde la df.appendpá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.appendes 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 locsolí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.

ingrese la descripción de la imagen aquí

Código de referencia para referencia.

cs95
fuente
66
La lista de anexos debería ser la mejor manera para este tipo de preguntas
YOBEN_S
99
Esto necesita ser votado un millón de veces más. ¡Nunca crezca un marco de datos!
Buggy
3
@ user3293236 Lástima que tenga que comenzar desde abajo cada vez que responda una pregunta anterior;)
cs95
2
Esta es una de las cosas que más odio. Esas muchas veces ves el 𝒓𝒆𝒂𝒍 𝒄𝒐𝒓𝒓𝒆𝒄𝒕 𝒂𝒏𝒔𝒘𝒆𝒓 que simplemente se queda abajo con pocos votos y nunca aceptado. Echo de menos el código con 𝚍𝚏 = 𝚙𝚍.𝙳𝚊𝚝𝚊𝙵𝚛𝚊𝚖𝚎 ([]) para crear un marco de datos de pandas vacío. Votando esta respuesta. Excelente explicación, @ cs95!
jonathan
1
Esto está literalmente en la documentación. "La adición iterativa de filas a un DataFrame puede ser más intensiva en cómputo 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.pydata.org/pandas-docs/version/0.21/generated/…
endolith
132

Inicializar 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 agrega filas dentro de un bucle, considere los problemas de rendimiento. 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 cosas finas dentro de un gran bucle (digamos 10M‌ de registros más o menos), es mejor que use una mezcla 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 tu rendimiento unas 10 veces.

Afshin Amiri
fuente
my_df = my_df.append(my_df2)no funciona para mí a menos que especifique ignore_index=True.
Nasif Imtiaz Ohi
0

Asumir un marco de datos con 19 filas

index=range(0,19)
index

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

Mantener la columna A como 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])con cualquier valor

Ajay Ohri
fuente