note.nkmk.me

pandasのインデックス参照で行・列を選択し取得

Date: 2018-02-12 / Modified: 2019-10-31 / tags: Python, pandas

pandas.DataFrame, pandas.Seriesのインデックス(添字)[]を指定することで、行・列または要素の値を選択し取得することができる。[]の中に指定する値のタイプによって取得できるデータが異なる。

ここでは以下の内容について説明する。

  • pandas.DataFrameの列を取得
    • [列名]: 単独の列をpandas.Seriesとして取得
    • [列名のリスト]: 単独または複数列をpandas.DataFrameとして取得
  • pandas.DataFrameの行を取得
    • [行名・行番号のスライス]: 単独または複数行をpandas.DataFrameとして取得
  • pandas.Seriesの値を取得
    • [ラベル名・番号]: 単独の要素の値をそれぞれの型で取得
    • [ラベル名・番号のリスト]: 単独または複数の要素の値をpandas.Seriesとして取得
    • [ラベル名・番号のスライス]: 単独または複数の要素の値をpandas.Seriesとして取得
  • pandas.DataFrameの要素の値を取得
  • 行名・列名が整数値の場合の注意点

pandas.DataFrameの場合、リストだと列の取得、スライスだと行の取得になるなど、慣れていないうちは混乱するような仕様になっている。at, iat, loc, ilocを使うとより明確に範囲の選択が可能。pandas.DataFrameで要素の値を抽出したり、列をスライス、行を行名・行番号やそのリストで選択することもできる。

以下の記事を参照。

今回のサンプルコードでは以下の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

既存のDataFrameの列をset_index()indexに指定することもできる。

スポンサーリンク

pandas.DataFrameの列を取得

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

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

print(df['age'])
print(type(df['age']))
# name
# Alice      24
# Bob        42
# Charlie    18
# Dave       68
# Ellen      24
# Frank      30
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>

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

print(df.age)
print(type(df.age))
# name
# Alice      24
# Bob        42
# Charlie    18
# Dave       68
# Ellen      24
# Frank      30
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>

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

列名のリストを指定すると、選択した複数列が抽出されpandas.DataFrameとして取得できる。

print(df[['age', 'point']])
print(type(df[['age', 'point']]))
#          age  point
# name               
# Alice     24     64
# Bob       42     92
# Charlie   18     70
# Dave      68     70
# Ellen     24     88
# Frank     30     57
# <class 'pandas.core.frame.DataFrame'>

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

print(df[['age']])
print(type(df[['age']]))
#          age
# name        
# Alice     24
# Bob       42
# Charlie   18
# Dave      68
# Ellen     24
# Frank     30
# <class 'pandas.core.frame.DataFrame'>

スライスの場合は空のpandas.DataFrameとなる。スライスは行の指定だとみなされるため(後述)。

print(df['age':'point'])
# Empty DataFrame
# Columns: [age, state, point]
# Index: []

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

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

print(df.iloc[:, [0, 2]])
print(type(df.iloc[:, [0, 2]]))
#          age  point
# name               
# Alice     24     64
# Bob       42     92
# Charlie   18     70
# Dave      68     70
# Ellen     24     88
# Frank     30     57
# <class 'pandas.core.frame.DataFrame'>

pandas.DataFrameの行を取得

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

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

print(df[1:4])
print(type(df[1:4]))
#          age state  point
# name                     
# Bob       42    CA     92
# Charlie   18    CA     70
# Dave      68    TX     70
# <class 'pandas.core.frame.DataFrame'>

マイナスの値を指定したり、start:stop:stepのようにstepを指定したりすることも可能。奇数行または偶数行を抽出して取得できる。

print(df[:-3])
print(type(df[1:-3]))
#          age state  point
# name                     
# Alice     24    NY     64
# Bob       42    CA     92
# Charlie   18    CA     70
# <class 'pandas.core.frame.DataFrame'>

print(df[::2])
print(type(df[::2]))
#          age state  point
# name                     
# Alice     24    NY     64
# Charlie   18    CA     70
# Ellen     24    CA     88
# <class 'pandas.core.frame.DataFrame'>

print(df[1::2])
print(type(df[1::2]))
#        age state  point
# name                   
# Bob     42    CA     92
# Dave    68    TX     70
# Frank   30    NY     57
# <class 'pandas.core.frame.DataFrame'>

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

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

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

