如果未定义索引操作 returns 是视图还是副本,pandas 中的观点是什么?

What is the point of views in pandas if it is undefined whether an indexing operation returns a view or a copy?

我已经从 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

我认为我缺少一些东西:如果我们永远不能真正确定我们是否创建了一个视图,那么视图有什么用?来自 pandas 文档 (http://pandas-docs.github.io/pandas-docs-travis/indexing.html?highlight=view#indexing-view-versus-copy)

Outside of simple cases, it’s very hard to predict whether it [getitem] will return a view or a copy (it depends on the memory layout of the array, about which pandas makes no guarantees)

对于不同的索引方法可以找到类似的警告。

我发现在我的代码中散布 .copy() 调用非常麻烦且容易出错。我是否使用了错误的样式来操作我的 DataFrame?还是性能提升如此之高以致于明显的尴尬?

我同意这有点好笑。我目前的做法是为我想做的任何事情寻找一个 "functional" 方法(根据我的经验,除了重命名列和系列之外,这些方法几乎总是存在的)。有时它使代码更优雅,有时它使它变得更糟(我不喜欢 assignlambda),但至少我不必担心可变性。

因此对于索引,您可以使用 query 而不是使用切片表示法,默认情况下会 return 一个副本:

In [5]: df_a.query('col1 > 1')
Out[5]:
   col1
1     2
2     3
3     4

我在 this blog post.

中对其进行了一些扩展

编辑: 正如评论中提到的那样,我似乎对 query return 默认复制了一个副本是错误的,但是如果你使用 assign 风格,然后 assign 会在 return 得到你的结果之前制作一个副本,你们都很好:

df_b = (df_a.query('col1 > 1')
            .assign(newcol = 2*df_a['col1']))

好问题!

简短的回答是:这是 pandas 中的一个缺陷,正在修复中。

您可以找到关于 the problem here, but the main take-away is that we're now moving to a "copy-on-write" behavior in which any time you slice, you get a new copy, and you never have to think about views. The fix will soon come through this refactoring project. I actually tried to fix it directly (see here) 的性质的更长的讨论,但这在当前架构中是不可行的。

事实上,我们会将视图保留在后台——当可以提供它们时,它们使 pandas 超级内存高效和快速——但我们最终会向用户隐藏它们,所以,从从用户的角度来看,如果您对 DataFrame 进行切片、索引或剪切,您得到的实际上是一个新副本。

(这是通过在用户只读取数据时创建视图来实现的,但是每当使用赋值操作时,视图将在赋值发生之前转换为副本。)

最好的猜测是修复将在一年内完成——与此同时,恐怕可能需要一些 .copy(),抱歉!