在 Pandas DataFrame 子集(副本)上设置值很慢

Posted

技术标签:

【中文标题】在 Pandas DataFrame 子集(副本)上设置值很慢【英文标题】:Setting values on Pandas DataFrame subset (copy) is slow 【发布时间】:2016-11-04 10:21:33 【问题描述】:
import timeit
import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.rand(10, 10))

dft = df[[True, False] * 5]
# df = dft
dft2 = dft.copy()

new_data = np.random.rand(5, 10)

print(timeit.timeit('dft.loc[:, :] = new_data', setup='from __main__ import dft, new_data', number=100))
print(timeit.timeit('dft2.loc[:, :] = new_data', setup='from __main__ import dft2, new_data', number=100))

在我的笔记本电脑上,dft(原始子集)中的设置值比dft2dft 的深层副本)中的设置值慢大约 160 倍。

为什么会这样?

编辑:移除关于代理对象的推测。

作为c。皮革建议,这可能是因为在副本 (dft) 与原始数据帧 (dft2) 上设置值时的代码路径不同。

额外问题:删除对原始 DataFrame df 的引用(通过取消注释 df = dft 行),在我的笔记本电脑上将速度因子降低到大约 2。知道为什么会这样吗?

【问题讨论】:

在底层,df[[True, False] * 5] 调用 Dataframe.__getitem__() 当索引器是一个列表时调用 Dataframe._getitem_array()。这反过来又调用Dataframe.take(),它有一个属性is_copy。我发现如果我运行df.take([0,2,4,6,8], is_copy=True),我的速度会比df.take([0,2,4,6,8], is_copy=False) 慢,在您的示例中 is_copy=True 产生与 dft 相同的运行时间,而 is_copy=False 产生与 dft2 相同的运行时间。因此,由于这个 is_copy 属性,可能在Dataframe.__setitem__ 期间出现减速。 然而,is_copy 属性的实际用途非常模糊,可能需要深入了解__setitem__。我认为您对返回的数组作为视图/代理的感觉很好,我认为这与此属性有关。 谢谢@c.leather。想知道这些检查是什么。 【参考方案1】:

这并不是关于 SO 的新问题。 This 和 this 是相关帖子。 This is the link to the current docs 解释它。

@c.leather 的 cmets 走在了正确的轨道上。问题是dft 是一个视图,而不是数据框df 的副本,如链接文章中所述。但是 pandas 无法知道它是否真的是副本以及操作是否安全,因此需要进行大量检查以确保执行分配是安全的,而这可以通过简单地避免制作副本。

这是一个相关问题,Github 有完整的讨论。我看过很多建议,我最喜欢的一个是文档应该鼓励df[[True,False] * 5].copy() 成语,可以称之为slice & copy 成语。

我找不到确切的检查,并且在 github 问题上,仅通过一些开发人员发布的一些推文提到了这种性能细微差别,并指出了这种行为。也许更多参与熊猫开发的人可以添加更多输入。

【讨论】:

问题不在于视图与复制,而在于速度差异的原因。我认为我对代理对象的猜测具有误导性(并且正在删除它)。感谢 github 页面的链接!

以上是关于在 Pandas DataFrame 子集(副本)上设置值很慢的主要内容,如果未能解决你的问题,请参考以下文章

Pandas:根据条件为多索引数据帧的子集设置值的正确方法

如何更新 MultiIndex pandas DataFrame 的子集

根据时间对 pandas DataFrame 进行子集化

Pandas DataFrame:SettingWithCopyWarning:试图在 DataFrame 的切片副本上设置一个值 [重复]

选择由 DatetimeIndex 索引的 Pandas DataFrame 的子集和时间戳列表

Python/Pandas:通过匹配的索引标准对 Dataframe 进行子集化