pandasのインデックス指定で行・列を抽出

Modified: | Tags: Python, pandas

pandas.DataFrame, pandas.Series[]によるインデックス指定で、行・列および要素を選択し抽出・取得できる。[]に指定する値のタイプによって取得できるデータが異なる。

at, iat, loc, ilocを使うと、より明確に範囲の選択が可能。列をスライス、行を行名・行番号やそのリストで選択することもできる。

列をindexに割り当てるにはset_index()を使う。

本記事のサンプルコードの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

pandas.DataFrameの列を抽出

[列名]: 単独の列をpandas.Seriesとして取得

[]に列名(列ラベル)を指定すると、選択した列をpandas.Seriesとして取得できる。

print(df['col_2'])
print(type(df['col_2']))
# row_0    02
# row_1    12
# row_2    22
# row_3    32
# row_4    42
# Name: col_2, dtype: object
# <class 'pandas.core.series.Series'>

属性のように.に続けて列名を指定することもできる。ただし、列名が既存のメソッド名や属性名などと重複しているとそちらが優先されるので注意。

print(df.col_2)
# row_0    02
# row_1    12
# row_2    22
# row_3    32
# row_4    42
# Name: col_2, dtype: object

[列名のリスト]: 単独・複数列をpandas.DataFrameとして取得

列名のリストを指定すると、選択した複数列をpandas.DataFrameとして取得できる。列の順番は指定したリストの順番になる。

print(df[['col_2', 'col_0']])
print(type(df[['col_2', 'col_0']]))
#       col_2 col_0
# row_0    02    00
# row_1    12    10
# row_2    22    20
# row_3    32    30
# row_4    42    40
# <class 'pandas.core.frame.DataFrame'>

要素数1のリストも一列のpandas.DataFrameとなる。pandas.Seriesではない。

print(df[['col_2']])
print(type(df[['col_2']]))
#       col_2
# row_0    02
# row_1    12
# row_2    22
# row_3    32
# row_4    42
# <class 'pandas.core.frame.DataFrame'>

loc, ilocの例

locを用いると列名のスライスも指定可能。またilocを用いると列番号で指定することもできる。詳細は以下の記事を参照。

print(df.loc[:, 'col_1':'col_3'])
#       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.DataFrameの行を抽出

[行名・行番号のスライス]: 単独・複数行をpandas.DataFrameとして取得

[]にスライスを指定すると、該当範囲の行をpandas.DataFrameとして取得できる。

print(df[1:4])
print(type(df[1:4]))
#       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
# <class 'pandas.core.frame.DataFrame'>

通常のスライスと同様に、マイナスの値で末尾からの位置を指定したり、start:stop:stepのようにstepを指定したりすることも可能。奇数行または偶数行を抽出して取得できる。

print(df[:-3])
#       col_0 col_1 col_2 col_3
# row_0    00    01    02    03
# row_1    10    11    12    13

print(df[::2])
#       col_0 col_1 col_2 col_3
# row_0    00    01    02    03
# row_2    20    21    22    23
# row_4    40    41    42    43

print(df[1::2])
#       col_0 col_1 col_2 col_3
# row_1    10    11    12    13
# row_3    30    31    32    33

あくまでもスライスでないとダメで、行番号を単独で指定するとエラーとなる。

# print(df[1])
# KeyError: 1

一行だけ選択される場合も、取得できるのはpandas.DataFramepandas.Seriesにはならない。

print(df[1:2])
print(type(df[1:2]))
#       col_0 col_1 col_2 col_3
# row_1    10    11    12    13
# <class 'pandas.core.frame.DataFrame'>

行番号ではなく行名(行ラベル)でスライスを指定することもできる。行名(行ラベル)のスライスの場合はstart:stopstopの行も含まれる。

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

loc, ilocの例

locilocを用いると、行名・行番号を単独で指定してpandas.Seriesとして取得したり、リストで複数行を選択したりできる。詳細は以下の記事を参照。

print(df.loc[['row_1', 'row_3']])
#       col_0 col_1 col_2 col_3
# row_1    10    11    12    13
# row_3    30    31    32    33

print(df.iloc[1])
# col_0    10
# col_1    11
# col_2    12
# col_3    13
# Name: row_1, dtype: object

[boolのリスト・Series]: Trueの行をpandas.DataFrameとして取得

bool値(True, False)を要素とするリストやnumpy.ndarrayを指定すると、Trueの行が抽出されpandas.DataFrameとして取得できる。

