pandas

【Pandas】locとilocでビューとコピーが返ってくる条件【Python】

MAX

pandasのDataFrameには色々な操作をするメソッドがあり、戻り値として操作後のDataFrameが返ってくる。

その戻ってきたDataFrameにはビューとコピーの2種類がある。

ビューに対して、ビューと意識せずに操作をしてしまうと、思わぬところでバグが発生したりする。

かといってコピーばかりにすると、大きなデータを扱う際に多量のメモリを消費しやすくなる。

まずは違いを認識することが大切。

スポンサーリンク

ビューとコピーの違い

ビューのDataFrameを更新すると元のDataFrameも更新される。その逆も然りで、元のDataFrameを更新するとビューのDataFrameも更新される。

一方、コピーの場合はコピー後のDataFrameを更新しても、コピー元のDataFrmaeは更新されない(コピーなので当たり前か)

ビューとコピーのDataFrame作成

1import pandas as pd
2
3data = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
4cols = ["A", "B", "C"]
5index = [0, 1, 2]
6df = pd.DataFrame(data=data, columns=cols, index=index)
7
8print(df)
9#    A  B  C
10# 0  1  2  3
11# 1  2  3  4
12# 2  3  4  5
13
14# コピーの作成
15df_copy = df.copy()
16print(df_copy._is_view)
17# False
18
19# ビューの作成
20df_view = df.iloc[0:, 0:]
21print(df_view._is_view)
22# True

DataFrameの_is_viewプロパティにアクセスすることでビューかどうかを判定できる。

コピーを更新してもコピー元は更新されない

コピー後のDataFrameの0行A列の値を更新しても、元のDataFrameは更新なし。

1# コピーされたDFに対して変更を加えてもコピー元のDFは変更されない
2df_copy.iloc[0, 0] = 10
3print(df_copy)
4#      A   B   C
5# 0  10  2  3
6# 1   2   3  4
7# 2   3   4  5
8print(df)
9#     A  B  C
10# 0  1  2  3
11# 1  2  3  4
12# 2  3  4  5

ビューを更新すると元のDataFrameも更新される

1# ビューのDFを更新すると元のDFも更新される
2df_view.iloc[0, 0] = 20
3print(df_view)
4#       A  B  C
5# 0  20  2  3
6# 1    2  3  4
7# 2    3  4  5
8print(df)
9#       A  B  C
10# 0  20  2  3
11# 1   2  3  4
12# 2   3  4  5

ビューのDataFrameの0行A列を更新すると、ビューの作成元のData Frameの値も更新される。

元のDataFrameを更新するとビューも更新される

1# 元のDFを更新すると、ビューのDFも更新される
2df.iloc[1, 1] = 30
3print(df)
4#       A   B  C
5# 0  20   2  3
6# 1   2  30  4
7# 2   3   4   5
8print(df_view)
9#       A   B  C
10# 0  20   2  3
11# 1   2  30  4
12# 2   3   4   5

元のDataFrameの1行B列を更新すると、ビューのDataFrameの1行B列も更新される。

locでビューとコピーが返ってくるパターン

locでは指定した条件によってビューかコピーのどちらかが返ってくる。

基本的には、列をスライスで指定すると、ビューが返ってくると思っていればOK。

データ作成

以下のような列とインデックスのDataFrameを作成。

あえて列も行も一意にならないようにしているが、ビューかコピーのどちらが返ってくるかには特に影響なし。

1data = [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8]]
2cols = ["A", "B", "C", "C", "D"]
3index = [0, 1, 1, 2]
4df = pd.DataFrame(data=data, columns=cols, index=index)
5
6print(df)
7#     A  B  C  C  D
8# 0  1  2  3  4  5
9# 1  2  3  4  5  6
10# 1  3  4  5  6  7
11# 3  4  5  6  7  8

ビューが返ってくるパターン

基本的には列をスライス指定した場合にビューが返ってくる。

