熊猫视图与副本:文档说“没人知道”?
Posted
技术标签:
【中文标题】熊猫视图与副本:文档说“没人知道”?【英文标题】:pandas views vs copy : the docs says "nobody knows"? 【发布时间】:2016-12-30 07:51:06 【问题描述】:*** 上有很多关于链式索引以及特定操作是创建视图还是副本的问题。 (例如,here 或 here)。我仍然没有完全理解,但令人惊奇的是官方文档说“没人知道”。 (!?!??)这是文档中的一个示例;你能告诉我他们是否真的是这个意思,还是他们只是轻率?
来自http://pandas-docs.github.io/pandas-docs-travis/indexing.html?highlight=view#why-does-assignment-fail-when-using-chained-indexing
def do_something(df):
foo = df[['bar', 'baz']] # Is foo a view? A copy? Nobody knows!
# ... many lines here ...
foo['quux'] = value # We don't know whether this will modify df or not!
return foo
真的吗?对于那个具体的例子,“没有人知道”真的是真的吗,这是不确定的吗?这真的会在两个不同的数据帧上表现不同吗?规则真的那么复杂吗?还是这家伙的意思是有一个明确的答案,只是大多数人都不知道?
【问题讨论】:
是的,这令人沮丧。为了增加痛苦,同一页面稍后说:>“这有时可以工作,但不能保证,因此应该避免:”dfc = dfc.copy()
那么,我们应该如何确保 DataFrame 是传递给函数的不仅仅是另一个 DataFrame 的副本或切片??
【参考方案1】:
以下是我认为您可能错过的核心文档:
除了简单的情况,很难预测它会返回一个视图还是一个副本(这取决于数组的内存布局,pandas 对此不做任何保证)
所以有一个具有某种内存布局的底层 numpy 数组。 pandas 并不关心对此有任何了解。除此之外,我没有仔细阅读文档,但我认为他们有某种方法,如果你真的想要 设置,你应该价值观。
【讨论】:
是的,我看到了那条线;但这没有帮助。我应该采用的替代方案是什么?上面的例子看起来是一件很合理的事情,所以如果不允许这样做,那我们应该怎么做呢?在每个方法之后调用 .copy() 以防万一?!? 但是在您的代码示例中,您获取了原始 df 的一个子集,然后您尝试添加一个新列,那么这里的意图是什么?原始df或df副本的新列?我不认为这应该被认为是明确的 @ayhan 我想这取决于底层的 np 数组和内存布局,但在代码方面我不清楚代码 sn-p 的语义,我总是在子集上明确调用copy()
确保我在不依赖任何假设的情况下制作副本
@ayhan 我没有看到,但这取决于您是否尝试修改视图,请参阅我的回答
@ayhan 是的,代码 是 来自文档,但它说明了您可能不应该做的事情 - 或者至少是一个模棱两可的情况。【参考方案2】:
我想我可以展示一些东西来澄清你的情况,在你的例子中,最初它是一个视图,但是一旦你尝试通过添加一个列来修改它就会变成一个副本。您可以通过查看属性._is_view
来测试这一点:
In [29]:
df = pd.DataFrame(np.random.randn(5,3), columns=list('abc'))
def doSomething(df):
a = df[['b','c']]
print('before ', a._is_view)
a['d'] = 0
print('after ', a._is_view)
doSomething(df)
df
before True
after False
Out[29]:
a b c
0 0.108790 0.580745 1.820328
1 1.066503 -0.238707 -0.655881
2 -1.320731 2.038194 -0.894984
3 -0.962753 -3.961181 0.109476
4 -1.887774 0.909539 1.318677
所以在这里我们可以看到最初a
是原始df 的原始小节的视图,但是一旦向其添加列,这不再正确,我们可以看到原始df 没有被修改.
【讨论】:
但在您的示例中,它以特定的方式工作,具有您可以理解和解释的规则。但如果所有数据帧都是这种情况,为什么官方文档会说“没人知道”?他们是否暗示其他数据帧的行为可能不同?因为如果它总是按照你说的方式工作,那么文档可以提供“如果你想要 X,那么总是做 Y”的规则。 我不认为文档中的那个例子对我来说是一个很好的例子,在链式索引的情况下会发出警告,这里你是否参考这个事实是模棱两可的是否应该在原始 df 中添加新列,以查看原始 df。在这种情况下它不会。【参考方案3】:这是一个我认为很好地说明不一致的例子。
我对数据框进行子集化,它返回一个视图。然后我可以覆盖整个列中的值,但根据我在语法上的执行方式,我会得到不同的结果。
df = pd.DataFrame(np.random.randn(100, 100))
x = df[(df > 2).any(axis=1)]
print x._is_view
>>> True
# Prove that below we are referring to the exact same slice of the dataframe
assert (x.iloc[:len(x), 1] == x.iloc[:, 1]).all()
# Assign using equivalent notation to below
x.iloc[:len(x), 1] = 1
print x._is_view
>>> True
# Assign using slightly different syntax
x.iloc[:, 1] = 1
print x._is_view
>>> False
【讨论】:
很好的例子。你有没有找到一致的理由来解释为什么会这样?以上是关于熊猫视图与副本:文档说“没人知道”?的主要内容,如果未能解决你的问题,请参考以下文章