Error "El objeto 'DataFrame' no tiene el atributo 'añadir'"
Estoy intentando agregar un diccionario a un objeto DataFrame, pero aparece el siguiente error:
AttributeError: el objeto 'DataFrame' no tiene el atributo 'añadir'
Hasta donde yo sé, DataFrame tiene el método "añadir".
Fragmento de código:
df = pd.DataFrame(df).append(new_row, ignore_index=True)
Esperaba que el diccionario new_row
se agregara como una nueva fila.
¿Cómo puedo arreglarlo?
A partir de pandas 2.0, append
(anteriormente obsoleto) se eliminó .
En su lugar, debe utilizar concat
(para la mayoría de las aplicaciones):
df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
Como señaló @cottontail , también es posible usarlo loc
, aunque esto solo funciona si el nuevo índice aún no está presente en el DataFrame (normalmente, este será el caso si el índice es RangeIndex
:
df.loc[len(df)] = new_row # only use with a RangeIndex!
¿Por qué fue eliminado?
Con frecuencia vemos nuevos usuarios depandasIntente codificar como lo harían en Python puro. Se utilizan iterrows
para acceder a elementos en un bucle (vea aquí por qué no debería hacerlo), o append
de una manera similar a Python list.append
.
Sin embargo, como se señaló en el número 35407 de pandas , los pandas append
y list.append
en realidad no son lo mismo . list.append
está en su lugar, mientras que pandas append
crea un nuevo DataFrame:
Creo que deberíamos desaprobar Series.append y DataFrame.append. Están haciendo una analogía con list.append, pero es una mala analogía ya que el comportamiento no está (y no puede estar) implementado. Los datos del índice y los valores deben copiarse para crear el resultado.
Estos también son métodos aparentemente populares. DataFrame.append ocupa aproximadamente la décima página más visitada en nuestros documentos API.
A menos que me equivoque, siempre es mejor para los usuarios crear una lista de valores y pasarlos al constructor, o crear una lista de NDFrames seguidos de un único concat.
Como consecuencia, while list.append
se amortiza O(1) en cada paso del ciclo, pandas' append
is O(n)
, lo que lo hace ineficiente cuando se realiza una inserción repetida .
¿Qué pasa si necesito repetir el proceso?
Usar append
o concat
repetidamente no es una buena idea (esto tiene un comportamiento cuadrático ya que crea un nuevo DataFrame para cada paso).
En tal caso, los nuevos elementos deben recopilarse en una lista y, al final del ciclo, convertirse DataFrame
y eventualmente concatenarse con el original DataFrame
.
lst = []
for new_row in items_generation_logic:
lst.append(new_row)
# create extension
df_extended = pd.DataFrame(lst, columns=['A', 'B', 'C'])
# or columns=df.columns if identical columns
# concatenate to original
out = pd.concat([df, df_extended])
Descargo de responsabilidad: esta respuesta parece atraer popularidad, pero no se debe utilizar el enfoque propuesto . append
no se cambió a _append
, _append
es un método interno privado y append
se eliminó de la API de pandas. La afirmación "El append
método en pandas es similar a list.append en Python. Es por eso que el método append en pandas ahora se modifica a _append
". es completamente incorrecto. El encabezado _
solo significa una cosa: el método es privado y no está diseñado para usarse fuera del código interno de pandas.
En la nueva versión de Pandas , el append
método se cambia a _append
. Simplemente puedes usar _append
en lugar de append
, es decir, df._append(df2)
.
df = df1._append(df2,ignore_index=True)
¿Por qué se cambia?
El append
método en pandas es similar a list.append en Python. Es por eso que el método append en pandas ahora se modifica a _append
.
Si está ampliando un marco de datos en un bucle usando DataFrame.append
o concat
o loc
, considere reescribir su código para ampliar una lista de Python y construir un marco de datos una vez. A veces, es posible que ni siquiera necesites pd.concat
, es posible que solo necesites un constructor de DataFrame en una lista de dicts.
Un ejemplo bastante común de agregar nuevas filas a un marco de datos es extraer datos de una página web y almacenarlos en un marco de datos. En ese caso, en lugar de agregarlo a un marco de datos, literalmente simplemente reemplace el marco de datos con una lista y llame pd.DataFrame()
o pd.concat
una vez al final. Un ejemplo:
Entonces en lugar de:
df = pd.DataFrame() # <--- initial dataframe (doesn't have to be empty)
for url in ticker_list:
data = pd.read_csv(url)
df = df.append(data, ignore_index=True) # <--- enlarge dataframe
usar:
lst = [] # <--- initial list (doesn't have to be empty;
for url in ticker_list: # could store the initial df)
data = pd.read_csv(url)
lst.append(data) # <--- enlarge list
df = pd.concat(lst) # <--- concatenate the frames
La lógica de lectura de datos podría ser datos de respuesta de una API, datos extraídos de una página web, lo que sea, la refactorización del código es realmente mínima. En el ejemplo anterior, asumimos que lst
es una lista de marcos de datos, pero si fuera una lista de dictados/listas, etc., entonces podríamos usarla df = pd.DataFrame(lst)
en la última línea de código.
Dicho esto, si se va a agregar una sola filaloc
a un marco de datos, también podría funcionar.
df.loc[len(df)] = new_row
Con la loc
llamada, el marco de datos se amplía con la etiqueta de índice len(df)
, lo que sólo tiene sentido si el índice es RangeIndex
; RangeIndex
se crea de forma predeterminada si no se pasa un índice explícito al constructor del marco de datos.
Un ejemplo práctico:
df = pd.DataFrame({'A': range(3), 'B': list('abc')})
df.loc[len(df)] = [4, 'd']
df.loc[len(df)] = {'A': 5, 'B': 'e'}
df.loc[len(df)] = pd.Series({'A': 6, 'B': 'f'})
Como señaló @mozway, ampliar un marco de datos de pandas tiene una complejidad O(n^2) porque en cada iteración, se debe leer y copiar todo el marco de datos. El siguiente diagrama de rendimiento muestra la diferencia de tiempo de ejecución en relación con la concatenación realizada una vez. 1 Como puede ver, ambas formas de ampliar un marco de datos son mucho, mucho más lentas que ampliar una lista y construir un marco de datos una vez (por ejemplo, para un marco de datos con 10k filas, concat
en un bucle es aproximadamente 800 veces más lento y loc
en un bucle es aproximadamente 1600 veces más lento).
1 El código utilizado para producir el diagrama de rendimiento:
import pandas as pd
import perfplot
def concat_loop(lst):
df = pd.DataFrame(columns=['A', 'B'])
for dic in lst:
df = pd.concat([df, pd.DataFrame([dic])], ignore_index=True)
return df.infer_objects()
def concat_once(lst):
df = pd.DataFrame(columns=['A', 'B'])
df = pd.concat([df, pd.DataFrame(lst)], ignore_index=True)
return df.infer_objects()
def loc_loop(lst):
df = pd.DataFrame(columns=['A', 'B'])
for dic in lst:
df.loc[len(df)] = dic
return df
perfplot.plot(
setup=lambda n: [{'A': i, 'B': 'a'*(i%5+1)} for i in range(n)],
kernels=[concat_loop, concat_once, loc_loop],
labels= ['concat in a loop', 'concat once', 'loc in a loop'],
n_range=[2**k for k in range(16)],
xlabel='Length of dataframe',
title='Enlarging a dataframe in a loop',
relative_to=1,
equality_check=pd.DataFrame.equals);