为什么熊猫对象的副本会更改原始对象上的一列? (切片副本)

时间:2019-12-06 09:17:14

标签: python-3.x pandas copy

据我了解,通过切片进行的复制会复制结构的较高级别,但不会复制较低的结构(我不确定何时)。

但是,在这种情况下,我通过切片来制作副本,并且在编辑副本的两列时,原始文档的一列被更改,而另一列则没有更改。

怎么可能?为什么是一列,而不是全部或都不选?

代码如下:

import pandas as pd
import numpy as np
url = 'https://raw.githubusercontent.com/udacity/deep-learning-v2-pytorch/master/intro-neural-networks/student-admissions/student_data.csv'
data = pd.read_csv(url)

# Copy data
processed_data = data[:]
print(data[:10])

# Edit copy
processed_data['gre'] = processed_data['gre']/800.0
processed_data['gpa'] = processed_data['gpa']/4.0

# gpa column has changed
print(data[:10])

另一方面,如果我将processed_data = data[:]更改为processed_data = data.copy(),则效果很好。

此处,原始数据已编辑:

recreation

1 个答案:

答案 0 :(得分:1)

  

据我了解,通过切片进行的复制会复制结构的上层而不是下层。

这对Python列表有效。切片会创建浅拷贝。

In [44]: lst = [[1, 2], 3, 4]                                                      

In [45]: lst2 = lst[:]                                                             

In [46]: lst2[1] = 100                                                             

In [47]: lst  # unchanged                                                          
Out[47]: [[1, 2], 3, 4]

In [48]: lst2[0].append(3)                                                         

In [49]: lst  # changed                                                            
Out[49]: [[1, 2, 3], 3, 4]

但是,对于numpy / pandas并非如此。在切片数组时,numpy通常会返回视图。

In [50]: arr = np.array([1, 2, 3])                                                 

In [51]: arr2 = arr[:]                                                             

In [52]: arr2[0] = 100                                                             

In [53]: arr                                                                       
Out[53]: array([100,   2,   3])

如果您的DataFrame具有单个dtype,则您看到的行为是相同的:

In [62]: df = pd.DataFrame([[1, 2, 3], [4, 5, 6]])                                 

In [63]: df                                                                        
Out[63]: 
   0  1  2
0  1  2  3
1  4  5  6

In [64]: df2 = df[:]                                                               

In [65]: df2.iloc[0, 0] = 100                                                      

In [66]: df                                                                        
Out[66]: 
     0  1  2
0  100  2  3
1    4  5  6

但是当您混合使用dtypes时,行为是不可预测的,这是臭名昭著的SettingWithCopyWarning的主要来源:

dfmi['one']['second'] = value
# becomes
dfmi.__getitem__('one').__setitem__('second', value)
     

看到那里的__getitem__吗?除了简单的案例,这很难   预测是否返回视图或副本(取决于   数组的内存布局,有关熊猫对此不做任何保证),   因此__setitem__会修改dfmi还是临时   此后立即被抛出的对象。那就是   SettingWithCopy正在警告您!

就您而言,我的猜测是这是熊猫处理不同dtypes的结果。每个dtype都有其自己的块,对于gpa列,该块本身就是该列。 gre并非如此-您还有其他整数列。当我将字符串列添加到data并在processed_data中对其进行修改时,我会看到相同的行为。当我在data中将浮点数增加到2时,在gre中更改processed_data不再影响原始的data

简而言之,行为是实现细节的结果,您不应该依赖该细节。如果要复制DataFrame,则应显式使用.copy();如果要修改DataFrame的某些部分,则不应将这些部分分配给其他变量。您应该使用.loc.iloc直接修改它们。