pandas.DataFrameの任意の行・列を指定してデータを取得・変更します。
機械学習の前処理では、条件に合う特定の行・列を抽出する場合や、条件分岐でデータをふるい分ける場合などに使用します。
行・列を指定する方法
pandas.DataFrameで作成したデータの一部を取得して確認したり、データを変更したい場合は、データが格納されている位置を特定して抽出します。
・行ラベル(行名)、列ラベル(列名)で指定: loc、at
・行番号、列番号で指定: iloc、iat
行ラベルと行番号は混乱しやすいので、違いを確認します。
行番号と行ラベルが同じ
pandas.DataFrameを作成するとき、行ラベル(行名)を指定しないと0からの連番が割り振られます。
この場合は、以下のように行番号と行ラベルが同じになっています。
import pandas as pd
data_abc = ['a', 'b', 'c', 'd', 'e']
data_123 = [1, 2, 3, 4, 5]
sample = pd.DataFrame({'abc': data_abc, '123':data_123})
sample
abc 123
0 a 1
1 b 2
2 c 3
3 d 4
4 e 5
左端の列がインデックス(行ラベル・行名)です。
インデックスは、縦方向のラベルで、列名ではないです。
カラムが、横方向のラベル。
ilocで行番号を指定して取得:iloc[行番号, 列番号(省略可)]
行番号は0から始まる整数です。
2行目(行番号「1」)を取得してみます。
sample.iloc[1]
abc b
123 2
Name: 1, dtype: object
0列と1列を、列番号で指定します。
リスト形式で取得したい列番号を指定します。
sample.iloc[1, [0, 1]]
abc b
123 2
Name: 1, dtype: object
列番号は、get_locメソッドで取得できます。
col_abc = sample.columns.get_loc('abc')
col_abc
0
get_locメソッド を使用すると、列の追加や削除があっても正確な列番号を指定できます。
# 列番号を取得
col_abc = sample.columns.get_loc('abc')
col_123 = sample.columns.get_loc('123')
sample.iloc[1, [col_abc, col_123]]
abc b
123 2
Name: 1, dtype: object
列番号を:(コロン)にすると、すべて取得できます。
sample.iloc[1, :]
abc b
123 2
Name: 1, dtype: object
locで行ラベルを指定して取得:loc[行ラベル, 列ラベル(省略可)]
行ラベルで行、列ラベルで列を指定します。
この例のデータは、行番号と行ラベルは同じ値であることに注意してください。
第1引数の「1」は、行ラベルを指定しています。
sample.loc[1, ['abc', '123']]
abc b
123 2
Name: 1, dtype: object
ilocと同様に、第2引数を省略したり、第2引数を:(コロン)にすると、すべての列を取得できます。
行番号と行ラベルが異なる
データsampleの奇数行を抽出して、データkisuuを作成します。
列ラベル「123」が1からの連番なので、2で割った余りが1となる行が奇数行です。
kisuu = sample[sample['123']%2 == 1].copy()
kisuu
abc 123
0 a 1
2 c 3
4 e 5
ilocで行番号を指定して取得:iloc[行番号, 列番号(省略可)]
行番号は0から始まる整数です。
2行目 (行番号「1」) を取得してみます。
kisuu.iloc[1]
abc c
123 3
Name: 2, dtype: object
行番号「1」を指定して、行ラベル「2」の行が取得できました。
ilocで指定するのは行番号なので、単純に縦方向の連番となります。
locで行ラベルを指定して取得:loc[行ラベル, 列ラベル(省略可)]
行ラベル「2」を指定して行を取得してみます。
kisuu.loc[2]
abc c
123 3
Name: 2, dtype: object
行ラベル「2」を指定して、行番号「1」の行が取得できました。
locで指定するのは行ラベルなので、インデックスで表示される値となります。
取得・変更するデータ
pandas.DataFrameのデータを取得するとき、要素の数によってプロパティが異なります。
・要素の数が1つ: at[行ラベル, 列ラベル(省略不可)]、iat[行番号, 列番号(省略不可)]
・要素の数が複数: loc [行ラベル, 列ラベル (省略可) ] 、iloc [行番号, 列番号(省略可)]
loc、ilocで1列のみを指定すると、at、iatと同様に要素の数が1つのデータを取得できます。
要素の数が1つの場合は、 at、iat の方が高速。
行ラベルが重複する場合
pandas.DataFrameを作成するときに、インデックスを指定していなければ、0からの連番になりますが、カラムと同じく行ごとにラベルを付けることができます。
また、複数の行に同じラベルを使用することもできます。
data_abc = ['a', 'b', 'c', 'd', 'e']
data_123 = [1, 2, 3, 4, 5]
data_index = [1, 2, 3, 1, 2]
sample_dup = pd.DataFrame({'abc': data_abc, '123':data_123}, index=data_index)
sample_dup
abc 123
1 a 1
2 b 2
3 c 3
1 d 4
2 e 5
行番号「0, 4」は、同じ行ラベル「1」になっています。
locで行ラベル「1」を取得してみます。
sample_dup.loc[1]
abc 123
1 a 1
1 d 4
locで、重複した行ラベルを指定すると、複数の値が取得されます。
pandas.DataFrameから、for文で1行ずつ取り出す処理などではエラーとなりますので、行ラベルでの指定は注意が必要です。
行・列を指定して取得・変更するコード例
使用するデータを作成します。
行ラベルを意識するため、行ラベルは「row+行番号」とします。
data_abc = ['a', 'b', 'c', 'd', 'e']
data_123 = [1, 2, 3, 4, 5]
data_index = ['row0', 'row1', 'row2', 'row3', 'row4']
sample = pd.DataFrame({'abc': data_abc, '123':data_123}, index=data_index)
sample
abc 123
row0 a 1
row1 b 2
row2 c 3
row3 d 4
row4 e 5
for文で、ilocを使用して値を変更
行番号を指定して取得した行の、列番号「1」の値を10倍します。
len(sample)で、データの行数を取得。>> 5
range(行数)で、行番号のリストを取得。 >> [0, 1, 2, 3, 4]
for i in range(len(sample)):
sample.iloc[i,1] = sample.iloc[i, 1] * 10
print(i, sample.iloc[i,1])
0 10
1 20
2 30
3 40
4 50
for文で、locを使用して値を変更
データsampleを更に加工します。
行ラベルを指定して取得した行の、列ラベル「123」の値を10倍します。
sample.indexで、行ラベルのリストを取得。 >> [row0, row1, row2, row3, row4]
for i in sample.index:
sample.loc[i, '123'] = sample.loc[i, '123'] * 10
print(i, sample.loc[i, '123'])
row0 100
row1 200
row2 300
row3 400
row4 500
行ラベルに重複がなかったので、エラーは発生しませんでした。
行ラベルが重複していないかは、sample.index.is_uniqueで確認できます。
重複していない場合は、True。
重複している場合は、False。
余談
pandas.DataFrameのデータ処理をfor文を使って行いました。
for文は、各行・各列に対する処理が直感的に理解しやすいですが、処理速度が遅いです。
少ないデータ数なら問題はありませんし、気長に処理を待つことも1つの選択肢です。
処理時間が待てなくなってきたときは、
・pandas.DataFrameのスライシングを使用する
・pandas.DataFrameのapplyメソッドを使用する
など、処理速度の速い方法を選択してみるとよいです。
参考までに、applyメソッドの例です。
for文の処理をぐるぐる回す感じをイメージして、各行の値を無名関数に入れてください。
f_X10 = lambda x: x*10 # 値を10倍にする無名関数。
sample['123'] = sample['123'].apply(f_X10) # 無名関数を列ラベル「123」に適用
sample
おすすめは、スライシングです。
sample.iloc[:, 1] = sample.iloc[:, 1] * 10
sample
まとめ
- 行ラベル(行名)・列ラベル(列名)と、行番号・列番号の違いを理解できた
- pandas.DataFrameのデータを、任意の位置で取得・変更することができた