Pandas#

in English or the language of your choice.

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

PandasNumPyarrayのようにデータを扱うパッケージだが,Pandas特有のデータ構造を提供し複雑なデータを扱いやすくしている。例えば,行列にはラベルを使うことにより,インデックス番号だけではなくラベル名(例えば,GDP)を使って操作することが可能となる。PandasにはDataFrame(データフレーム)とSeries(シリーズ)と呼ばれるオブジェクトがある。前者はエクセルのスプレッド・シートをイメージすれば良いだろう。後者は,スプレッド・シートから1つの行または列を取り出したようなデータと思えば良い。また,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を行ラベルに設定する。

  • 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という列名が残るが,それを消す方法:

    1. メソッド.rename_axis('')を使う。ここで''は空の文字列である。.rename_axis(None)でも同じ結果となる。

    2. df.index.name = ''とする。ここで''は空の文字列

行数が大きい場合(例えば,10000),全てを表示してもあまり意味がない。そこでよく使うメソッドに最初や最後の数行だけを表示すものがある。

dfの最初の5行を表示させる。

df.head()
gdp inv con pop id
year
2000 100 20.0 80.0 8 a
2001 95 25.0 70.0 9 b
2002 93 21.0 72.0 10 a
2003 100 30.0 70.0 11 b
2004 110 39.0 71.0 12 a

引数に2を指定すると最初の2行のみ表示される。

df.head(2)
gdp inv con pop id
year
2000 100 20.0 80.0 8 a
2001 95 25.0 70.0 9 b

最後の5行を表示させる。引数に整数を入れて表示行数を指定することも可能。

df.tail()
gdp inv con pop id
year
2006 113 50.0 63.0 15 a
2007 118 53.0 65.0 17 b
2008 119 60.0 59.0 18 a
2009 200 62.0 NaN 20 b
2010 210 NaN NaN 21 a

dfの情報を確認する。

