Error "El objeto 'DataFrame' no tiene el atributo 'añadir'"

Resuelto Maksimjeet Chowdhary asked hace 1 año • 0 respuestas

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_rowse agregara como una nueva fila.

¿Cómo puedo arreglarlo?

Maksimjeet Chowdhary avatar Apr 07 '23 14:04 Maksimjeet Chowdhary
Aceptado

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 iterrowspara acceder a elementos en un bucle (vea aquí por qué no debería hacerlo), o appendde una manera similar a Python list.append.

Sin embargo, como se señaló en el número 35407 de pandas , los pandas appendy list.appenden realidad no son lo mismo . list.appendestá en su lugar, mientras que pandas appendcrea 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.appendse amortiza O(1) en cada paso del ciclo, pandas' appendis O(n), lo que lo hace ineficiente cuando se realiza una inserción repetida .

¿Qué pasa si necesito repetir el proceso?

Usar appendo concatrepetidamente 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 DataFramey 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])
mozway avatar Apr 07 '2023 07:04 mozway

Descargo de responsabilidad: esta respuesta parece atraer popularidad, pero no se debe utilizar el enfoque propuesto . appendno se cambió a _append, _appendes un método interno privado y appendse eliminó de la API de pandas. La afirmación "El appendmé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 appendmétodo se cambia a _append. Simplemente puedes usar _appenden lugar de append, es decir, df._append(df2).

df = df1._append(df2,ignore_index=True)

¿Por qué se cambia?

El appendmé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.

Anubhav avatar Jun 11 '2023 06:06 Anubhav

Si está ampliando un marco de datos en un bucle usando DataFrame.appendo concato 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.concatuna 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 lstes 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 locllamada, 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; RangeIndexse 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'})

resultado

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, concaten un bucle es aproximadamente 800 veces más lento y locen un bucle es aproximadamente 1600 veces más lento).

diagrama perf

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);
cottontail avatar Apr 28 '2023 19:04 cottontail