拯救pandas計(jì)劃(9)——淺談pandas中的深淺拷貝
拯救pandas計(jì)劃(9)——淺談pandas中的深淺拷貝
最近發(fā)現(xiàn)周圍的很多小伙伴們都不太樂意使用pandas,轉(zhuǎn)而投向其他的數(shù)據(jù)操作庫,身為一個(gè)數(shù)據(jù)工作者,基本上是張口pandas,閉口pandas了,故而寫下此系列以讓更多的小伙伴們愛上pandas。
系列文章說明:
系列名(系列文章序號(hào))——此次系列文章具體解決的需求
平臺(tái):
windows 10 python 3.8 pandas >=1.2.4
/ 數(shù)據(jù)需求
有時(shí)在修改pandas對(duì)象中不想改變?cè)瓟?shù)據(jù)框會(huì)使用.copy()給原數(shù)據(jù)框做一份拷貝,但數(shù)據(jù)內(nèi)容中包含可變對(duì)象時(shí),使用卻不會(huì)按本意進(jìn)行,官方也在函數(shù)中給出說明,這里一起了解下pandas中的深淺復(fù)制。
/ .copy(deep=True)
函數(shù)詳解文檔:
Signature:?df.copy(deep:?'bool_t'?=?True)?->?'FrameOrSeries'
Docstring:
Make?a?copy?of?this?object's?indices?and?data.
When?``deep=True``?(default),?a?new?object?will?be?created?with?a
copy?of?the?calling?object's?data?and?indices.?Modifications?to
the?data?or?indices?of?the?copy?will?not?be?reflected?in?the
original?object?(see?notes?below).
When?``deep=False``,?a?new?object?will?be?created?without?copying
the?calling?object's?data?or?index?(only?references?to?the?data
and?index?are?copied).?Any?changes?to?the?data?of?the?original
will?be?reflected?in?the?shallow?copy?(and?vice?versa).
Parameters
----------
deep?:?bool,?default?True
????Make?a?deep?copy,?including?a?copy?of?the?data?and?the?indices.
????With?``deep=False``?neither?the?indices?nor?the?data?are?copied.
Returns
-------
copy?:?Series?or?DataFrame
????Object?type?matches?caller.
Notes
-----
When?``deep=True``,?data?is?copied?but?actual?Python?objects
will?not?be?copied?recursively,?only?the?reference?to?the?object.
This?is?in?contrast?to?`copy.deepcopy`?in?the?Standard?Library,
which?recursively?copies?object?data?(see?examples?below).
While?``Index``?objects?are?copied?when?``deep=True``,?the?underlying
numpy?array?is?not?copied?for?performance?reasons.?Since?``Index``?is
immutable,?the?underlying?data?can?be?safely?shared?and?a?copy
is?not?needed.
在該函數(shù)文檔中可以看出,默認(rèn)為深復(fù)制(deep=True),會(huì)創(chuàng)建一個(gè)與原數(shù)據(jù)框不同的數(shù)據(jù)對(duì)象,在副本中修改數(shù)據(jù)均不會(huì)使原數(shù)據(jù)發(fā)生修改,在下面的Notes部分提到,如果數(shù)據(jù)內(nèi)容是可變對(duì)象依然會(huì)改變?cè)瓟?shù)據(jù)框內(nèi)容,原因是pandas的深復(fù)制僅是引用python對(duì)象使用,也有部分減少性能的消耗,如Index不可變對(duì)象可以安全無誤的復(fù)制到新對(duì)象中,不同于copy.deepcopy會(huì)遞歸復(fù)制python對(duì)象,所以使用pandas.copy復(fù)制pandas對(duì)象,如果數(shù)據(jù)內(nèi)容包含可變對(duì)象,仍然是不安全的,修改其內(nèi)的數(shù)據(jù)會(huì)使原數(shù)據(jù)發(fā)生改變。
(手動(dòng)水印:原創(chuàng)CSDN宿者朽命,https://blog.csdn.net/weixin_46281427?spm=1011.2124.3001.5343,公眾號(hào)A11Dot派)
/ 函數(shù)使用
數(shù)據(jù):
import?pandas?as?pd
data?=?{'A':?[1,?2,?3],
?'B':?[['厲害',?'真棒'],?['值得鼓勵(lì)',?'繼續(xù)加油'],?['相信未來--勇闖天涯']],
?'C':?[{'key':?'試一試',?'value':?'try'},?{'key':?'看一看',?'value':?'look'},?{'key':?'拍一拍',?'value':?'tickle'}]}
df?=?pd.DataFrame(data)
df_shallow?=?df.copy(deep=False)?#?淺復(fù)制
df_copy?=?df.copy()?#?深復(fù)制

生成df,包含3列數(shù)據(jù),A列為數(shù)值型,不可變對(duì)象, B和C都是可變對(duì)象。

查看深淺復(fù)制對(duì)象,都與原對(duì)象不同,說明完成拷貝。
修改df中的A列數(shù)據(jù)
df.loc[0,?'A']?=?50

df_shallow會(huì)跟著df的改變而發(fā)生改變,而df_copy不會(huì)發(fā)生變化,也證實(shí)了?df.copy(deep=False)?是淺層復(fù)制,新建了一個(gè)與df數(shù)據(jù)內(nèi)容及索引相同的但對(duì)象不同的數(shù)據(jù)框。
修改B列數(shù)據(jù)
df.loc[0,?'B'][0]?=?'lihai'

在B列修改過程中所有復(fù)制出來的數(shù)據(jù)框都發(fā)生了變化,正如Notes所說,是對(duì)對(duì)象的引用,是直接修改引用到的數(shù)據(jù),那么在數(shù)據(jù)框顯示部分看到B列的內(nèi)容發(fā)生變化,原始數(shù)據(jù)內(nèi)容是否也已經(jīng)改變,打印data:

data中的B所對(duì)應(yīng)的值也已經(jīng)發(fā)生改變,那么有什么方法可以僅改變深復(fù)制后的數(shù)據(jù)對(duì)象,又不改變?cè)紨?shù)據(jù),這里需要借助copy模塊,C列的數(shù)據(jù)層次與B列一樣,下面修改C列數(shù)據(jù)。
修改C列數(shù)據(jù)
from?copy?import?copy,?deepcopy
def?value_upper(dic):
????"""將傳入的dic使用深淺拷貝復(fù)制給另一個(gè)新的對(duì)象"""
????dic?=?copy(dic)??#?如果層級(jí)大于一層,請(qǐng)考慮使用deepcopy
????dic['value']?=?dic['value'].upper()
????#?返回修改后的字典對(duì)象
????return?dic
df_copy['C'].apply(value_upper)??#?未實(shí)際改變df_copy['C']

