Pandas
:データ分析#
import pandas as pd
import numpy as np
import py4macro
# 警告メッセージを非表示
import warnings
warnings.filterwarnings("ignore")
説明#
Pandas
はデータを扱う上で欠かせないパッケージであり,エクセルをイメージすれば良いだろう。Pandas
にはスプレッド・シートに対応するDataFrame
(データ・フレーム)とSeries
(シリーズ)呼ばれるオブジェクトがあり,それらを駆使してデータ分析をおこなう。
データ・フレームは下の図のようになっている。X
,Y
,Z
は「列ラベル」(アルファベットや記号など)であり、一番左の1
,2
,3
は「行インデックス」(数字)である(「行ラベル」(アルファベットや記号など)を設定することも可能)。その他の数字がデータとなっている。
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
も同様に同じ列にあるデータから構成される。Series
は1つの列からなるスプレッド・シートと考えれば良いだろう。
この特徴により複雑なデータでも扱いが簡単になる。例えば,NumPy
を使うと目的のデータがどこにあるのかは,何番目の行で何番目の列なのかを把握する必要があるが,Pandas
では行・列にラベルを使うことにより(例えば,行「1980年」の列「GDP」のようにデータを特定することが可能となる),データの操作が非常に簡単になる。また,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
を行ラベルに設定してPandas
の使い方について説明することにする。
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)
でも同じ結果となる。
行数が大きい場合(例えば,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を指定すると最初の3行のみ表示される。
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.values
で抽出できる。列ラベル:
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)
<注意>
&
を使っている。理由は,cond1
とcond2
のそれぞれの行の要素を比較して真偽値から構成されるSeries
を返すためである。従って,一つの値をもう一つの値と比べて真偽値を計算するand
を使うとエラーが発生する。DataFrame
やSeries
で&
を使う場合は,比較コードを()
の中に入れることを推奨する。&
は優先されて計算されるためである。(上の例では問題は発生しなし。)
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 |
<注意>
|
を使っている。理由は,cond1
とcond2
のそれぞれの行の要素を比較して真偽値から構成されるSeries
を返すためである。従って,一つの値をもう一つの値と比べて真偽値を計算するor
を使うとエラーが発生する。DataFrame
やSeries
で|
を使う場合は,比較コードを()
の中に入れることを推奨する。|
は優先されて計算されるためである。(上の例では問題は発生しなし。)
<コメント>
上の例では,
&
がand
,|
がor
の役割を果たしている。同様に~
がnot
(真偽値の反転)の代わりに使われる。DataFrame
やSeries
で~
を使う場合は,コードを()
の中に入れることを推奨する。~
は優先されて計算されるためである。
例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
df
にない変数で条件を設定する場合@
が必要になる。例えば,変数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
Warning
数字で始まる列ラベルに.query()
を使うとエラーが発生するため,列ラベルを変更する必要がある。列ラベルを変更できない場合は異なる方法を使うように。
列と行の追加と削除#
列の追加 [ ]
#
[]
に列ラベルを使って列を抽出することができるが,[]
は列の追加にも使えるので,ここではその使い方を説明する。まず,全ての行が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()
:df
の上から下に行(rows)を縦断して,それぞれの列の中にあるTrue
数える。デフォルトで引数
axis='rows'
が指定されている。引数値
rows
は複数!(0
でも可)
sum(axis='columns')
:df
の左から右に列(columns)を横断して,それぞれの行の中にあるTrue
を数える。引数値
columns
は複数!(1
でも可)
(注意)sum()
のaxis
は「行を縦断」か「列を横断」かを指定する。
df.isna().sum()
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()
: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 | 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()
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 |
(注意)オプションsubset=
には削除する列が1つであってもリスト[]
で指定する。
並び替え#
df
を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 |
降順の場合
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']
の順番が対応している。
行インデックスと列ラベル#
インデックスを振り直す#
データの読み込みとデータのチェックでデータを読み込んだ後,直ぐに.set_index('year')
を使い,列year
を行ラベルに設定した。もちろん,必ずしもそうする必要はなく,行インデックス(0
,1
,2
,…)のままで作業をおこなっても全く問題ない。また行ラベルを設定した後に,行インデックスに戻したい場合もあるだろう。その場合には,メソッド.reset_index()
を使うと,行のインデックスを0,1,2,..と振り直すことができる。df
を使うと次のようになる。
df.reset_index()
year | gdp | inv | con | pop | id | |
---|---|---|---|---|---|---|
0 | 2000 | 100.0 | 20.0 | 80.0 | 8.0 | a |
1 | 2001 | 95.0 | 25.0 | 70.0 | 9.0 | b |
2 | 2002 | 93.0 | 21.0 | 72.0 | 10.0 | a |
3 | 2003 | 100.0 | 30.0 | 70.0 | 11.0 | b |
4 | 2004 | 110.0 | 39.0 | 71.0 | 12.0 | a |
5 | 2005 | 115.0 | 55.0 | 60.0 | 14.0 | b |
6 | 2006 | 113.0 | 50.0 | 63.0 | 15.0 | a |
7 | 2007 | 118.0 | 53.0 | 65.0 | 17.0 | b |
8 | 2008 | 119.0 | 60.0 | 59.0 | 18.0 | a |
9 | 2009 | 200.0 | 62.0 | NaN | 20.0 | b |
10 | 2010 | 210.0 | NaN | NaN | 21.0 | a |
ここでは行のインデックスがyear
として新たな列として追加されているが、reset_index()
に引数drop=True
を加えると,列year
が自動的に削除されることになる。
df.reset_index(drop=True).head()
gdp | inv | con | pop | id | |
---|---|---|---|---|---|
0 | 100.0 | 20.0 | 80.0 | 8.0 | a |
1 | 95.0 | 25.0 | 70.0 | 9.0 | b |
2 | 93.0 | 21.0 | 72.0 | 10.0 | a |
3 | 100.0 | 30.0 | 70.0 | 11.0 | b |
4 | 110.0 | 39.0 | 71.0 | 12.0 | a |
列のラベルの変更#
メソッド.rename()
を使い列のラベルを変更する。引数は次の形で設定する。
ここで「辞書」は次のルールで指定する。
key
:元のラベルvalue
:新しいラベル
下のコードでは,df
のpop
をpop_new
に,id
をid_new
に変更している。
df.rename(columns={'pop':'pop_new','id':'id_new'}).head()
gdp | inv | con | pop_new | id_new | |
---|---|---|---|---|---|
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 |
ただし,再割り当てしないとdf
は変更されないので注意しよう。即ち,上のコードではdf
の行ラベルは変更されていない。
.rename()
以外にも次のように変更することも可能である。
df.columns = ['gdp','inv','con','pop_new','id']
このコードは
右辺の文字列のリストを
df.columns
に割り当てる
と読むことができる。実際このコードにより割り当てが完了し,df
は変更されることになる。df
を表示してみよう。
df.head(2)
gdp | inv | con | pop_new | id | |
---|---|---|---|---|---|
year | |||||
2000 | 100.0 | 20.0 | 80.0 | 8.0 | a |
2001 | 95.0 | 25.0 | 70.0 | 9.0 | b |
さらにもう一つ注意点がある。全ての列ラベルをリストとして準備する必要がある点である。列の数が多いと面倒なので,そういう場合は.rename()
の方が使いやすいだろう。
よく使うメソッド#
DataFrame
には様々なメソッドが用意されており,ここでは頻繁に用いるメソッド幾つか紹介しよう。
統計関連のメソッド#
上でも出てきたが.sum()
は各列の合計を返す(axis='rows'
がデフォルト)。その際,欠損値は無視される。注意する点は合計を計算して意味のある列のみを選択することである。
df.iloc[:,:-1].sum()
gdp 1373.0
inv 415.0
con 610.0
pop_new 155.0
dtype: float64
各行の合計を計算したい場合は引数axis=1
もしくはaxis='columns'
を指定する。ここでcolumns
となるのは列を横断すると覚えれば良いだろう。古いPandas
のバージョンでは自動で文字列を無視していたが,より新しいバージョンでは合計できる列だけを選択する必要がある。
df.iloc[:,:-1].sum(axis='columns')
year
2000 208.0
2001 199.0
2002 196.0
2003 211.0
2004 232.0
2005 244.0
2006 241.0
2007 253.0
2008 256.0
2009 282.0
2010 231.0
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()
を計算してみよう。
df.iloc[:,:-1].cov()
gdp | inv | con | pop_new | |
---|---|---|---|---|
gdp | 1657.763636 | 346.944444 | -50.500000 | 152.118182 |
inv | 346.944444 | 271.388889 | -94.944444 | 63.666667 |
con | -50.500000 | -94.944444 | 44.444444 | -20.333333 |
pop_new | 152.118182 | 63.666667 | -20.333333 | 20.090909 |
対角成分は分散であり,その他は行と列のラベルに対応する共分散となる。.corr()
も同じ位置関係となる。
また変数の変化率(成長率)を計算するには.pct_change()
が便利である(percent changeの略)。これは毎期ごとの変化率を返すことになる。
df.iloc[:,:-1].pct_change()
gdp | inv | con | pop_new | |
---|---|---|---|---|
year | ||||
2000 | NaN | NaN | NaN | NaN |
2001 | -0.050000 | 0.250000 | -0.125000 | 0.125000 |
2002 | -0.021053 | -0.160000 | 0.028571 | 0.111111 |
2003 | 0.075269 | 0.428571 | -0.027778 | 0.100000 |
2004 | 0.100000 | 0.300000 | 0.014286 | 0.090909 |
2005 | 0.045455 | 0.410256 | -0.154930 | 0.166667 |
2006 | -0.017391 | -0.090909 | 0.050000 | 0.071429 |
2007 | 0.044248 | 0.060000 | 0.031746 | 0.133333 |
2008 | 0.008475 | 0.132075 | -0.092308 | 0.058824 |
2009 | 0.680672 | 0.033333 | 0.000000 | 0.111111 |
2010 | 0.050000 | 0.000000 | 0.000000 | 0.050000 |
1999
年が無いため,2000
年の成長率は欠損値となっている。.pct_change()
と.mean()
を続けて書くと簡単に(算術)平均成長率を計算することができる。
df.iloc[:,:-1].pct_change().mean()
gdp 0.091567
inv 0.136333
con -0.027541
pop_new 0.101838
dtype: float64
その他#
列id
には文字列があり,行のデータをカテゴリー別に分けていると考えることができる。メソッド.unique()
を使うと,選択した列に重複したデータがある場合,ユニークなものだけを抽出できる。
df['id'].unique()
array(['a', 'b'], dtype=object)
まな類似するメソッドに.nunique()
があり,カテゴリー数を返す。
df['id'].nunique()
2
関連するメソッドに.value_counts()
がある。これを使うとカテゴリーの内訳を確認するすることができる。カテゴリーの行数を表示するには
df.loc[:,'id'].value_counts()
id
a 6
b 5
Name: count, dtype: int64
とする。引数normalize=True
を追加すると,頻度(パーセント)として表示できる。
df.loc[:,'id'].value_counts(normalize=True)
id
a 0.545455
b 0.454545
Name: proportion, dtype: float64
df
のように行数が少ないDataFrame
の場合,これらのメソッドの有用性を見出すことは難しいが,何千行あるデータを扱っていると想像してみよう。そのような大きなデータを扱う場合は非常に重宝するメソッドとなる。
Series
について#
Series
について簡単に説明する。Series
はスプレッド・シートから1つの行または列を取り出したようなデータとイメージすれば良いだろう。まずdf
の列gdp
からSeries
を作ってみよう。
s = df.loc[:,'gdp']
s
year
2000 100.0
2001 95.0
2002 93.0
2003 100.0
2004 110.0
2005 115.0
2006 113.0
2007 118.0
2008 119.0
2009 200.0
2010 210.0
Name: gdp, dtype: float64
Note
df.loc[:,['gdp']]
もしくは
df[['gdp']]
で列を抽出するとDataFrame
が返される。また次のようにs
のメソッド.to_frame()
を使うとSeries
をDataFrame
に変換できるので、覚えておくと便利かも知れない。
s.to_frame()
構成要素#
この例を使いSeries
の構成要素について説明する。
データ:
s.to_numpy()
もしくはs.values
で抽出可能Series
名:s.name
で抽出可能df
の列ラベルから引き継がれているが、空の場合もある。
インデックス・ラベル(index label):
s.index
で抽出可能df
の行ラベルから引き継がれているインデックス(index)は
0
,1
,2
,…の順番で表される。
インデックス名:
s.index.name
で抽出可能df
の行ラベル名から引き継がれていが、空の場合もある。
まずデータ自体を抽出する。
s.to_numpy()
array([100., 95., 93., 100., 110., 115., 113., 118., 119., 200., 210.])
type(s.to_numpy())
numpy.ndarray
Series
名の抽出
s.name
'gdp'
インデックスの抽出
s.index
Index([2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010], dtype='int64', name='year')
戻り値の最後にname='year'
とあるように、インデックス名はyear
であり、次のコードでアクスすできる。
s.index.name
'year'
またdtype='int64'
とあるが、インデックスは整数であることがわかる。
要素の抽出#
要素の抽出方法はNumPy
のarray
と辞書に似ている。この点を説明するために、次のコードを使いs
からインデックスを文字列に変換したs_new
を作成する。
s_new = s.copy()
s_new.index = [i for i in 'abcdefghijk']
s_new
a 100.0
b 95.0
c 93.0
d 100.0
e 110.0
f 115.0
g 113.0
h 118.0
i 119.0
j 200.0
k 210.0
Name: gdp, dtype: float64
1行目:
s
のコピーを作りs_new
に割り当てる。2行目の右辺:内包表記を使い次のリストを作成する。
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
2行目:
s_new
のインデックス・ラベルを変更 3行目:s_new
の表示
まずインデックスを使い一つの要素の抽出は次のようにする。
s_new.iloc[5]
115.0
複数のインデックスを使うことも可能である。
s_new.iloc[[1,3,5]]
b 95.0
d 100.0
f 115.0
Name: gdp, dtype: float64
これはarray
と同じである。またスライシングも同様にできる。
s_new.iloc[5:-2]
f 115.0
g 113.0
h 118.0
i 119.0
Name: gdp, dtype: float64
Warning
s
の場合、順番を表すインデックスで要素アクセスしようとするとエラーが発生する。これはラベルが整数2000,2001...
になっている理由のようである。この場合は、ラベルである2000
や2008
を使い次のようにするとエラーは発生しない。
s[2000]
次にラベルを使った要素のアクセス方法を考える。
s_new.loc['b']
95.0
s_new.loc[['b','e','j']]
b 95.0
e 110.0
j 200.0
Name: gdp, dtype: float64
ラベルを使う場合,.loc
を省略することができる。辞書と同じような形となる。
s_new['b']
95.0
s_new[['b','e','j']]
b 95.0
e 110.0
j 200.0
Name: gdp, dtype: float64
統計学関連メソッド#
Series
にもDataFrame
と同じような統計学関連メソッドが実装されているので,py4macro.see()
で調べてみよう。
py4macro.see(s_new)
.T .a .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
.b .backfill .between .between_time
.bfill .bool .c .case_when
.clip .combine .combine_first .compare
.convert_dtypes .copy .corr .count
.cov .cummax .cummin .cumprod
.cumsum .d .describe .diff
.div .divide .divmod .dot
.drop .drop_duplicates .droplevel .dropna
.dtype .dtypes .duplicated .e
.empty .eq .equals .ewm
.expanding .explode .f .factorize
.ffill .fillna .filter .first
.first_valid_index .flags .floordiv .g
.ge .get .groupby .gt
.h .hasnans .head .hist
.i .iat .idxmax .idxmin
.iloc .index .infer_objects .info
.interpolate .is_monotonic_decreasing .is_monotonic_increasing .is_unique
.isin .isna .isnull .item
.items .j .k .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
グループ計算#
上で使ったdf
を表示してみよう。
df.head()
gdp | inv | con | pop_new | 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 |
df
のメソッド.mean()
を使うと列の平均を簡単に計算できる。列を取り出して計算してみよう。
df.iloc[:,1:-1].mean()
inv 41.500000
con 67.777778
pop_new 14.090909
dtype: float64
一方で,列id
にはa
とb
があり,df
を2つのグループに分けることができる。ここで問題になるのは,グループ別に列の平均を計算したい場合である。まず考えられる方法はa
グループだけを抽出して平均を計算し,同じようにb
も計算するということだろう。もしグループ数が多い場合はfor
ループが必要になり,より長いコードを書く必要が発生し面倒に感じることになる。
こういう場合のために,DataFrame
には簡単にグループ計算を可能にする方法が用意されている。それが.groupby()
というメソッドである。以下では.groupby()
の使い方をdf
を使って3つのステップに分けて説明する。
ステップ1:グループ化する列を指定#
最初のステップでは,グループ化したい列名を引数として.groupby()
を実行する。df
をid
でグループ化したいので次のコードとなる。
df.groupby('id')
<注意>
このコードはDataFrame
を返すわけではない。返すのはグループ計算用のオブジェクトであり,それを使ってグループ計算をおこなう事になる。
実際にコードを実行し,変数df_groupby
に割り当てよう。
df_groupby = df.groupby('id')
df_groupby
を実行してみよう。
df_groupby
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x11f14add0>
何も返されない。表示されているのは,PCメモリーのある箇所にDataFrameGroupBy
というオブジェクトが存在すると伝えているだけである。
ステップ2:グループ計算したい列を指定#
次にグループ計算したい列を指定するが,次の様な書き方となる。
df_groupby[<グループ計算したい列ラベル>]
ここで[]
を使っていることに注意しよう。例としてgdp
をグループ計算するとしよう。
df_groupby['gdp']
<pandas.core.groupby.generic.SeriesGroupBy object at 0x11f20ec80>
ここでも新たなオブジェクトSeriesGroupBy
のメモリーアドレスが表示されるだけである。どの様に計算するかを指定することにより,計算結果が返されることになる。それが次のステップである。
ステップ3:計算方法の指定#
ステップ1と2がグループ計算の準備段階であり,あとは実際にどのように計算したいかを指定する。例として,平均を考えてみよう。上でも出てきたが,平均はメソッド.mean()
を使う。
df_groupby['gdp'].mean()
id
a 124.166667
b 125.600000
Name: gdp, dtype: float64
グループ計算結果はSeries
として返されている。.mean()
以外にも使える関数は準備されいる。py4macro.see()
を使って属性・メソッドを調べてみよう。
py4macro.see(df_groupby['gdp'])
.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)
id
a 124.166667
b 125.600000
Name: gdp, dtype: float64
Warning
この場合,()
がなく,関数名だけを.agg()
の引数にしている。関数名だけを.agg
に渡し,.agg
が渡された関数を実行するというイメージである。()
を付けると.agg()
に渡す前に関数を実行することになりエラーとなってしまう。
次のステップ#
1行で書く#
上の説明では3つのステップに分けたが,もちろん次のように3つのステップを次のように1行で書いても構わないし,むしろその場合が多いだろう。
df.groupby('id')['gdp'].mean()
id
a 124.166667
b 125.600000
Name: gdp, dtype: float64
このコードは次の様に読むことができる。
df
をid
でグループ分けして,gdp
の平均を計算する。
複数選択#
各ステップでは列や関数を一つだけ選択・設定しているが,それぞれ複数選択することも可能である。
ステップ1ではグループ化する上で基準となる列を複数選択
ステップ2では計算対象となる列を複数選択
ステップ3では
.agg()
の引数に複数の関数を指定
という具合である。ただその場合は,リストとして列や関数名を指定する必要がある。例えば,ステップ2でgdp
とinv
を選ぶとしよう。
df.groupby('id')[['gdp','inv']].mean()
gdp | inv | |
---|---|---|
id | ||
a | 124.166667 | 38.0 |
b | 125.600000 | 45.0 |
結果はDataFrame
として返されている。ステップ1もしくは3で複数選択するとDataFrame
がMultiIndex
(階層的な行と列)として返されることになるが,その簡単な説明についてはGapminderを参照して欲しい。より詳しい説明は他のサイトに譲ることにする。
df
のような小さなDataFrame
では.groupby
の威力はあまりピンとこないかも知れない。しかし大きなDataFrame
を使うとその恩恵を強く感じることだろう。Gapminderではマクロ経済データを使い.groupby()
の使い方の例を示している。興味がある人は是非!