Pandas:データ分析#

in English or the language of your choice.

import pandas as pd
import numpy as np
import py4macro

# 警告メッセージを非表示
import warnings
warnings.filterwarnings("ignore")

説明#

Pandasはデータを扱う上で欠かせないパッケージであり,エクセルをイメージすれば良いだろう。Pandasにはスプレッド・シートに対応するDataFrame(データ・フレーム)とSeries(シリーズ)呼ばれるオブジェクトがあり,それらを駆使してデータ分析をおこなう。

データ・フレームは下の図のようになっている。XYZは「列ラベル」(アルファベットや記号など)であり、一番左の123は「行インデックス」(数字)である(「行ラベル」(アルファベットや記号など)を設定することも可能)。その他の数字がデータとなっている。

X

Y

Z

0

10

5.0

3.0

1

20

30.0

2.0

2

30

15.0

5.0

Xのデータはその列にある数字102030であり,YZも同様に同じ列にあるデータから構成される。Seriesは1つの列からなるスプレッド・シートと考えれば良いだろう。

この特徴により複雑なデータでも扱いが簡単になる。例えば,NumPyを使うと目的のデータがどこにあるのかは,何番目の行で何番目の列なのかを把握する必要があるが,Pandasでは行・列にラベルを使うことにより(例えば,行「1980年」の列「GDP」のようにデータを特定することが可能となる),データの操作が非常に簡単になる。また,PandasNumPyに基づいているため,ベクトル演算の機能が使える。

ここで説明できない他の使い方についてはこのサイトこのサイトが参考になる。

通常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(整数)(データ型には6432という数字がついている場合がある。それらは数字をコンピュータのメモリに記憶させる際,何ビット必要かを示している。より重要なのは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の列にどのようなのデータ型かを示す

    • float64int64が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

これで分かることは,メインのデータの部分はNumPyndarrayn次元array)であることが分かる。即ち,PandasNumPyに基づいて構築されており,データ値の計算などは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]

要素の抽出#

NumPyarrayの場合,[,]を使い要素を抽出した。Pandasの場合,様々な抽出方法があるが,覚えやすく少しでも間違いの可能性を減らすために,そして可読性向上のためにarrayに対応する以下の2つの方法を使うことにする。

  • ラベルを使う方法:.loc[,]

  • インデックスを使う方法:.iloc[,](これはarray[ ]と同じと考えて良い)

1つ目のlocはラベルのlocationと覚えよう。2つ目はのilociはインデックス(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:idaの行を抽出#

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)

<注意>

  • &を使っている。理由は,cond1cond2のそれぞれの行の要素を比較して真偽値から構成されるSeriesを返すためである。従って,一つの値をもう一つの値と比べて真偽値を計算するandを使うとエラーが発生する。

  • DataFrameSeries&を使う場合は,比較コードを()の中に入れることを推奨する。&は優先されて計算されるためである。(上の例では問題は発生しなし。)

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

<注意>

  • |を使っている。理由は,cond1cond2のそれぞれの行の要素を比較して真偽値から構成されるSeriesを返すためである。従って,一つの値をもう一つの値と比べて真偽値を計算するorを使うとエラーが発生する。

  • DataFrameSeries|を使う場合は,比較コードを()の中に入れることを推奨する。|は優先されて計算されるためである。(上の例では問題は発生しなし。)

<コメント>

  • 上の例では,&and|orの役割を果たしている。同様に~not(真偽値の反転)の代わりに使われる。

  • DataFrameSeries~を使う場合は,コードを()の中に入れることを推奨する。~は優先されて計算されるためである。

例5#

以下の条件のどちらかが満たされ

  • gdpは200以上

  • conは60以下

かつ以下の条件も同時に満たされる場合:

  • idaと等しい

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の代わりにでも可

  • コピーを作るだけなので,元の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

invconNaNがあることがわかる。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)

とするか,削除後のdfdf自体に再割り当てする。

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つであってもリスト[]で指定する。

並び替え#

dfgdpの昇順に並び替える。

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を行ラベルに設定した。もちろん,必ずしもそうする必要はなく,行インデックス(012,…)のままで作業をおこなっても全く問題ない。また行ラベルを設定した後に,行インデックスに戻したい場合もあるだろう。その場合には,メソッド.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()を使い列のラベルを変更する。引数は次の形で設定する。

\[\text{.rename}\left(\text{columns=}辞書\right)\]

ここで「辞書」は次のルールで指定する。

  • key:元のラベル

  • value:新しいラベル

下のコードでは,dfpoppop_newに,idid_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

pandasnumpyでは,標準偏差,分散,共分散を計算する際の引数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()を使うとSeriesDataFrameに変換できるので、覚えておくと便利かも知れない。

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'とあるが、インデックスは整数であることがわかる。

要素の抽出#

要素の抽出方法はNumPyarrayと辞書に似ている。この点を説明するために、次のコードを使い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...になっている理由のようである。この場合は、ラベルである20002008を使い次のようにするとエラーは発生しない。

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にはabがあり,dfを2つのグループに分けることができる。ここで問題になるのは,グループ別に列の平均を計算したい場合である。まず考えられる方法はaグループだけを抽出して平均を計算し,同じようにbも計算するということだろう。もしグループ数が多い場合はforループが必要になり,より長いコードを書く必要が発生し面倒に感じることになる。

こういう場合のために,DataFrameには簡単にグループ計算を可能にする方法が用意されている。それが.groupby()というメソッドである。以下では.groupby()の使い方をdfを使って3つのステップに分けて説明する。

ステップ1:グループ化する列を指定#

最初のステップでは,グループ化したい列名を引数として.groupby()を実行する。dfidでグループ化したいので次のコードとなる。

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

このコードは次の様に読むことができる。

dfidでグループ分けして,gdpの平均を計算する。

複数選択#

各ステップでは列や関数を一つだけ選択・設定しているが,それぞれ複数選択することも可能である。

  • ステップ1ではグループ化する上で基準となる列を複数選択

  • ステップ2では計算対象となる列を複数選択

  • ステップ3では.agg()の引数に複数の関数を指定

という具合である。ただその場合は,リストとして列や関数名を指定する必要がある。例えば,ステップ2でgdpinvを選ぶとしよう。

df.groupby('id')[['gdp','inv']].mean()
gdp inv
id
a 124.166667 38.0
b 125.600000 45.0

結果はDataFrameとして返されている。ステップ1もしくは3で複数選択するとDataFrameMultiIndex(階層的な行と列)として返されることになるが,その簡単な説明についてはGapminderを参照して欲しい。より詳しい説明は他のサイトに譲ることにする。

dfのような小さなDataFrameでは.groupbyの威力はあまりピンとこないかも知れない。しかし大きなDataFrameを使うとその恩恵を強く感じることだろう。Gapminderではマクロ経済データを使い.groupby()の使い方の例を示している。興味がある人は是非!