熟悉pandas的會(huì)明白這樣僅讓df_copy中的C列參與了函數(shù)執(zhí)行,沒有實(shí)際改變C列數(shù)據(jù),但如果沒有第5行中的copy操作,即使沒有return語句也會(huì)使df_copy發(fā)生改變,原因見修改B列數(shù)據(jù)部分。如果需要改變C列數(shù)據(jù)將運(yùn)行后的結(jié)果賦值給C列,由于C列被重新賦值也就不存在修改df_copy中C列數(shù)據(jù)會(huì)影響到df或df_shallow乃至data中的C列數(shù)據(jù)。
df_copy['C']?=?df_copy['C'].apply(value_upper)?

可以看到僅df_copy['C']發(fā)生了變化。
/ 總結(jié)
偶爾修改pandas對(duì)象中的數(shù)據(jù)時(shí)會(huì)被提醒所修改的數(shù)據(jù)視圖會(huì)影響原數(shù)據(jù)框,可以考慮使用df.copy方法避免修改,而當(dāng)數(shù)據(jù)中含有可變對(duì)象,且只修改其中的一部分內(nèi)容,卻不會(huì)產(chǎn)生這樣的警告,而這樣的結(jié)果又不能被接受時(shí),可以查看下函數(shù)文檔,pandas中的copy與python對(duì)象的復(fù)制在使用上有部分不同,例如deepcopy是會(huì)對(duì)python進(jìn)行遞歸操作,而pandas.copy僅將數(shù)據(jù)引用及索引進(jìn)行復(fù)制。如果你問我deepcopy(df)是否可以避免上述情況發(fā)生,很遺憾的告訴你不行,對(duì)該篇有任何疑問歡迎聯(lián)系作者,簡(jiǎn)述你的見解。
當(dāng)你難以分辨兩者之間是否有某種關(guān)聯(lián)時(shí),最好的辦法是改變其中一個(gè)的特征。
于二〇二二年三月二十日作
