错误“‘DataFrame’对象没有属性‘append’”

Mak*_*ary 205 python attributeerror dataframe pandas

我试图将字典附加到 DataFrame 对象,但出现以下错误:

AttributeError:“DataFrame”对象没有属性“append”

据我所知,DataFrame确实有“append”方法。

代码片段:

df = pd.DataFrame(df).append(new_row, ignore_index=True)
Run Code Online (Sandbox Code Playgroud)

我期待字典new_row被添加为新行。

我该如何修复它?

moz*_*way 312

从 pandas 2.0 开始,append(之前已弃用)已被删除

您需要改用concat(对于大多数应用程序):

df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
Run Code Online (Sandbox Code Playgroud)

正如 @cottontail 所指出的,也可以使用loc,尽管这仅在新索引尚未存在于 DataFrame 中时才有效(通常,如果索引是 a ,则会出现这种情况RangeIndex

df.loc[len(df)] = new_row # only use with a RangeIndex!
Run Code Online (Sandbox Code Playgroud)

为什么被删除?

的新用户尝试像使用纯 Python 一样进行编码。它们用于iterrows访问循环中的项目(请参阅此处为什么不应该),或者append以类似于 python 的方式访问项目list.append

然而,正如 pandas 的问题#35407中所指出的,pandasappendlist.append确实不是同一件事list.append已就位,而 pandasappend创建了一个新的 DataFrame:

我认为我们应该弃用 Series.append 和 DataFrame.append。他们正在与 list.append 进行类比,但这是一个糟糕的类比,因为该行为尚未(也不可能)到位。需要复制索引和值的数据才能创建结果。

这些显然也是流行的方法。DataFrame.append 大约是我们的 API 文档中访问量排名第十的页面。

除非我弄错了,否则用户最好构建一个值列表并将它们传递给构造函数,或者构建一个 NDFrame 列表,然后进行单个连接。

因此, while在循环的每一步都摊销为 O(1),list.appendpandas 'appendO(n)在执行重复插入时效率低下

如果我需要重复该过程怎么办?

重复使用appendorconcat不是一个好主意(这具有二次行为,因为它为每个步骤创建一个新的 DataFrame)。

在这种情况下,新项目应收集在列表中,并在循环结束时转换为DataFrame并最终连接到原始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])
Run Code Online (Sandbox Code Playgroud)


小智 53

免责声明:这个答案似乎很受欢迎,但不应使用建议的方法。append没有更改_append_append是一个私有内部方法,append已从pandas API 中删除。声明appendpandas 中的方法看起来类似于Python 中的list.append。这就是为什么 pandas 中的append 方法现在修改为_append”。是完全错误的。前导_仅意味着一件事:该方法是私有的,不能在 pandas 的内部代码之外使用。


在新版本的Pandas中,append方法改为_append. 您可以简单地使用, ie_append来代替。appenddf._append(df2)

df = df1._append(df2,ignore_index=True)
Run Code Online (Sandbox Code Playgroud)

为什么会改变呢?

pandas 中的方法与 Python 中的list.appendappend类似。这就是为什么pandas中的append方法现在修改为._append

  • 这是一个私有方法,因此不属于官方 API 的一部分。它无法可靠地使用(该方法可能会更改或删除,恕不另行通知)。说实话,几乎每次有人问有关“append”的问题时,都有比使用它更好的选择。 (4认同)
  • 我在答案中添加了免责声明。我们现在开始看到在 SO 上使用 `_append` 的问题,这是对 API 的完全错误的使用。`_append` 无意成为公共 API 的一部分。`append` 已经导致了错误的代码,请不要鼓励其他人做更糟糕的事情...... (3认同)
  • @Nils我在[我的答案](/a/75956237/16343464)中解释了这一点,与“list.append”不同,“list.append”追加一个项目已就位并分摊,“DataFrame.append”为每个步骤创建一个新的DataFrame。因此,在循环中,复杂度是二次的(1+2+3+4+...)。 (3认同)
  • 这比切换到 concat 来修复旧代码更容易。泰 (2认同)
  • @gumdropsteve 更容易,直到内部 pandas 代码发生变化并且这会中断......此外,重要的部分是“append”效率低下。你应该抓住机会修复代码。如果您有不想更新的旧代码,请使用旧版本的库。 (2认同)

cot*_*ail 33

DataFrame.append如果您要使用orconcat或循环放大数据帧loc,请考虑重写代码以放大 Python 列表并构造一次数据帧。有时,您甚至可能不需要pd.concat,您可能只需要字典列表上的 DataFrame 构造函数。

将新行附加到数据帧的一个非常常见的示例是从网页中抓取数据并将其存储在数据帧中。在这种情况下,不要附加到数据帧,而是直接用列表替换数据帧并在最后调用一次pd.DataFrame()或一次。pd.concat一个例子:

所以而不是:

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
Run Code Online (Sandbox Code Playgroud)

使用:

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
Run Code Online (Sandbox Code Playgroud)

数据读取逻辑可以是来自 API 的响应数据、从网页抓取的数据等等,代码重构确实很少。在上面的示例中,我们假设它lst是一个数据帧列表,但如果它是一个字典/列表等列表,那么我们可以df = pd.DataFrame(lst)在最后一行代码中使用它。


也就是说,如果要将单行loc附加到数据帧,也可以完成这项工作。

df.loc[len(df)] = new_row
Run Code Online (Sandbox Code Playgroud)

通过loc调用,数据帧会使用索引标签进行放大len(df),仅当索引为 时才有意义RangeIndexRangeIndex如果未将显式索引传递给数据帧构造函数,则默认创建。

一个工作示例:

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'})
Run Code Online (Sandbox Code Playgroud)

结果

正如 @mozway 所指出的,放大 pandas 数据帧具有 O(n^2) 复杂度,因为在每次迭代中,必须读取和复制整个数据帧。以下 perfplot 显示了相对于一次串联的运行时差异。1如您所见,两种扩大数据帧的方法都比扩大列表并构建一次数据帧要慢得多(例如,对于具有 10k 行的数据帧,concat循环中大约慢 800 倍,loc循环中大约慢 1600 倍)慢几倍)。

性能图

1用于生成 perfplot 的代码:

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);
Run Code Online (Sandbox Code Playgroud)