l_bool = [True, False, False, True, True]
print(df[l_bool])
print(type(df[l_bool]))
#       col_0 col_1 col_2 col_3
# row_0    00    01    02    03
# row_3    30    31    32    33
# row_4    40    41    42    43
# <class 'pandas.core.frame.DataFrame'>

Trueの要素が1つだけでも、一行のpandas.DataFramepandas.Seriesにはならない。

l_bool = [True, False, False, False, False]
print(df[l_bool])
print(type(df[l_bool]))
#       col_0 col_1 col_2 col_3
# row_0    00    01    02    03
# <class 'pandas.core.frame.DataFrame'>

要素数と行数が一致していないとエラー。

# print(df[[True, False, False]])
# ValueError: Item wrong length 3 instead of 5.

bool値を要素とするpandas.Seriesも指定可能。順番ではなくラベルに従ってマスクされる。

s_bool = pd.Series([True, False, False, True, True], index=reversed(df.index))
print(s_bool)
# row_4     True
# row_3    False
# row_2    False
# row_1     True
# row_0     True
# dtype: bool

print(df[s_bool])
#       col_0 col_1 col_2 col_3
# row_0    00    01    02    03
# row_1    10    11    12    13
# row_4    40    41    42    43

要素数およびラベルが一致していないとエラー。

s_bool_wrong = pd.Series([True, False, False, True, True],
                         index=['A', 'B', 'C', 'D', 'E'])
# print(df[s_bool_wrong])
# IndexingError: Unalignable boolean Series provided as indexer (index of the boolean Series and of the indexed object do not match).

bool値を要素とするpandas.Seriesによる行の抽出を利用して、条件を満たす行を抽出できる。

pandas.Seriesの要素の値を取得

以下のpandas.Seriesを例とする。

s = df['col_0']
print(s)
# row_0    00
# row_1    10
# row_2    20
# row_3    30
# row_4    40
# Name: col_0, dtype: object

[ラベル名・番号]: 単独の要素の値をそれぞれの型で取得

ラベル名・番号を単独で指定すると、その値がそのまま取得できる。

print(s[3])
print(type(s[3]))
# 30
# <class 'str'>

番号の場合、負の値で末尾からの位置を指定可能。-1が末尾(一番最後)。

また、pandas.DataFrameの列名指定のように、.に続けてラベル名を指定することもできる。既存のメソッド名や属性名などと重複しているとそちらが優先されるので注意。

print(s[-1])
# 40

print(s['row_0'])
# 00

print(s.row_0)
# 00

[ラベル名・番号のリスト]: 単独・複数の要素の値をpandas.Seriesとして取得

リストの場合は選択した複数の値をpandas.Seriesとして取得できる。順番は指定したリストの順番になる。要素数1のリストの場合も、要素自体ではなく要素数1のpandas.Series

print(s[[3, 1]])
print(type(s[[3, 1]]))
# row_3    30
# row_1    10
# Name: col_0, dtype: object
# <class 'pandas.core.series.Series'>

print(s[[1]])
print(type(s[[1]]))
# row_1    10
# Name: col_0, dtype: object
# <class 'pandas.core.series.Series'>

ラベル名のリストも指定可能。

print(s[['row_3', 'row_1']])
# row_3    30
# row_1    10
# Name: col_0, dtype: object

print(s[['row_1']])
# row_1    10
# Name: col_0, dtype: object

[ラベル名・番号のスライス]: 単独・複数の要素の値をpandas.Seriesとして取得

スライスの場合も選択した複数の値をpandas.Seriesとして取得できる。要素1個が選択された場合、要素数1のpandas.Seriesとなる。

print(s[1:3])
print(type(s[1:3]))
# row_1    10
# row_2    20
# Name: col_0, dtype: object
# <class 'pandas.core.series.Series'>

print(s[1:2])
print(type(s[1:2]))
# row_1    10
# Name: col_0, dtype: object
# <class 'pandas.core.series.Series'>

ラベル名のスライスの場合はstart:stopstopの要素も含まれる。

print(s['row_1':'row_3'])
# row_1    10
# row_2    20
# row_3    30
# Name: col_0, dtype: object

print(s['row_1':'row_1'])
# row_1    10
# Name: col_0, dtype: object

[boolのリスト・Series]: Trueの要素をpandas.Seriesとして取得

bool値(True, False)のリストやnumpy.ndarrayを指定すると、Trueの要素が抽出されpandas.Seriesとして取得できる。

