为什么我应该在 Pandas 中复制一个数据框
- 2025-01-03 08:41:00
- admin 原创
- 87
问题描述:
当从父数据框中选择子数据框时,我注意到一些程序员使用该方法复制数据框.copy()
。例如,
X = my_dataframe[features_list].copy()
...而不仅仅是
X = my_dataframe[features_list]
他们为什么要复制数据框?如果我不复制会发生什么?
解决方案 1:
此答案已在较新版本的 pandas 中弃用。请参阅文档
这扩展了 Paul 的回答。在 Pandas 中,索引 DataFrame 会返回对初始 DataFrame 的引用。因此,更改子集将更改初始 DataFrame。因此,如果您想确保初始 DataFrame 不会更改,则需要使用副本。请考虑以下代码:
df = DataFrame({'x': [1,2]})
df_sub = df[0:1]
df_sub.x = -1
print(df)
您将获得:
x
0 -1
1 2
相反,下面的代码保持 df 不变:
df_sub_copy = df[0:1].copy()
df_sub_copy.x = -1
解决方案 2:
因为如果您不复制,那么即使您将 dataFrame 分配给不同的名称,索引仍然可以在其他地方被操作。
例如:
df2 = df
func1(df2)
func2(df)
func1 可以通过修改 df2 来修改 df,因此为了避免这种情况:
df2 = df.copy()
func1(df2)
func2(df)
解决方案 3:
需要提到的是,返回的副本或视图取决于索引的类型。
熊猫文档说:
返回视图与副本
关于何时返回数据视图的规则完全取决于 NumPy。每当标签数组或布尔向量参与索引操作时,结果将是副本。使用单标签/标量索引和切片,例如 df.ix[3:6] 或 df.ix[:, 'A'],将返回视图。
解决方案 4:
主要目的是避免链式索引并消除SettingWithCopyWarning
。
这里的链式索引类似于dfc['A'][0] = 111
文档中说,在返回视图而不是副本时应避免使用链式索引。以下是该文档中稍作修改的示例:
In [1]: import pandas as pd
In [2]: dfc = pd.DataFrame({'A':['aaa','bbb','ccc'],'B':[1,2,3]})
In [3]: dfc
Out[3]:
A B
0 aaa 1
1 bbb 2
2 ccc 3
In [4]: aColumn = dfc['A']
In [5]: aColumn[0] = 111
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
In [6]: dfc
Out[6]:
A B
0 111 1
1 bbb 2
2 ccc 3
此处aColumn
是视图,而不是原始 DataFrame 的副本,因此修改aColumn
会导致原始数据dfc
也发生修改。接下来,如果我们首先索引该行:
In [7]: zero_row = dfc.loc[0]
In [8]: zero_row['A'] = 222
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
In [9]: dfc
Out[9]:
A B
0 111 1
1 bbb 2
2 ccc 3
这次zero_row
是复印件,所以原件dfc
没有被修改。
从上面的两个例子中,我们可以看出是否要更改原始 DataFrame 是模棱两可的。如果您编写以下内容,这尤其危险:
In [10]: dfc.loc[0]['A'] = 333
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
In [11]: dfc
Out[11]:
A B
0 111 1
1 bbb 2
2 ccc 3
这次根本不起作用。在这里我们想改变dfc
,但实际上我们修改了一个中间值dfc.loc[0]
,它是一个副本,并立即被丢弃。很难预测像dfc.loc[0]
或这样的中间值dfc['A']
是视图还是副本,因此无法保证原始 DataFrame 是否会被更新。这就是为什么应该避免链式索引,而 pandasSettingWithCopyWarning
为这种链式索引更新生成了。
现在使用.copy()
。为了消除警告,请复制一份以明确表达您的意图:
In [12]: zero_row_copy = dfc.loc[0].copy()
In [13]: zero_row_copy['A'] = 444 # This time no warning
由于您正在修改副本,因此您知道原始内容dfc
永远不会改变,并且您也不期望它会改变。您的期望与行为相匹配,然后SettingWithCopyWarning
消失。
注意,如果您确实想修改原始 DataFrame,文档建议您使用loc
:
In [14]: dfc.loc[0,'A'] = 555
In [15]: dfc
Out[15]:
A B
0 555 1
1 bbb 2
2 ccc 3
解决方案 5:
假设您有如下数据框
df1
A B C D
4 -1.0 -1.0 -1.0 -1.0
5 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
当你想创建另一个df2
相同的df1
,没有copy
df2=df1
df2
A B C D
4 -1.0 -1.0 -1.0 -1.0
5 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
并且只想修改 df2 值,如下所示
df2.iloc[0,0]='changed'
df2
A B C D
4 changed -1.0 -1.0 -1.0
5 -1 -1.0 -1.0 -1.0
6 -1 -1.0 -1.0 -1.0
6 -1 -1.0 -1.0 -1.0
同时 df1 也发生了变化
df1
A B C D
4 changed -1.0 -1.0 -1.0
5 -1 -1.0 -1.0 -1.0
6 -1 -1.0 -1.0 -1.0
6 -1 -1.0 -1.0 -1.0
由于两个 df 相同object
,我们可以使用id
id(df1)
140367679979600
id(df2)
140367679979600
因此,它们作为同一个对象,当一个对象改变另一个对象时,也会传递相同的值。
如果我们添加copy
,现在df1
和df2
被视为不同的object
,如果我们对其中一个进行相同的更改,另一个将不会改变。
df2=df1.copy()
id(df1)
140367679979600
id(df2)
140367674641232
df1.iloc[0,0]='changedback'
df2
A B C D
4 changed -1.0 -1.0 -1.0
5 -1 -1.0 -1.0 -1.0
6 -1 -1.0 -1.0 -1.0
6 -1 -1.0 -1.0 -1.0
值得一提的是,当你对原始数据框进行子集化时,也可以安全地添加副本,以避免SettingWithCopyWarning
解决方案 6:
一般来说,处理副本比处理原始数据框更安全,除非你知道不再需要原始数据框,而想要继续处理处理后的版本。通常,你仍然会用到原始数据框来与处理后的版本进行比较等。因此,大多数人处理副本并在最后合并。
解决方案 7:
Pandas 深度复制保持初始 DataFrame 不变。
当你想要规范化 DataFrame 并希望保持初始 df 不变时,此功能特别有用。例如:
df = pd.DataFrame(np.arange(20).reshape(2,10))
然后你规范化数据:
# Using Sklearn MinMaxSacaler method
scaler = preprocessing.MinMaxScaler()
并且您根据第一个 df 创建新的 df 并希望第一个 df 保持不变,则必须使用 .copy() 方法
new_df = pd.DataFrame(df).copy() # Deep Copy
for i in range(10):
pd_features[i] = scaler.fit_transform(unnormal_pd_features[i].values.reshape(-1,1))
否则你原来的 df 也会改变。
解决方案 8:
我在使用 copy() 时非常粗心,直到我使用下面那行代码而不使用 copy(),df_genel3 中的更改影响了 df_genel
df_genel3 = df_genel
df_genel3.loc[(df_genel3['Hareket']=='İmha') , 'Hareket_Tutar'] = tutar
copy() 解决了这个问题
df_genel3 = df_genel.copy()
df_genel3.loc[(df_genel3['Hareket']=='İmha') , 'Hareket_Tutar'] = tutar