如果未定义索引操作是返回视图还是副本,熊猫的观点是啥?
Posted
技术标签:
【中文标题】如果未定义索引操作是返回视图还是副本,熊猫的观点是啥?【英文标题】:What is the point of views in pandas if it is undefined whether an indexing operation returns a view or a copy?如果未定义索引操作是返回视图还是副本,熊猫的观点是什么? 【发布时间】:2016-04-25 09:12:21 【问题描述】:我已从 R 切换到 pandas。当我做类似的事情时,我经常会收到 SettingWithCopyWarnings
df_a = pd.DataFrame('col1': [1,2,3,4])
# Filtering step, which may or may not return a view
df_b = df_a[df_a['col1'] > 1]
# Add a new column to df_b
df_b['new_col'] = 2 * df_b['col1']
# SettingWithCopyWarning!!
我想我理解问题所在,但我很乐意了解我做错了什么。在给定的示例中,未定义 df_b
是否是 df_a
上的视图。因此,分配给df_b
的效果尚不清楚:它会影响df_a
吗?该问题可以通过过滤时显式复制来解决:
df_a = pd.DataFrame('col1': [1,2,3,4])
# Filtering step, definitely a copy now
df_b = df_a[df_a['col1'] > 1].copy()
# Add a new column to df_b
df_b['new_col'] = 2 * df_b['col1']
# No Warning now
我认为我缺少一些东西:如果我们永远无法确定是否创建视图,那么视图有什么用?来自熊猫文档 (http://pandas-docs.github.io/pandas-docs-travis/indexing.html?highlight=view#indexing-view-versus-copy)
除了简单的情况外,很难预测 [getitem] 会返回视图还是副本(这取决于数组的内存布局,pandas 对此不做任何保证)
对于不同的索引方法,可以找到类似的警告。
我发现在我的代码中散布 .copy() 调用非常麻烦且容易出错。我是否使用错误的样式来操作我的 DataFrames?还是性能提升如此之高,以至于证明了明显的尴尬是合理的?
【问题讨论】:
您可以通过以下分配安全地禁用此新警告。pd.options.mode.chained_assignment = None
嗯,也许可以帮助重置索引df_b = df_a[df_a['col1'] > 1].reset_index(drop=True)
。
@GeorgePetrov 我强烈建议不要禁用它!出现警告是有充分理由的——如果有的话,我实际上建议您将其提升为异常而不是警告。
【参考方案1】:
我同意这有点好笑。我目前的做法是为我想做的任何事情寻找一种“功能性”的方法(根据我的经验,这些方法几乎总是存在,除了重命名列和系列)。有时它使代码更优雅,有时它变得更糟(我不喜欢assign
和lambda
),但至少我不必担心可变性。
所以对于索引,您可以使用query
,而不是使用切片表示法,默认情况下会返回一个副本:
In [5]: df_a.query('col1 > 1')
Out[5]:
col1
1 2
2 3
3 4
我在this blog post. 中对此进行了一些扩展
编辑: 正如在 cmets 中提出的,我对 query
默认返回一个副本似乎是错误的,但是如果您使用 assign
样式,那么 assign 将创建一个在返回结果之前复制,一切顺利:
df_b = (df_a.query('col1 > 1')
.assign(newcol = 2*df_a['col1']))
【讨论】:
为什么序列:df_b = df_a.query('col1 > 1')
后跟df_b['new_col'] = 2 * df_b['col1']
仍然给出 SettingWithCopyWarning?
@maxymoo:这回答了我问题的第二部分:避免 SettingWithCopy 问题的编程风格。谢谢!我真的很喜欢你的博文!你能回答screenpaver的问题吗?我认为您博客文章中的大多数建议都非常有效,但是 .query() 似乎并非在所有情况下都返回副本!那么我可以在方法链中进行过滤吗?
好的,谢谢。或者这也可以(无需查询):df_b = df_a[df_a.col1>1].assign(newcol = 2*df_a['col1'])
【参考方案2】:
好问题!
简短的回答是:这是 pandas 中正在修复的缺陷。
您可以找到关于 the problem here 本质的更长讨论,但主要的收获是我们现在正在转向“写时复制”行为,在这种行为中,任何时候切片,都会得到新副本,您永远不必考虑视图。修复很快就会通过这个refactoring project. 我实际上尝试直接修复它(see here),但它在当前架构中是不可行的。
事实上,我们会将视图保留在后台 - 当它们可以提供时,它们会使 pandas 超级高效和快速 - 但我们最终会对用户隐藏它们,因此,从用户的角度来看,如果您对 DataFrame 进行切片、索引或剪切,您得到的实际上将是一个新副本。
(这是通过在用户只读取数据时创建视图来实现的,但每当使用赋值操作时,视图将在赋值发生之前转换为副本。)
最好的猜测是修复将在一年内完成 - 同时,我担心一些 .copy()
可能是必要的,抱歉!
【讨论】:
以上是关于如果未定义索引操作是返回视图还是副本,熊猫的观点是啥?的主要内容,如果未能解决你的问题,请参考以下文章