df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 11 entries, 2000 to 2010
Data columns (total 5 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   gdp     11 non-null     int64  
 1   inv     10 non-null     float64
 2   con     9 non-null      float64
 3   pop     11 non-null     int64  
 4   id      11 non-null     object 
dtypes: float64(2), int64(2), object(1)
memory usage: 528.0+ bytes

説明

  • <class 'pandas.core.frame.DataFrame'>

    • クラス名

    • type(1)とするとintというデータ型が表示するが,これはクラス名でもある。print(type(1))とすると<class 'int'>と表示される。

  • Int64Index: 11 entries, 2000 to 2010

    • 行のインデックスの情報

    • データ型はInt64(整数)(データ型には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.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 )

condを引数に使い行を抽出する。

df.loc[cond, :]
gdp inv con pop id
year
2000 100 20.0 80.0 8 a
2003 100 30.0 70.0 11 b

例4#

以下の条件のどちらかが満たされる場合:

  • gdpは200以上

  • conは60以下

cond1 = ( df['gdp'] >= 200 )
cond2 = ( df['con'] <= 60 )
cond = ( cond1 | cond2 )

df.loc[cond, :]
gdp inv con pop id
year
2005 115 55.0 60.0 14 b
2008 119 60.0 59.0 18 a
2009 200 62.0 NaN 20 b
2010 210 NaN NaN 21 a

例5#

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

  • gdpは200以上

  • conは60以下

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

  • 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

変数で条件を設定する場合@が必要になる。例えば,変数zという変数があるとしよう。

z = 100

変数zの値に基づいて行の抽出をする場合は次のようにする。

df.query('gdp < @z')

gdp inv con pop id
year
2001 95 25.0 70.0 9 b
2002 93 21.0 72.0 10 a

列と行の追加と削除#

列の追加 [ ]#

[]に列ラベルを使って列を抽出することができるが,[]は列の追加にも使えるので,ここではその使い方を説明する。まず,全ての行が1.0となる列を作成するとしよう。その場合,以下のようにする。

df['Intercept'] = 1.0
df.head(2)
gdp inv con pop id Intercept
year
2000 100 20.0 80.0 8 a 1.0
2001 95 25.0 70.0 9 b 1.0

次の例では既存の列から新たな列を作成する。まず1人当たりGDPの計算を計算し,それを変数gdp_pcに割り当てる。

gdp_pc = df['gdp']/df['pop']
gdp_pc.head()
year
2000    12.500000
2001    10.555556
2002     9.300000
2003     9.090909
2004     9.166667
dtype: float64

これはSeriesであり,GDPpcとしてdfに追加する。

df['gdp_pc'] = gdp_pc
df.head(2)
gdp inv con pop id Intercept gdp_pc
year
2000 100 20.0 80.0 8 a 1.0 12.500000
2001 95 25.0 70.0 9 b 1.0 10.555556

列の追加 .loc[,]#

.loc[]は行と列の抽出に使ったが,追加にも使える。[]と同じと考えれば良い。次の例ではpopを2倍した列を追加している。

df.loc[:,'2pop'] = 2*df['pop']

列の削除 [ ]#

del df['2pop']

列の削除 drop()#

DataFrameには.drop()というメソッドが用意されているので,それを使うことも可能である。

下のコードの説明:

  • オプションaxis=の値をcolumnsの代わりにでも可

  • コピーを作るだけなので,元のdfを書き換えたい場合は以下のどちらかが必要

    • dfに代入する

    • オプションinplace=True(デフォルトはFalse)を追加する。

df = df.drop(['Intercept','gdp_pc'], axis='columns')

# df.drop('Intercept', axis='columns', inplace=True)

行の追加 .loc[,]#

行と列の抽出,そして列の追加に使ったが,行の追加にも使える。

df.loc[2011,:] = [215, 100, 115, 22, 'b']
df.tail(3)
gdp inv con pop id
year
2009 200.0 62.0 NaN 20.0 b
2010 210.0 NaN NaN 21.0 a
2011 215.0 100.0 115.0 22.0 b

上の例では,最初の4つの要素は整数として入力されたが,dfの中では浮動小数点に変換されている。

行の削除 drop()#

これは列を削除する際に紹介したメソッド.drop()である。

  • オプションaxis=の値をrowsの代わりに0でも可

  • コピーを作るだけなので,元のdfを書き換えたい場合は以下のどちらかが必要

    • dfに代入する

    • オプションinplace=True(デフォルトはFalse)を追加する。

df = df.drop(2011, axis='rows')

# df.drop(2011, axis=0, inplace=True)

欠損値の扱い#

Pandasでは欠損値はNaNと表示されるが,naもしくはnullと呼んだりもする。

欠損値の確認#

欠損値があるかどうかの確認は,df.info()でもできるが,以下のメソッドを組み合わせることでも可能である。

  • isna():それぞれの要素についてNaNの場合Trueを,そうでない場合はFalseを返す。(DataFrameの全ての要素がTrue/Falseとなる。)

  • sum(axis='rows')dfの上から下に(rows)を縦断して,それぞれの列の中にあるTrue数える。

    • rowsは複数!(0でも可)

  • sum(axis='columns')dfの左から右に(columns)を横断して,それぞれの行の中にあるTrueを数える。

    • columnsは複数!(1でも可)

(注意)sum()axisは「行を縦断」か「列を横断」かを指定する。

df.isna().sum(axis='rows')
gdp    0
inv    1
con    2
pop    0
id     0
dtype: int64

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(axis='rows')dfの上から下に行(rows)を縦断して,それぞれの列の中で一つ以上Trueがある場合にはTrueを,一つもない場合はFalseを返す。

    • rowsは複数!(0でも可)

  • any(axis='columns'):dfの左から右に列(columns)を横断して,それぞれの行の中で一つ以上Trueがある場合にはTrueを,一つもない場合はFalseを返す。

    • columnsは複数!(1でも可)

(注意)any()axisは「行を縦断」か「列を横断」かを指定する。

cond = df.isna().any(axis='columns')
df.loc[cond,:]
gdp inv con pop id
year
2009 200.0 62.0 NaN 20.0 b
2010 210.0 NaN NaN 21.0 a

これでNaNがある行を抽出することができる。

欠損値がある行の削除#

欠損値がある行を削除するには,dfのメソッド.dropna()を使う。次のコードでは,欠損値がある全ての行を削除する。

df.dropna()
gdp inv con pop id
year
2000 100.0 20.0 80.0 8.0 a
2001 95.0 25.0 70.0 9.0 b
2002 93.0 21.0 72.0 10.0 a
2003 100.0 30.0 70.0 11.0 b
2004 110.0 39.0 71.0 12.0 a
2005 115.0 55.0 60.0 14.0 b
2006 113.0 50.0 63.0 15.0 a
2007 118.0 53.0 65.0 17.0 b
2008 119.0 60.0 59.0 18.0 a

このメソッドは,欠損値を削除するとどうなるかを示すだけでありdf自体は影響は受けない。df自体からNaNがある行を削除する場合はinplace=Trueのオプション(デフォルトではFalseになっている)を加えて

df.dropna(inplace=True)

とするか,削除後のdfを変数dfに再度割り当てる。

df = df.dropna()

一方で,ある列にNaNがある場合のみ行を削除したい場合もあるだろう。その場合は引数subsetを使う。

df.dropna(subset=['inv'])
gdp inv con pop id
year
2000 100.0 20.0 80.0 8.0 a
2001 95.0 25.0 70.0 9.0 b
2002 93.0 21.0 72.0 10.0 a
2003 100.0 30.0 70.0 11.0 b
2004 110.0 39.0 71.0 12.0 a
2005 115.0 55.0 60.0 14.0 b
2006 113.0 50.0 63.0 15.0 a
2007 118.0 53.0 65.0 17.0 b
2008 119.0 60.0 59.0 18.0 a
2009 200.0 62.0 NaN 20.0 b

conNaNが残っている。

<注意>
subset=['inv']のように,NaNがあるかを確認する列が1つであってもリスト[]で指定する。複数であれば,次のように列ラベルをリストとして並べれば良い。

subset=['inv','id']

非常に便利な引数なので覚えておこう!

並び替え#

dfの行を並び替えるにはメソッド.sort_values()を使う。引数には,並び替える基準となる列ラベルを指定し,デフォルトでは昇順となる。次のコードではgdpに従って昇順に並び替えている。

df.sort_values('gdp').head()
gdp inv con pop id
year
2002 93.0 21.0 72.0 10.0 a
2001 95.0 25.0 70.0 9.0 b
2000 100.0 20.0 80.0 8.0 a
2003 100.0 30.0 70.0 11.0 b
2004 110.0 39.0 71.0 12.0 a

引数ascending=Falseを使うと降順になる。

df.sort_values('gdp', ascending=False).head()
gdp inv con pop id
year
2010 210.0 NaN NaN 21.0 a
2009 200.0 62.0 NaN 20.0 b
2008 119.0 60.0 59.0 18.0 a
2007 118.0 53.0 65.0 17.0 b
2005 115.0 55.0 60.0 14.0 b

複数の列を基準として,昇順と降順を別々に指定することも可能である。

df.sort_values(['id','gdp'], ascending=[True,False]).head()
gdp inv con pop id
year
2010 210.0 NaN NaN 21.0 a
2008 119.0 60.0 59.0 18.0 a
2006 113.0 50.0 63.0 15.0 a
2004 110.0 39.0 71.0 12.0 a
2000 100.0 20.0 80.0 8.0 a

ここではidに従って先に並び替えられ,その後にgdpに従って並び替えられている。ascendingは昇順(True)か降順(False)かを指定する引数であり,['id','gdp']ascending=['True','False']の順番が対応している。即ち,idは昇順,gdpは降順となるように並び替えている。

DataFrameの結合#

データを扱うと必ずと言って良いほど必要になるのがデータ・セットの結合である。例えば,日本銀行と米国連邦準備制度(米国の中央銀行)のデータからなるdfを作るとすると,それぞれのサイトから取得したデータ・セットを結合する必要がある。DataFrameには,そのための便利なメソッドが準備されているので,ここではそれを紹介する。

3つの.csvファイルを使う。

url = 'https://raw.githubusercontent.com/Haruyama-KobeU/Py4Basics/master/data/'
df1 = pd.read_csv(url+'data1.csv')
df2 = pd.read_csv(url+'data2.csv')
df3 = pd.read_csv(url+'data3.csv')
df1
year gdp inv con pop id
0 2000 100 20.0 80.0 8 a
1 2001 95 25.0 70.0 9 b
2 2002 93 21.0 72.0 10 a
3 2003 100 30.0 70.0 11 b
4 2004 110 39.0 71.0 12 a
5 2005 115 55.0 60.0 14 b
6 2006 113 50.0 63.0 15 a
7 2007 118 53.0 65.0 17 b
8 2008 119 60.0 59.0 18 a
9 2009 200 62.0 NaN 20 b
10 2010 210 NaN NaN 21 a
df2
year gnp id2
0 1995 101 1
1 1996 97 1
2 1997 96 1
3 2000 103 1
4 2001 103 0
5 2002 102 0
6 2003 105 0
7 2004 110 0
df3
year gdp inv con pop id
0 2011 212 70 142 22 b
1 2012 215 72 143 23 a
2 2015 220 80 140 24 b
df3.loc[0,'year']
2011

横結合:merge()#

merge()以外にも結合に使える関数はあるが,ここではmerge()のみを考える。

df1を「左」,df2を「右」に横結合する。

pd.merge(df1, df2, on=None, how='inner')
  • onはどの列を基準にして結合するかを指定(ここでは「基準列」と呼ぼう)

    • 例えば,df1df2の両方にyearの列がある場合,on='year'とすると列yearが基準列となる。

      • df1df2の別々の列を指定する場合はleft_on=right_on=を使う。

    • 基準列に基づいて残す行を決める(howで説明する)

    • 基準列にある要素の順番が合ってなくても,自動でマッチさせる。

    • 複数指定も可

    • デフォルトはNone

  • howonで指定した基準列に基づいてどのように結合するかを指定

    • innerdf1df2の両方に基準列ある行だけを残す(デフォルト)。

    • leftdf1の行は全て残し,df2からはマッチする行だけが残る(マッチしない行は削除)。またdf1にありdf2にない行にはNaNが入る。

    • rightdf2の行は全て残し,df1からはマッチする行だけが残る(マッチしない行は削除)。またdf2にありdf1にない行にはNaNが入る。

    • outerdf1df2の両方の行を残し,マッチする行がない場合はNaNを入れる。

(コメント) この他に様々な引数があるのでこのサイトを参照。例えば,場合によっては次の引数を使う必要があるかも知れないので確認しよう。

  • left_index=Truedf1では行インデックスを基準列にする。

  • right_index=Truedf2では行インデックスを基準列にする。

例1#

  • yearを基準列とする。

  • 共通する行だけを残す。

pd.merge(df1, df2, on='year', how='inner')
year gdp inv con pop id gnp id2
0 2000 100 20.0 80.0 8 a 103 1
1 2001 95 25.0 70.0 9 b 103 0
2 2002 93 21.0 72.0 10 a 102 0
3 2003 100 30.0 70.0 11 b 105 0
4 2004 110 39.0 71.0 12 a 110 0

例2#

  • yearを基準列とする。

  • df1の全ての行を残す。

  • df2ではマッチする行だけ残り,マッチしない行は削除される。

  • df1にありdf2にない行にはNaNが入る。

pd.merge(df1, df2, on='year', how='left')
year gdp inv con pop id gnp id2
0 2000 100 20.0 80.0 8 a 103.0 1.0
1 2001 95 25.0 70.0 9 b 103.0 0.0
2 2002 93 21.0 72.0 10 a 102.0 0.0
3 2003 100 30.0 70.0 11 b 105.0 0.0
4 2004 110 39.0 71.0 12 a 110.0 0.0
5 2005 115 55.0 60.0 14 b NaN NaN
6 2006 113 50.0 63.0 15 a NaN NaN
7 2007 118 53.0 65.0 17 b NaN NaN
8 2008 119 60.0 59.0 18 a NaN NaN
9 2009 200 62.0 NaN 20 b NaN NaN
10 2010 210 NaN NaN 21 a NaN NaN

例3#

  • yearを基準列とする。

  • df2の全ての行を残す。

  • df1ではマッチする行だけ残り,マッチしない行は削除される。

  • df1にありdf2にない行にはNaNが入る。

pd.merge(df1, df2, on='year', how='right')
year gdp inv con pop id gnp id2
0 1995 NaN NaN NaN NaN NaN 101 1
1 1996 NaN NaN NaN NaN NaN 97 1
2 1997 NaN NaN NaN NaN NaN 96 1
3 2000 100.0 20.0 80.0 8.0 a 103 1
4 2001 95.0 25.0 70.0 9.0 b 103 0
5 2002 93.0 21.0 72.0 10.0 a 102 0
6 2003 100.0 30.0 70.0 11.0 b 105 0
7 2004 110.0 39.0 71.0 12.0 a 110 0

例4#

  • yearを基準列とする。

  • df1df2の全ての行は残る。

  • df1にありdf2にない行にはNaNが入る。

  • df2にありdf1にない行にはNaNが入る。

pd.merge(df1, df2, on='year', how='outer')
year gdp inv con pop id gnp id2
0 1995 NaN NaN NaN NaN NaN 101.0 1.0
1 1996 NaN NaN NaN NaN NaN 97.0 1.0
2 1997 NaN NaN NaN NaN NaN 96.0 1.0
3 2000 100.0 20.0 80.0 8.0 a 103.0 1.0
4 2001 95.0 25.0 70.0 9.0 b 103.0 0.0
5 2002 93.0 21.0 72.0 10.0 a 102.0 0.0
6 2003 100.0 30.0 70.0 11.0 b 105.0 0.0
7 2004 110.0 39.0 71.0 12.0 a 110.0 0.0
8 2005 115.0 55.0 60.0 14.0 b NaN NaN
9 2006 113.0 50.0 63.0 15.0 a NaN NaN
10 2007 118.0 53.0 65.0 17.0 b NaN NaN
11 2008 119.0 60.0 59.0 18.0 a NaN NaN
12 2009 200.0 62.0 NaN 20.0 b NaN NaN
13 2010 210.0 NaN NaN 21.0 a NaN NaN

縦結合#

concat()は横結合にも使えるが,縦結合のみ考える。

引数には複数のDataFrameをリストとして書く。

df13 = pd.concat([df1,df3])
df13.tail()
year gdp inv con pop id
9 2009 200 62.0 NaN 20 b
10 2010 210 NaN NaN 21 a
0 2011 212 70.0 142.0 22 b
1 2012 215 72.0 143.0 23 a
2 2015 220 80.0 140.0 24 b

その他#

インデックスを振り直す#

メソッド.reset_index()を使うと,行のインデックスを0,1,2,..と振り直すことができる。df13を使い説明する。

df13.reset_index()
index year gdp inv con pop id
0 0 2000 100 20.0 80.0 8 a
1 1 2001 95 25.0 70.0 9 b
2 2 2002 93 21.0 72.0 10 a
3 3 2003 100 30.0 70.0 11 b
4 4 2004 110 39.0 71.0 12 a
5 5 2005 115 55.0 60.0 14 b
6 6 2006 113 50.0 63.0 15 a
7 7 2007 118 53.0 65.0 17 b
8 8 2008 119 60.0 59.0 18 a
9 9 2009 200 62.0 NaN 20 b
10 10 2010 210 NaN NaN 21 a
11 0 2011 212 70.0 142.0 22 b
12 1 2012 215 72.0 143.0 23 a
13 2 2015 220 80.0 140.0 24 b

この例では元のインデックスが列indexとして追加されている。reset_index()に引数drop=Trueを加えると,列indexは追加されない。

df13.reset_index(drop=True).head()
year gdp inv con pop id
0 2000 100 20.0 80.0 8 a
1 2001 95 25.0 70.0 9 b
2 2002 93 21.0 72.0 10 a
3 2003 100 30.0 70.0 11 b
4 2004 110 39.0 71.0 12 a

列のラベルの変更#

メソッド.rename()を使い列のラベルを変更する。引数は次の形で設定する。

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

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

  • key:元のラベル

  • value:新しいラベル

下のコードでは,df13を使い新しいラベルとしてpop_newid_newを使っている。

df13.rename(columns={'pop':'pop_new','id':'id_new'}).head()
year gdp inv con pop_new id_new
0 2000 100 20.0 80.0 8 a
1 2001 95 25.0 70.0 9 b
2 2002 93 21.0 72.0 10 a
3 2003 100 30.0 70.0 11 b
4 2004 110 39.0 71.0 12 a

.rename()以外にリストを使って列ラベルを変更することも可能である。例えば,

df.columns = ['year', 'gdp', 'inv', 'con', 'pop_new', 'id_new']

この場合,列の数とリストの要素数が合わないとエラーとなるので注意しよう。

列の並び替え#

アルファベット順#

メソッド.sort_index()を使い,引数にはaxis='columns'もしくはaxis=1を指定する。

(コメント)引数がaxis='rows'もしくはaxis=0の場合,行が並び替えられる。

df13.sort_index(axis='columns').head()
con gdp id inv pop year
0 80.0 100 a 20.0 8 2000
1 70.0 95 b 25.0 9 2001
2 72.0 93 a 21.0 10 2002
3 70.0 100 b 30.0 11 2003
4 71.0 110 a 39.0 12 2004

順番を指定する#

方法1#

列を選択する方法を使い並び替える。

var = ['id','year','gdp','con','inv','pop']

df13.loc[:,var].head()
id year gdp con inv pop
0 a 2000 100 80.0 20.0 8
1 b 2001 95 70.0 25.0 9
2 a 2002 93 72.0 21.0 10
3 b 2003 100 70.0 30.0 11
4 a 2004 110 71.0 39.0 12
方法2#

次の列の選択方法でも可。

df13[var].head()
id year gdp con inv pop
0 a 2000 100 80.0 20.0 8
1 b 2001 95 70.0 25.0 9
2 a 2002 93 72.0 21.0 10
3 b 2003 100 70.0 30.0 11
4 a 2004 110 71.0 39.0 12
方法3#

メソッド.reindex()を使う。引数はcolumns=[]

df13.reindex(columns=['id','year','gdp','con','inv','pop']).head()
id year gdp con inv pop
0 a 2000 100 80.0 20.0 8
1 b 2001 95 70.0 25.0 9
2 a 2002 93 72.0 21.0 10
3 b 2003 100 70.0 30.0 11
4 a 2004 110 71.0 39.0 12
方法3の応用#

最後の行を最初に移動する。

  • .columns.tolist():コラムのラベルを取得し,それをリストに変換

  • [col[-1]]+col[0:-2]の分解と結合

    • [col[-1]]colの最後の要素を抽出するが,文字列として返されるので(外側の)[ ]を使ってリストに変換

    • col[0:-2]colの最初から最後から二番目の要素をリストとして取得

    • +:リストの結合

col = df13.columns.tolist()

new_col = [col[-1]]+col[0:-2]

df.reindex(columns=new_col).head()
id year gdp inv con
year
2000 a NaN 100.0 20.0 80.0
2001 b NaN 95.0 25.0 70.0
2002 a NaN 93.0 21.0 72.0
2003 b NaN 100.0 30.0 70.0
2004 a NaN 110.0 39.0 71.0