pandasで任意の位置の値を取得・変更するat, iat, loc, iloc
pandas.DataFrame
の任意の位置のデータを取り出したり変更(代入)したりする場合、pandas.DataFrame
のプロパティ、at
, iat
, loc
, iloc
を使う。at()
ではなくat[]
のように記述する。
- pandas.DataFrame.at — pandas 1.0.4 documentation
- pandas.DataFrame.iat — pandas 1.0.4 documentation
- pandas.DataFrame.loc — pandas 1.0.4 documentation
- pandas.DataFrame.iloc — pandas 1.0.4 documentation
以下のような違いがある。
- 位置の指定方法
at
,loc
: 行名(行ラベル)、列名(列ラベル)iat
,iloc
: 行番号、列番号
- 選択し取得・変更できるデータ
at
,iat
: 単独の要素の値loc
,iloc
: 単独および複数の要素の値- リスト、スライスで範囲を指定可能
- 行・列を選択して取得・変更可能
- その他
- 処理速度は
at
とiat
のほうがloc
とiloc
よりも高速 - ラベルと番号を組み合わせて位置を指定したい場合は
at
またはloc
とindex
やcolumn
を組み合わせる
- 処理速度は
これを踏まえて、ここでは以下の内容について説明する。
at
,iat
: 単独の要素の値を選択、取得・変更loc
,iloc
: 単独および複数の要素の値を選択、取得・変更- 単独の要素の値を選択
- 複数の要素の値を選択
- 行・列を選択
- 行名・列名が重複した値を持っている場合
- 番号とラベルで位置を指定する
- 行を
pandas.Series
で選択する際の暗黙の型変換
pandas.DataFrame
の行・列、pandas.Series
の要素の値を選択・取得する場合はインデックス参照df[]
も使用できる。
なお、DataFrame.get_value()
、DataFrame.ix[]
もあるが、どちらも最新のバージョンでは非推奨(Deprecated)。
DataFrame.get_value()
:v0.21.0
から非推奨DataFrame.ix[]
:v0.20.0
から非推奨
今回のサンプルコードでは以下のCSVデータをread_csv
で読み込んで使用する。
引数index_col
で最初の列をindex
としている。
import pandas as pd
df = pd.read_csv('data/src/sample_pandas_normal.csv', index_col=0)
print(df)
# age state point
# name
# Alice 24 NY 64
# Bob 42 CA 92
# Charlie 18 CA 70
# Dave 68 TX 70
# Ellen 24 CA 88
# Frank 30 NY 57
print(df.index.values)
# ['Alice' 'Bob' 'Charlie' 'Dave' 'Ellen' 'Frank']
print(df.columns.values)
# ['age' 'state' 'point']
at, iat : 単独の要素の値を選択、取得・変更
at
は行名と列名で位置を指定する。
データを取得するだけでなく、その位置に新たな値を設定(代入)することもできる。
print(df.at['Bob', 'age'])
print(df.at['Dave', 'state'])
# 42
# TX
df.at['Bob', 'age'] = 60
print(df.at['Bob', 'age'])
# 60
iat
は行番号と列番号で位置を指定する。行番号・列番号は0
はじまり。
iat
もat
と同じく、データを取得するだけでなく、その位置に新たな値を設定(代入)することができる。
print(df.iat[1, 0])
print(df.iat[3, 1])
# 60
# TX
df.iat[1, 0] = 42
print(df.iat[1, 0])
# 42
loc, iloc : 単独および複数の要素の値を選択、取得・変更
loc
とiloc
は単独の値だけでなく、範囲を指定して複数のデータを選択することができる。
loc
は行名と列名で位置を指定、iloc
は行番号と列番号で位置を指定する。
単独の要素の値を選択
単独の値にアクセスする場合はat
、iat
と同じ。処理速度はat
、iat
のほうが速い。
print(df.loc['Bob', 'age'])
print(df.iloc[3, 1])
# 42
# TX
データを参照するだけでなく、その位置に新たな値を設定(代入)することもできる。
df.loc['Bob', 'age'] = 60
print(df.loc['Bob', 'age'])
# 60
df.iloc[1, 0] = 42
print(df.iloc[1, 0])
# 42
複数の要素の値を選択
複数の値にアクセスする場合は、リスト[a, b, c, ...]
やスライスstart:stop:step
でデータの範囲・位置を指定する。pandas.Series
またはpandas.DataFrame
が返される。
スライスは通常のスライスと同じ書き方。step
は省略可。
スライスstart:stop:step
で指定するとき、iloc
で行番号・列番号を使う場合は通常のスライスと同様にstop
の一つ前までになるが、loc
で行名・列名を使う場合はstop
も含まれるので注意。
print(df.loc['Bob':'Dave', 'age'])
print(type(df.loc['Bob':'Dave', 'age']))
# name
# Bob 42
# Charlie 18
# Dave 68
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>
print(df.loc[:'Dave', ['age', 'point']])
print(type(df.loc[:'Dave', 'age':'point']))
# age point
# name
# Alice 24 64
# Bob 42 92
# Charlie 18 70
# Dave 68 70
# <class 'pandas.core.frame.DataFrame'>
print(df.iloc[:3, [0, 2]])
print(type(df.iloc[:3, [0, 2]]))
# age point
# name
# Alice 24 64
# Bob 42 92
# Charlie 18 70
# <class 'pandas.core.frame.DataFrame'>
step
を指定すると、奇数行または偶数行を抽出して取得したりもできる。
print(df.iloc[::2, 0])
print(type(df.iloc[::2, 0]))
# name
# Alice 24
# Charlie 18
# Ellen 24
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>
print(df.iloc[1::2, 0])
print(type(df.iloc[1::2, 0]))
# name
# Bob 42
# Dave 68
# Frank 30
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>
複数の値を一括で変更することも可能。
df.loc['Bob':'Dave', 'age'] = [20, 30, 40]
print(df.loc['Bob':'Dave', 'age'])
# name
# Bob 20
# Charlie 30
# Dave 40
# Name: age, dtype: int64
行・列を選択
インデックス参照df[]
で行・列を選択できるが、以下の指定方法に限られる。
- 行の選択 : 行名・行番号のスライス
- 列の選択 : 列名、または、列名のリスト
print(df['Bob':'Ellen'])
# age state point
# name
# Bob 20 CA 92
# Charlie 30 CA 70
# Dave 40 TX 70
# Ellen 24 CA 88
print(df[:3])
# age state point
# name
# Alice 24 NY 64
# Bob 20 CA 92
# Charlie 30 CA 70
print(df['age'])
# name
# Alice 24
# Bob 20
# Charlie 30
# Dave 40
# Ellen 24
# Frank 30
# Name: age, dtype: int64
print(df[['age', 'point']])
# age point
# name
# Alice 24 64
# Bob 20 92
# Charlie 30 70
# Dave 40 70
# Ellen 24 88
# Frank 30 57
詳細は以下の記事を参照。
loc
, iloc
で行・列を選択する場合はインデックス参照df[]
よりも柔軟に指定することができる。
loc
, iloc
で列の指定を省略すると行の参照になる。インデックス参照では出来ない行名・行番号単独での指定やリストによる指定も可能。
print(df.loc['Bob'])
print(type(df.loc['Bob']))
# age 20
# state CA
# point 92
# Name: Bob, dtype: object
# <class 'pandas.core.series.Series'>
print(df.iloc[[1, 4]])
print(type(df.iloc[[1, 4]]))
# age state point
# name
# Bob 20 CA 92
# Ellen 24 CA 88
# <class 'pandas.core.frame.DataFrame'>
loc
, iloc
で行の指定を:
(全体のスライス)にすると列を参照できる。インデックス参照では出来ないスライスによる指定が可能。iloc
で列番号を使うことも可能。
print(df.loc[:, 'age':'point'])
print(type(df.loc[:, 'age':'point']))
# age state point
# name
# Alice 24 NY 64
# Bob 20 CA 92
# Charlie 30 CA 70
# Dave 40 TX 70
# Ellen 24 CA 88
# Frank 30 NY 57
# <class 'pandas.core.frame.DataFrame'>
print(df.iloc[:, [0, 2]])
print(type(df.iloc[:, [0, 2]]))
# age point
# name
# Alice 24 64
# Bob 20 92
# Charlie 30 70
# Dave 40 70
# Ellen 24 88
# Frank 30 57
# <class 'pandas.core.frame.DataFrame'>
行名・行番号、列名・列番号を単独で指定して一行・一列を選択する場合はpandas.Series
が返されるが、同じ一行・一列を選択する場合でも、スライスやリストで指定した場合はpandas.DataFrame
となる。
print(df.loc['Bob'])
print(type(df.loc['Bob']))
# age 20
# state CA
# point 92
# Name: Bob, dtype: object
# <class 'pandas.core.series.Series'>
print(df.loc['Bob':'Bob'])
print(type(df.loc['Bob':'Bob']))
# age state point
# name
# Bob 20 CA 92
# <class 'pandas.core.frame.DataFrame'>
print(df.loc[['Bob']])
print(type(df.loc[['Bob']]))
# age state point
# name
# Bob 20 CA 92
# <class 'pandas.core.frame.DataFrame'>
print(df.iloc[:, 1])
print(type(df.iloc[:, 1]))
# name
# Alice NY
# Bob CA
# Charlie CA
# Dave TX
# Ellen CA
# Frank NY
# Name: state, dtype: object
# <class 'pandas.core.series.Series'>
print(df.iloc[:, 1:2])
print(type(df.iloc[:, 1:2]))
# state
# name
# Alice NY
# Bob CA
# Charlie CA
# Dave TX
# Ellen CA
# Frank NY
# <class 'pandas.core.frame.DataFrame'>
print(df.iloc[:, [1]])
print(type(df.iloc[:, [1]]))
# state
# name
# Alice NY
# Bob CA
# Charlie CA
# Dave TX
# Ellen CA
# Frank NY
# <class 'pandas.core.frame.DataFrame'>
特に、行をpandas.Series
で選択すると暗黙の型変換が行われる可能性があるので要注意。詳細は後述。
行名・列名が重複した値を持っている場合
行名(行ラベル)index
、列名(列ラベル)columns
に重複する値が含まれていてもエラーにならない。
重複した値を持つ列をindex
に指定した場合を例とする。
df_state = pd.read_csv('data/src/sample_pandas_normal.csv', index_col=2)
print(df_state)
# name age point
# state
# NY Alice 24 64
# CA Bob 42 92
# CA Charlie 18 70
# TX Dave 68 70
# CA Ellen 24 88
# NY Frank 30 57
print(df_state.index.values)
# ['NY' 'CA' 'CA' 'TX' 'CA' 'NY']
at
で重複した行名を指定すると、numpy.ndarray
で複数の値が返ってくる。
print(df_state.at['NY', 'age'])
print(type(df_state.at['NY', 'age']))
# [24 30]
# <class 'numpy.ndarray'>
loc
で重複した行名を指定すると、pandas.DataFrame
またはpandas.Series
で返ってくる。
print(df_state.loc['NY', 'age'])
print(type(df_state.loc['NY', 'age']))
# state
# NY 24
# NY 30
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>
print(df_state.loc['NY', ['age', 'point']])
print(type(df_state.loc['NY', ['age', 'point']]))
# age point
# state
# NY 24 64
# NY 30 57
# <class 'pandas.core.frame.DataFrame'>
iat
やiloc
で行番号で指定する場合は、行名が重複していても特に問題ない。
print(df_state.iat[0, 1])
# 24
混乱のもとになるので、強い理由がないのであれば行名・列名は一意の値にしたほうがよい。
行名・列名が一意の値になっている(重複していない)かどうかはindex.is_unique
, columns.is_unique
で確認できる。
print(df_state.index.is_unique)
# False
print(df_state.columns.is_unique)
# True
行名・列名の変更については以下の記事を参照。
番号とラベルで位置を指定する
行番号と列ラベルのように番号とラベルの組み合わせで位置を指定したい場合、at
またはloc
とindex
やcolumn
を使う方法がある。
index
やcolumn
で行番号または列番号からその行ラベル、列ラベルを取得できる。
print(df)
# age state point
# name
# Alice 24 NY 64
# Bob 20 CA 92
# Charlie 30 CA 70
# Dave 40 TX 70
# Ellen 24 CA 88
# Frank 30 NY 57
print(df.index[2])
# Charlie
print(df.columns[1])
# state
これとat
またはloc
を利用して、番号とラベルの組み合わせで位置を指定できる。
print(df.at[df.index[2], 'age'])
# 30
print(df.loc[['Alice', 'Dave'], df.columns[1]])
# name
# Alice NY
# Dave TX
# Name: state, dtype: object
上述のように、スライスstart:stop:step
で指定するとき、loc
で行ラベル・列ラベルを使う場合はstop
まで含まれるが、iloc
で行番号・列番号を使う場合はstop
の一つ前までになる。stop
の値を番号からラベルに変換する場合はindex[n - 1]
のようにする必要があるので注意。
以下のように[]
やloc
,iloc
を繰り返す書き方もできる。が、これはchained indexingと呼ばれ、SettingWithCopyWarning
という警告の要因となる。
また、次に説明するように、一行を選択する場合は暗黙の型変換が行われることもある。
先に示したindex
やcolumn
を使って一つのat
やloc
にまとめるほうがよい。
print(df['age'][2])
# 30
print(df.age[2])
# 30
print(df.loc[['Alice', 'Dave']].iloc[:, 1])
# name
# Alice NY
# Dave TX
# Name: state, dtype: object
行をpandas.Seriesで選択する際の暗黙の型変換
loc
やiloc
で一行をpandas.Series
で選択して取得する場合、データ型dtype
が統一されるため、元のpandas.DataFrame
の各列のデータ型が異なっていると暗黙の型変換が行われる。
整数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
で1行を取得すると、float
のpandas.Series
となる。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
を使うほうがよい。at
やiat
であれば元の型の要素が取得できる。
print(df_mix.at['B', 'col_int'])
# 1
print(type(df_mix.at['B', 'col_int']))
# <class 'numpy.int64'>
loc
やiloc
で要素数1のリストを指定した場合は、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