如果未定义索引操作 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" 方法(根据我的经验,除了重命名列和系列之外,这些方法几乎总是存在的)。有时它使代码更优雅,有时它使它变得更糟(我不喜欢 assign
和 lambda
),但至少我不必担心可变性。
因此对于索引,您可以使用 query
而不是使用切片表示法,默认情况下会 return 一个副本:
In [5]: df_a.query('col1 > 1')
Out[5]:
col1
1 2
2 3
3 4
中对其进行了一些扩展
编辑: 正如评论中提到的那样,我似乎对 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()
,抱歉!
我已经从 R 切换到 pandas。当我做类似
的事情时,我经常会收到 SettingWithCopyWarningsdf_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" 方法(根据我的经验,除了重命名列和系列之外,这些方法几乎总是存在的)。有时它使代码更优雅,有时它使它变得更糟(我不喜欢 assign
和 lambda
),但至少我不必担心可变性。
因此对于索引,您可以使用 query
而不是使用切片表示法,默认情况下会 return 一个副本:
In [5]: df_a.query('col1 > 1')
Out[5]:
col1
1 2
2 3
3 4
中对其进行了一些扩展
编辑: 正如评论中提到的那样,我似乎对 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()
,抱歉!