Pandas#
# 警告メッセージを非表示
import warnings
warnings.filterwarnings("ignore")
Pandas
はNumPy
のarray
のようにデータを扱うパッケージだが,Pandas特有のデータ構造を提供し複雑なデータを扱いやすくしている。例えば,行列にはラベルを使うことにより,インデックス番号だけではなくラベル名(例えば,GDP)を使って操作することが可能となる。PandasにはDataFrame
(データフレーム)とSeries
(シリーズ)と呼ばれるオブジェクトがある。前者はエクセルのスプレッド・シートをイメージすれば良いだろう。後者は,スプレッド・シートから1つの行または列を取り出したようなデータと思えば良い。また,Pandas
はNumPy
に基づいているため,ベクトル演算(ブロードキャスティング)の機能が使える。
ここで説明できない他の使い方についてはこのサイトとこのサイトが参考になる。
通常pd
という名前で読み込む。
import pandas as pd
データの読み込みとデータのチェック#
様々なデータを読み込むことが可能だが,ここではread_csv()
関数を使ってインターネット上の.csv
ファイルを読み込む。
# url の設定
url = 'https://raw.githubusercontent.com/Haruyama-KobeU/Py4Basics/master/data/data1.csv'
# 読み込み
df = pd.read_csv(url)
df
全体を表示させる。
df
year | gdp | inv | con | pop | id | |
---|---|---|---|---|---|---|
0 | 2000 | 100 | 20.0 | 80.0 | 8 | a |
1 | 2001 | 95 | 25.0 | 70.0 | 9 | b |
2 | 2002 | 93 | 21.0 | 72.0 | 10 | a |
3 | 2003 | 100 | 30.0 | 70.0 | 11 | b |
4 | 2004 | 110 | 39.0 | 71.0 | 12 | a |
5 | 2005 | 115 | 55.0 | 60.0 | 14 | b |
6 | 2006 | 113 | 50.0 | 63.0 | 15 | a |
7 | 2007 | 118 | 53.0 | 65.0 | 17 | b |
8 | 2008 | 119 | 60.0 | 59.0 | 18 | a |
9 | 2009 | 200 | 62.0 | NaN | 20 | b |
10 | 2010 | 210 | NaN | NaN | 21 | a |
行ラベルがインデックス(番号)のままなので,列year
を行ラベルに設定する。
set_index()
:選択された列を行ラベルにするメソッド
df = df.set_index('year')
df
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2000 | 100 | 20.0 | 80.0 | 8 | a |
2001 | 95 | 25.0 | 70.0 | 9 | b |
2002 | 93 | 21.0 | 72.0 | 10 | a |
2003 | 100 | 30.0 | 70.0 | 11 | b |
2004 | 110 | 39.0 | 71.0 | 12 | a |
2005 | 115 | 55.0 | 60.0 | 14 | b |
2006 | 113 | 50.0 | 63.0 | 15 | a |
2007 | 118 | 53.0 | 65.0 | 17 | b |
2008 | 119 | 60.0 | 59.0 | 18 | a |
2009 | 200 | 62.0 | NaN | 20 | b |
2010 | 210 | NaN | NaN | 21 | a |
Tip
df.set_index('year')
は直接df
に影響を与えない。単に,書き換えるとどうなるかを表示している。ここではdf
に再度割り当てることによりdf
自体を上書きしている。出力にある
NaN
(Not a Number)は欠損値を示す。行ラベルに
year
という列名が残るが,それを消す方法:メソッド
.rename_axis('')
を使う。ここで''
は空の文字列である。.rename_axis(None)
でも同じ結果となる。df.index.name = ''
とする。ここで''
は空の文字列
行数が大きい場合(例えば,10000),全てを表示してもあまり意味がない。そこでよく使うメソッドに最初や最後の数行だけを表示すものがある。
df
の最初の5行を表示させる。
df.head()
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2000 | 100 | 20.0 | 80.0 | 8 | a |
2001 | 95 | 25.0 | 70.0 | 9 | b |
2002 | 93 | 21.0 | 72.0 | 10 | a |
2003 | 100 | 30.0 | 70.0 | 11 | b |
2004 | 110 | 39.0 | 71.0 | 12 | a |
引数に2を指定すると最初の2行のみ表示される。
df.head(2)
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2000 | 100 | 20.0 | 80.0 | 8 | a |
2001 | 95 | 25.0 | 70.0 | 9 | b |
最後の5行を表示させる。引数に整数を入れて表示行数を指定することも可能。
df.tail()
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2006 | 113 | 50.0 | 63.0 | 15 | a |
2007 | 118 | 53.0 | 65.0 | 17 | b |
2008 | 119 | 60.0 | 59.0 | 18 | a |
2009 | 200 | 62.0 | NaN | 20 | b |
2010 | 210 | NaN | NaN | 21 | a |
df
の情報を確認する。
df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 11 entries, 2000 to 2010
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 gdp 11 non-null int64
1 inv 10 non-null float64
2 con 9 non-null float64
3 pop 11 non-null int64
4 id 11 non-null object
dtypes: float64(2), int64(2), object(1)
memory usage: 528.0+ bytes
説明:
<class 'pandas.core.frame.DataFrame'>
クラス名
type(1)
とするとint
というデータ型が表示するが,これはクラス名でもある。print(type(1))
とすると<class 'int'>
と表示される。
Int64Index: 11 entries, 2000 to 2010
行のインデックスの情報
データ型は
Int64
(整数)(データ型には64
や32
という数字がついている場合がある。それらは数字をコンピュータのメモリに記憶させる際,何ビット必要かを示している。より重要なのはInt
(整数)の部分である。)11個のデータで2000から2010
Data columns (total 5 columns):
データ列の数(5つ)
gdp 11 non-null int64
データ型は
int64
11のデータがあり,欠損値なし(
non-null
とは欠損値ではないデータ)
inv 10 non-null float64
データ型は
float64
10のデータがあり,欠損値数は1(=11-10)
con 9 non-null float64
データ型は
float64
9のデータがあり,欠損値数は2(=11-9)
pop 11 non-null int64
データ型は
int64
11のデータがあり,欠損値数なし
id 11 non-null object
データ型は
object
(文字列などの場合)11のデータがあり,欠損値数なし
dtypes: float64(2), int64(2), object(1)
df
の列にどのようなのデータ型かを示すfloat64
とint64
が2列つずつ,文字列は1列
memory usage: 528.0+ bytes
メモリー使用量は約528.0バイト
データを読み込んだら必ずinfo()
を使って欠損値の数や列のデータ型を確認すること。
また,データの統計的な特徴は次のメソッドでチェックできる。
df.describe()
gdp | inv | con | pop | |
---|---|---|---|---|
count | 11.000000 | 10.000000 | 9.000000 | 11.000000 |
mean | 124.818182 | 41.500000 | 67.777778 | 14.090909 |
std | 40.715644 | 16.473885 | 6.666667 | 4.482288 |
min | 93.000000 | 20.000000 | 59.000000 | 8.000000 |
25% | 100.000000 | 26.250000 | 63.000000 | 10.500000 |
50% | 113.000000 | 44.500000 | 70.000000 | 14.000000 |
75% | 118.500000 | 54.500000 | 71.000000 | 17.500000 |
max | 210.000000 | 62.000000 | 80.000000 | 21.000000 |
count
:観測値の数mean
:平均std
:標準偏差min
:最小値max
:最大値25%
:第1四分位数50%
:第2四分位数(中央値)75%
:第3四分位数max
:最大値
次のデータ属性を使ってdf
の行と列の長さを確認することができる。返値はタプルで,(行の数,列の数)
と解釈する。
df.shape
(11, 5)
返値はタプルなので,行数は以下で取得できる。
df.shape[0]
11
以下でも行数を示すことができる。
len(df)
11
DataFrameの構成要素#
DataFrame
には様々な属性があるが,ここでは以下の3点について説明する。
データ(
df.to_numpy()
)列ラベル(
df.columns
)行ラベル(
df.index
)
まずデータ自体を抽出する。
df.to_numpy()
array([[100, 20.0, 80.0, 8, 'a'],
[95, 25.0, 70.0, 9, 'b'],
[93, 21.0, 72.0, 10, 'a'],
[100, 30.0, 70.0, 11, 'b'],
[110, 39.0, 71.0, 12, 'a'],
[115, 55.0, 60.0, 14, 'b'],
[113, 50.0, 63.0, 15, 'a'],
[118, 53.0, 65.0, 17, 'b'],
[119, 60.0, 59.0, 18, 'a'],
[200, 62.0, nan, 20, 'b'],
[210, nan, nan, 21, 'a']], dtype=object)
type(df.to_numpy())
numpy.ndarray
これで分かることは,メインのデータの部分はNumPy
のndarray
(n
次元array
)であることが分かる。即ち,Pandas
はNumPy
に基づいて構築されており,データ値の計算などはarray
が裏で動いているということである。また行と列のラベルを追加し,より直感的に使えるように拡張しているのである。
次に列ラベルを取り出してみる。
df.columns
Index(['gdp', 'inv', 'con', 'pop', 'id'], dtype='object')
dtype='object'
から列ラベルに使われているデータ型(dtype
)はオブジェクト型(object
)だとわかる。
オブジェクト型とは文字型を含む「その他」のデータ型と理解すれば良いだろう。
dtype='object'
とdtype=object
は同じ意味。
列ラベル自体のクラスは次のコードで調べることができる。
type(df.columns)
pandas.core.indexes.base.Index
dir()
もしくはpy4macro
モジュールに含まれるsee()
関数で調べると多くのメソッドや属性が確認できるが,その中に.tolist()
が含まれており,これを使うことにより列ラベルをリストに変換することができる。
df_columns = df.columns.tolist()
df_columns
['gdp', 'inv', 'con', 'pop', 'id']
行ラベルについても同じことができる。
df.index
Index([2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010], dtype='int64', name='year')
行ラベルのデータ型dtype
は整数であるint64
。列year
を行ラベルに指定したため,name='year'
はその列ラベルを表示している。行ラベルのデータ型(クラス)は
type(df.index)
pandas.core.indexes.base.Index
であり,ラベルをリストとして抽出することもできる。
df_index = df.index.tolist()
df_index
[2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010]
要素の抽出#
NumPy
のarray
の場合,[,]
を使い要素を抽出した。Pandas
の場合,様々な抽出方法があるが,覚えやすく少しでも間違いの可能性を減らすために,そして可読性向上のためにarray
に対応する以下の2つの方法を使うことにする。
ラベルを使う方法:
.loc[,]
インデックスを使う方法:
.iloc[,]
(これはarray
の[ ]
と同じと考えて良い)
1つ目のloc
はラベルのlocationと覚えよう。2つ目はのiloc
のi
はインデックス(index)のi
であり,index locationという意味である。使い方はarray
の場合と基本的に同じである。
,
の左は行,右は列を表す。行または列を連続して選択する(slicing)場合は
:
を使う。(start:end
):
の左右を省略する場合は,「全て」という意味になる。:
の左を省略すると「最初から」という意味になる。:
の右を省略すると「最後まで」という意味になる。.loc[,]
の場合,end
を含む。(要注意!).iloc[,]
の場合,end
は含まず,その1つ前のインデックスまでが含まれる。
,
の右に書く:
は省略可能であるが省略しないことを推奨する。
「特例」として.loc[,]
と.iloc[,]
以外に
ラベルと
[]
だけを使い列を選択する方法
も説明する。
Warning
.loc[,]
の場合,end
を含む。(要注意!).iloc[,]
の場合,end
は含まず,その1つ前のインデックスまでが含まれる。
.loc[,]
(ラベル使用)#
1つの行をSeries
として抽出
df.loc[2005,:]
gdp 115
inv 55.0
con 60.0
pop 14
id b
Name: 2005, dtype: object
1つの行をDataFrame
として抽出
df.loc[[2005],:]
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2005 | 115 | 55.0 | 60.0 | 14 | b |
複数行を抽出
df.loc[[2005, 2010],:]
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2005 | 115 | 55.0 | 60.0 | 14 | b |
2010 | 210 | NaN | NaN | 21 | a |
複数行を連続抽出(slicing)
df.loc[2005:2008,:]
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2005 | 115 | 55.0 | 60.0 | 14 | b |
2006 | 113 | 50.0 | 63.0 | 15 | a |
2007 | 118 | 53.0 | 65.0 | 17 | b |
2008 | 119 | 60.0 | 59.0 | 18 | a |
1つの列をSeries
として抽出
df.loc[:,'gdp']
year
2000 100
2001 95
2002 93
2003 100
2004 110
2005 115
2006 113
2007 118
2008 119
2009 200
2010 210
Name: gdp, dtype: int64
複数列を抽出
df.loc[:,['gdp','pop']]
gdp | pop | |
---|---|---|
year | ||
2000 | 100 | 8 |
2001 | 95 | 9 |
2002 | 93 | 10 |
2003 | 100 | 11 |
2004 | 110 | 12 |
2005 | 115 | 14 |
2006 | 113 | 15 |
2007 | 118 | 17 |
2008 | 119 | 18 |
2009 | 200 | 20 |
2010 | 210 | 21 |
複数列を連続抽出(slicing)
df.loc[:,'inv':'pop']
inv | con | pop | |
---|---|---|---|
year | |||
2000 | 20.0 | 80.0 | 8 |
2001 | 25.0 | 70.0 | 9 |
2002 | 21.0 | 72.0 | 10 |
2003 | 30.0 | 70.0 | 11 |
2004 | 39.0 | 71.0 | 12 |
2005 | 55.0 | 60.0 | 14 |
2006 | 50.0 | 63.0 | 15 |
2007 | 53.0 | 65.0 | 17 |
2008 | 60.0 | 59.0 | 18 |
2009 | 62.0 | NaN | 20 |
2010 | NaN | NaN | 21 |
.iloc[]
(インデックス使用)#
1つの行をSeries
として抽出
df.iloc[1,:]
gdp 95
inv 25.0
con 70.0
pop 9
id b
Name: 2001, dtype: object
複数行を抽出
df.iloc[[1,4],:]
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2001 | 95 | 25.0 | 70.0 | 9 | b |
2004 | 110 | 39.0 | 71.0 | 12 | a |
複数行を連続抽出(slicing)
df.iloc[1:4,:]
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2001 | 95 | 25.0 | 70.0 | 9 | b |
2002 | 93 | 21.0 | 72.0 | 10 | a |
2003 | 100 | 30.0 | 70.0 | 11 | b |
1つの列をSeries
として抽出
df.iloc[:,1]
year
2000 20.0
2001 25.0
2002 21.0
2003 30.0
2004 39.0
2005 55.0
2006 50.0
2007 53.0
2008 60.0
2009 62.0
2010 NaN
Name: inv, dtype: float64
1つの列をDataFrame
として抽出
df.iloc[:,[1]]
inv | |
---|---|
year | |
2000 | 20.0 |
2001 | 25.0 |
2002 | 21.0 |
2003 | 30.0 |
2004 | 39.0 |
2005 | 55.0 |
2006 | 50.0 |
2007 | 53.0 |
2008 | 60.0 |
2009 | 62.0 |
2010 | NaN |
複数列を選択
df.iloc[:,[1,3]]
inv | pop | |
---|---|---|
year | ||
2000 | 20.0 | 8 |
2001 | 25.0 | 9 |
2002 | 21.0 | 10 |
2003 | 30.0 | 11 |
2004 | 39.0 | 12 |
2005 | 55.0 | 14 |
2006 | 50.0 | 15 |
2007 | 53.0 | 17 |
2008 | 60.0 | 18 |
2009 | 62.0 | 20 |
2010 | NaN | 21 |
複数列を連続抽出(slicing)
df.iloc[:,1:3]
inv | con | |
---|---|---|
year | ||
2000 | 20.0 | 80.0 |
2001 | 25.0 | 70.0 |
2002 | 21.0 | 72.0 |
2003 | 30.0 | 70.0 |
2004 | 39.0 | 71.0 |
2005 | 55.0 | 60.0 |
2006 | 50.0 | 63.0 |
2007 | 53.0 | 65.0 |
2008 | 60.0 | 59.0 |
2009 | 62.0 | NaN |
2010 | NaN | NaN |
[]
で列の選択(ラベル使用)#
1つの列をSeries
として抽出
df['gdp']
year
2000 100
2001 95
2002 93
2003 100
2004 110
2005 115
2006 113
2007 118
2008 119
2009 200
2010 210
Name: gdp, dtype: int64
1つの列をDataFrame
として抽出
df[['gdp']]
gdp | |
---|---|
year | |
2000 | 100 |
2001 | 95 |
2002 | 93 |
2003 | 100 |
2004 | 110 |
2005 | 115 |
2006 | 113 |
2007 | 118 |
2008 | 119 |
2009 | 200 |
2010 | 210 |
複数列を選択
df[['gdp','pop']]
gdp | pop | |
---|---|---|
year | ||
2000 | 100 | 8 |
2001 | 95 | 9 |
2002 | 93 | 10 |
2003 | 100 | 11 |
2004 | 110 | 12 |
2005 | 115 | 14 |
2006 | 113 | 15 |
2007 | 118 | 17 |
2008 | 119 | 18 |
2009 | 200 | 20 |
2010 | 210 | 21 |
ある条件の下で行の抽出#
1つの条件の場合#
例1:GDPが100未満の行の抽出#
まず条件を作る。
df['gdp'] < 100
year
2000 False
2001 True
2002 True
2003 False
2004 False
2005 False
2006 False
2007 False
2008 False
2009 False
2010 False
Name: gdp, dtype: bool
この条件では,GDPが100未満の行はTrue
,以上の行はFalse
となる。この条件をcond
というの変数に割り当てる。()
を省いても良いが,ある方が分かりやすいだろう。
cond = ( df['gdp'] < 100 )
cond
を.loc[,]
の引数とすることにより,True
の行だけを抽出できる。(注意:cond
を使って行を抽出しようとしているので,
の左側に書く。)
df.loc[cond,:]
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2001 | 95 | 25.0 | 70.0 | 9 | b |
2002 | 93 | 21.0 | 72.0 | 10 | a |
この条件の下で\(inv\)だけを抽出したい場合
df.loc[cond,'inv']
とする。
Warning
以下のように抽出を連続ですることも可能だが,避けるように!
df.loc[cond,:]['inv']
df.loc[cond,:].loc[:,'inv']
例2:id
がa
の行を抽出#
cond = ( df.loc[:,'id'] == 'a' )
df.loc[cond,:]
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2000 | 100 | 20.0 | 80.0 | 8 | a |
2002 | 93 | 21.0 | 72.0 | 10 | a |
2004 | 110 | 39.0 | 71.0 | 12 | a |
2006 | 113 | 50.0 | 63.0 | 15 | a |
2008 | 119 | 60.0 | 59.0 | 18 | a |
2010 | 210 | NaN | NaN | 21 | a |
複数条件の場合#
例3#
以下の条件の両方が満たされる場合:
gdp
が100以上inv
が30以下
それぞれの条件を作成する。
cond1 = ( df['gdp'] >= 100 )
cond2 = ( df['inv'] <= 30 )
2つの条件が同時に満たされる条件を作成する。
cond = ( cond1 & cond2 )
cond
を引数に使い行を抽出する。
df.loc[cond, :]
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2000 | 100 | 20.0 | 80.0 | 8 | a |
2003 | 100 | 30.0 | 70.0 | 11 | b |
例4#
以下の条件のどちらかが満たされる場合:
gdp
は200以上con
は60以下
cond1 = ( df['gdp'] >= 200 )
cond2 = ( df['con'] <= 60 )
cond = ( cond1 | cond2 )
df.loc[cond, :]
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2005 | 115 | 55.0 | 60.0 | 14 | b |
2008 | 119 | 60.0 | 59.0 | 18 | a |
2009 | 200 | 62.0 | NaN | 20 | b |
2010 | 210 | NaN | NaN | 21 | a |
例5#
以下の条件のどちらかが満たされ
gdp
は200以上con
は60以下
かつ以下の条件も同時に満たされる場合:
id
がa
と等しい
cond1 = ( df['gdp'] >= 200 )
cond2 = ( df['con'] <= 60 )
cond3 = ( df['id'] == 'a' )
cond = ( (cond1 | cond2) & cond3 )
df.loc[cond, :]
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2008 | 119 | 60.0 | 59.0 | 18 | a |
2010 | 210 | NaN | NaN | 21 | a |
query()
#
query()
というメソッドでは文字列を使い行の抽出コードを書くことができる。これにより直感的なコード書くことが可能である。
例1の場合:#
df.query('gdp < 100')
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2001 | 95 | 25.0 | 70.0 | 9 | b |
2002 | 93 | 21.0 | 72.0 | 10 | a |
例2の場合#
df.query('id == "a"')
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2000 | 100 | 20.0 | 80.0 | 8 | a |
2002 | 93 | 21.0 | 72.0 | 10 | a |
2004 | 110 | 39.0 | 71.0 | 12 | a |
2006 | 113 | 50.0 | 63.0 | 15 | a |
2008 | 119 | 60.0 | 59.0 | 18 | a |
2010 | 210 | NaN | NaN | 21 | a |
例3の場合#
df.query('(gdp >= 100) & (inv <= 30)')
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2000 | 100 | 20.0 | 80.0 | 8 | a |
2003 | 100 | 30.0 | 70.0 | 11 | b |
例4の場合#
df.query('(gdp >= 200) | (con <= 60)')
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2005 | 115 | 55.0 | 60.0 | 14 | b |
2008 | 119 | 60.0 | 59.0 | 18 | a |
2009 | 200 | 62.0 | NaN | 20 | b |
2010 | 210 | NaN | NaN | 21 | a |
例5の場合#
df.query('(gdp >= 200 | con <= 60) & (id == "a")')
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2008 | 119 | 60.0 | 59.0 | 18 | a |
2010 | 210 | NaN | NaN | 21 | a |
Tip
変数で条件を設定する場合@
が必要になる。例えば,変数z
という変数があるとしよう。
z = 100
変数z
の値に基づいて行の抽出をする場合は次のようにする。
df.query('gdp < @z')
gdp
inv
con
pop
id
year
2001
95
25.0
70.0
9
b
2002
93
21.0
72.0
10
a
列と行の追加と削除#
列の追加 [ ]
#
[]
に列ラベルを使って列を抽出することができるが,[]
は列の追加にも使えるので,ここではその使い方を説明する。まず,全ての行が1.0
となる列を作成するとしよう。その場合,以下のようにする。
df['Intercept'] = 1.0
df.head(2)
gdp | inv | con | pop | id | Intercept | |
---|---|---|---|---|---|---|
year | ||||||
2000 | 100 | 20.0 | 80.0 | 8 | a | 1.0 |
2001 | 95 | 25.0 | 70.0 | 9 | b | 1.0 |
次の例では既存の列から新たな列を作成する。まず1人当たりGDPの計算を計算し,それを変数gdp_pc
に割り当てる。
gdp_pc = df['gdp']/df['pop']
gdp_pc.head()
year
2000 12.500000
2001 10.555556
2002 9.300000
2003 9.090909
2004 9.166667
dtype: float64
これはSeries
であり,GDPpc
としてdf
に追加する。
df['gdp_pc'] = gdp_pc
df.head(2)
gdp | inv | con | pop | id | Intercept | gdp_pc | |
---|---|---|---|---|---|---|---|
year | |||||||
2000 | 100 | 20.0 | 80.0 | 8 | a | 1.0 | 12.500000 |
2001 | 95 | 25.0 | 70.0 | 9 | b | 1.0 | 10.555556 |
列の追加 .loc[,]
#
.loc[]
は行と列の抽出に使ったが,追加にも使える。[]
と同じと考えれば良い。次の例ではpop
を2倍した列を追加している。
df.loc[:,'2pop'] = 2*df['pop']
列の削除 [ ]
#
del df['2pop']
列の削除 drop()
#
DataFrame
には.drop()
というメソッドが用意されているので,それを使うことも可能である。
下のコードの説明:
オプション
axis=
の値をcolumns
の代わりに1
でも可コピーを作るだけなので,元のdfを書き換えたい場合は以下のどちらかが必要
df
に代入するオプション
inplace=True
(デフォルトはFalse
)を追加する。
df = df.drop(['Intercept','gdp_pc'], axis='columns')
# df.drop('Intercept', axis='columns', inplace=True)
行の追加 .loc[,]
#
行と列の抽出,そして列の追加に使ったが,行の追加にも使える。
df.loc[2011,:] = [215, 100, 115, 22, 'b']
df.tail(3)
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2009 | 200.0 | 62.0 | NaN | 20.0 | b |
2010 | 210.0 | NaN | NaN | 21.0 | a |
2011 | 215.0 | 100.0 | 115.0 | 22.0 | b |
上の例では,最初の4つの要素は整数として入力されたが,df
の中では浮動小数点に変換されている。
行の削除 drop()
#
これは列を削除する際に紹介したメソッド.drop()
である。
オプション
axis=
の値をrows
の代わりに0
でも可コピーを作るだけなので,元のdfを書き換えたい場合は以下のどちらかが必要
df
に代入するオプション
inplace=True
(デフォルトはFalse
)を追加する。
df = df.drop(2011, axis='rows')
# df.drop(2011, axis=0, inplace=True)
欠損値の扱い#
Pandas
では欠損値はNaN
と表示されるが,na
もしくはnull
と呼んだりもする。
欠損値の確認#
欠損値があるかどうかの確認は,df.info()
でもできるが,以下のメソッドを組み合わせることでも可能である。
isna()
:それぞれの要素についてNaN
の場合True
を,そうでない場合はFalse
を返す。(DataFrame
の全ての要素がTrue/False
となる。)sum(axis='rows')
:df
の上から下に行(rows)を縦断して,それぞれの列の中にあるTrue
数える。rows
は複数!(0
でも可)
sum(axis='columns')
:df
の左から右に列(columns)を横断して,それぞれの行の中にあるTrue
を数える。columns
は複数!(1
でも可)
(注意)sum()
のaxis
は「行を縦断」か「列を横断」かを指定する。
df.isna().sum(axis='rows')
gdp 0
inv 1
con 2
pop 0
id 0
dtype: int64
inv
とcon
にNaN
があることがわかる。axis='columns'
を設定するとNaN
がある行を確認できる。
df.isna().sum(axis='columns')
year
2000 0
2001 0
2002 0
2003 0
2004 0
2005 0
2006 0
2007 0
2008 0
2009 1
2010 2
dtype: int64
NaN
がある行だけを抽出したい場合がある。その場合はメソッドany()
が役に立つ。
any(axis='rows')
:df
の上から下に行(rows
)を縦断して,それぞれの列の中で一つ以上True
がある場合にはTrue
を,一つもない場合はFalse
を返す。rows
は複数!(0でも可)
any(axis='columns')
:dfの左から右に列(columns
)を横断して,それぞれの行の中で一つ以上True
がある場合にはTrue
を,一つもない場合はFalse
を返す。columns
は複数!(1でも可)
(注意)any()
のaxis
は「行を縦断」か「列を横断」かを指定する。
cond = df.isna().any(axis='columns')
df.loc[cond,:]
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2009 | 200.0 | 62.0 | NaN | 20.0 | b |
2010 | 210.0 | NaN | NaN | 21.0 | a |
これでNaN
がある行を抽出することができる。
欠損値がある行の削除#
欠損値がある行を削除するには,df
のメソッド.dropna()
を使う。次のコードでは,欠損値がある全ての行を削除する。
df.dropna()
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2000 | 100.0 | 20.0 | 80.0 | 8.0 | a |
2001 | 95.0 | 25.0 | 70.0 | 9.0 | b |
2002 | 93.0 | 21.0 | 72.0 | 10.0 | a |
2003 | 100.0 | 30.0 | 70.0 | 11.0 | b |
2004 | 110.0 | 39.0 | 71.0 | 12.0 | a |
2005 | 115.0 | 55.0 | 60.0 | 14.0 | b |
2006 | 113.0 | 50.0 | 63.0 | 15.0 | a |
2007 | 118.0 | 53.0 | 65.0 | 17.0 | b |
2008 | 119.0 | 60.0 | 59.0 | 18.0 | a |
このメソッドは,欠損値を削除するとどうなるかを示すだけでありdf
自体は影響は受けない。df
自体からNaN
がある行を削除する場合はinplace=True
のオプション(デフォルトではFalse
になっている)を加えて
df.dropna(inplace=True)
とするか,削除後のdf
を変数df
に再度割り当てる。
df = df.dropna()
一方で,ある列にNaN
がある場合のみ行を削除したい場合もあるだろう。その場合は引数subset
を使う。
df.dropna(subset=['inv'])
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2000 | 100.0 | 20.0 | 80.0 | 8.0 | a |
2001 | 95.0 | 25.0 | 70.0 | 9.0 | b |
2002 | 93.0 | 21.0 | 72.0 | 10.0 | a |
2003 | 100.0 | 30.0 | 70.0 | 11.0 | b |
2004 | 110.0 | 39.0 | 71.0 | 12.0 | a |
2005 | 115.0 | 55.0 | 60.0 | 14.0 | b |
2006 | 113.0 | 50.0 | 63.0 | 15.0 | a |
2007 | 118.0 | 53.0 | 65.0 | 17.0 | b |
2008 | 119.0 | 60.0 | 59.0 | 18.0 | a |
2009 | 200.0 | 62.0 | NaN | 20.0 | b |
列con
にNaN
が残っている。
<注意>
subset=['inv']
のように,NaN
があるかを確認する列が1つであってもリスト[]
で指定する。複数であれば,次のように列ラベルをリストとして並べれば良い。
subset=['inv','id']
非常に便利な引数なので覚えておこう!
並び替え#
df
の行を並び替えるにはメソッド.sort_values()
を使う。引数には,並び替える基準となる列ラベルを指定し,デフォルトでは昇順となる。次のコードではgdp
に従って昇順に並び替えている。
df.sort_values('gdp').head()
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2002 | 93.0 | 21.0 | 72.0 | 10.0 | a |
2001 | 95.0 | 25.0 | 70.0 | 9.0 | b |
2000 | 100.0 | 20.0 | 80.0 | 8.0 | a |
2003 | 100.0 | 30.0 | 70.0 | 11.0 | b |
2004 | 110.0 | 39.0 | 71.0 | 12.0 | a |
引数ascending=False
を使うと降順になる。
df.sort_values('gdp', ascending=False).head()
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2010 | 210.0 | NaN | NaN | 21.0 | a |
2009 | 200.0 | 62.0 | NaN | 20.0 | b |
2008 | 119.0 | 60.0 | 59.0 | 18.0 | a |
2007 | 118.0 | 53.0 | 65.0 | 17.0 | b |
2005 | 115.0 | 55.0 | 60.0 | 14.0 | b |
複数の列を基準として,昇順と降順を別々に指定することも可能である。
df.sort_values(['id','gdp'], ascending=[True,False]).head()
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
year | |||||
2010 | 210.0 | NaN | NaN | 21.0 | a |
2008 | 119.0 | 60.0 | 59.0 | 18.0 | a |
2006 | 113.0 | 50.0 | 63.0 | 15.0 | a |
2004 | 110.0 | 39.0 | 71.0 | 12.0 | a |
2000 | 100.0 | 20.0 | 80.0 | 8.0 | a |
ここではid
に従って先に並び替えられ,その後にgdp
に従って並び替えられている。ascending
は昇順(True
)か降順(False
)かを指定する引数であり,['id','gdp']
とascending=['True','False']
の順番が対応している。即ち,id
は昇順,gdp
は降順となるように並び替えている。
DataFrameの結合#
データを扱うと必ずと言って良いほど必要になるのがデータ・セットの結合である。例えば,日本銀行と米国連邦準備制度(米国の中央銀行)のデータからなるdf
を作るとすると,それぞれのサイトから取得したデータ・セットを結合する必要がある。DataFrame
には,そのための便利なメソッドが準備されているので,ここではそれを紹介する。
3つの.csv
ファイルを使う。
url = 'https://raw.githubusercontent.com/Haruyama-KobeU/Py4Basics/master/data/'
df1 = pd.read_csv(url+'data1.csv')
df2 = pd.read_csv(url+'data2.csv')
df3 = pd.read_csv(url+'data3.csv')
df1
year | gdp | inv | con | pop | id | |
---|---|---|---|---|---|---|
0 | 2000 | 100 | 20.0 | 80.0 | 8 | a |
1 | 2001 | 95 | 25.0 | 70.0 | 9 | b |
2 | 2002 | 93 | 21.0 | 72.0 | 10 | a |
3 | 2003 | 100 | 30.0 | 70.0 | 11 | b |
4 | 2004 | 110 | 39.0 | 71.0 | 12 | a |
5 | 2005 | 115 | 55.0 | 60.0 | 14 | b |
6 | 2006 | 113 | 50.0 | 63.0 | 15 | a |
7 | 2007 | 118 | 53.0 | 65.0 | 17 | b |
8 | 2008 | 119 | 60.0 | 59.0 | 18 | a |
9 | 2009 | 200 | 62.0 | NaN | 20 | b |
10 | 2010 | 210 | NaN | NaN | 21 | a |
df2
year | gnp | id2 | |
---|---|---|---|
0 | 1995 | 101 | 1 |
1 | 1996 | 97 | 1 |
2 | 1997 | 96 | 1 |
3 | 2000 | 103 | 1 |
4 | 2001 | 103 | 0 |
5 | 2002 | 102 | 0 |
6 | 2003 | 105 | 0 |
7 | 2004 | 110 | 0 |
df3
year | gdp | inv | con | pop | id | |
---|---|---|---|---|---|---|
0 | 2011 | 212 | 70 | 142 | 22 | b |
1 | 2012 | 215 | 72 | 143 | 23 | a |
2 | 2015 | 220 | 80 | 140 | 24 | b |
df3.loc[0,'year']
2011
横結合:merge()
#
merge()
以外にも結合に使える関数はあるが,ここではmerge()
のみを考える。
df1
を「左」,df2
を「右」に横結合する。
pd.merge(df1, df2, on=None, how='inner')
on
はどの列を基準にして結合するかを指定(ここでは「基準列」と呼ぼう)例えば,
df1
とdf2
の両方にyear
の列がある場合,on='year'
とすると列year
が基準列となる。df1
とdf2
の別々の列を指定する場合はleft_on=
とright_on=
を使う。
基準列に基づいて残す行を決める(
how
で説明する)基準列にある要素の順番が合ってなくても,自動でマッチさせる。
複数指定も可
デフォルトは
None
how
はon
で指定した基準列に基づいてどのように結合するかを指定inner
:df1
とdf2
の両方に基準列ある行だけを残す(デフォルト)。left
:df1
の行は全て残し,df2
からはマッチする行だけが残る(マッチしない行は削除)。またdf1
にありdf2
にない行にはNaN
が入る。right
:df2
の行は全て残し,df1
からはマッチする行だけが残る(マッチしない行は削除)。またdf2
にありdf1
にない行にはNaN
が入る。outer
:df1
とdf2
の両方の行を残し,マッチする行がない場合はNaN
を入れる。
(コメント) この他に様々な引数があるのでこのサイトを参照。例えば,場合によっては次の引数を使う必要があるかも知れないので確認しよう。
left_index=True
:df1
では行インデックスを基準列にする。right_index=True
:df2
では行インデックスを基準列にする。
例1#
year
を基準列とする。共通する行だけを残す。
pd.merge(df1, df2, on='year', how='inner')
year | gdp | inv | con | pop | id | gnp | id2 | |
---|---|---|---|---|---|---|---|---|
0 | 2000 | 100 | 20.0 | 80.0 | 8 | a | 103 | 1 |
1 | 2001 | 95 | 25.0 | 70.0 | 9 | b | 103 | 0 |
2 | 2002 | 93 | 21.0 | 72.0 | 10 | a | 102 | 0 |
3 | 2003 | 100 | 30.0 | 70.0 | 11 | b | 105 | 0 |
4 | 2004 | 110 | 39.0 | 71.0 | 12 | a | 110 | 0 |
例2#
year
を基準列とする。df1
の全ての行を残す。df2
ではマッチする行だけ残り,マッチしない行は削除される。df1
にありdf2
にない行にはNaN
が入る。
pd.merge(df1, df2, on='year', how='left')
year | gdp | inv | con | pop | id | gnp | id2 | |
---|---|---|---|---|---|---|---|---|
0 | 2000 | 100 | 20.0 | 80.0 | 8 | a | 103.0 | 1.0 |
1 | 2001 | 95 | 25.0 | 70.0 | 9 | b | 103.0 | 0.0 |
2 | 2002 | 93 | 21.0 | 72.0 | 10 | a | 102.0 | 0.0 |
3 | 2003 | 100 | 30.0 | 70.0 | 11 | b | 105.0 | 0.0 |
4 | 2004 | 110 | 39.0 | 71.0 | 12 | a | 110.0 | 0.0 |
5 | 2005 | 115 | 55.0 | 60.0 | 14 | b | NaN | NaN |
6 | 2006 | 113 | 50.0 | 63.0 | 15 | a | NaN | NaN |
7 | 2007 | 118 | 53.0 | 65.0 | 17 | b | NaN | NaN |
8 | 2008 | 119 | 60.0 | 59.0 | 18 | a | NaN | NaN |
9 | 2009 | 200 | 62.0 | NaN | 20 | b | NaN | NaN |
10 | 2010 | 210 | NaN | NaN | 21 | a | NaN | NaN |
例3#
year
を基準列とする。df2
の全ての行を残す。df1
ではマッチする行だけ残り,マッチしない行は削除される。df1
にありdf2
にない行にはNaN
が入る。
pd.merge(df1, df2, on='year', how='right')
year | gdp | inv | con | pop | id | gnp | id2 | |
---|---|---|---|---|---|---|---|---|
0 | 1995 | NaN | NaN | NaN | NaN | NaN | 101 | 1 |
1 | 1996 | NaN | NaN | NaN | NaN | NaN | 97 | 1 |
2 | 1997 | NaN | NaN | NaN | NaN | NaN | 96 | 1 |
3 | 2000 | 100.0 | 20.0 | 80.0 | 8.0 | a | 103 | 1 |
4 | 2001 | 95.0 | 25.0 | 70.0 | 9.0 | b | 103 | 0 |
5 | 2002 | 93.0 | 21.0 | 72.0 | 10.0 | a | 102 | 0 |
6 | 2003 | 100.0 | 30.0 | 70.0 | 11.0 | b | 105 | 0 |
7 | 2004 | 110.0 | 39.0 | 71.0 | 12.0 | a | 110 | 0 |
例4#
year
を基準列とする。df1
とdf2
の全ての行は残る。df1
にありdf2
にない行にはNaN
が入る。df2
にありdf1
にない行にはNaN
が入る。
pd.merge(df1, df2, on='year', how='outer')
year | gdp | inv | con | pop | id | gnp | id2 | |
---|---|---|---|---|---|---|---|---|
0 | 1995 | NaN | NaN | NaN | NaN | NaN | 101.0 | 1.0 |
1 | 1996 | NaN | NaN | NaN | NaN | NaN | 97.0 | 1.0 |
2 | 1997 | NaN | NaN | NaN | NaN | NaN | 96.0 | 1.0 |
3 | 2000 | 100.0 | 20.0 | 80.0 | 8.0 | a | 103.0 | 1.0 |
4 | 2001 | 95.0 | 25.0 | 70.0 | 9.0 | b | 103.0 | 0.0 |
5 | 2002 | 93.0 | 21.0 | 72.0 | 10.0 | a | 102.0 | 0.0 |
6 | 2003 | 100.0 | 30.0 | 70.0 | 11.0 | b | 105.0 | 0.0 |
7 | 2004 | 110.0 | 39.0 | 71.0 | 12.0 | a | 110.0 | 0.0 |
8 | 2005 | 115.0 | 55.0 | 60.0 | 14.0 | b | NaN | NaN |
9 | 2006 | 113.0 | 50.0 | 63.0 | 15.0 | a | NaN | NaN |
10 | 2007 | 118.0 | 53.0 | 65.0 | 17.0 | b | NaN | NaN |
11 | 2008 | 119.0 | 60.0 | 59.0 | 18.0 | a | NaN | NaN |
12 | 2009 | 200.0 | 62.0 | NaN | 20.0 | b | NaN | NaN |
13 | 2010 | 210.0 | NaN | NaN | 21.0 | a | NaN | NaN |
縦結合#
concat()
は横結合にも使えるが,縦結合のみ考える。
引数には複数のDataFrame
をリストとして書く。
df13 = pd.concat([df1,df3])
df13.tail()
year | gdp | inv | con | pop | id | |
---|---|---|---|---|---|---|
9 | 2009 | 200 | 62.0 | NaN | 20 | b |
10 | 2010 | 210 | NaN | NaN | 21 | a |
0 | 2011 | 212 | 70.0 | 142.0 | 22 | b |
1 | 2012 | 215 | 72.0 | 143.0 | 23 | a |
2 | 2015 | 220 | 80.0 | 140.0 | 24 | b |
その他#
インデックスを振り直す#
メソッド.reset_index()
を使うと,行のインデックスを0,1,2,..と振り直すことができる。df13
を使い説明する。
df13.reset_index()
index | year | gdp | inv | con | pop | id | |
---|---|---|---|---|---|---|---|
0 | 0 | 2000 | 100 | 20.0 | 80.0 | 8 | a |
1 | 1 | 2001 | 95 | 25.0 | 70.0 | 9 | b |
2 | 2 | 2002 | 93 | 21.0 | 72.0 | 10 | a |
3 | 3 | 2003 | 100 | 30.0 | 70.0 | 11 | b |
4 | 4 | 2004 | 110 | 39.0 | 71.0 | 12 | a |
5 | 5 | 2005 | 115 | 55.0 | 60.0 | 14 | b |
6 | 6 | 2006 | 113 | 50.0 | 63.0 | 15 | a |
7 | 7 | 2007 | 118 | 53.0 | 65.0 | 17 | b |
8 | 8 | 2008 | 119 | 60.0 | 59.0 | 18 | a |
9 | 9 | 2009 | 200 | 62.0 | NaN | 20 | b |
10 | 10 | 2010 | 210 | NaN | NaN | 21 | a |
11 | 0 | 2011 | 212 | 70.0 | 142.0 | 22 | b |
12 | 1 | 2012 | 215 | 72.0 | 143.0 | 23 | a |
13 | 2 | 2015 | 220 | 80.0 | 140.0 | 24 | b |
この例では元のインデックスが列index
として追加されている。reset_index()
に引数drop=True
を加えると,列index
は追加されない。
df13.reset_index(drop=True).head()
year | gdp | inv | con | pop | id | |
---|---|---|---|---|---|---|
0 | 2000 | 100 | 20.0 | 80.0 | 8 | a |
1 | 2001 | 95 | 25.0 | 70.0 | 9 | b |
2 | 2002 | 93 | 21.0 | 72.0 | 10 | a |
3 | 2003 | 100 | 30.0 | 70.0 | 11 | b |
4 | 2004 | 110 | 39.0 | 71.0 | 12 | a |
列のラベルの変更#
メソッド.rename()
を使い列のラベルを変更する。引数は次の形で設定する。
ここで「辞書」は次のルールで指定する。
key
:元のラベルvalue
:新しいラベル
下のコードでは,df13
を使い新しいラベルとしてpop_new
とid_new
を使っている。
df13.rename(columns={'pop':'pop_new','id':'id_new'}).head()
year | gdp | inv | con | pop_new | id_new | |
---|---|---|---|---|---|---|
0 | 2000 | 100 | 20.0 | 80.0 | 8 | a |
1 | 2001 | 95 | 25.0 | 70.0 | 9 | b |
2 | 2002 | 93 | 21.0 | 72.0 | 10 | a |
3 | 2003 | 100 | 30.0 | 70.0 | 11 | b |
4 | 2004 | 110 | 39.0 | 71.0 | 12 | a |
.rename()
以外にリストを使って列ラベルを変更することも可能である。例えば,
df.columns = ['year', 'gdp', 'inv', 'con', 'pop_new', 'id_new']
この場合,列の数とリストの要素数が合わないとエラーとなるので注意しよう。
列の並び替え#
アルファベット順#
メソッド.sort_index()
を使い,引数にはaxis='columns'
もしくはaxis=1
を指定する。
(コメント)引数がaxis='rows'
もしくはaxis=0
の場合,行が並び替えられる。
df13.sort_index(axis='columns').head()
con | gdp | id | inv | pop | year | |
---|---|---|---|---|---|---|
0 | 80.0 | 100 | a | 20.0 | 8 | 2000 |
1 | 70.0 | 95 | b | 25.0 | 9 | 2001 |
2 | 72.0 | 93 | a | 21.0 | 10 | 2002 |
3 | 70.0 | 100 | b | 30.0 | 11 | 2003 |
4 | 71.0 | 110 | a | 39.0 | 12 | 2004 |
順番を指定する#
方法1#
列を選択する方法を使い並び替える。
var = ['id','year','gdp','con','inv','pop']
df13.loc[:,var].head()
id | year | gdp | con | inv | pop | |
---|---|---|---|---|---|---|
0 | a | 2000 | 100 | 80.0 | 20.0 | 8 |
1 | b | 2001 | 95 | 70.0 | 25.0 | 9 |
2 | a | 2002 | 93 | 72.0 | 21.0 | 10 |
3 | b | 2003 | 100 | 70.0 | 30.0 | 11 |
4 | a | 2004 | 110 | 71.0 | 39.0 | 12 |
方法2#
次の列の選択方法でも可。
df13[var].head()
id | year | gdp | con | inv | pop | |
---|---|---|---|---|---|---|
0 | a | 2000 | 100 | 80.0 | 20.0 | 8 |
1 | b | 2001 | 95 | 70.0 | 25.0 | 9 |
2 | a | 2002 | 93 | 72.0 | 21.0 | 10 |
3 | b | 2003 | 100 | 70.0 | 30.0 | 11 |
4 | a | 2004 | 110 | 71.0 | 39.0 | 12 |
方法3#
メソッド.reindex()
を使う。引数はcolumns=[]
。
df13.reindex(columns=['id','year','gdp','con','inv','pop']).head()
id | year | gdp | con | inv | pop | |
---|---|---|---|---|---|---|
0 | a | 2000 | 100 | 80.0 | 20.0 | 8 |
1 | b | 2001 | 95 | 70.0 | 25.0 | 9 |
2 | a | 2002 | 93 | 72.0 | 21.0 | 10 |
3 | b | 2003 | 100 | 70.0 | 30.0 | 11 |
4 | a | 2004 | 110 | 71.0 | 39.0 | 12 |
方法3の応用#
最後の行を最初に移動する。
.columns.tolist()
:コラムのラベルを取得し,それをリストに変換[col[-1]]+col[0:-2]
の分解と結合[col[-1]]
:col
の最後の要素を抽出するが,文字列として返されるので(外側の)[ ]
を使ってリストに変換col[0:-2]
:col
の最初から最後から二番目の要素をリストとして取得+
:リストの結合
col = df13.columns.tolist()
new_col = [col[-1]]+col[0:-2]
df.reindex(columns=new_col).head()
id | year | gdp | inv | con | |
---|---|---|---|---|---|
year | |||||
2000 | a | NaN | 100.0 | 20.0 | 80.0 |
2001 | b | NaN | 95.0 | 25.0 | 70.0 |
2002 | a | NaN | 93.0 | 21.0 | 72.0 |
2003 | b | NaN | 100.0 | 30.0 | 70.0 |
2004 | a | NaN | 110.0 | 39.0 | 71.0 |