为啥分配为 None 时会创建副本?

Posted

技术标签:

【中文标题】为啥分配为 None 时会创建副本?【英文标题】:Why does a copy get created when assigned with None?为什么分配为 None 时会创建副本? 【发布时间】:2014-10-29 14:09:51 【问题描述】:
In[216]: foo = pd.DataFrame('a':[1,2,3], 'b':[3,4,5])
In[217]: bar = foo.ix[:1]
In[218]: bar
Out[218]: 
   a  b
0  1  3
1  2  4

按预期创建视图。

In[219]: bar['a'] = 100
In[220]: bar
Out[220]: 
     a  b
0  100  3
1  100  4
In[221]: foo
Out[221]: 
     a  b
0  100  3
1  100  4
2    3  5

如果视图被修改,那么原始数据框 foo 也会被修改。 但是,如果使用 None 完成分配,则似乎制作了一份副本。 任何人都可以阐明正在发生的事情以及背后的逻辑吗?

In[222]: bar['a'] = None
In[223]: bar
Out[223]: 
      a  b
0  None  3
1  None  4
In[224]: foo
Out[224]: 
     a  b
0  100  3
1  100  4
2    3  5

【问题讨论】:

我不像 numpy 那样了解 Pandas 的详细信息,但我敢打赌,通过强制列将其 dtype 从 I4 更改为 @ 987654325@,您导致它为该列分配一个新数组,然后您写入该新数组而不是与原始 DataFrame 共享的数组。 (我将其发布为评论而不是答案,因为即使我是对的,一个好的答案也应该准确解释这是如何工作的,而不仅仅是挥手致意……) @abarnert 这正是幕后发生的事情。继续发帖作为答案。 @Jeff:好的,但我仍然认为最好在文档中给出解释的指针,而不是一个 numpy 用户可以猜测 Pandas 可能是如何实现的......跨度> 我提出了一个答案。它在很多地方都得到了很好的警告/记录。如果用户不阅读文档,则无能为力。 感谢杰夫和其他人!我确实遇到了文档的“返回视图与副本”部分。很抱歉没有详细介绍。现在会这样做:) 【参考方案1】:

当您分配 bar['a'] = None 时,您将强制列将其 dtype 从例如 I4 更改为 object

这样做会强制它为该列分配一个新的 object 数组,然后它当然会写入该新数组,而不是写入与原始 DataFrame 共享的旧数组。

【讨论】:

【参考方案2】:

您正在执行一种链式分配,请参阅here 为什么这是一个非常糟糕的主意。

也看到这个问题here

Pandas 通常会警告您正在修改视图(在 0.15.0 中更是如此)。

In [49]: foo = pd.DataFrame('a':[1,2,3], 'b':[3,4,5])

In [51]: foo
Out[51]: 
   a  b
0  1  3
1  2  4
2  3  5

In [52]: bar = foo.ix[:1]

In [53]: bar
Out[53]: 
   a  b
0  1  3
1  2  4

In [54]: bar.dtypes
Out[54]: 
a    int64
b    int64
dtype: object

# this is an internal method (but is for illustration)
In [56]: bar._is_view
Out[56]: True

# this will warn in 0.15.0
In [57]: bar['a'] = 100
/usr/local/bin/ipython:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  #!/usr/local/bin/python

In [58]: bar._is_view
Out[58]: True

# bar is now a copied object (and will replace the existing dtypes with new ones).
In [59]: bar['a'] = None

In [60]: bar.dtypes
Out[60]: 
a    object
b     int64
dtype: object

你应该从不依赖某物是否是视图(即使在 numpy 中),除非在某些非常高效的情况下。它不是一个有保证的构造,具体取决于底层数据的内存布局。

您应该非常非常非常少地尝试设置数据以通过视图进行传播。当您混合 dtypes 时,在pandas 中执行此操作几乎总是会造成麻烦。 (在 numpy 中,你 can 只能查看单个 dtype;我什至不确定 dtype 的 changes 多类型数组的视图是什么,或者如果它甚至允许)。

【讨论】:

以上是关于为啥分配为 None 时会创建副本?的主要内容,如果未能解决你的问题,请参考以下文章

enumerate 是不是会创建其参数的副本?

每次在其中输入内容时,JTextField 都会创建自身的副本[关闭]

访问 MemoryCache 会创建副本吗?

Swift:为什么三元运算符会创建数组的副本而不是引用原始数据?

使用索引/范围对数组进行切片是不是会创建数组的副本

将数据从 hdfs 导入到 hbase 是不是会创建一个副本