熊猫视图与副本:文档说“没人知道”?

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

【讨论】:

很好的例子。你有没有找到一致的理由来解释为什么会这样?

以上是关于熊猫视图与副本:文档说“没人知道”?的主要内容,如果未能解决你的问题,请参考以下文章

关于 python 何时自动创建数组副本的文档

熊猫:链式作业[重复]

熊猫“试图在数据帧的切片副本上设置一个值”

使用 Python pandas 数据框时返回副本与视图警告

熊猫:啥是视图?

Python机器学习(四十七)NumPy 副本和视图