print(df[1:2])
print(type(df[1:2]))
#       age state  point
# name                  
# Bob    42    CA     92
# <class 'pandas.core.frame.DataFrame'>

行番号ではなく行名(行ラベル)でスライスを指定することもできる。行名(行ラベル)のスライスの場合はstopの行も選択されるという違いがある。

print(df['Bob':'Ellen'])
print(type(df['Bob':'Ellen']))
#          age state  point
# name                     
# Bob       42    CA     92
# Charlie   18    CA     70
# Dave      68    TX     70
# Ellen     24    CA     88
# <class 'pandas.core.frame.DataFrame'>

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

print(df.loc['Bob'])
print(type(df.loc['Bob']))
# age      42
# state    CA
# point    92
# Name: Bob, dtype: object
# <class 'pandas.core.series.Series'>

print(df.loc[['Bob', 'Ellen']])
print(type(df.loc[['Bob', 'Ellen']]))
#        age state  point
# name                   
# Bob     42    CA     92
# Ellen   24    CA     88
# <class 'pandas.core.frame.DataFrame'>

print(df.iloc[[1, 4]])
print(type(df.iloc[[1, 4]]))
#        age state  point
# name                   
# Bob     42    CA     92
# Ellen   24    CA     88
# <class 'pandas.core.frame.DataFrame'>

pandas.Seriesの値を取得

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

s = df['age']
print(s)
# name
# Alice      24
# Bob        42
# Charlie    18
# Dave       68
# Ellen      24
# Frank      30
# Name: age, dtype: int64

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

ラベル名・番号を単独で指定した場合はその値がそのまま取得できる。番号の場合、負の値で末尾からの位置を指定可能。-1が末尾(一番最後)。

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

print(s[3])
print(type(s[3]))
# 68
# <class 'numpy.int64'>

print(s['Dave'])
print(type(s['Dave']))
# 68
# <class 'numpy.int64'>

print(s[-1])
print(type(s[-1]))
# 30
# <class 'numpy.int64'>

print(s.Dave)
print(type(s.Dave))
# 68
# <class 'numpy.int64'>

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

リストの場合は選択した複数の値をpandas.Seriesとして取得できる。

print(s[[1, 3]])
print(type(s[[1, 3]]))
# name
# Bob     42
# Dave    68
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>

print(s[['Bob', 'Dave']])
print(type(s[['Bob', 'Dave']]))
# name
# Bob     42
# Dave    68
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>

要素数1のリストの場合、その要素そのものではなく要素数1のpandas.Seriesとなる。

print(s[[1]])
print(type(s[[1]]))
# name
# Bob    42
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>

print(s[['Bob']])
print(type(s[['Bob']]))
# name
# Bob    42
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>

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

スライスの場合も選択した複数の値をpandas.Seriesとして取得できる。ラベル名のスライスの場合はstopの行も選択される。

print(s[1:3])
print(type(s[1:3]))
# name
# Bob        42
# Charlie    18
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>

print(s['Bob':'Dave'])
print(type(s['Bob':'Dave']))
# name
# Bob        42
# Charlie    18
# Dave       68
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>

要素1個が選択された場合、要素数1のpandas.Seriesとなる。

print(s[1:2])
print(type(s[1:2]))
# name
# Bob    42
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>

print(s['Bob':'Bob'])
print(type(s['Bob':'Bob']))
# name
# Bob    42
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>

pandas.DataFrameの要素の値を取得

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

print(df['age']['Alice'])
# 24

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

print(df['Bob':'Dave'][['age', 'point']])
#          age  point
# name               
# Bob       42     92
# Charlie   18     70
# Dave      68     70

ただし、このようにインデックス参照を繰り返す([...][...])方法はchained indexingと呼ばれ、選択範囲に値を代入する際にSettingWithCopyWarningが発生する場合があるため推奨されていない。

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

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

print(df.at['Alice', 'age'])
# 24

print(df.loc['Bob':'Dave', ['age', 'point']])
#          age  point
# name               
# Bob       42     92
# Charlie   18     70
# Dave      68     70

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

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

以下の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(行番号)を使う。

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を使えばOK。

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

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

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

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事