错误“‘DataFrame’对象没有属性‘append’”
- 2024-12-09 08:30:00
- admin 原创
- 159
问题描述:
我正在尝试将字典附加到 DataFrame 对象,但出现以下错误:
AttributeError:'DataFrame'对象没有属性'append'
据我所知,DataFrame 确实有“append”方法。
代码片段:
df = pd.DataFrame(df).append(new_row, ignore_index=True)
我希望字典new_row
能作为新行添加。
我该如何修复它?
解决方案 1:
从 pandas 2.0 开始,append
(之前已弃用)已被删除。
您需要使用concat
(对于大多数应用程序):
df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
正如 @cottontail 所指出的,也可以使用loc
,但这仅在新索引尚未存在于 DataFrame 中时才有效(通常,如果索引是,就会出现这种情况RangeIndex
:
df.loc[len(df)] = new_row # only use with a RangeIndex!
为什么它被删除了?
我们经常看到熊猫尝试像使用纯 Python 那样编写代码。他们习惯于iterrows
在循环中访问项目(请参阅此处了解为什么不应该这样做),或者append
以类似于 python 的方式访问项目list.append
。
但是,正如 pandas 问题#35407中所述,pandas 的append
和list.append
实际上并不是一回事。list.append
已经到位,而 pandasappend
会创建一个新的 DataFrame:
我认为我们应该弃用 Series.append 和 DataFrame.append。它们与 list.append 进行了类比,但这种类比并不恰当,因为这种行为并不适用(也不可能适用)。需要复制索引和值的数据才能创建结果。
这些显然也是很流行的方法。DataFrame.append 在我们的 API 文档中访问量排名第 10 位。
除非我弄错了,否则用户最好总是建立一个值列表并将它们传递给构造函数,或者建立一个 NDFrames 列表然后进行单个连接。
因此,虽然在循环的每一步中摊销的时间list.append
都是O(1) ,但pandas 的摊销append
时间是O(1) O(n)
,这在执行重复插入时效率低下。
如果我需要重复这个过程该怎么办?
重复使用append
或concat
并不是一个好主意(这具有二次行为,因为它为每个步骤创建一个新的 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])
解决方案 2:
免责声明:这个答案似乎很受欢迎,但不应使用建议的方法。append
未更改为_append
,_append
是一种私有内部方法,append
已从pandas API 中删除。 声称“ append
pandas 中的方法看起来类似于Python 中的list.append。 这就是 pandas 中的 append 方法现在被修改为 的原因_append
。”是完全错误的。 前导_
仅表示一件事:该方法是私有的,不打算在 pandas 的内部代码之外使用。
在新版本的Pandas中,该append
方法改为_append
。您可以直接使用_append
而不是append
,即df._append(df2)
。
df = df1._append(df2,ignore_index=True)
为什么会改变呢?
pandas 中的方法与 Python 中的list.appendappend
类似。因此, pandas 中的append方法现在被修改为。_append
解决方案 3:
DataFrame.append
如果您使用或concat
或循环扩大数据框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
使用:
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
数据读取逻辑可以是来自 API 的响应数据、从网页抓取的数据,无论什么,代码重构都非常少。在上面的例子中,我们假设这lst
是一个数据框列表,但如果它是一个字典/列表等列表,那么我们可以df = pd.DataFrame(lst)
在最后一行代码中使用它。
也就是说,如果要将一行附加到数据框,loc
也可以完成这项工作。
df.loc[len(df)] = new_row
通过调用loc
,数据框会用索引标签扩大len(df)
,这仅在索引为时才有意义RangeIndex
;RangeIndex
如果没有将显式索引传递给数据框构造函数,则默认创建。
一个工作示例:
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'})
正如 @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);