我喜欢panda,并且已经用了很多年了,我很自信我能很好地处理 Dataframe 子集,并适当地处理视图和副本(尽管我使用了很多Assert来确保)。我还知道有很多关于SettingWithCopyWarning的问题,例如How to deal with SettingWithCopyWarning in Pandas?,以及一些最近的很棒的指南,当它发生时,让你转过头来,例如Understanding SettingWithCopyWarning in pandas。
但我也知道一些具体的东西,比如引用this answer,已经不在最新的文档(0.22.0
)中了,而且这些年来很多东西都被弃用了(导致了一些不合适的旧SO答案),这些东西是continuing to change。
最近,在教Pandas完成了关于避免链式索引(以及使用.iloc
/.loc
)等非常基本的Python常识之后,我仍然在努力提供 * 一般经验法则 ,以了解何时注意SettingWithCopyWarning
是重要的(例如,何时忽略它是安全的)。
我个人发现,根据一些规则(例如切片或布尔运算)对 Dataframe 进行子集化,然后修改该子集( 独立于原始 Dataframe *)的特定模式比文档建议的操作要常见得多。在这种情况下,我们希望 * 修改副本而不是原始 Dataframe *,警告对新手来说是令人困惑/恐惧的。
我知道提前知道什么时候返回一个视图和一个副本不是件小事,例如。
What rules does Pandas use to generate a view vs a copy?
Checking whether data frame is copy or view in Pandas
因此,我在寻找一个更一般(初学者友好)的问题的答案:对子集化的 Dataframe 执行操作何时会影响创建该 Dataframe 的原始 Dataframe ,何时它们是独立的?。
我在下面创建了一些我认为合理的用例,但我不确定是否有我遗漏的“要点”,或者是否有任何更简单的方法来思考/检查这一点。我希望有人能证实我对以下用例的直觉是正确的,因为它们与我上面的问题有关。
import pandas as pd
df1 = pd.DataFrame({'A':[2,4,6,8,10],'B':[1,3,5,7,9],'C':[10,20,30,40,50]})
1)警告:否
原始变更:没有
# df1 will be unaffected because we use .copy() method explicitly
df2 = df1.copy()
#
# Reference: docs
df2.iloc[0,1] = 100
2)警告:是(我不太明白为什么)
原始变更:没有
# df1 will be unaffected because .query() always returns a copy
#
# Reference:
# https://stackoverflow.com/a/23296545/8022335
df2 = df1.query('A < 10')
df2.iloc[0,1] = 100
3)警告:是
原始变更:没有
# df1 will be unaffected because boolean indexing with .loc
# always returns a copy
#
# Reference:
# https://stackoverflow.com/a/17961468/8022335
df2 = df1.loc[df1['A'] < 10,:]
df2.iloc[0,1] = 100
4)警告:否
原始变更:没有
# df1 will be unaffected because list indexing with .loc (or .iloc)
# always returns a copy
#
# Reference:
# Same as 4)
df2 = df1.loc[[0,3,4],:]
df2.iloc[0,1] = 100
5)警告:否
原始变更:是(新来者会感到困惑,但有意义)
# df1 will be affected because scalar/slice indexing with .iloc/.loc
# always references the original dataframe, but may sometimes
# provide a view and sometimes provide a copy
#
# Reference: docs
df2 = df1.loc[:10,:]
df2.iloc[0,1] = 100
tl;dr从原始 Dataframe 创建新 Dataframe 时,更改新 Dataframe :
当使用.loc/.iloc建立标量/片段索引来建立新的数据框时,会变更原始数据框。
- 使用.loc、
.query()
或.copy()
的布尔索引创建新 Dataframe 时**不会 * 更改原始 Dataframe **
- 使用.loc、
4条答案
按热度按时间ux6nzvsh1#
这是panda的一个令人困惑甚至沮丧的部分,但在大多数情况下,如果你遵循一些简单的工作流规则,你应该不必担心这个问题。特别要注意的是,这里只有两种一般情况,当你有两个 Dataframe ,其中一个是另一个的子集。
在这种情况下,Python的Zen规则“显式优于隐式”是一个很好的指导方针。
情况A:对
df2
的更改不应影响df1
当然,这很简单。您需要两个完全独立的 Dataframe ,因此只需显式地创建一个副本:
在此之后,对
df2
所做的任何操作都只会影响df2
,而不会影响df1
,反之亦然。情况B:对
df2
的更改也应影响df1
在这种情况下,我不认为有一种通用的方法可以解决问题,因为这取决于您要做什么。但是,有几种标准方法非常简单,不应该对它们的工作原理有任何模糊之处。
方法1:将df1复制到df2,然后使用df2更新df1
在这种情况下,你基本上可以对上面的例子进行一对一的转换。下面是例子#2:
不幸的是,通过
append
进行重新合并有点冗长。你可以用下面的代码更干净地完成,尽管它有将整数转换为浮点数的副作用。方法2:使用遮罩(根本不建立
df2
)我认为这里最好的通用方法是根本不创建
df2
,而是让它成为df1
的一个屏蔽版本。有点遗憾的是,由于上面的代码混合了loc
和iloc
,所以您不能直接翻译上面的代码,这对于本示例来说是好的,但对于实际使用来说可能是不现实的。这样做的好处是你可以编写非常简单和可读的代码。这里是上面示例2的另一个版本,其中
df2
实际上只是df1
的一个屏蔽版本。但是我不通过iloc
来改变,而是改变if column“C”== 10。现在,如果你打印
df1
或df1[df2_mask]
,你会看到每个 Dataframe 的第一行的列“B”= 100。显然,这在这里并不奇怪,但这是遵循“显式优于隐式”的固有优势。busg9geu2#
我有同样的疑问,我搜索了这个响应在过去没有成功.所以现在,我只是证明,原来是没有改变,并使用这个和平的代码程序在beginning删除警告:
h9vpoimq3#
下面是一个简单的效果示例,说明为什么需要.copy()
当下面的第二个代码块第一次执行时,它将切掉“12”,第二次执行时,它将切掉“34”,等等。
| 可乐|列B
零|小行星123456789|小行星123456789
| 可乐|列B
零|小行星3456789|小行星123456789
| 可乐|列B
零|小行星56789|小行星123456789
如上所述,修复方法是更改第二个块:
u3r8eeie4#
您只需将
.iloc[0,1]
替换为.iat[0,1]
。更一般地说,如果你只想修改一个元素,你应该使用
.iat
或.at
方法。相反,当你一次修改多个元素时,你应该使用.loc
或.iloc
方法。这样做Pandas不该抛出任何警告。