摆脱烦人的设置withcopywarning消息
介绍
熊猫中的索引操作非常灵活,因此,许多情况可能会大不相同,因此产生意外的结果。此外,很难预测何时提出了SettingWithCopyWarningis
,这是什么意思。我将显示几个不同的方案,以及每个操作如何影响您的代码。之后,我们将查看一个名为Copy on Write
的新功能,该功能可帮助您摆脱不一致之处和SettingWithCopyWarnings
。我们还将研究这可能如何影响性能和其他方法。
索引操作
让我们看一下索引操作当前如何在熊猫中工作。如果您已经熟悉索引操作,则可以跳到下一节。但是请注意,有很多情况具有不同形式的行为。确切的遗迹很难预测。
当父型数据框和新数据框的基础数据未共享时,熊猫中的操作会产生副本。视图是与父对象共享数据的对象。对视图的修改可能会影响父对象。
截至目前,一些索引操作返回副本,而另一些索引操作返回视图。即使对于经验丰富的用户,确切的行为也很难预测。过去,这对我来说是一个很大的烦恼。
让我们从具有两个列的数据框开始:
df = pd.DataFrame({"user_id": [1, 2, 3], "score": [10, 15, 20]})
a getItem 在数据框架或系列上的操作返回初始对象的子集。该子集可能由一组或一组列,一组或一组行或两者的混合物组成。 setItem 在数据框架或系列上的操作更新初始对象的子集。子集本身由呼叫的参数定义。
常规 getItem 在大多数情况下都提供了视图:
view = df["user_id"]
因此,新对象view
仍然引用父对象df
及其数据。因此,写入视图也将修改父对象。
view.iloc[0] = 10
此 setItem 操作不仅会更新我们的view
,还将更新df
。发生这种情况是因为两个对象之间共享了基础数据。
这是正确的,如果列user_id
仅在df
中出现一次。 user_id
重复后, getItem 操作将返回数据框架。这意味着返回的对象是副本而不是视图:
df = pd.DataFrame(
[[1, 10, 2], [3, 15, 4]],
columns=["user_id", "score", "user_id"],
)
not_a_view = df["user_id"]
not_a_view.iloc[0] = 10
setItem 操作不会更新df
。即使这是一个完全可以接受的操作,我们也获得了第一个SettingWithCopyWarning
。 getItem 操作本身具有更多的案例,例如列表键,例如df[["user_id"]]
,多索引 - 列等。我将在后续帖子中详细介绍,以查看执行索引操作及其行为的不同形式。
让我们看一个比单个 getItem 操作更复杂的情况:链接索引。链式索引意味着用布尔面膜进行过滤,然后是 getItem 操作或相反的操作。这是一步完成的。我们没有创建一个新变量来存储第一个操作的结果。
我们再次以常规数据帧开始:
df = pd.DataFrame({"user_id": [1, 2, 3], "score": [10, 15, 20]})
我们可以更新所有的分数大于15至15的user_ids
:
df["user_id"][df["score"] > 15] = 5
我们采用列user_id
并之后应用过滤器。这很好,因为列选择会创建视图和 setItem 操作更新所述视图。我们也可以切换两个操作:
df[df["score"] > 15]["user_id"] = 5
此执行顺序产生另一个SettingWithCopyWarning
。与我们之前的例子相反,什么也没有发生。 dataFrame df
未修改。这是一个无声的不合适。布尔掩码总是创建初始数据框架的副本。因此,初始 getItem 操作返回副本。返回值未分配给任何变量,只是临时结果。 SetItem操作更新此临时副本。结果,修改丢失。当列选择返回视图时,蒙版返回副本的事实是实现细节。理想情况下,此类实施细节不应可见。
这样做的另一种方法如下:
new_df = df[df["score"] > 15]
new_df["user_id"] = 10
此操作按预期更新new_df
,但无论如何都会显示SettingWithCopyWarning
,因为我们无法更新df
。在这种情况下,我们大多数人可能永远不想更新初始对象(例如df
),但是无论如何我们都会收到警告。根据我的经验,这导致不必要的副本语句散布在代码基础上。
这只是索引操作中当前不一致和烦恼的一小部分。
由于很难预测实际行为,因此这会迫使其他方法中的许多防御副本。例如,
- 列的下降
- 设置新索引
- 重置索引
所有复制基础数据。从实施的角度来看,这些副本是不需要的。这些方法可以很容易地返回视图,但是返回的视图将导致以后无法预测的行为。从理论上讲,一个 setItem 操作可以通过整个呼叫链传播,一次更新许多数据范围。
复制写
让我们看一下如何在Write(Cow)上使用新功能来帮助我们摆脱代码库中的这些不一致之处。牛意味着以任何方式源自另一个的数据框架或系列总是表现为副本。结果,我们只能通过修改对象本身来更改对象的值。牛取消更新数据框或与另一个数据框架或系列对象Inplope共享数据的系列。有了这些信息,我们可以再次查看我们的最初示例:
df = pd.DataFrame({"user_id": [1, 2, 3], "score": [10, 15, 20]})
view = df["user_id"]
view.iloc[0] = 10
getItem 操作提供了对df
及其数据的视图。 setItem 操作在将10
写入第一行之前会触发基础数据的副本。因此,操作不会修改df
。这种行为的一个优点是,我们不必担心user_id
可能被重复或使用df[["user_id"]]
而不是df["user_id"]
。所有这些案件的行为完全相同,没有显示令人讨厌的警告。
在更新对象的值之前触发副本具有性能含义。对于某些操作,这肯定会导致较小的放缓。另一方面,许多其他操作可以避免防御副本,从而极大地提高性能。以下操作都可以用牛返回视图:
- 删除列
- 设置新索引
- 重置索引
- 以及更多。
让我们考虑以下数据帧:
na = np.array(np.random.rand(1_000_000, 100))
cols = [f"col_{i}" for i in range(100)]
df = pd.DataFrame(na, columns=cols)
使用add_prefix
将给定的字符串(例如test
)添加到每个列名的开头:
df.add_prefix("test")
无母牛,这将在内部复制数据。仅查看操作时,这不是必需的。但是,由于返回视图可能会产生副作用,因此该方法返回副本。结果,操作本身非常慢:
482 ms ± 3.43 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
这需要很长时间。实际上,我们仅修改100个字符串文字,而无需触摸数据。在这种情况下,返回视图提供了显着的加速:
46.4 µs ± 1.04 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
同一操作运行多个数量级。更重要的是,使用牛时,add_prefix
的运行时间是常数,并且不取决于您的数据框架的大小。此操作是在熊猫的主要分支上进行的。
仅当两个不同的对象共享相同的基础数据时,副本才有必要。在上面的示例中,view
和df
都引用相同的数据。如果数据是一个DataFrame
对象的独有的,则不需要副本,我们可以继续修改数据INPOPH:
df = pd.DataFrame({"user_id": [1, 2, 3], "score": [10, 15, 20]})
df.iloc[0] = 10
在这种情况下, setItem 操作将继续在不触发副本的情况下继续操作。
因此,我们最初看到的所有不同场景现在都具有完全相同的行为。我们不必再担心细微的矛盾了。
当前有奇怪且难以预测行为的另一种情况是链接索引。在牛下链接索引从不工作。这是牛机制的直接结果。列的初始选择可能会返回视图,但是当我们执行后续setItem操作时会触发副本。幸运的是,我们可以轻松地修改代码以避免链接索引:
df["user_id"][df["score"] > 15] = 10
我们可以使用loc
立即进行这两个操作:
df.loc[df["score"] > 15, "user_id"] = 10
总结,我们创建的每个对象的行为就像父对象的副本。除了我们当前正在使用的对象外,我们不能意外地更新一个对象。
如何尝试
您可以从PANDAS 1.5.0开始尝试牛功能。开发仍在进行中,但是总体机制已经起作用。
您可以通过以下陈述在全球范围内设置牛标志:
pd.set_option("mode.copy_on_write", True)
pd.options.mode.copy_on_write = True
另外,您可以在本地启用:
with pd.option_context("mode.copy_on_write", True):
...
结论
我们已经看到,熊猫中的索引操作具有许多边缘病例和行为上的细微差异,难以预测。牛是一项旨在解决这些差异的新功能。它可能会根据我们试图使用数据来积极或负面影响性能。可以找到牛的完整建议。
感谢您的阅读。随时接触以分享您的想法和有关索引和复制的反馈。我将写下以下内容。专注于此主题的帖子和大熊猫一般。