pandasで任意の位置の値を取得・変更するat, iat, loc, iloc
pandas.DataFrameの任意の位置のデータを取り出したり変更(代入)したりするには、at, iat, loc, ilocを使う。at()ではなくat[]のように記述する。
- pandas.DataFrame.at — pandas 2.0.3 documentation
- pandas.DataFrame.iat — pandas 2.0.3 documentation
- pandas.DataFrame.loc — pandas 2.0.3 documentation
- pandas.DataFrame.iloc — pandas 2.0.3 documentation
位置の指定方法および選択できる範囲に違いがある。
- 位置の指定方法
at,loc: 行名(行ラベル)、列名(列ラベル)iat,iloc: 行番号、列番号
- 選択し取得・変更できるデータ
at,iat: 単独の要素の値loc,iloc: 単独および複数の要素の値- リストやスライスで範囲を指定可能
- 単独の要素にアクセスする場合は
atとiatのほうが高速
pandas.DataFrameの行・列、pandas.Seriesの要素を選択する場合はインデックス参照df[]も使用できる。
- 関連記事: pandasのインデックス指定で行・列を抽出
なお、以前あったget_value()およびix[]は、バージョン1.0で削除された。
本記事のサンプルコードのpandasのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。以下のpandas.DataFrameを例とする。
import pandas as pd
print(pd.__version__)
# 2.0.3
df = pd.DataFrame({'col_0': ['00', '10', '20', '30', '40'],
'col_1': ['01', '11', '21', '31', '41'],
'col_2': ['02', '12', '22', '32', '42'],
'col_3': ['03', '13', '23', '33', '43']},
index=['row_0', 'row_1', 'row_2', 'row_3', 'row_4'])
print(df)
# col_0 col_1 col_2 col_3
# row_0 00 01 02 03
# row_1 10 11 12 13
# row_2 20 21 22 23
# row_3 30 31 32 33
# row_4 40 41 42 43
at, iat: 単独の要素の値を選択、取得・変更
atは行名と列名で位置を指定する。
データを取得するだけでなく、その位置に新たな値を設定(代入)することもできる。
print(df.at['row_1', 'col_2'])
# 12
df.at['row_1', 'col_2'] = '0'
print(df.at['row_1', 'col_2'])
# 0
iatは行番号と列番号で位置を指定する。行番号・列番号は0始まり。
atと同じく、データを取得するだけでなく、その位置に新たな値を設定(代入)できる。
print(df.iat[1, 2])
# 0
df.iat[1, 2] = '12'
print(df.iat[1, 2])
# 12
loc, iloc: 単独および複数の要素の値を選択、取得・変更
locとilocは、単独の値だけでなく範囲を指定して複数のデータを選択できる。locは行名と列名、ilocは行番号と列番号で位置を指定する。
単独の要素の値を選択
単独の値にアクセスする場合はat, iatと同じ。処理速度はat, iatのほうが速い。
print(df.loc['row_1', 'col_2'])
# 12
print(df.iloc[1, 2])
# 12
データを取得するだけでなく、その位置に新たな値を設定(代入)することもできる。
df.loc['row_1', 'col_2'] = '0'
print(df.loc['row_1', 'col_2'])
# 0
df.iloc[1, 2] = '12'
print(df.iloc[1, 2])
# 12
複数の要素の値を選択(リスト・スライスで範囲指定)
loc, ilocでは、行名や列番号などのスカラー値のほか、リスト[a, b, c, ...]やスライスstart:stop:stepでデータの範囲・位置を指定して複数の値にアクセスできる。
スライスの書き方は通常のスライスと同じ。stepは省略可。
スライスstart:stop:stepで指定するとき、loc(行名・列名)ではstopの要素も含まれるので注意。iloc(行番号・列番号)では通常のスライスと同様にstopの一つ前まで。
また、リストで指定すると、行や列の順番は指定したリストの順番になる。
print(df.loc['row_1':'row_3', ['col_2', 'col_0']])
# col_2 col_0
# row_1 12 10
# row_2 22 20
# row_3 32 30
print(df.iloc[1:3, [2, 0]])
# col_2 col_0
# row_1 12 10
# row_2 22 20
スライスでstepを指定すると、奇数行・偶数行を抽出したりできる。
print(df.iloc[::2, [0, 3]])
# col_0 col_3
# row_0 00 03
# row_2 20 23
# row_4 40 43
print(df.iloc[1::2, [0, 3]])
# col_0 col_3
# row_1 10 13
# row_3 30 33
複数の値を一括で変更することが可能。スカラー値を代入するとすべての要素がその値になる。範囲に対して代入する場合は、二次元リスト(リストのリスト)や二次元のNumPy配列ndarrayなどを使う。
df.iloc[1:3, [2, 0]] = '0'
print(df)
# col_0 col_1 col_2 col_3
# row_0 00 01 02 03
# row_1 0 11 0 13
# row_2 0 21 0 23
# row_3 30 31 32 33
# row_4 40 41 42 43
df.iloc[1:3, [2, 0]] = [['12', '10'], ['22', '20']]
print(df)
# col_0 col_1 col_2 col_3
# row_0 00 01 02 03
# row_1 10 11 12 13
# row_2 20 21 22 23
# row_3 30 31 32 33
# row_4 40 41 42 43
なお、行・列をスカラー値で指定して一行・一列を選択するとpandas.Seriesが返されるが、同じ一行・一列でも、スライスやリストで指定するとpandas.DataFrameとなる。
特に、行をpandas.Seriesで取得すると暗黙の型変換が行われる可能性があるので要注意。詳細は後述。
print(df.loc['row_1', ['col_0', 'col_2']])
print(type(df.loc['row_1', ['col_0', 'col_2']]))
# col_0 10
# col_2 12
# Name: row_1, dtype: object
# <class 'pandas.core.series.Series'>
print(df.loc['row_1':'row_1', ['col_0', 'col_2']])
print(type(df.loc['row_1':'row_1', ['col_0', 'col_2']]))
# col_0 col_2
# row_1 10 12
# <class 'pandas.core.frame.DataFrame'>
print(df.loc[['row_1'], ['col_0', 'col_2']])
print(type(df.loc[['row_1'], ['col_0', 'col_2']]))
# col_0 col_2
# row_1 10 12
# <class 'pandas.core.frame.DataFrame'>
行・列を選択
インデックス参照df[]で行・列を選択できるが、以下の指定方法に限られる。
- 行の選択: 行名・行番号のスライス
- 列の選択: 列名、または、列名のリスト
詳細は以下の記事を参照。
- 関連記事: pandasのインデックス指定で行・列を抽出
print(df['row_1':'row_3'])
# col_0 col_1 col_2 col_3
# row_1 10 11 12 13
# row_2 20 21 22 23
# row_3 30 31 32 33
print(df[1:3])
# col_0 col_1 col_2 col_3
# row_1 10 11 12 13
# row_2 20 21 22 23
print(df['col_1'])
# row_0 01
# row_1 11
# row_2 21
# row_3 31
# row_4 41
# Name: col_1, dtype: object
print(df[['col_1', 'col_3']])
# col_1 col_3
# row_0 01 03
# row_1 11 13
# row_2 21 23
# row_3 31 33
# row_4 41 43
loc, ilocを使うと、より柔軟に行・列を指定できる。
loc, ilocで列の指定を省略すると行が選択できる。行名・行番号単独での指定やリストによる指定も可能。
print(df.loc['row_2'])
# col_0 20
# col_1 21
# col_2 22
# col_3 23
# Name: row_2, dtype: object
print(df.iloc[[1, 3]])
# col_0 col_1 col_2 col_3
# row_1 10 11 12 13
# row_3 30 31 32 33
loc, ilocで行の指定を:(全体のスライス)にすると列を選択できる。スライスによる指定やilocでの列番号による指定も可能。
print(df.loc[:, 'col_1':])
# col_1 col_2 col_3
# row_0 01 02 03
# row_1 11 12 13
# row_2 21 22 23
# row_3 31 32 33
# row_4 41 42 43
print(df.iloc[:, 2])
# row_0 02
# row_1 12
# row_2 22
# row_3 32
# row_4 42
# Name: col_2, dtype: object
上述のように、行・列をスカラー値で指定して一行・一列を選択するとpandas.Seriesが返されるが、同じ一行・一列でも、スライスやリストで指定するとpandas.DataFrameとなる。
特に、行をpandas.Seriesで選択すると暗黙の型変換が行われる可能性があるので要注意。詳細は後述。
print(df.loc['row_2'])
print(type(df.loc['row_2']))
# col_0 20
# col_1 21
# col_2 22
# col_3 23
# Name: row_2, dtype: object
# <class 'pandas.core.series.Series'>
print(df.loc['row_2':'row_2'])
print(type(df.loc['row_2':'row_2']))
# col_0 col_1 col_2 col_3
# row_2 20 21 22 23
# <class 'pandas.core.frame.DataFrame'>
print(df.loc[['row_2']])
print(type(df.loc[['row_2']]))
# col_0 col_1 col_2 col_3
# row_2 20 21 22 23
# <class 'pandas.core.frame.DataFrame'>
boolのリストによるマスク(ブーリアンインデックス)
loc, ilocでは、bool値(True, False)のリストやnumpy.ndarrayを指定してデータをマスクすることができる。例では行に指定しているが、列に指定することも可能。
l_bool = [True, False, False, True, False]
print(df.loc[l_bool, ['col_0', 'col_2']])
# col_0 col_2
# row_0 00 02
# row_3 30 32
print(df.iloc[l_bool, [0, 2]])
# col_0 col_2
# row_0 00 02
# row_3 30 32
要素数が一致していないとエラー。
l_bool_wrong = [True, False, False]
# print(df.loc[l_bool_wrong, ['col_0', 'col_2']])
# IndexError: Boolean index has wrong length: 3 instead of 5
locではbool値を要素とするpandas.Seriesも指定可能。順番ではなくラベルを元にマスクされる。
s_bool = pd.Series([True, False, False, True, False], index=reversed(df.index))
print(s_bool)
# row_4 True
# row_3 False
# row_2 False
# row_1 True
# row_0 False
# dtype: bool
print(df.loc[s_bool, ['col_0', 'col_2']])
# col_0 col_2
# row_1 10 12
# row_4 40 42
ilocではpandas.Seriesは指定不可。
# print(df.iloc[s_bool, [0, 2]])
# ValueError: Location based indexing can only have [integer, integer slice (START point is INCLUDED, END point is EXCLUDED), listlike of integers, boolean array] types
locでも要素数やラベルが一致していないとエラー。
s_bool_wrong = pd.Series([True, False, False], index=['row_0', 'row_1', 'row_2'])
# print(df.loc[s_bool_wrong, ['col_0', 'col_2']])
# IndexingError: Unalignable boolean Series provided as indexer (index of the boolean Series and of the indexed object do not match).
s_bool_wrong = pd.Series([True, False, False, True, False],
index=['row_0', 'row_1', 'row_2', 'row_3', 'XXX'])
# print(df.loc[s_bool_wrong, ['col_0', 'col_2']])
# IndexingError: Unalignable boolean Series provided as indexer (index of the boolean Series and of the indexed object do not match).
行名・列名が重複している場合
行名index、列名columnsに重複する値が含まれていてもエラーにならない。
重複する行名・列名を持つpandas.DataFrameを例とする。
df_duplicated = df.rename(columns={'col_2': 'col_1'}, index={'row_3': 'row_2'})
print(df_duplicated)
# col_0 col_1 col_1 col_3
# row_0 00 01 02 03
# row_1 10 11 12 13
# row_2 20 21 22 23
# row_2 30 31 32 33
# row_4 40 41 42 43
at, locでは、重複したラベルを指定すると該当の複数要素が選択される。
print(df_duplicated.at['row_2', 'col_1'])
print(type(df_duplicated.at['row_2', 'col_1']))
# col_1 col_1
# row_2 21 22
# row_2 31 32
# <class 'pandas.core.frame.DataFrame'>
print(df_duplicated.loc[:'row_2', ['col_1', 'col_3']])
print(type(df_duplicated.loc[:'row_2', ['col_1', 'col_3']]))
# col_1 col_1 col_3
# row_0 01 02 03
# row_1 11 12 13
# row_2 21 22 23
# row_2 31 32 33
# <class 'pandas.core.frame.DataFrame'>
iat, ilocで行番号・列番号で指定する場合は、位置を基準とするのでラベルが重複していても特に問題ない。
print(df_duplicated.iat[2, 1])
# 21
print(df_duplicated.iloc[:2, [1, 3]])
# col_1 col_3
# row_0 01 03
# row_1 11 13
混乱のもとになるので、強い理由がないのであれば行名・列名は一意の値にしたほうがよい。
行名・列名が一意の値になっている(重複していない)かどうかはindex.is_unique, columns.is_uniqueで確認できる。
print(df_duplicated.index.is_unique)
# False
print(df_duplicated.columns.is_unique)
# False
行名・列名の変更については以下の記事を参照。
番号と名前(ラベル)で位置を指定
行番号と列名(列ラベル)のように番号と名前(ラベル)の組み合わせで位置を指定したい場合、atまたはlocとindex, column属性を使う方法がある。
index, column属性で行番号または列番号からその行名・列名を取得できる。
print(df.index[2])
# row_2
print(df.columns[2])
# col_2
スライスやリストを指定して複数の行名・列名を取得することも可能。
print(df.index[1:4])
# Index(['row_1', 'row_2', 'row_3'], dtype='object')
print(df.columns[[1, 3]])
# Index(['col_1', 'col_3'], dtype='object')
これを利用すると、atまたはlocで番号と名前の組み合わせで位置を指定できる。
print(df.at[df.index[2], 'col_2'])
# 22
print(df.loc[['row_0', 'row_3'], df.columns[[1, 3]]])
# col_1 col_3
# row_0 01 03
# row_3 31 33
以下のように[]やloc,ilocを繰り返す書き方もできるが、これはchained indexingと呼ばれ、SettingWithCopyWarningという警告の要因となる。
データを取得・確認するだけであれば問題ないが、新たな値を代入すると想定外の結果となる場合があるので注意。
print(df['col_2'][2])
# 22
print(df.loc[['row_0', 'row_3']].iloc[:, [1, 3]])
# col_1 col_3
# row_0 01 03
# row_3 31 33
行をpandas.Seriesで選択する際の暗黙の型変換
locやilocで一行を選択してpandas.Seriesで取得する場合、元のpandas.DataFrameの各列のデータ型dtypeが異なっていると暗黙の型変換が行われる。
整数intの列と浮動小数点数floatの列を持つpandas.DataFrameを例とする。
df_mix = pd.DataFrame({'col_int': [0, 1, 2], 'col_float': [0.1, 0.2, 0.3]}, index=['A', 'B', 'C'])
print(df_mix)
# col_int col_float
# A 0 0.1
# B 1 0.2
# C 2 0.3
print(df_mix.dtypes)
# col_int int64
# col_float float64
# dtype: object
locやilocで一行をpandas.Seriesとして取得すると、そのデータ型はfloatとなる。intの列にあった要素はfloatに変換される。
print(df_mix.loc['B'])
# col_int 1.0
# col_float 0.2
# Name: B, dtype: float64
print(type(df_mix.loc['B']))
# <class 'pandas.core.series.Series'>
以下のように[]を続けて書くと、floatに変換されたpandas.Seriesの要素が返される。元の型と異なる型として要素の値が返されてしまうので要注意。
print(df_mix.loc['B']['col_int'])
# 1.0
print(type(df_mix.loc['B']['col_int']))
# <class 'numpy.float64'>
[]やloc,ilocを繰り返す書き方は避けてatやiatを使えば、元の型の要素が取得できる。
print(df_mix.at['B', 'col_int'])
# 1
print(type(df_mix.at['B', 'col_int']))
# <class 'numpy.int64'>
なお、locやilocにおいてリストやスライスで一行を選択した場合は、pandas.Seriesではなくpandas.DataFrameが返される。この場合は元のデータ型dtypeが保持される。
print(df_mix.loc[['B']])
# col_int col_float
# B 1 0.2
print(type(df_mix.loc[['B']]))
# <class 'pandas.core.frame.DataFrame'>
print(df_mix.loc[['B']].dtypes)
# col_int int64
# col_float float64
# dtype: object