pandasのリストをコピーする


データの用意

df = pd.DataFrame([[None for j in range(2)]  for i in range(3)], columns=['before', 'after'])
df['before'] = df['before'].apply(lambda x:[0,0])
before after
[0, 0] None
[0, 0] None
[0, 0] None

上記のようなテーブルがあったときに、beforeをafterへコピーしたいと思います。

完成予想図

before after
[0, 0] [0, 0]
[0, 0] [0, 0]
[0, 0] [0, 0]

OKな例

import copy
df["after"] = df["before"].apply(lambda x:copy.deepcopy(x))

このように一行づつdeepcopyする必要があります。

NGな例

以下、間違えていた方法を挙げます。

浅いコピー

df["after"] = df["before"].copy()

カラムの中身がstrやintであれば実行できますが、listは参照になってしまいます。

カラムをdeepcopy

df["after"] = copy.deepcopy(df["before"])

deepcopyしているので実行できそうですが、こちらもダメです。

確認する

# カラムの識別値
print(id(df["before"])) # >>> 140355706322576
print(id(df["after"]))  # >>> 140355202828368

# リストの識別値
print(id(df["before"][0])) # >>> 140355188788272
print(id(df["after"][0]))  # >>> 140355188788272

id関数を使用するとオブジェクトの識別値を確認することができます。
カラムは識別値が違うため異なるオブジェクトとして存在していますが、リストは値が同じで参照していることがわかります。

そのためafterの中身を変更しようとするとbeforeまで変わってしまいます。

なのでOK例のように1行づつdeepcopyするのが良さそうです。

参考文献