pythonのpandasにおいては、dataframeのコピーを=で行うと、データのコピーではなく、いわゆる参照渡しになる。
そうすると、下記のように、コピー先の変数に対して操作を行った結果が、コピー元の変数に対して反映される。
import pandas as pd
import numpy as np
## それぞれのやり方でコピーした場合のID
df = pd.DataFrame({ 'A' : 'FOO',
'B' : 'foo',
'C' : 'ふー'}, index=[1,2,3])
# 確認(Before)
display(df)
# コピー(参照渡し)
df_copy = df
# コピーに対して、1を代入したD列を追加する
df_copy['D'] = 1
# 確認(After)
display(df)
A | B | C | |
---|---|---|---|
1 | FOO | foo | ふー |
2 | FOO | foo | ふー |
3 | FOO | foo | ふー |
A | B | C | D | |
---|---|---|---|---|
1 | FOO | foo | ふー | 1 |
2 | FOO | foo | ふー | 1 |
3 | FOO | foo | ふー | 1 |
そのため、基本オブジェクトをコピーしたいときは、copy()を使うようにする。
また、copy()の場合でも、deep=Falseをした場合、参照渡しとなる、といくつかの記事に書いてある。
ただ、それぞれのやり方でコピーした場合のIDを見ると、下記のよう、=を使った場合のみ、idが一致しているので、
=とcopy(deep=False) では挙動が異なるのではと思い確認してみた。
import pandas as pd
import numpy as np
df = pd.DataFrame({ 'A' : 'FOO',
'B' : 'foo',
'C' : 'ふー'}, index=[1,2,3])
equal = df
deep = df.copy() # defaultはtrue
shallow = df.copy(deep=False)
print('id(df):',id(df))
print('id(equal):',id(equal))
print('id(deep):',id(deep))
print('id(shallw):',id(shallow))
id(df): 4569239336
id(equal): 4569239336
id(deep): 4569145072
id(shallw): 4569180200
結論
=を使った場合は、値の更新だろうと追加だろうと全てリンクするが、copy(deep=False) の場合は値の更新のみリンクする。
※当然、copy(deep=True)の場合は全く異なるオブジェクトなので、更新も追加もリンクしない
- [追加を行った場合] ▶ equalのみ追加される
- [値の変更(行)の変更を行った場合] ▶ equal, shallowの変更が適用される
- [値の変更(列)の変更を行った場合] ▶ equal, shallowの変更が適用される
- [列の追加&変更を行った場合] ▶ equalは変更もできるが、shallowは値の変更はできなくなる
追加を行った場合
df = pd.DataFrame({ 'A' : 'FOO',
'B' : 'foo',
'C' : 'ふー'}, index=[1,2,3])
print('----Before----')
# 確認(Before)
display(df)
# dfを元にコピー
equal = df
deep = df.copy() # defaultはtrue
shallow = df.copy(deep=False)
# コピー先を操作
equal['D'] = 'equal'
deep['E'] = 'deep'
shallow['F'] = 'shallow'
print('----After----')
# 確認(After)
display(df) # equalのみ追加される
----Before----
A | B | C | |
---|---|---|---|
1 | FOO | foo | ふー |
2 | FOO | foo | ふー |
3 | FOO | foo | ふー |
----After----
A | B | C | D | |
---|---|---|---|---|
1 | FOO | foo | ふー | equal |
2 | FOO | foo | ふー | equal |
3 | FOO | foo | ふー | equal |
値の変更(行)の変更を行った場合
df = pd.DataFrame({ 'A' : 'FOO',
'B' : 'foo',
'C' : 'ふー'}, index=[1,2,3])
print('----Before----')
# 確認(Before)
display(df)
# dfを元にコピー
equal = df
deep = df.copy() # defaultはtrue
shallow = df.copy(deep=False)
# コピー先を操作
equal.iloc[0,:] = 'equal'
deep.iloc[1,:] = 'deep'
shallow.iloc[2,:] = 'shallow'
print('----After----')
# 確認(After)
display(df) # equal, shallowの変更が適用される
----Before----
A | B | C | |
---|---|---|---|
1 | FOO | foo | ふー |
2 | FOO | foo | ふー |
3 | FOO | foo | ふー |
----After----
A | B | C | |
---|---|---|---|
1 | equal | equal | equal |
2 | FOO | foo | ふー |
3 | shallow | shallow | shallow |
値の変更(列)の変更を行った場合
df = pd.DataFrame({ 'A' : 'FOO',
'B' : 'foo',
'C' : 'ふー'}, index=[1,2,3])
print('----Before----')
# 確認(Before)
display(df)
# dfを元にコピー
equal = df
deep = df.copy() # defaultはtrue
shallow = df.copy(deep=False)
# コピー先を操作
equal.iloc[:,0] = 'equal'
deep.iloc[:,1] = 'deep'
shallow.iloc[:,2] = 'shallow'
print('----After----')
# 確認(After)
display(df) # equal, shallowの変更が適用される
----Before----
A | B | C | |
---|---|---|---|
1 | FOO | foo | ふー |
2 | FOO | foo | ふー |
3 | FOO | foo | ふー |
----After----
A | B | C | |
---|---|---|---|
1 | equal | foo | shallow |
2 | equal | foo | shallow |
3 | equal | foo | shallow |
列の追加&変更を行った場合
df = pd.DataFrame({ 'A' : 'FOO',
'B' : 'foo',
'C' : 'ふー'}, index=[1,2,3])
print('----Before----')
# 確認(Before)
display(df)
# dfを元にコピー
equal = df
deep = df.copy() # defaultはtrue
shallow = df.copy(deep=False)
# コピー先を操作
equal['D'] = 'add_df' # 参照元に追加
equal['E'] = 'add_equal' # 参照先に追加
equal.iloc[0,:] = 'equal'# 追加後に値の変更
deep.iloc[1,:] = 'deep' # 追加後に値の変更
shallow.iloc[2,:] = 'shallow' # 追加後に値の変更
print('----After----')
# 確認(After)
display(df) # equalは変更もできるが、shallowは値の変更はできなくなる
----Before----
A | B | C | |
---|---|---|---|
1 | FOO | foo | ふー |
2 | FOO | foo | ふー |
3 | FOO | foo | ふー |
----After----
A | B | C | D | E | |
---|---|---|---|---|---|
1 | equal | equal | equal | equal | equal |
2 | FOO | foo | ふー | add_df | add_equal |
3 | FOO | foo | ふー | add_df | add_equal |