l_bool = [True, False, False, True, True]
print(s[l_bool])
print(type(s[l_bool]))
# row_0    00
# row_3    30
# row_4    40
# Name: col_0, dtype: object
# <class 'pandas.core.series.Series'>

Trueの要素が1つだけでも、要素数1のpandas.Series

l_bool = [True, False, False, False, False]
print(s[l_bool])
print(type(s[l_bool]))
# row_0    00
# Name: col_0, dtype: object
# <class 'pandas.core.series.Series'>

要素数が一致していないとエラー。

# print(s[[True, False, False]])
# IndexError: Boolean index has wrong length: 3 instead of 5

bool値を要素とするpandas.Seriesも指定可能。順番ではなくラベルを元にマスクされる。

s_bool = pd.Series(l_bool, index=reversed(df.index))
print(s_bool)
# row_4     True
# row_3    False
# row_2    False
# row_1    False
# row_0    False
# dtype: bool

print(s[s_bool])
# row_4    40
# Name: col_0, dtype: object

要素数およびラベルが一致していないとエラー。

s_bool_wrong = pd.Series([True, False, False, True, True],
                         index=['A', 'B', 'C', 'D', 'E'])
# print(s[s_bool_wrong])
# IndexingError: Unalignable boolean Series provided as indexer (index of the boolean Series and of the indexed object do not match).

pandas.DataFrameの要素の値を取得

pandas.DataFrameからpandas.Seriesを抽出し、さらにそのpandas.Seriesから値を選択して取得することで、pandas.DataFrameから要素の値を取得できる。

print(df['col_1']['row_2'])
# 21

スライスやリストを組み合わせて任意の範囲を抽出することもできる。

print(df['row_1':'row_3'][['col_1', 'col_3']])
#       col_1 col_3
# row_1    11    13
# row_2    21    23
# row_3    31    33

このようにインデックス指定を繰り返す([...][...])方法はchained indexingと呼ばれ、選択範囲に値を代入する際にSettingWithCopyWarningが発生する場合がある。

詳細は以下の記事を参照。

at, iat, loc, ilocを使うと行・列を一度に指定して選択可能。こちらのほうが望ましい。

print(df.at['row_2', 'col_1'])
# 21

print(df.loc['row_1':'row_3', ['col_1', 'col_3']])
#       col_1 col_3
# row_1    11    13
# row_2    21    23
# row_3    31    33

行名・列名が整数値の場合の注意点

これまでの例は行名・列名が文字列だったが、行名・列名が整数値の場合は注意が必要。

以下のpandas.DataFrameを例とする。

df = pd.DataFrame([[0, 10, 20], [30, 40, 50], [60, 70, 80]],
                  index=[2, 0, 1], columns=[1, 2, 0])
print(df)
#     1   2   0
# 2   0  10  20
# 0  30  40  50
# 1  60  70  80

[スカラー値], [リスト]の場合、指定した値は列名とみなされる。

print(df[0])
# 2    20
# 0    50
# 1    80
# Name: 0, dtype: int64

print(df[[0, 2]])
#     0   2
# 2  20  10
# 0  50  40
# 1  80  70

[スライス]の場合、指定した値は行名ではなく行番号とみなされる。負の値も使える。

print(df[:2])
#     1   2   0
# 2   0  10  20
# 0  30  40  50

print(df[-2:])
#     1   2   0
# 0  30  40  50
# 1  60  70  80

行名か行番号かを明確に指定するにはloc(行名)またはiloc(行番号)を使う。locの場合、start:stopstopの行も含まれる。

print(df.loc[:2])
#    1   2   0
# 2  0  10  20

print(df.iloc[:2])
#     1   2   0
# 2   0  10  20
# 0  30  40  50

pandas.Seriesの場合。以下を例とする。

s = df[2]
print(s)
# 2    10
# 0    40
# 1    70
# Name: 2, dtype: int64

pandas.Seriesでは指定した値が番号ではなくラベル名だとみなされる。

print(s[0])
# 40

ラベル名か番号かを明確に指定するにはat, iatを使う。loc, ilocでもよいが、at, iatのほうが高速。

print(s.at[0])
# 40

print(s.iat[0])
# 10

特に、末尾の値を取得しようとして[-1]とすると-1という名前のラベルの値の選択とみなされてしまうので注意。iatを使えば問題ない。

# print(s[-1])
# KeyError: -1

print(s.iat[-1])
# 70

このように、行名・列名などが整数値の場合は混乱を避けるために、at, iat, loc, ilocを使ったほうがよい。

関連カテゴリー

関連記事