1
2# 列の一部をスライスを使って指定
3df_loc_slice1 = df.loc[:, :"D"]
4print(df_loc_slice1._is_view)
5# True
6
7# 行と列の一部をスライスを使って指定
8df_loc_slice2 = df.loc[0:, :"D"]
9print(df_loc_slice2._is_view)
10# True
11
12# 列の一部をスライスを使って指定。行は個別で指定。
13df_loc_slice3 = df.loc[[0, 1, 2], "C":"D"]
14print(df_loc_slice3._is_view)
15# True
16
17# 行の一部をスライスを使って指定
18df_loc_slice4 = df.loc[0:, :]
19print(df_loc_slice4._is_view)
20# True

コピーが返ってくるパターン

列をスライスなしで指定した場合はコピーが返ってくる。

また、行も列もスライスの記号だけを指定した場合もコピーが返ってくる。

1# 行も列もスライスの記号だけを指定した場合はコピーが返ってくる
2df_loc_slice5 = df.loc[:, :]
3print(df_loc_slice5._is_view)
4# False
5
6# 列をスライスなしで指定
7df_loc1 = df.loc[0:, ["A", "B", "C", "D"]]
8print(df_loc1._is_view)
9# False

ilocでビューとコピーが返ってくるパターン

ビューとコピーが返ってくるパターンはlocと同じ。

ビューが返ってくるパターン

基本的には列をスライス指定した場合にビューが返ってくる。

1
2# 列の一部をスライスを使って指定
3df_iloc_slice1 = df.iloc[:, :5]
4print(df_iloc_slice1._is_view)
5# True
6
7# 行と列の一部をスライスを使って指定
8df_iloc_slice2 = df.iloc[0:, :5]
9print(df_iloc_slice2._is_view)
10# True
11
12# 列の一部をスライスを使って指定。行は個別で指定。
13df_iloc_slice3 = df.iloc[[0, 1, 3], :5]
14print(df_iloc_slice3._is_view)
15# True
16
17# 行の一部をスライスを使って指定
18df_iloc_slice4 = df.iloc[0:, :]
19print(df_iloc_slice4._is_view)
20# True

コピーが返ってくるパターン

列をスライスなしで指定した場合はコピーが返ってくる。

また、行も列もスライスの記号だけを指定した場合もコピーが返ってくる。

1# 列をスライスなしで指定
2df_iloc1 = df.iloc[0:, [0, 1, 2, 3, 4]]
3print(df_iloc1._is_view)
4# False
5
6# 行も列もスライスの記号だけを指定した場合はコピーが返ってくる
7df_iloc_slice5 = df.iloc[:, :]
8print(df_iloc_slice5._is_view)
9# False

まとめ

  • 列をスライス指定すると基本的にはビューが返ってくる
  • 列を個別に指定すると基本的にはコピーが返ってくる
1# ビューが返ってくるパターン(locもilocも同じ)
2# 列の一部をスライスを使って指定
3df_loc_slice1 = df.loc[:, :"D"]
4
5# 行と列の一部をスライスを使って指定
6df_loc_slice2 = df.loc[0:, :"D"]
7
8# 列の一部をスライスを使って指定。行は個別で指定。
9df_loc_slice3 = df.loc[[0, 1, 2], "C":"D"]
10
11# 行の一部をスライスを使って指定
12df_loc_slice4 = df.loc[0:, :]
13
14# コピーが返ってくるパターン(locもilocも同じ)
15# 行も列もスライスの記号だけを指定した場合はコピーが返ってくる
16df_loc_slice5 = df.loc[:, :]
17
18# 列をスライスなしで指定
19df_loc1 = df.loc[0:, ["A", "B", "C", "D"]]

ビューが返ってくるメソッドには、loc, iloc以外にも、filter, query, groupbyなどがある。

スポンサーリンク
ABOUT ME
MAX
MAX
ITエンジニア、データサイエンティスト
新卒でSIerに入社し、フリーランスになってWEB系へ転向。
その後AIの世界へ足を踏み入れ、正社員に戻る。 テーブルデータの分析がメイン。
スポンサーリンク
記事URLをコピーしました