Pandas
:データ分析#
import pandas as pd
import py4macro
# 警告メッセージを非表示
import warnings
warnings.filterwarnings("ignore")
説明#
Pandas
はデータを扱う上で欠かせないパッケージであり,エクセルをイメージすれば良いだろう。Pandas
にはエクセルのスプレッド・シートに対応するDataFrame
(データ・フレーム)とSeries
(シリーズ)呼ばれるオブジェクトがあり,それらを駆使してデータ分析をおこなう。
DataFrame
は次の表の様になっている。まず、用語の整理をしよう。
X |
Y |
Z |
|
---|---|---|---|
0 |
10 |
5.0 |
3.0 |
1 |
20 |
30.0 |
2.0 |
2 |
30 |
15.0 |
5.0 |
列とは縦に表示されたデータの集まりであり、
X
の列には10
、20
、30
がある。Y
とZ
も同様にそれぞれの値から構成される列となる。行とは横に表示されたデータの集まりであり、
0
の行には10
、5.0
、3.0
がある。1
と2
も同様にそれぞれの値から構成される行となる。
行と列には、その位置を示す番号とラベルがある。このサイトでは次の呼称とする。
位置を示す番号をインデックスもしくはインデックス番号と呼ぶ。
上の例では、左端の
0
は0
番目の行インデックス、1
は1
番目の行インデックス、2
は2
番目の行インデックスを示す。上の例では、一番上の
X
には0
の列インデックスが付与されており、Y
には1
の列インデックスが付与されており、Z
には2
の列インデックスが付与されている。
位置をラベルで表すこともできる。
上の例では、行ラベルは設定されていない。
上の例では、一番上の
X
,Y
,Z
が列ラベルとなる。
上の例のように、DataFrame
は1つ以上の行と列から構成されている。一方、Series
には1つの列(もしくは行と考えることもできる)のみしかない点が大きな違いである。
これらの特徴により複雑なデータの扱いが簡単になる。例えば,NumPy
を使うと目的のデータにアクセスしたい場合、行番号と列番号を把握する必要がある。一方、Pandas
では行と列にラベルを使うことにより(例えば,行Japan
の列gdp
のようにデータを特定することが可能となる),データの操作が非常に簡単になる。また,Pandas
はNumPy
に基づいているため,ベクトル演算の機能が使える。
ここで説明できない他の使い方についてはこのサイトとこのサイトが参考になるだろう。
Pandas
は通常pd
という名前で読み込む。
import pandas as pd
データの読み込みとデータのチェック#
様々なデータを読み込むことが可能だが,ここではread_csv()
関数を使って.csv
ファイルを読み込むことにする。(csv
はcomma separated valuesの略称であり、.csvファイルについてはこの検索結果を参照)ここで使うdata1.csv
はここをクリックをマウスの右クリックすることによりダウンロードすることもできる。
# データの読み込み
df = pd.read_csv('./data/data1.csv')
df
全体を表示させる。
df
country | gdp | con | inv | pop | continent | |
---|---|---|---|---|---|---|
0 | China | 20118 | 7796.0 | 9137.0 | 1434 | Asia |
1 | France | 2950 | 1598.0 | 806.0 | 67 | Europe |
2 | Germany | 4274 | 2294.0 | 971.0 | 84 | Europe |
3 | India | 9142 | 5936.0 | 2471.0 | 1366 | Asia |
4 | Italy | 2463 | 1423.0 | 614.0 | 61 | Europe |
5 | Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
6 | Korea | 2157 | 942.0 | 705.0 | 51 | Asia |
7 | Singapore | 477 | 176.0 | 135.0 | 6 | Asia |
8 | Taiwan | 1100 | 607.0 | NaN | 24 | Asia |
9 | UK | 2994 | NaN | NaN | 68 | Europe |
数値はPenn World Tableと呼ばれるデータセットに基づき作成している。
country
:国名gdp
:国内総生産(Gross Domestic Product, current PPPs; in billions 2011US$)con
:消費(consumption, current PPPs; in billions 2011US$)inv
:投資(investment, current PPPs; in billions 2011US$)pop
:人口(population, in millions)continent
:大陸(continent)
行はインデックス(番号)になっており,そのまま使っても全く問題ない。ここでは列country
を行ラベルに設定してPandas
の使い方について説明することにする。
set_index()
:引数の列を行ラベルにするメソッド(行ラベルは文字列とすること)
df = df.set_index('country')
df
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia |
France | 2950 | 1598.0 | 806.0 | 67 | Europe |
Germany | 4274 | 2294.0 | 971.0 | 84 | Europe |
India | 9142 | 5936.0 | 2471.0 | 1366 | Asia |
Italy | 2463 | 1423.0 | 614.0 | 61 | Europe |
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
Korea | 2157 | 942.0 | 705.0 | 51 | Asia |
Singapore | 477 | 176.0 | 135.0 | 6 | Asia |
Taiwan | 1100 | 607.0 | NaN | 24 | Asia |
UK | 2994 | NaN | NaN | 68 | Europe |
Tip
df.set_index('country')
は直接df
に影響を与えない。単に,書き換えるとどうなるかを表示しているだけとなる。ここではdf
に再度割り当てることによりdf
自体を上書きしている。出力にある
NaN
(Not a Number)は欠損値を示す。行ラベルに
country
という列ラベルが残るが,それを消すにはメソッド.rename_axis('')
を使うと良いだろう。ここで''
は空の文字列である。.rename_axis(None)
でも同じ結果となる。もしくは次のコードでも良い。
df.index.name = ''
行数が多い場合(例えば,10000)、全てを表示してもあまり意味がない。そこでよく使うdf
のメソッドに、最初や最後の数行だけを表示するメソッド.head()
と.tail()
を紹介する。
df
の最初の5行を表示させる。
df.head()
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia |
France | 2950 | 1598.0 | 806.0 | 67 | Europe |
Germany | 4274 | 2294.0 | 971.0 | 84 | Europe |
India | 9142 | 5936.0 | 2471.0 | 1366 | Asia |
Italy | 2463 | 1423.0 | 614.0 | 61 | Europe |
引数に2
を指定すると最初の2
行のみ表示される。
df.head(2)
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia |
France | 2950 | 1598.0 | 806.0 | 67 | Europe |
最後の5
行を表示させる。引数に整数を入れて表示行数を指定することも可能。
df.tail()
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
Korea | 2157 | 942.0 | 705.0 | 51 | Asia |
Singapore | 477 | 176.0 | 135.0 | 6 | Asia |
Taiwan | 1100 | 607.0 | NaN | 24 | Asia |
UK | 2994 | NaN | NaN | 68 | Europe |
次にdf
の情報の特徴を確認するメソッド.info()
を使ってみよう。
df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 10 entries, China to UK
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 gdp 10 non-null int64
1 con 9 non-null float64
2 inv 8 non-null float64
3 pop 10 non-null int64
4 continent 10 non-null object
dtypes: float64(2), int64(2), object(1)
memory usage: 480.0+ bytes
説明:
<class 'pandas.core.frame.DataFrame'>
class
とはデータ型と同じ意味である。pandas.core.frame.DataFrame
がデータ型名でありクラス名でもある。例えば、
type(1)
を実行するとint
(整数型)というデータ型が表示するが,これはクラス名でもある。print(type(1))
を実行すると<class 'int'>
と表示され、クラス名もint
であることが分かる。
Index: 10 entries, Chine to UK
行のインデックスの情報
Chine
からUK
までの10
個のデータ
Data columns (total 5 columns):
列の数(5つ)
gdp 10 non-null int64
列インデックスは
0
10
の非欠損値のデータ(non-null
とは非欠損値を意味)があり,欠損値なし(行数が10
であり、10
の非欠損値であるため,即ち,欠損値数は11-11=0
)データ型は
int64
con 9 non-null float64
列インデックスは
1
9の非欠損値データがあり,欠損値数は
1=10-9
データ型は
float64
inv 8 non-null float64
列インデックスは
2
8
の非欠損値データがあり,欠損値数は2=10-8
データ型は
float64
pop 10 non-null int64
列インデックスは
3
10
の非欠損値データがあり,欠損値数なし0=10-10
データ型は
int64
continent 10 non-null object
列インデックスは
4
10
の非欠損値データがあり,欠損値数なし0=10-10
データ型は
object
(文字列などのデータ型)(注意)整数型や浮動小数点型に文字列が混ざっている場合にも列のデータ型は
object
になる
dtypes: float64(2), int64(2), object(1)
df
の列にどのようなのデータ型があるかを示すfloat64
とint64
が2列つずつ,文字列は1列
memory usage: 480.0+ bytes
メモリー使用量は約
480.0
バイト
データを読み込んだら必ずinfo()
を使って欠損値の数や列のデータ型を確認するようにしよう。
また,データの統計的な特徴は次の.describe()
メソッドでチェックできる。
df.describe()
gdp | con | inv | pop | |
---|---|---|---|---|
count | 10.000000 | 9.000000 | 8.000000 | 10.000000 |
mean | 5070.700000 | 2605.222222 | 2009.625000 | 328.800000 |
std | 5817.610106 | 2579.061107 | 2959.228564 | 565.729578 |
min | 477.000000 | 176.000000 | 135.000000 | 6.000000 |
25% | 2233.500000 | 942.000000 | 682.250000 | 53.500000 |
50% | 2972.000000 | 1598.000000 | 888.500000 | 67.500000 |
75% | 4842.500000 | 2675.000000 | 1546.250000 | 116.250000 |
max | 20118.000000 | 7796.000000 | 9137.000000 | 1434.000000 |
count
:観測値の数mean
:平均std
:標準偏差min
:最小値max
:最大値25%
:第1四分位数50%
:第2四分位数(中央値)75%
:第3四分位数max
:最大値
次のデータ属性.shape
を使ってdf
の行と列の長さを確認することができる。返値はタプルで,(行の数,列の数)
と解釈する。
df.shape
(10, 5)
返値はタプルなので,行数は以下で取得できる。
df.shape[0]
10
len()
関数を使って行数を示すこともできる。
len(df)
10
DataFrame
の構成要素#
DataFrame
には様々な属性があるが、次の3点について説明する。
データ:
df.to_numpy()
もしくはdf.values
で抽出できる。列ラベル:
df.columns
で抽出できる。行ラベル:
df.index
で抽出できる。
まずデータ自体を抽出してみよう。
df.to_numpy()
array([[20118, 7796.0, 9137.0, 1434, 'Asia'],
[2950, 1598.0, 806.0, 67, 'Europe'],
[4274, 2294.0, 971.0, 84, 'Europe'],
[9142, 5936.0, 2471.0, 1366, 'Asia'],
[2463, 1423.0, 614.0, 61, 'Europe'],
[5032, 2675.0, 1238.0, 127, 'Asia'],
[2157, 942.0, 705.0, 51, 'Asia'],
[477, 176.0, 135.0, 6, 'Asia'],
[1100, 607.0, nan, 24, 'Asia'],
[2994, nan, nan, 68, 'Europe']], dtype=object)
type(df.to_numpy())
numpy.ndarray
これで分かることは,メインのデータの部分はNumPy
のndarray
(n
次元array
)であることだ。即ち,Pandas
はNumPy
に基づいて構築されており,データ値の計算などはarray
が裏で動いているということである。Pandas
は行と列のラベルを追加し,より直感的に使えるように拡張しているのである。
次に列ラベルを取り出してみる。
df.columns
Index(['gdp', 'con', 'inv', 'pop', 'continent'], dtype='object')
dtype='object'
から列ラベルに使われているデータ型(dtype
)はオブジェクト型(object
)だとわかる。
上でも簡単に説明したが、オブジェクト型とは文字型を含む「その他」のデータ型と理解すれば良いだろう。
df.columns
の戻り値にあるdtype='object'
とdf.to_numpy()
の戻り値にあるdtype=object
は同じ意味。
列ラベル自体のデータ型(もしくは、クラス名)は次のコードで調べることができる。
type(df.columns)
pandas.core.indexes.base.Index
dir()
もしくはpy4macro.see()
で調べると多くのメソッドや属性が確認できるが,その中に.tolist()
が含まれており,これを使うことにより列ラベルをリストに変換することができる。
df_columns = df.columns.tolist()
df_columns
['gdp', 'con', 'inv', 'pop', 'continent']
行ラベルについても同じことができる。
df.index
Index(['China', 'France', 'Germany', 'India', 'Italy', 'Japan', 'Korea',
'Singapore', 'Taiwan', 'UK'],
dtype='object', name='country')
行ラベルの各要素のデータ型object
は文字列を意味している。列country
を行ラベルに指定したが,行ラベル名を削除したのでname=''
となっている。行ラベル自体(もしくは全体)のデータ型(クラス名)は
type(df.index)
pandas.core.indexes.base.Index
であり,ラベルをリストとして抽出することもできる。
df_index = df.index.tolist()
df_index
['China',
'France',
'Germany',
'India',
'Italy',
'Japan',
'Korea',
'Singapore',
'Taiwan',
'UK']
要素の抽出#
説明#
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['Japan',:]
gdp 5032
con 2675.0
inv 1238.0
pop 127
continent Asia
Name: Japan, dtype: object
1つの行をDataFrame
として抽出
df.loc[['Japan'],:]
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
Series
とDataFrame
を比べると、表示が異なることが分かると思う。コードには次の違いがある。
DataFrame
で抽出する場合、:
の左側に[]
を使う。Series
で抽出する場合、:
の左側に[]
を使わない。
複数行を抽出
df.loc[['Japan', 'UK'],:]
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
UK | 2994 | NaN | NaN | 68 | Europe |
上のコードでは、:
の左側にリストが使われている。次のコードでも同じ結果となる。
rows = ['Japan', 'UK'] #1
df.loc[rows,:] #2
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
UK | 2994 | NaN | NaN | 68 | Europe |
#1
のrows
はリストであり、#2
の:
の左側にリストが使われていることが分かる。
複数行を連続抽出(slicing)
df.loc['France':'Japan',:]
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
France | 2950 | 1598.0 | 806.0 | 67 | Europe |
Germany | 4274 | 2294.0 | 971.0 | 84 | Europe |
India | 9142 | 5936.0 | 2471.0 | 1366 | Asia |
Italy | 2463 | 1423.0 | 614.0 | 61 | Europe |
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
スライシングの場合、:
の左右に[]
は使わない。
1つの列をSeries
として抽出
df.loc[:,'gdp']
country
China 20118
France 2950
Germany 4274
India 9142
Italy 2463
Japan 5032
Korea 2157
Singapore 477
Taiwan 1100
UK 2994
Name: gdp, dtype: int64
Series
で抽出するので、:
の右に[]
は使わない。
1つの列をDataFrame
として抽出
df.loc[:,['gdp']]
gdp | |
---|---|
country | |
China | 20118 |
France | 2950 |
Germany | 4274 |
India | 9142 |
Italy | 2463 |
Japan | 5032 |
Korea | 2157 |
Singapore | 477 |
Taiwan | 1100 |
UK | 2994 |
DataFrame
として抽出するために、:
の右に[]
を使っている。
複数列を抽出
df.loc[:,['gdp','pop']]
gdp | pop | |
---|---|---|
country | ||
China | 20118 | 1434 |
France | 2950 | 67 |
Germany | 4274 | 84 |
India | 9142 | 1366 |
Italy | 2463 | 61 |
Japan | 5032 | 127 |
Korea | 2157 | 51 |
Singapore | 477 | 6 |
Taiwan | 1100 | 24 |
UK | 2994 | 68 |
次のようにしても同じ結果となる。
cols = ['gdp','pop']
df.loc[:,cols]
gdp | pop | |
---|---|---|
country | ||
China | 20118 | 1434 |
France | 2950 | 67 |
Germany | 4274 | 84 |
India | 9142 | 1366 |
Italy | 2463 | 61 |
Japan | 5032 | 127 |
Korea | 2157 | 51 |
Singapore | 477 | 6 |
Taiwan | 1100 | 24 |
UK | 2994 | 68 |
複数列を連続抽出(slicing)
df.loc[:,'con':'pop']
con | inv | pop | |
---|---|---|---|
country | |||
China | 7796.0 | 9137.0 | 1434 |
France | 1598.0 | 806.0 | 67 |
Germany | 2294.0 | 971.0 | 84 |
India | 5936.0 | 2471.0 | 1366 |
Italy | 1423.0 | 614.0 | 61 |
Japan | 2675.0 | 1238.0 | 127 |
Korea | 942.0 | 705.0 | 51 |
Singapore | 176.0 | 135.0 | 6 |
Taiwan | 607.0 | NaN | 24 |
UK | NaN | NaN | 68 |
.iloc[]
(位置番号を使用)#
1つの行をSeries
として抽出
df.iloc[5,:]
gdp 5032
con 2675.0
inv 1238.0
pop 127
continent Asia
Name: Japan, dtype: object
1つの行をDataFrame
として抽出
df.iloc[[5],:]
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
複数行を抽出
df.iloc[[1,5],:]
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
France | 2950 | 1598.0 | 806.0 | 67 | Europe |
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
複数行を連続抽出(slicing)
df.iloc[1:6,:]
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
France | 2950 | 1598.0 | 806.0 | 67 | Europe |
Germany | 4274 | 2294.0 | 971.0 | 84 | Europe |
India | 9142 | 5936.0 | 2471.0 | 1366 | Asia |
Italy | 2463 | 1423.0 | 614.0 | 61 | Europe |
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
1つの列をSeries
として抽出
df.iloc[:,1]
country
China 7796.0
France 1598.0
Germany 2294.0
India 5936.0
Italy 1423.0
Japan 2675.0
Korea 942.0
Singapore 176.0
Taiwan 607.0
UK NaN
Name: con, dtype: float64
1つの列をDataFrame
として抽出
df.iloc[:,[1]]
con | |
---|---|
country | |
China | 7796.0 |
France | 1598.0 |
Germany | 2294.0 |
India | 5936.0 |
Italy | 1423.0 |
Japan | 2675.0 |
Korea | 942.0 |
Singapore | 176.0 |
Taiwan | 607.0 |
UK | NaN |
複数列を選択
df.iloc[:,[1,3]]
con | pop | |
---|---|---|
country | ||
China | 7796.0 | 1434 |
France | 1598.0 | 67 |
Germany | 2294.0 | 84 |
India | 5936.0 | 1366 |
Italy | 1423.0 | 61 |
Japan | 2675.0 | 127 |
Korea | 942.0 | 51 |
Singapore | 176.0 | 6 |
Taiwan | 607.0 | 24 |
UK | NaN | 68 |
次のコードでも同じ結果となる。
cols = [1, 3]
df.iloc[:,cols]
con | pop | |
---|---|---|
country | ||
China | 7796.0 | 1434 |
France | 1598.0 | 67 |
Germany | 2294.0 | 84 |
India | 5936.0 | 1366 |
Italy | 1423.0 | 61 |
Japan | 2675.0 | 127 |
Korea | 942.0 | 51 |
Singapore | 176.0 | 6 |
Taiwan | 607.0 | 24 |
UK | NaN | 68 |
複数列を連続抽出(slicing)
df.iloc[:,1:3]
con | inv | |
---|---|---|
country | ||
China | 7796.0 | 9137.0 |
France | 1598.0 | 806.0 |
Germany | 2294.0 | 971.0 |
India | 5936.0 | 2471.0 |
Italy | 1423.0 | 614.0 |
Japan | 2675.0 | 1238.0 |
Korea | 942.0 | 705.0 |
Singapore | 176.0 | 135.0 |
Taiwan | 607.0 | NaN |
UK | NaN | NaN |
[]
で列の選択(ラベル使用)#
1つの列をSeries
として抽出
df['gdp']
country
China 20118
France 2950
Germany 4274
India 9142
Italy 2463
Japan 5032
Korea 2157
Singapore 477
Taiwan 1100
UK 2994
Name: gdp, dtype: int64
1つの列をDataFrame
として抽出
df[['gdp']]
gdp | |
---|---|
country | |
China | 20118 |
France | 2950 |
Germany | 4274 |
India | 9142 |
Italy | 2463 |
Japan | 5032 |
Korea | 2157 |
Singapore | 477 |
Taiwan | 1100 |
UK | 2994 |
上の2つのコードの違いに注意しよう。
DataFrame
で抽出する場合、角括弧が二重になっている。即ち、[[]]
Series
で抽出する場合、角括弧が二重になっていない。即ち、[]
次の様に考えよう。[]
を使って列を抽出する場合、
[]
の中にラベル名をそのまま書くとSeries
として抽出される。[]
の中にラベル名をリストとして書くとDataFrame
として抽出される。
複数列を選択
df[['gdp','pop']]
gdp | pop | |
---|---|---|
country | ||
China | 20118 | 1434 |
France | 2950 | 67 |
Germany | 4274 | 84 |
India | 9142 | 1366 |
Italy | 2463 | 61 |
Japan | 5032 | 127 |
Korea | 2157 | 51 |
Singapore | 477 | 6 |
Taiwan | 1100 | 24 |
UK | 2994 | 68 |
ある条件の下で行の抽出#
1つの条件の場合#
例1:GDPが1200未満の行の抽出#
まず条件を作る。
df['gdp'] < 1200
country
China False
France False
Germany False
India False
Italy False
Japan False
Korea False
Singapore True
Taiwan True
UK False
Name: gdp, dtype: bool
この条件では,GDPが100未満の行はTrue
,以上の行はFalse
となる。この条件をcond
というの変数に割り当てる。()
を省いても良いが,ある方が分かりやすいだろう。
cond = (df['gdp'] < 1200)
cond
を.loc[,]
の引数とすることにより,True
の行だけを抽出できる。(注意:cond
を使って行を抽出しようとしているので,
の左側に書く。)
df.loc[cond,:]
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Singapore | 477 | 176.0 | 135.0 | 6 | Asia |
Taiwan | 1100 | 607.0 | NaN | 24 | Asia |
この条件の下でinv
だけを抽出したい場合
df.loc[cond,'inv']
とする。
Warning
以下のように抽出を連続ですることも可能だが,避けるように!
df.loc[cond,:]['inv']
df.loc[cond,:].loc[:,'inv']
例2:continent
がAsia
の行を抽出#
cond = (df.loc[:,'continent'] == 'Asia')
df.loc[cond,:]
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia |
India | 9142 | 5936.0 | 2471.0 | 1366 | Asia |
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
Korea | 2157 | 942.0 | 705.0 | 51 | Asia |
Singapore | 477 | 176.0 | 135.0 | 6 | Asia |
Taiwan | 1100 | 607.0 | NaN | 24 | Asia |
複数条件の場合#
例3#
以下の条件の両方が満たされる場合:
gdp
が5000
以上inv
が2000
以下
それぞれの条件を作成する。
df
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia |
France | 2950 | 1598.0 | 806.0 | 67 | Europe |
Germany | 4274 | 2294.0 | 971.0 | 84 | Europe |
India | 9142 | 5936.0 | 2471.0 | 1366 | Asia |
Italy | 2463 | 1423.0 | 614.0 | 61 | Europe |
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
Korea | 2157 | 942.0 | 705.0 | 51 | Asia |
Singapore | 477 | 176.0 | 135.0 | 6 | Asia |
Taiwan | 1100 | 607.0 | NaN | 24 | Asia |
UK | 2994 | NaN | NaN | 68 | Europe |
cond1 = (df['gdp'] >= 5000)
cond2 = (df['inv'] <= 2000)
2つの条件が同時に満たされる条件を作成する。
cond = (cond1 & cond2)
<注意>
&
を使っている。理由は,cond1
とcond2
のそれぞれの行の要素を比較して真偽値から構成されるSeries
を返すためである。従って,一つの値をもう一つの値と比べて真偽値を計算するand
を使うとエラーが発生する。DataFrame
やSeries
で&
を使う場合は,比較コードを()
の中に入れることを推奨する。&
は優先されて計算されるためである。(上の例では問題は発生しなし。)
cond
を引数に使い行を抽出する。
df.loc[cond, :]
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
例4#
以下の条件のどちらかが満たされる場合:
gdp
は5000
以上con
は2000
以下
cond1 = (df['gdp'] >= 5000)
cond2 = (df['con'] <= 2000)
cond = (cond1 | cond2)
df.loc[cond, :]
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia |
France | 2950 | 1598.0 | 806.0 | 67 | Europe |
India | 9142 | 5936.0 | 2471.0 | 1366 | Asia |
Italy | 2463 | 1423.0 | 614.0 | 61 | Europe |
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
Korea | 2157 | 942.0 | 705.0 | 51 | Asia |
Singapore | 477 | 176.0 | 135.0 | 6 | Asia |
Taiwan | 1100 | 607.0 | NaN | 24 | Asia |
<注意>
|
を使っている。理由は,cond1
とcond2
のそれぞれの行の要素を比較して真偽値から構成されるSeries
を返すためである。従って,一つの値をもう一つの値と比べて真偽値を計算するor
を使うとエラーが発生する。DataFrame
やSeries
で|
を使う場合は,比較コードを()
の中に入れることを推奨する。|
は優先されて計算されるためである。(上の例では問題は発生しなし。)
<コメント>
上の例では,
&
がand
,|
がor
の役割を果たしている。同様に~
がnot
(真偽値の反転)の代わりに使われる。DataFrame
やSeries
で~
を使う場合は,コードを()
の中に入れることを推奨する。~
は優先されて計算されるためである。
例5#
以下の条件のどちらかが満たされ
gdp
は5000
以上con
は2000
以下
かつ以下の条件も同時に満たされる場合:
continent
がAsia
と等しい
cond1 = (df['gdp'] >= 5000)
cond2 = (df['con'] <= 2000)
cond3 = (df['continent'] == 'Asia')
cond = ((cond1 | cond2) & cond3)
df.loc[cond, :]
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia |
India | 9142 | 5936.0 | 2471.0 | 1366 | Asia |
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
Korea | 2157 | 942.0 | 705.0 | 51 | Asia |
Singapore | 477 | 176.0 | 135.0 | 6 | Asia |
Taiwan | 1100 | 607.0 | NaN | 24 | Asia |
query()
#
query()
というメソッドを使うと、文字列を使い行の抽出コードを書くことができる。これにより直感的なコード書くことが可能となる。
例1の場合:#
df.query('gdp < 1200')
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Singapore | 477 | 176.0 | 135.0 | 6 | Asia |
Taiwan | 1100 | 607.0 | NaN | 24 | Asia |
例2の場合#
df.query('continent == "Asia"')
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia |
India | 9142 | 5936.0 | 2471.0 | 1366 | Asia |
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
Korea | 2157 | 942.0 | 705.0 | 51 | Asia |
Singapore | 477 | 176.0 | 135.0 | 6 | Asia |
Taiwan | 1100 | 607.0 | NaN | 24 | Asia |
例3の場合#
df.query('(gdp >= 5000) & (inv <= 2000)')
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
例4の場合#
df.query('(gdp >= 5000) | (con <= 2000)')
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia |
France | 2950 | 1598.0 | 806.0 | 67 | Europe |
India | 9142 | 5936.0 | 2471.0 | 1366 | Asia |
Italy | 2463 | 1423.0 | 614.0 | 61 | Europe |
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
Korea | 2157 | 942.0 | 705.0 | 51 | Asia |
Singapore | 477 | 176.0 | 135.0 | 6 | Asia |
Taiwan | 1100 | 607.0 | NaN | 24 | Asia |
例5の場合#
df.query('(gdp >= 5000 | con <= 2000) & (continent == "Asia")')
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia |
India | 9142 | 5936.0 | 2471.0 | 1366 | Asia |
Japan | 5032 | 2675.0 | 1238.0 | 127 | Asia |
Korea | 2157 | 942.0 | 705.0 | 51 | Asia |
Singapore | 477 | 176.0 | 135.0 | 6 | Asia |
Taiwan | 1100 | 607.0 | NaN | 24 | Asia |
Tip
df
にない変数で条件を設定する場合@
が必要になる。例えば,変数z
という変数があるとしよう。
z = 100
変数z
の値に基づいて行の抽出をする場合は次のようにする。
df.query('gdp < @z')
gdp
con
inv
pop
continent
country
Singapore
477
176.0
135.0
6
Asia
Taiwan
1100
607.0
NaN
24
Asia
Warning
数字で始まる列ラベルに.query()
を使うとエラーが発生するため,列ラベルを変更する必要がある。列ラベルを変更できない場合は異なる方法を使うように。
列と行の追加と削除#
列の追加 [ ]
#
[]
に列ラベルを使って列を抽出することができるが,[]
は列の追加にも使えるので,ここではその使い方を説明する。まず,全ての行が1.0
となる列を作成するとしよう。その場合,以下のようにする。
df['Intercept'] = 1.0
df.head(2)
gdp | con | inv | pop | continent | Intercept | |
---|---|---|---|---|---|---|
country | ||||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia | 1.0 |
France | 2950 | 1598.0 | 806.0 | 67 | Europe | 1.0 |
次の例では既存の列から新たな列を作成する。まず1人当たりGDPの計算を計算し,それを変数gdp_pc
に割り当てる。
gdp_pc = df['gdp'] / df['pop']
gdp_pc.head()
country
China 14.029289
France 44.029851
Germany 50.880952
India 6.692533
Italy 40.377049
dtype: float64
これはSeries
であり,gdp_pc
としてdf
に追加する。
df['gdp_pc'] = gdp_pc
df.head(2)
gdp | con | inv | pop | continent | Intercept | gdp_pc | |
---|---|---|---|---|---|---|---|
country | |||||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia | 1.0 | 14.029289 |
France | 2950 | 1598.0 | 806.0 | 67 | Europe | 1.0 | 44.029851 |
列の追加 .loc[,]
#
.loc[]
は行と列の抽出に使ったが,追加にも使える。[]
と同じと考えれば良いだろう。次の例ではpop
を2倍した列を追加している。
df.loc[:,'2pop'] = 2 * df['pop']
df.head(3)
gdp | con | inv | pop | continent | Intercept | gdp_pc | 2pop | |
---|---|---|---|---|---|---|---|---|
country | ||||||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia | 1.0 | 14.029289 | 2868 |
France | 2950 | 1598.0 | 806.0 | 67 | Europe | 1.0 | 44.029851 | 134 |
Germany | 4274 | 2294.0 | 971.0 | 84 | Europe | 1.0 | 50.880952 | 168 |
列の削除 del
#
del df['2pop']
ここでdel
はdeleteの略。
df.head(2)
gdp | con | inv | pop | continent | Intercept | gdp_pc | |
---|---|---|---|---|---|---|---|
country | |||||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia | 1.0 | 14.029289 |
France | 2950 | 1598.0 | 806.0 | 67 | Europe | 1.0 | 44.029851 |
列2pop
が削除されている。
列の削除 drop()
#
DataFrame
には.drop()
というメソッドが用意されているので,それを使うことも可能である。
下のコードの説明:
引数
第1引数(位置引数):削除する列ラベルのリスト(一つの列のみを指定する場合はリストではなく、そのまま列ラベルを書いても可)
第2引数(キーワード引数):
axis=columns
もしくはaxis=1
(1
は列を表しし、0
は行を表す)
df.drop()
はコピーを作るだけなので,元のdf
を上書きしたい場合は次のどちらかが必要となる。df
に再割り当てする。オプション
inplace=True
(デフォルトはFalse
)を追加する。
df = df.drop(['Intercept','gdp_pc'], axis='columns')
# df.drop('Intercept', axis='columns', inplace=True)
df.head(2)
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118 | 7796.0 | 9137.0 | 1434 | Asia |
France | 2950 | 1598.0 | 806.0 | 67 | Europe |
行の追加 .loc[,]
#
.loc[,]
はh行と列の抽出,そして列の追加に使ったが,行の追加にも使える。
df.loc['US',:] = [20566, 14462, 4557, 329, 'North America']
df.tail(3)
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Taiwan | 1100.0 | 607.0 | NaN | 24.0 | Asia |
UK | 2994.0 | NaN | NaN | 68.0 | Europe |
US | 20566.0 | 14462.0 | 4557.0 | 329.0 | North America |
上の例では,最初の4つの要素は整数として入力されたが,df
の中では浮動小数点に変換されている。
行の削除 drop()
#
.drop()
は列を削除する際に紹介したが、行の削除にも使えるメソッドである。
引数
第1引数(位置引数):削除する行ラベルのリスト(一つの行のみを指定する場合はリストではなく、そのまま行ラベルを書いても可)
第2引数(キーワード引数):
axis=rows
もしくはaxis=0
(1
は列を表しし、0
は行を表す)
df.drop()
はコピーを作るだけなので,元のdf
を上書きしたい場合は次のどちらかが必要となる。df
に再割り当てする。オプション
inplace=True
(デフォルトはFalse
)を追加する。
df = df.drop('US', axis='rows')
# df.drop('US', axis=0, inplace=True)
df.tail()
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Japan | 5032.0 | 2675.0 | 1238.0 | 127.0 | Asia |
Korea | 2157.0 | 942.0 | 705.0 | 51.0 | Asia |
Singapore | 477.0 | 176.0 | 135.0 | 6.0 | Asia |
Taiwan | 1100.0 | 607.0 | NaN | 24.0 | Asia |
UK | 2994.0 | NaN | NaN | 68.0 | Europe |
US
の行が削除されている。
欠損値の扱い#
Pandas
では欠損値はNaN
(Not a Number)や<NA>
(Not Availabel)と表示されるが,null
と呼んだりもする。
欠損値の確認#
欠損値があるかどうかの確認は,df.info()
でもできるが,以下のメソッドを組み合わせることでも可能である。
isna()
:(is it na?の略)それぞれの要素についてNaN
の場合True
を,そうでない場合はFalse
を返す。(DataFrame
の全ての要素がTrue/False
となる。)sum()
:df
の上から下に行(rows)を縦断して,それぞれの列の中にあるTrue
数える。デフォルトで引数
axis='rows'
が指定されている。引数値
rows
は複数!(0
でも可)
sum(axis='columns')
:df
の左から右に列(columns)を横断して,それぞれの行の中にあるTrue
を数える。引数値
columns
は複数!(1
でも可)
(注意)sum()
のaxis
は「行を縦断」か「列を横断」かを指定する。
df.isna().sum()
gdp 0
con 1
inv 2
pop 0
continent 0
dtype: int64
inv
とcon
にNaN
があることがわかる。axis='columns'
を設定するとNaN
がある行を確認できる。
df.isna().sum(axis='columns')
country
China 0
France 0
Germany 0
India 0
Italy 0
Japan 0
Korea 0
Singapore 0
Taiwan 1
UK 2
dtype: int64
NaN
がある行だけを抽出したい場合がある。その場合はメソッドany()
が役に立つ。
any()
:df
の上から下に行(rows
)を縦断して,それぞれの列の中で一つ以上True
がある場合にはTrue
を,一つもない場合はFalse
を返す。デフォルトで引数
axis='rows'
が指定されている。引数値
rows
は複数!(0
でも可)
any(axis='columns')
:dfの左から右に列(columns
)を横断して,それぞれの行の中で一つ以上True
がある場合にはTrue
を,一つもない場合はFalse
を返す。引数値
columns
は複数!(1でも可)
(注意)any()
のaxis
は「行を縦断」か「列を横断」かを指定する。
cond = df.isna().any(axis='columns')
df.loc[cond,:]
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Taiwan | 1100.0 | 607.0 | NaN | 24.0 | Asia |
UK | 2994.0 | NaN | NaN | 68.0 | Europe |
これでNaN
がある行だけを抽出できた。
欠損値がある行の削除#
欠損値がある全ての行を削除する。
df.dropna()
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118.0 | 7796.0 | 9137.0 | 1434.0 | Asia |
France | 2950.0 | 1598.0 | 806.0 | 67.0 | Europe |
Germany | 4274.0 | 2294.0 | 971.0 | 84.0 | Europe |
India | 9142.0 | 5936.0 | 2471.0 | 1366.0 | Asia |
Italy | 2463.0 | 1423.0 | 614.0 | 61.0 | Europe |
Japan | 5032.0 | 2675.0 | 1238.0 | 127.0 | Asia |
Korea | 2157.0 | 942.0 | 705.0 | 51.0 | Asia |
Singapore | 477.0 | 176.0 | 135.0 | 6.0 | Asia |
Taiwan
とUK
が削除されている。
このメソッドは,欠損値を削除するとどうなるかを示すだけでありdf
自体は影響は受けない。df
自体からNaN
がある行を削除する場合はinplace=True
のオプション(デフォルトではFalse
になっている)を加えて
df.dropna(inplace=True)
とするか,削除後のdf
をdf
自体に再割り当てする。
df = df.dropna()
また,ある列でNaN
がある行のみを削除する場合は,引数subset
を使う。
次のコードでは、con
にNaN
がある行のみを削除している。
df.dropna(subset=['con'])
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118.0 | 7796.0 | 9137.0 | 1434.0 | Asia |
France | 2950.0 | 1598.0 | 806.0 | 67.0 | Europe |
Germany | 4274.0 | 2294.0 | 971.0 | 84.0 | Europe |
India | 9142.0 | 5936.0 | 2471.0 | 1366.0 | Asia |
Italy | 2463.0 | 1423.0 | 614.0 | 61.0 | Europe |
Japan | 5032.0 | 2675.0 | 1238.0 | 127.0 | Asia |
Korea | 2157.0 | 942.0 | 705.0 | 51.0 | Asia |
Singapore | 477.0 | 176.0 | 135.0 | 6.0 | Asia |
Taiwan | 1100.0 | 607.0 | NaN | 24.0 | Asia |
(注意)オプションsubset=
には削除する列が1つであっても、[]
を使いリストとして指定する。
並び替え#
行の並び替えにはメソッド.sort_values()
を使う。引数に列ラベルを指定することにより、その列を基準に行を昇順に並び替える。
df
をgdp
の昇順に並び替えてみよう。
df.sort_values('gdp').head()
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
Singapore | 477.0 | 176.0 | 135.0 | 6.0 | Asia |
Taiwan | 1100.0 | 607.0 | NaN | 24.0 | Asia |
Korea | 2157.0 | 942.0 | 705.0 | 51.0 | Asia |
Italy | 2463.0 | 1423.0 | 614.0 | 61.0 | Europe |
France | 2950.0 | 1598.0 | 806.0 | 67.0 | Europe |
降順にする場合は、引数ascending=False
を付け加える(デフォルトではascending=True
となっている)。
df.sort_values('gdp', ascending=False).head()
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118.0 | 7796.0 | 9137.0 | 1434.0 | Asia |
India | 9142.0 | 5936.0 | 2471.0 | 1366.0 | Asia |
Japan | 5032.0 | 2675.0 | 1238.0 | 127.0 | Asia |
Germany | 4274.0 | 2294.0 | 971.0 | 84.0 | Europe |
UK | 2994.0 | NaN | NaN | 68.0 | Europe |
複数の列を基準に並び替えする場合は、引数ascending=
に真偽値をリストとして指定する。
df.sort_values(['continent','gdp'], ascending=[True,False])
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118.0 | 7796.0 | 9137.0 | 1434.0 | Asia |
India | 9142.0 | 5936.0 | 2471.0 | 1366.0 | Asia |
Japan | 5032.0 | 2675.0 | 1238.0 | 127.0 | Asia |
Korea | 2157.0 | 942.0 | 705.0 | 51.0 | Asia |
Taiwan | 1100.0 | 607.0 | NaN | 24.0 | Asia |
Singapore | 477.0 | 176.0 | 135.0 | 6.0 | Asia |
Germany | 4274.0 | 2294.0 | 971.0 | 84.0 | Europe |
UK | 2994.0 | NaN | NaN | 68.0 | Europe |
France | 2950.0 | 1598.0 | 806.0 | 67.0 | Europe |
Italy | 2463.0 | 1423.0 | 614.0 | 61.0 | Europe |
ここではcontinent
に従って先に並び替えられ,その後にgdp
に従って並び替えられている。ascending
は昇順(True
)か降順(False
)かを指定する引数であり,['continent','gdp']
とascending=['True','False']
の順番が対応している。
行番号を振り直す#
データの読み込みとデータのチェックにおいてデータを読み込んだ後,直ぐに.set_index('year')
を使い,列year
を行ラベルに設定した。もちろん,必ずしもそうする必要はなく,行番号(0
,1
,2
,…)のままで作業をおこなっても全く問題ない。また行ラベルを設定した後に,行インデックスに戻したい場合もあるだろう。その場合には,メソッド.reset_index()
を使うと,行のインデックスを0,1,2,..と振り直すことができる。df
を使うと次のようになる。
df.reset_index()
country | gdp | con | inv | pop | continent | |
---|---|---|---|---|---|---|
0 | China | 20118.0 | 7796.0 | 9137.0 | 1434.0 | Asia |
1 | France | 2950.0 | 1598.0 | 806.0 | 67.0 | Europe |
2 | Germany | 4274.0 | 2294.0 | 971.0 | 84.0 | Europe |
3 | India | 9142.0 | 5936.0 | 2471.0 | 1366.0 | Asia |
4 | Italy | 2463.0 | 1423.0 | 614.0 | 61.0 | Europe |
5 | Japan | 5032.0 | 2675.0 | 1238.0 | 127.0 | Asia |
6 | Korea | 2157.0 | 942.0 | 705.0 | 51.0 | Asia |
7 | Singapore | 477.0 | 176.0 | 135.0 | 6.0 | Asia |
8 | Taiwan | 1100.0 | 607.0 | NaN | 24.0 | Asia |
9 | UK | 2994.0 | NaN | NaN | 68.0 | Europe |
行ラベルがなくなり、元の行ラベル名は削除していたので、列ラベルないまま新たな列として追加されている。列ラベルを追加したい場合は、以下で説明するが、次のようにすれば良い。
df = df.rename({'':'country'}, axis='columns')
ここで''
は空の文字列を示す。一方で、元の行ラベルを列として追加したくない場合もある。その場合、reset_index()
に引数drop=True
を加えると良い。
df.reset_index(drop=True).head()
gdp | con | inv | pop | continent | |
---|---|---|---|---|---|
0 | 20118.0 | 7796.0 | 9137.0 | 1434.0 | Asia |
1 | 2950.0 | 1598.0 | 806.0 | 67.0 | Europe |
2 | 4274.0 | 2294.0 | 971.0 | 84.0 | Europe |
3 | 9142.0 | 5936.0 | 2471.0 | 1366.0 | Asia |
4 | 2463.0 | 1423.0 | 614.0 | 61.0 | Europe |
.reset_index()
は、変更した場合にどのように見えるかを表示しているだけなので、df
自体を変更する場合はdf
に再割り当てする必要がある。
列のラベルの変更#
列ラベルを変更する場合は、メソッド.rename()
を使うと良いだろう。引数は次の形で設定する。
ここで「辞書」は次のルールで指定する。
key
:元のラベルvalue
:新しいラベル
次のコードでは,df
のpop
をpop_new
に,id
をcontinent_new
に変更している。
df.rename(columns={'pop':'pop_new','continent':'continent_new'}).head()
gdp | con | inv | pop_new | continent_new | |
---|---|---|---|---|---|
country | |||||
China | 20118.0 | 7796.0 | 9137.0 | 1434.0 | Asia |
France | 2950.0 | 1598.0 | 806.0 | 67.0 | Europe |
Germany | 4274.0 | 2294.0 | 971.0 | 84.0 | Europe |
India | 9142.0 | 5936.0 | 2471.0 | 1366.0 | Asia |
Italy | 2463.0 | 1423.0 | 614.0 | 61.0 | Europe |
ただし,再割り当てしないとdf
は変更されないので注意しよう。即ち,上のコードではdf
の行ラベルは変更されていない。
.rename()
以外にも次のように変更することも可能である。
df.columns = ['gdp','inv','con','pop_new','continent_new']
このコードは
右辺の文字列のリストを
df.columns
に割り当てる
と読むことができる。このコードにより割り当てが完了し,df
は変更されることになる。df
を表示してみよう。
df.head(2)
gdp | inv | con | pop_new | continent_new | |
---|---|---|---|---|---|
country | |||||
China | 20118.0 | 7796.0 | 9137.0 | 1434.0 | Asia |
France | 2950.0 | 1598.0 | 806.0 | 67.0 | Europe |
この方法が簡単そうだが、全ての列ラベルをリストとして準備する必要がある点である。列の数が多いと面倒なので,そういう場合は.rename()
の方が使いやすいだろう。元の列ラベルに戻しておこう。
df = df.rename({'pop_new':'pop', 'continent_new':'continent'}, axis='columns')
便利な使うメソッド#
列id
には文字列があり,行のデータをカテゴリー別に分けていると考えることができる。メソッド.unique()
を使うと,選択した列に重複したデータがある場合,ユニークなものだけを抽出できる。
df['continent'].unique()
array(['Asia', 'Europe'], dtype=object)
まな類似するメソッドに.nunique()
があり,カテゴリー数を返す。
df['continent'].nunique()
2
関連するメソッドに.value_counts()
がある。これを使うとカテゴリーの内訳を確認するすることができる。各カテゴリーに対応する行数(度数)を表示するには
df.loc[:,'continent'].value_counts()
continent
Asia 6
Europe 4
Name: count, dtype: int64
とする。引数normalize=True
を追加すると,相対度数として表示できる。
df.loc[:,'continent'].value_counts(normalize=True)
continent
Asia 0.6
Europe 0.4
Name: proportion, dtype: float64
df
のように行数が少ないDataFrame
の場合,これらのメソッドの有用性を見出すことは難しいが,何千行あるデータを扱っていると想像してみよう。そのような大きなデータを扱う場合は非常に重宝するメソッドだろう。
統計関連のメソッド#
時系列データ#
統計関連のメソッドを幾つか紹介するために日本の時系列データからなる.csv
ファイルを読み込むことにする。data2.csv
はここをクリックをマウスの右クリックすることによりダウンロードすることもできる。
jp = pd.read_csv('./data/data2.csv')
jp
year | gdp | con | inv | gov | net_ex | |
---|---|---|---|---|---|---|
0 | 2012 | 517928.400 | 295751.750 | 98682.200 | 127135.975 | -3941.775 |
1 | 2013 | 528522.025 | 303606.600 | 100590.050 | 129984.100 | -6006.850 |
2 | 2014 | 529656.425 | 300593.375 | 103200.725 | 131434.275 | -5477.075 |
3 | 2015 | 538098.750 | 300107.925 | 108697.000 | 132249.450 | -2955.575 |
4 | 2016 | 541964.600 | 298675.925 | 109195.975 | 134433.625 | -340.550 |
5 | 2017 | 551205.775 | 301894.650 | 111970.275 | 134762.325 | 2800.425 |
6 | 2018 | 554349.275 | 302496.325 | 113151.350 | 136011.050 | 2901.725 |
7 | 2019 | 553019.775 | 300927.225 | 113488.750 | 138450.375 | 281.825 |
8 | 2020 | 527946.300 | 285155.350 | 104932.325 | 142151.400 | -4601.625 |
9 | 2021 | 536811.775 | 288895.200 | 103434.900 | 143454.975 | 1222.325 |
数値は日本の四半期データを年平均(単位:10
億円)にしている。
year
:年gdp
:国内総生産(Gross Domestic Product)con
:消費(consumption)inv
:投資(investment)gov
:政府支出(government expenditure)net_ex
:純輸出(net exports)
列year
を行ラベルに設定して説明を進めていくことにする。
jp = jp.set_index('year')
jp.head(2)
gdp | con | inv | gov | net_ex | |
---|---|---|---|---|---|
year | |||||
2012 | 517928.400 | 295751.75 | 98682.20 | 127135.975 | -3941.775 |
2013 | 528522.025 | 303606.60 | 100590.05 | 129984.100 | -6006.850 |
メソッド#
<以下で説明する統計関連のメソッドに関する注意点>
計算して意味のある列のみを選択すること。
pandas
のバージョンにもよるが、文字列型の列を計算に含めるとエラーが発生する。jp
は全てが浮動小数点となるため、以下では全ての列について計算を行なう。数値に含まれる欠損値は無視されることになる。
上でも出てきたが.sum()
は各列の合計を返す(axis='rows'
がデフォルト)。
jp.sum()
gdp 5379503.100
con 2978104.325
inv 1067343.550
gov 1350067.550
net_ex -16117.150
dtype: float64
各行の合計を計算したい場合は引数axis=1
もしくはaxis='columns'
を指定する。ここでcolumns
となるのは列を横断すると覚えれば良いだろう。支出要素だけを合計してみよう。
jp.iloc[:,1:].sum(axis='columns')
year
2012 517628.150
2013 528173.900
2014 529751.300
2015 538098.800
2016 541964.975
2017 551427.675
2018 554560.450
2019 553148.175
2020 527637.450
2021 537007.400
dtype: float64
同じ使い方で次のメソッドが利用できる。
.max()
:最大値.min()
:最小値.mean()
:(算術)平均.std()
:標準偏差引数
ddof=1
はデフォルトで1
std()
はデフォルトで不偏標準偏差を計算する
.var()
:分散引数
ddof=1
はデフォルトで1
var()
はデフォルトで不偏分散を計算する
次の2つも便利である。計算する際,欠損値は無視され,結果はDataFrame
として返される。
.cov()
:分散共分散行列引数
ddof=1
はデフォルトで1
cov()
はデフォルトで不偏分散・共分散を計算する
.corr()
:相関係数
Warning
pandas
とnumpy
では,標準偏差,分散,共分散を計算する際の引数ddof
のデフォルトの値が異なるので注意が必要!
DataFrame
のメソッド.std()
,.var()
,.cov()
の全てでddof=1
がデフォルト
numpy
の関数.std()
,.var()
ではddof=0
がデフォルト.cov()
ではddof=1
がデフォルト
母集団の標準偏差・分散・共分散の不偏推定量を計算するにはddof=1
が必要!!
Warning
DataFrame
のメソッド.cov()
と.corr()
は欠損値を自動的に無視する。従って,相関係数の場合,.corr()
の結果と.std()
を使う「手計算」の結果を比べる際は欠損値がどのように扱われているかに注意する必要がある。
例えば,.cov()
を計算してみよう。
jp.cov()
gdp | con | inv | gov | net_ex | |
---|---|---|---|---|---|
gdp | 1.497548e+08 | 3.133412e+07 | 6.187068e+07 | 2.398800e+07 | 3.475253e+07 |
con | 3.133412e+07 | 3.773237e+07 | 1.138839e+07 | -2.010831e+07 | 2.691150e+06 |
inv | 6.187068e+07 | 1.138839e+07 | 2.814845e+07 | 9.745975e+06 | 1.342318e+07 |
gov | 2.398800e+07 | -2.010831e+07 | 9.745975e+06 | 2.702392e+07 | 7.750688e+06 |
net_ex | 3.475253e+07 | 2.691150e+06 | 1.342318e+07 | 7.750688e+06 | 1.150046e+07 |
対角成分は分散であり,その他は行と列のラベルに対応する共分散となる。.corr()
も同じ位置関係となる。
jp.corr()
gdp | con | inv | gov | net_ex | |
---|---|---|---|---|---|
gdp | 1.000000 | 0.416841 | 0.952944 | 0.377076 | 0.837411 |
con | 0.416841 | 1.000000 | 0.349444 | -0.629715 | 0.129188 |
inv | 0.952944 | 0.349444 | 1.000000 | 0.353365 | 0.746054 |
gov | 0.377076 | -0.629715 | 0.353365 | 1.000000 | 0.439651 |
net_ex | 0.837411 | 0.129188 | 0.746054 | 0.439651 | 1.000000 |
また変数の変化率(成長率)を計算するには.pct_change()
が便利である(percent changeの略)。\(x_t\)を変数とすると、次式に従って毎期ごとの変化率を返すことになる。
jp.pct_change()
gdp | con | inv | gov | net_ex | |
---|---|---|---|---|---|
year | |||||
2012 | NaN | NaN | NaN | NaN | NaN |
2013 | 0.020454 | 0.026559 | 0.019333 | 0.022402 | 0.523895 |
2014 | 0.002146 | -0.009925 | 0.025954 | 0.011157 | -0.088195 |
2015 | 0.015939 | -0.001615 | 0.053258 | 0.006202 | -0.460373 |
2016 | 0.007184 | -0.004772 | 0.004591 | 0.016516 | -0.884777 |
2017 | 0.017051 | 0.010777 | 0.025407 | 0.002445 | -9.223242 |
2018 | 0.005703 | 0.001993 | 0.010548 | 0.009266 | 0.036173 |
2019 | -0.002398 | -0.005187 | 0.002982 | 0.017935 | -0.902877 |
2020 | -0.045339 | -0.052411 | -0.075394 | 0.026732 | -17.327952 |
2021 | 0.016792 | 0.013115 | -0.014270 | 0.009170 | -1.265629 |
2011
年が無いため,2012
年の成長率は欠損値となっている。%表示するのであれば
100 * jp.pct_change()
とすれば良いだろう。
.pct_change()
と.mean()
を続けて書くと簡単に(算術)平均成長率を計算することができる。
jp.pct_change().mean()
gdp 0.004170
con -0.002385
inv 0.005823
gov 0.013536
net_ex -3.288109
dtype: float64
Series
について#
Series
について簡単に説明する。Series
はスプレッド・シートから1つの行または列を取り出したようなデータとイメージすれば良いだろう。Series
には行と列を区別しないが、Series
のインデックスはDataFrame
のインデックスと同じと考えて良いだろう。ただし、以下では次の表現を使う。
インデックスが位置番号(
0
、1
、2
)をインデックス番号と呼ぶ。インデックスがラベルの場合は、単にラベルと呼ぶ。
まずdf
の列gdp
からSeries
を作ってみよう。
s = df.loc[:,'gdp']
s
country
China 20118.0
France 2950.0
Germany 4274.0
India 9142.0
Italy 2463.0
Japan 5032.0
Korea 2157.0
Singapore 477.0
Taiwan 1100.0
UK 2994.0
Name: gdp, dtype: float64
Note
df.loc[:,['gdp']]
もしくは
df[['gdp']]
で列を抽出するとDataFrame
が返される。また次のようにs
のメソッド.to_frame()
を使うとSeries
をDataFrame
に変換できるので、覚えておくと便利かも知れない。
s.to_frame()
新たに列ラベルを設定する場合は
s.to_frame('新たな列ラベル')
とする。
df
の行ラベルが整数型であり、それがs
のインデックスに使われている。
また、gdp
はs
の名前(Name
)として使われている。
構成要素#
この例を使いSeries
の構成要素について説明する。
データ:
s.to_numpy()
もしくはs.values
で抽出可能Series
名:s.name
で抽出可能df
の列ラベルから引き継がれているが、空の場合もある。
インデックス(番号もしくはラベル):
s.index
で抽出可能上の例では
df
の行ラベルから引き継がれているインデックス番号であれば、
0
,1
,2
,…と表示される。
インデックス名:
s.index.name
で抽出可能df
の行ラベル名から引き継がれていが、空の場合もある。
まずデータ自体を抽出する。
s.to_numpy()
array([20118., 2950., 4274., 9142., 2463., 5032., 2157., 477.,
1100., 2994.])
type(s.to_numpy())
numpy.ndarray
Series
名の抽出
s.name
'gdp'
インデックスの抽出
s.index
Index(['China', 'France', 'Germany', 'India', 'Italy', 'Japan', 'Korea',
'Singapore', 'Taiwan', 'UK'],
dtype='object', name='country')
dtype='object'
とあるが、ラベルは文字列であることがわかる。
要素の抽出#
要素の抽出はラベルを使う方法とインデックス番号を使う方法がある。ラベルを使う場合を考えてみよう。DataFrame
と同様に.loc[]
を使う事ができる。違いは、Series
の場合、行と列の区別がないのでラベルだけを指定すれば良い。
s.loc['Japan']
5032.0
複数の場合はラベルをリストとする。
s.loc[['Japan','Korea','UK']]
country
Japan 5032.0
Korea 2157.0
UK 2994.0
Name: gdp, dtype: float64
ラベルを使う場合,.loc
を省略することができる。辞書と同じような形となる。
s['Japan']
5032.0
s[['Japan','Korea','UK']]
country
Japan 5032.0
Korea 2157.0
UK 2994.0
Name: gdp, dtype: float64
次にインデックス番号を使う要素の抽出方法を紹介しよう。DataFrame
と同様に.iloc[]
を使う。
s.iloc[5]
5032.0
複数のインデックスを使うことも可能である。
s.iloc[[1,3,5]]
country
France 2950.0
India 9142.0
Japan 5032.0
Name: gdp, dtype: float64
またスライシングも同様にできる。
s.iloc[5:-2]
country
Japan 5032.0
Korea 2157.0
Singapore 477.0
Name: gdp, dtype: float64
Warning
Pandasのバージョンによっては.iloc
を省略してs[1]
のようにして要素にアクセスできるかも知れないが、バージョンが進むと使えなくなるので注意しよう。
統計学関連メソッド#
Series
にもDataFrame
と同じような統計学関連メソッドが実装されているので,py4macro.see()
で調べてみよう。
py4macro.see(s)
.China .France .Germany .India
.Italy .Japan .Korea .Singapore
.T .Taiwan .UK .abs
.add .add_prefix .add_suffix .agg
.aggregate .align .all .any
.apply .argmax .argmin .argsort
.array .asfreq .asof .astype
.at .at_time .attrs .autocorr
.axes .backfill .between .between_time
.bfill .bool .case_when .clip
.combine .combine_first .compare .convert_dtypes
.copy .corr .count .cov
.cummax .cummin .cumprod .cumsum
.describe .diff .div .divide
.divmod .dot .drop .drop_duplicates
.droplevel .dropna .dtype .dtypes
.duplicated .empty .eq .equals
.ewm .expanding .explode .factorize
.ffill .fillna .filter .first
.first_valid_index .flags .floordiv .ge
.get .groupby .gt .hasnans
.head .hist .iat .idxmax
.idxmin .iloc .index .infer_objects
.info .interpolate .is_monotonic_decreasing .is_monotonic_increasing
.is_unique .isin .isna .isnull
.item .items .keys .kurt
.kurtosis .last .last_valid_index .le
.list .loc .lt .map
.mask .max .mean .median
.memory_usage .min .mod .mode
.mul .multiply .name .nbytes
.ndim .ne .nlargest .notna
.notnull .nsmallest .nunique .pad
.pct_change .pipe .plot .pop
.pow .prod .product .quantile
.radd .rank .ravel .rdiv
.rdivmod .reindex .reindex_like .rename
.rename_axis .reorder_levels .repeat .replace
.resample .reset_index .rfloordiv .rmod
.rmul .rolling .round .rpow
.rsub .rtruediv .sample .searchsorted
.sem .set_axis .set_flags .shape
.shift .size .skew .sort_index
.sort_values .squeeze .std .struct
.sub .subtract .sum .swapaxes
.swaplevel .tail .take .to_clipboard
.to_csv .to_dict .to_excel .to_frame
.to_hdf .to_json .to_latex .to_list
.to_markdown .to_numpy .to_period .to_pickle
.to_sql .to_string .to_timestamp .to_xarray
.transform .transpose .truediv .truncate
.tz_convert .tz_localize .unique .unstack
.update .value_counts .values .var
.view .where .xs
この中に
.mean
:平均.std
:標準偏差.var
:分散.sum
:合計
を計算するメソッドが含まれているのが確認できる。
グループ計算#
再度df
を使って説明する。表示してみよう。
df.head()
gdp | inv | con | pop | continent | |
---|---|---|---|---|---|
country | |||||
China | 20118.0 | 7796.0 | 9137.0 | 1434.0 | Asia |
France | 2950.0 | 1598.0 | 806.0 | 67.0 | Europe |
Germany | 4274.0 | 2294.0 | 971.0 | 84.0 | Europe |
India | 9142.0 | 5936.0 | 2471.0 | 1366.0 | Asia |
Italy | 2463.0 | 1423.0 | 614.0 | 61.0 | Europe |
df
のメソッド.mean()
を使うと列の平均を簡単に計算できることは説明した。id
以外の列を取り出して計算してみよう。
df.iloc[:,:-1].mean()
gdp 5070.700000
inv 2605.222222
con 2009.625000
pop 328.800000
dtype: float64
一方で,列continent
にはAsia
とEurope
があり,df
を2つのグループに分けることができる。ここで問題になるのは,グループ別に列の平均を計算したい場合である。まず考えられる方法はAsia
グループだけを抽出して平均を計算し,同じようにEurope
も計算するということだろう。もしグループ数が多い場合はfor
ループが必要になり,より長いコードを書く必要があり面倒に感じることになる。
こういう場合のために,DataFrame
には簡単にグループ計算を可能にする方法が用意されている。それが.groupby()
というメソッドである。以下では.groupby()
の使い方をdf
を使って3つのステップに分けて説明する。
ステップ1:グループ化する列を指定#
最初のステップでは,グループ化したい列名を引数として.groupby()
を実行する。df
をcontinent
でグループ化したいので次のコードとなる。
df.groupby('continent')
<注意>
このコードはDataFrame
を返すわけではない。返すのはグループ計算用のオブジェクトであり,それを使ってグループ計算をおこなう事になる。
実際にコードを実行し,変数df_groupby
に割り当てよう。
df_groupby = df.groupby('continent')
df_groupby
を実行してみよう。
df_groupby
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x124b10230>
何も返されない。表示されているのは,PCメモリーのある箇所にDataFrameGroupBy
というオブジェクトが存在すると伝えているだけである。
ステップ2:グループ計算したい列を指定#
次にグループ計算したい列を指定するが,次の様な書き方となる。
df_groupby[<グループ計算したい列ラベル>]
ここで[]
を使っていることに注意しよう。例としてgdp
をグループ計算するとしよう。
df_groupby['gdp']
<pandas.core.groupby.generic.SeriesGroupBy object at 0x124b10800>
ここでも新たなオブジェクトSeriesGroupBy
のメモリーアドレスが表示されるだけである。どの様に計算するかを指定することにより,計算結果が返されることになる。それが次のステップである。
ステップ3:計算方法の指定#
ステップ1と2がグループ計算の準備段階であり,あとは実際にどのように計算したいかを指定する。例として,平均を考えてみよう。上でも出てきたが,平均はメソッド.mean()
を使う。
df_groupby['gdp'].mean()
continent
Asia 6337.666667
Europe 3170.250000
Name: gdp, dtype: float64
グループ計算結果はSeries
として返されている。.mean()
以外にも使える関数は準備されいる。py4macro.see()
を使って属性・メソッドを調べてみよう。
py4macro.see(df_groupby['gdp'])
.China .France .Germany .India
.Italy .Japan .Korea .Singapore
.Taiwan .UK .agg .aggregate
.all .any .apply .bfill
.corr .count .cov .cumcount
.cummax .cummin .cumprod .cumsum
.describe .diff .dtype .ewm
.expanding .ffill .fillna .filter
.first .get_group .groups .head
.hist .idxmax .idxmin .indices
.is_monotonic_decreasing .is_monotonic_increasing .last .max
.mean .median .min .ndim
.ngroup .ngroups .nlargest .nsmallest
.nth .nunique .ohlc .pct_change
.pipe .plot .prod .quantile
.rank .resample .rolling .sample
.sem .shift .size .skew
.std .sum .tail .take
.transform .unique .value_counts .var
主なメソッドとして次を挙げることができる(これらの計算で欠損値は無視される)。
mean()
:平均median()
:中央値max()
:最大値min()
:最小値std()
:標準偏差var()
:分散sum()
:合計cumsum()
:累積和first()
:最初の値last()
:最後の値count()
:要素数などなど
このリストにない計算をしたい場合もあるだろう。その場合は,上のリストにもある.agg()
(aggregate()
も同じ)を使い,自作の関数を指定することができる。例えば,平均を計算する自作の関数my_mean()
を考えてみよう。
def my_mean(x):
return sum(x) / len(x)
ここでのx
はステップ2で指定する計算する対象の列と考えれば良いだろう。実際に実行してみよう。
df_groupby['gdp'].agg(my_mean)
continent
Asia 6337.666667
Europe 3170.250000
Name: gdp, dtype: float64
Warning
この場合,()
がなく,関数名だけを.agg()
の引数にしている。関数名だけを.agg
に渡し,.agg
が渡された関数を実行するというイメージである。()
を付けると.agg()
に渡す前に関数を実行することになりエラーとなってしまう。
次のステップ#
1行で書く#
上の説明では3つのステップに分けたが,もちろん次のように3つのステップを次のように1行で書いても構わないし,むしろその場合が多いだろう。
df.groupby('continent')['gdp'].mean()
continent
Asia 6337.666667
Europe 3170.250000
Name: gdp, dtype: float64
このコードは次の様に読むことができる。
df
をid
でグループ分けして,gdp
の平均を計算する。
複数選択#
各ステップでは列や関数を一つだけ選択・設定しているが,それぞれ複数選択することも可能である。
ステップ1ではグループ化する上で基準となる列を複数選択
ステップ2では計算対象となる列を複数選択
ステップ3では
.agg()
の引数に複数の関数を指定
という具合である。ただその場合は,リストとして列や関数名を指定する必要がある。例えば,ステップ2でgdp
とinv
を選ぶとしよう。
df.groupby('continent')[['gdp','inv']].mean()
gdp | inv | |
---|---|---|
continent | ||
Asia | 6337.666667 | 3022.000000 |
Europe | 3170.250000 | 1771.666667 |
結果はDataFrame
として返されている。ステップ1もしくは3で複数選択するとDataFrame
がMultiIndex
(階層的な行と列)として返されることになるが,その簡単な説明についてはGapminderを参照して欲しい。より詳しい説明は他のサイトに譲ることにする。
df
のような小さなDataFrame
では.groupby
の威力はあまりピンとこないかも知れない。しかし大きなDataFrame
を使うとその恩恵を強く感じることだろう。Gapminderではマクロ経済データを使い.groupby()
の使い方の例を示している。興味がある人は是非!