pandasとデータ分析

pandasはデータ分析では必ず利用する重要なツールです。この使い方を知るか知らないか、もしくは、やりたいことをグーグル検索しなくてもすぐに手を動かせるかどうかは、エンジニアとしての力量に直結します。ここでは、具体的なデータを元に私の経験から重要と思われるメソッドや使い方を説明します。他に重要な使い方に遭遇したらどんどん追記していきます。

github

  • jupyter notebook形式のファイルはこちら

google colaboratory

  • google colaboratory で実行する場合はこちら

筆者の環境

筆者のOSはmacOSです。LinuxやUnixのコマンドとはオプションが異なります。

!sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.6
BuildVersion:	18G6020
!python -V
Python 3.7.3

基本的なライブラリをインポートしそのバージョンを確認しておきます。

%matplotlib inline
%config InlineBackend.figure_format = 'svg'

import matplotlib
import matplotlib.pyplot as plt
import scipy
import numpy as np

print('matplotlib version :', matplotlib.__version__)
print('scipy version :', scipy.__version__)
print('numpy version :', np.__version__)
matplotlib version : 3.0.3
scipy version : 1.4.1
numpy version : 1.16.2

importとバージョン確認

import pandas as pd

print('pandas version :', pd.__version__)
pandas version : 1.0.3

基本操作

データの読み込みと表示

利用させてもらうデータはdanielさんのgithub になります。pandasの使い方の本を書いておられる有名な方のリポジトリです。Pythonデータ分析/機械学習のための基本コーディング! pandasライブラリ活用入門 です。僕も持っています。とても勉強になると思います。

データはエボラ出血の発生数(Case)と死者数(Death)だと思います。

read_csvを利用して、CSVを読み込み、先頭の5行目を表示してみます。

import pandas as pd

df = pd.read_csv('./country_timeseries.csv', sep=',')
df.head()

DateDayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_GuineaDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
01/5/20152892776.0NaN10030.0NaNNaNNaNNaNNaN1786.0NaN2977.0NaNNaNNaNNaNNaN
11/4/20152882775.0NaN9780.0NaNNaNNaNNaNNaN1781.0NaN2943.0NaNNaNNaNNaNNaN
21/3/20152872769.08166.09722.0NaNNaNNaNNaNNaN1767.03496.02915.0NaNNaNNaNNaNNaN
31/2/2015286NaN8157.0NaNNaNNaNNaNNaNNaNNaN3496.0NaNNaNNaNNaNNaNNaN
412/31/20142842730.08115.09633.0NaNNaNNaNNaNNaN1739.03471.02827.0NaNNaNNaNNaNNaN

末尾の5データを表示します。

df.tail()

DateDayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_GuineaDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
1173/27/20145103.08.06.0NaNNaNNaNNaNNaN66.06.05.0NaNNaNNaNNaNNaN
1183/26/2014486.0NaNNaNNaNNaNNaNNaNNaN62.0NaNNaNNaNNaNNaNNaNNaN
1193/25/2014386.0NaNNaNNaNNaNNaNNaNNaN60.0NaNNaNNaNNaNNaNNaNNaN
1203/24/2014286.0NaNNaNNaNNaNNaNNaNNaN59.0NaNNaNNaNNaNNaNNaNNaN
1213/22/2014049.0NaNNaNNaNNaNNaNNaNNaN29.0NaNNaNNaNNaNNaNNaNNaN

データの確認

データの型などの情報を取得

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 122 entries, 0 to 121
Data columns (total 18 columns):
 #   Column               Non-Null Count  Dtype
---  ------               --------------  -----
 0   Date                 122 non-null    object
 1   Day                  122 non-null    int64
 2   Cases_Guinea         93 non-null     float64
 3   Cases_Liberia        83 non-null     float64
 4   Cases_SierraLeone    87 non-null     float64
 5   Cases_Nigeria        38 non-null     float64
 6   Cases_Senegal        25 non-null     float64
 7   Cases_UnitedStates   18 non-null     float64
 8   Cases_Spain          16 non-null     float64
 9   Cases_Mali           12 non-null     float64
 10  Deaths_Guinea        92 non-null     float64
 11  Deaths_Liberia       81 non-null     float64
 12  Deaths_SierraLeone   87 non-null     float64
 13  Deaths_Nigeria       38 non-null     float64
 14  Deaths_Senegal       22 non-null     float64
 15  Deaths_UnitedStates  18 non-null     float64
 16  Deaths_Spain         16 non-null     float64
 17  Deaths_Mali          12 non-null     float64
dtypes: float64(16), int64(1), object(1)
memory usage: 17.3+ KB

大きさ(行数と列数)の確認

df.shape
(122, 18)

インデックスの確認

df.index
RangeIndex(start=0, stop=122, step=1)

カラム名の確認

df.columns
Index(['Date', 'Day', 'Cases_Guinea', 'Cases_Liberia', 'Cases_SierraLeone',
       'Cases_Nigeria', 'Cases_Senegal', 'Cases_UnitedStates', 'Cases_Spain',
       'Cases_Mali', 'Deaths_Guinea', 'Deaths_Liberia', 'Deaths_SierraLeone',
       'Deaths_Nigeria', 'Deaths_Senegal', 'Deaths_UnitedStates',
       'Deaths_Spain', 'Deaths_Mali'],
      dtype='object')

任意の列名のデータの取得

カラム名を指定して、任意のカラムだけ表示させます。

df[['Cases_UnitedStates','Deaths_UnitedStates']].head()

Cases_UnitedStatesDeaths_UnitedStates
0NaNNaN
1NaNNaN
2NaNNaN
3NaNNaN
4NaNNaN

行数や列数を指定してデータを取得

df.iloc[[6,7],[0,3]]

DateCases_Liberia
612/27/2014NaN
712/24/20147977.0

ある条件を満たしたデータを取得

df[df['Deaths_Liberia'] > 3000][['Deaths_Liberia']]

Deaths_Liberia
23496.0
33496.0
43471.0
53423.0
73413.0
93384.0
103376.0
123290.0
143177.0
163145.0
183016.0

カラムの削除

Deaths_Guineaというカラムを削除しています。

df.drop(['Deaths_Guinea'], axis=1, inplace=True)
df.columns
Index(['Date', 'Day', 'Cases_Guinea', 'Cases_Liberia', 'Cases_SierraLeone',
       'Cases_Nigeria', 'Cases_Senegal', 'Cases_UnitedStates', 'Cases_Spain',
       'Cases_Mali', 'Deaths_Liberia', 'Deaths_SierraLeone', 'Deaths_Nigeria',
       'Deaths_Senegal', 'Deaths_UnitedStates', 'Deaths_Spain', 'Deaths_Mali'],
      dtype='object')

統計量の取得

describe()を利用して、列ごとの統計量を取得することが出来ます。ぱっと見、概要を得たいときに有力です。

df.describe()

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
count122.00000093.00000083.00000087.00000038.00000025.0018.00000016.012.00000081.00000087.00000038.00000022.018.00000016.00000012.000000
mean144.778689911.0645162335.3373492427.36781616.7368421.083.2777781.03.5000001101.209877693.7011496.1315790.00.8333330.1875003.166667
std89.316460849.1088012987.9667213184.8039965.9985770.401.1785110.02.7468991297.208568869.9470732.7819010.00.3834820.4031132.405801
min0.00000049.0000003.0000000.0000000.0000001.001.0000001.01.0000002.0000000.0000000.0000000.00.0000000.0000001.000000
25%66.250000236.00000025.50000064.50000015.0000001.003.0000001.01.00000012.0000006.0000004.0000000.01.0000000.0000001.000000
50%150.000000495.000000516.000000783.00000020.0000001.004.0000001.02.500000294.000000334.0000008.0000000.01.0000000.0000002.000000
75%219.5000001519.0000004162.5000003801.00000020.0000001.004.0000001.06.2500002413.0000001176.0000008.0000000.01.0000000.0000006.000000
max289.0000002776.0000008166.00000010030.00000022.0000003.004.0000001.07.0000003496.0000002977.0000008.0000000.01.0000001.0000006.000000

また、value_countsメソッドを利用して、値の頻度を簡単に求める事ができます。今回用いたデータが連続量のため、少々わかりにくいですが、0.0のデータ数が15である事がわかります。その他のデータ数はすべて1個である事がわかります。

df['Deaths_Liberia'].value_counts()
11.0      9
12.0      4
3496.0    2
13.0      2
24.0      2
         ..
2963.0    1
88.0      1
3413.0    1
3177.0    1
105.0     1
Name: Deaths_Liberia, Length: 66, dtype: int64

インデックスをdatetime型に変更

インデックスをDateに変更し、上書きします。時系列データの場合、インデックスを日付にすると解析しやすいことが多いです。ただ、単純に文字列としてインデックスするよりも、pandaに標準で備わっているdatetime型に変換すると集計処理などが便利になります。

Dateというインデックス名をYYYYMMDDに変更します。。rename関数を利用します。

df.rename(columns={'Date':'YYYYMMDD'}, inplace=True)
df.set_index('YYYYMMDD', inplace=True)
df.index
Index(['1/5/2015', '1/4/2015', '1/3/2015', '1/2/2015', '12/31/2014',
       '12/28/2014', '12/27/2014', '12/24/2014', '12/21/2014', '12/20/2014',
       ...
       '4/4/2014', '4/1/2014', '3/31/2014', '3/29/2014', '3/28/2014',
       '3/27/2014', '3/26/2014', '3/25/2014', '3/24/2014', '3/22/2014'],
      dtype='object', name='YYYYMMDD', length=122)
df.head()

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
YYYYMMDD
1/5/20152892776.0NaN10030.0NaNNaNNaNNaNNaNNaN2977.0NaNNaNNaNNaNNaN
1/4/20152882775.0NaN9780.0NaNNaNNaNNaNNaNNaN2943.0NaNNaNNaNNaNNaN
1/3/20152872769.08166.09722.0NaNNaNNaNNaNNaN3496.02915.0NaNNaNNaNNaNNaN
1/2/2015286NaN8157.0NaNNaNNaNNaNNaNNaN3496.0NaNNaNNaNNaNNaNNaN
12/31/20142842730.08115.09633.0NaNNaNNaNNaNNaN3471.02827.0NaNNaNNaNNaNNaN
df.columns
Index(['Day', 'Cases_Guinea', 'Cases_Liberia', 'Cases_SierraLeone',
       'Cases_Nigeria', 'Cases_Senegal', 'Cases_UnitedStates', 'Cases_Spain',
       'Cases_Mali', 'Deaths_Liberia', 'Deaths_SierraLeone', 'Deaths_Nigeria',
       'Deaths_Senegal', 'Deaths_UnitedStates', 'Deaths_Spain', 'Deaths_Mali'],
      dtype='object')

インデックスでソートします。ただ、日付が文字列のオブジェクトになっているので、目論見通りのソートになっていません。

df.sort_index(ascending=True).head()

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
YYYYMMDD
1/2/2015286NaN8157.0NaNNaNNaNNaNNaNNaN3496.0NaNNaNNaNNaNNaNNaN
1/3/20152872769.08166.09722.0NaNNaNNaNNaNNaN3496.02915.0NaNNaNNaNNaNNaN
1/4/20152882775.0NaN9780.0NaNNaNNaNNaNNaNNaN2943.0NaNNaNNaNNaNNaN
1/5/20152892776.0NaN10030.0NaNNaNNaNNaNNaNNaN2977.0NaNNaNNaNNaNNaN
10/1/20141931199.03834.02437.020.01.01.0NaNNaN2069.0623.08.00.00.0NaNNaN

インデックスをdatetime型に変更します。

df.index
Index(['1/5/2015', '1/4/2015', '1/3/2015', '1/2/2015', '12/31/2014',
       '12/28/2014', '12/27/2014', '12/24/2014', '12/21/2014', '12/20/2014',
       ...
       '4/4/2014', '4/1/2014', '3/31/2014', '3/29/2014', '3/28/2014',
       '3/27/2014', '3/26/2014', '3/25/2014', '3/24/2014', '3/22/2014'],
      dtype='object', name='YYYYMMDD', length=122)
df.index = pd.to_datetime(df.index, format='%m/%d/%Y')
df.index
DatetimeIndex(['2015-01-05', '2015-01-04', '2015-01-03', '2015-01-02',
               '2014-12-31', '2014-12-28', '2014-12-27', '2014-12-24',
               '2014-12-21', '2014-12-20',
               ...
               '2014-04-04', '2014-04-01', '2014-03-31', '2014-03-29',
               '2014-03-28', '2014-03-27', '2014-03-26', '2014-03-25',
               '2014-03-24', '2014-03-22'],
              dtype='datetime64[ns]', name='YYYYMMDD', length=122, freq=None)

となり、dtype=‘object’からobject=‘datetime64’とdatetime型に変更されていることが分かります。そこでソートしてみます。

df.sort_index(ascending=True, inplace=True)
df.head(10)

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
YYYYMMDD
2014-03-22049.0NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2014-03-24286.0NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2014-03-25386.0NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2014-03-26486.0NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2014-03-275103.08.06.0NaNNaNNaNNaNNaN6.05.0NaNNaNNaNNaNNaN
2014-03-286112.03.02.0NaNNaNNaNNaNNaN3.02.0NaNNaNNaNNaNNaN
2014-03-297112.07.0NaNNaNNaNNaNNaNNaN2.0NaNNaNNaNNaNNaNNaN
2014-03-319122.08.02.0NaNNaNNaNNaNNaN4.02.0NaNNaNNaNNaNNaN
2014-04-0110127.08.02.0NaNNaNNaNNaNNaN5.02.0NaNNaNNaNNaNNaN
2014-04-0413143.018.02.0NaNNaNNaNNaNNaN7.02.0NaNNaNNaNNaNNaN
df.tail(10)

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
YYYYMMDD
2014-12-202722571.07862.08939.0NaNNaNNaNNaNNaN3384.02556.0NaNNaNNaNNaNNaN
2014-12-212732597.0NaN9004.0NaNNaNNaNNaNNaNNaN2582.0NaNNaNNaNNaNNaN
2014-12-242772630.07977.09203.0NaNNaNNaNNaNNaN3413.02655.0NaNNaNNaNNaNNaN
2014-12-272802695.0NaN9409.0NaNNaNNaNNaNNaNNaN2732.0NaNNaNNaNNaNNaN
2014-12-282812706.08018.09446.0NaNNaNNaNNaNNaN3423.02758.0NaNNaNNaNNaNNaN
2014-12-312842730.08115.09633.0NaNNaNNaNNaNNaN3471.02827.0NaNNaNNaNNaNNaN
2015-01-02286NaN8157.0NaNNaNNaNNaNNaNNaN3496.0NaNNaNNaNNaNNaNNaN
2015-01-032872769.08166.09722.0NaNNaNNaNNaNNaN3496.02915.0NaNNaNNaNNaNNaN
2015-01-042882775.0NaN9780.0NaNNaNNaNNaNNaNNaN2943.0NaNNaNNaNNaNNaN
2015-01-052892776.0NaN10030.0NaNNaNNaNNaNNaNNaN2977.0NaNNaNNaNNaNNaN

となり、想定通りのソートになっている事が分かります。

また、datetime型がインデックスに設定されたので、日付を扱いのが容易になっています。 例えば、2015年のデータを取得するのに、以下の様になります。

df['2015']

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
YYYYMMDD
2015-01-02286NaN8157.0NaNNaNNaNNaNNaNNaN3496.0NaNNaNNaNNaNNaNNaN
2015-01-032872769.08166.09722.0NaNNaNNaNNaNNaN3496.02915.0NaNNaNNaNNaNNaN
2015-01-042882775.0NaN9780.0NaNNaNNaNNaNNaNNaN2943.0NaNNaNNaNNaNNaN
2015-01-052892776.0NaN10030.0NaNNaNNaNNaNNaNNaN2977.0NaNNaNNaNNaNNaN
df['2014-12'].sort_index(ascending=True)

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
YYYYMMDD
2014-12-03256NaN7719.0NaNNaNNaNNaNNaNNaN3177.0NaNNaNNaNNaNNaNNaN
2014-12-072602292.0NaN7897.020.01.04.01.07.0NaN1768.08.00.01.00.06.0
2014-12-09262NaN7797.0NaNNaNNaNNaNNaNNaN3290.0NaNNaNNaNNaNNaNNaN
2014-12-142672416.0NaN8356.0NaNNaNNaNNaNNaNNaN2085.0NaNNaNNaNNaNNaN
2014-12-18271NaN7830.0NaNNaNNaNNaNNaNNaN3376.0NaNNaNNaNNaNNaNNaN
2014-12-202722571.07862.08939.0NaNNaNNaNNaNNaN3384.02556.0NaNNaNNaNNaNNaN
2014-12-212732597.0NaN9004.0NaNNaNNaNNaNNaNNaN2582.0NaNNaNNaNNaNNaN
2014-12-242772630.07977.09203.0NaNNaNNaNNaNNaN3413.02655.0NaNNaNNaNNaNNaN
2014-12-272802695.0NaN9409.0NaNNaNNaNNaNNaNNaN2732.0NaNNaNNaNNaNNaN
2014-12-282812706.08018.09446.0NaNNaNNaNNaNNaN3423.02758.0NaNNaNNaNNaNNaN
2014-12-312842730.08115.09633.0NaNNaNNaNNaNNaN3471.02827.0NaNNaNNaNNaNNaN

さらに、平均や合計値などの統計値を、年や月単位で簡単に取得することができます。

df.resample('Y').mean()

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
YYYYMMDD
2014-12-31139.940678848.9888892191.4814812162.48809516.7368421.083.2777781.03.51040.582278613.2976196.1315790.00.8333330.18753.166667
2015-12-31287.5000002773.3333338161.5000009844.000000NaNNaNNaNNaNNaN3496.0000002945.000000NaNNaNNaNNaNNaN
df.resample('M').mean()

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
YYYYMMDD
2014-03-314.50000094.5000006.5000003.333333NaNNaNNaNNaNNaN3.7500003.000000NaNNaNNaNNaNNaN
2014-04-3024.333333177.81818224.5555562.200000NaNNaNNaNNaNNaN9.6250001.111111NaNNaNNaNNaNNaN
2014-05-3151.888889248.77777812.5555567.333333NaNNaNNaNNaNNaN11.1111111.222222NaNNaNNaNNaNNaN
2014-06-3084.636364373.42857135.500000125.571429NaNNaNNaNNaNNaN28.00000029.375000NaNNaNNaNNaNNaN
2014-07-31115.700000423.000000212.300000420.5000001.333333NaNNaNNaNNaN121.300000189.5000000.666667NaNNaNNaNNaN
2014-08-31145.090909559.818182868.818182844.00000013.3636361.000000NaNNaNNaN468.454545353.0000003.545455NaNNaNNaNNaN
2014-09-30177.500000967.8888892815.6250001726.00000020.7142861.285714NaNNaNNaN1508.000000565.7777788.0000000.0NaNNaNNaN
2014-10-31207.4705881500.4444444758.7500003668.11111120.0000001.0000002.5555561.01.02419.0000001151.6666678.0000000.00.6666670.4285711.000
2014-11-30237.2142861950.5000007039.0000005843.62500020.0000001.0000004.0000001.04.02928.8571431256.7500008.0000000.01.0000000.0000003.625
2014-12-31271.1818182579.6250007902.5714298985.87500020.0000001.0000004.0000001.07.03362.0000002495.3750008.0000000.01.0000000.0000006.000
2015-01-31287.5000002773.3333338161.5000009844.000000NaNNaNNaNNaNNaN3496.0000002945.000000NaNNaNNaNNaNNaN
df.resample('Y').sum()

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
YYYYMMDD
2014-12-311651376409.0177510.0181649.0636.027.059.016.042.082206.051517.0233.00.015.03.038.0
2015-12-3111508320.016323.029532.00.00.00.00.00.06992.08835.00.00.00.00.00.0
df.resample('M').sum()

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Mali
YYYYMMDD
2014-03-3136756.026.010.00.00.00.00.00.015.09.00.00.00.00.00.0
2014-04-303651956.0221.022.00.00.00.00.00.077.010.00.00.00.00.00.0
2014-05-314672239.0113.066.00.00.00.00.00.0100.011.00.00.00.00.00.0
2014-06-309312614.0284.0879.00.00.00.00.00.0196.0235.00.00.00.00.00.0
2014-07-3111574230.02123.04205.04.00.00.00.00.01213.01895.02.00.00.00.00.0
2014-08-3115966158.09557.09284.0147.01.00.00.00.05153.03883.039.00.00.00.00.0
2014-09-3021308711.022525.015534.0145.09.00.00.00.012064.05092.056.00.00.00.00.0
2014-10-31352713504.038070.033013.0160.08.023.07.03.019352.010365.064.00.06.03.03.0
2014-11-30332115604.049273.046749.0160.08.032.08.032.020502.010054.064.00.08.00.029.0
2014-12-31298320637.055318.071887.020.01.04.01.07.023534.019963.08.00.01.00.06.0
2015-01-3111508320.016323.029532.00.00.00.00.00.06992.08835.00.00.00.00.00.0

とても便利です。さらに、datetime型のもう一つの利点として、.year.monthなどのメソッドを利用して、年月日を取得することが出来ます。

df.index.year
Int64Index([2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014,
            ...
            2014, 2014, 2014, 2014, 2014, 2014, 2015, 2015, 2015, 2015],
           dtype='int64', name='YYYYMMDD', length=122)
df.index.month
Int64Index([ 3,  3,  3,  3,  3,  3,  3,  3,  4,  4,
            ...
            12, 12, 12, 12, 12, 12,  1,  1,  1,  1],
           dtype='int64', name='YYYYMMDD', length=122)
df.index.day
Int64Index([22, 24, 25, 26, 27, 28, 29, 31,  1,  4,
            ...
            20, 21, 24, 27, 28, 31,  2,  3,  4,  5],
           dtype='int64', name='YYYYMMDD', length=122)

cut処理(ヒストグラムの作成)

データの解析をしていると、データを特定の条件の下分割して、集計したいという場面がよくあります。例えば、季節ごとに集計したい場合などがあると思います。ちょっと月と季節が合っていませんが、季節でラベリングする例です。

labels = ['春', '夏', '秋', '冬']
df['season'] = pd.cut(list(df.index.month),  bins=[0,3,6,9,12], labels=labels, right=True)
df[['season']][5:10]

season
YYYYMMDD
2014-03-28
2014-03-29
2014-03-31
2014-04-01
2014-04-04
df[['season']][73:78]

season
YYYYMMDD
2014-09-21
2014-09-23
2014-09-28
2014-10-01
2014-10-04

query, where, maskの使い方 (ソートも)

numpyと同じように、queryやwhereなども使うことが出来ます。使い方は直感的にnumpyと同じなので、すぐに使えると思います。感染者と死者数でクエリを実行してみます。

queryは抽出したい条件式を指定します。

df[['Deaths_Liberia','Cases_Liberia']].query('Deaths_Liberia > 100 and Cases_Liberia > 7000')

Deaths_LiberiaCases_Liberia
YYYYMMDD
2014-11-152964.07069.0
2014-11-182963.07082.0
2014-11-223016.07168.0
2014-11-283145.07635.0
2014-12-033177.07719.0
2014-12-093290.07797.0
2014-12-183376.07830.0
2014-12-203384.07862.0
2014-12-243413.07977.0
2014-12-283423.08018.0
2014-12-313471.08115.0
2015-01-023496.08157.0
2015-01-033496.08166.0

whereも条件を指定すると、条件を満たすデータはそのまま、見たさないデータはNaNが格納されたデータを返します。

df[['Deaths_Liberia']].where(df['Deaths_Liberia'] > 1000)

Deaths_Liberia
YYYYMMDD
2014-03-22NaN
2014-03-24NaN
2014-03-25NaN
2014-03-26NaN
2014-03-27NaN
......
2014-12-313471.0
2015-01-023496.0
2015-01-033496.0
2015-01-04NaN
2015-01-05NaN

122 rows × 1 columns

NaNではなく、別の数字を入れることも可能です。この辺はnumpyと同じでとても助かります。

df[['Deaths_Liberia']].where(df['Deaths_Liberia'] > 3000,0)

Deaths_Liberia
YYYYMMDD
2014-03-220.0
2014-03-240.0
2014-03-250.0
2014-03-260.0
2014-03-270.0
......
2014-12-313471.0
2015-01-023496.0
2015-01-033496.0
2015-01-040.0
2015-01-050.0

122 rows × 1 columns

maskメソッドはwhereと逆で、条件を満たすものを第二引数に書き換えます。

df[['Deaths_Liberia']].mask(df['Deaths_Liberia'] > 3000, 0)

Deaths_Liberia
YYYYMMDD
2014-03-22NaN
2014-03-24NaN
2014-03-25NaN
2014-03-26NaN
2014-03-276.0
......
2014-12-310.0
2015-01-020.0
2015-01-030.0
2015-01-04NaN
2015-01-05NaN

122 rows × 1 columns

nullの使い方

データにはしばしばNullが含まれるので、正しいデータ分析のためにはNullがどの程度含まれていて、それがどの程度解析に影響を及ぼすのか確認する必要があります。

isnullによって、Nullの部分をFalseにしたテーブルを作成する事が出来ます。

df.isnull()

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Maliseason
YYYYMMDD
2014-03-22FalseFalseTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueFalse
2014-03-24FalseFalseTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueFalse
2014-03-25FalseFalseTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueFalse
2014-03-26FalseFalseTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueFalse
2014-03-27FalseFalseFalseFalseTrueTrueTrueTrueTrueFalseFalseTrueTrueTrueTrueTrueFalse
......................................................
2014-12-31FalseFalseFalseFalseTrueTrueTrueTrueTrueFalseFalseTrueTrueTrueTrueTrueFalse
2015-01-02FalseTrueFalseTrueTrueTrueTrueTrueTrueFalseTrueTrueTrueTrueTrueTrueFalse
2015-01-03FalseFalseFalseFalseTrueTrueTrueTrueTrueFalseFalseTrueTrueTrueTrueTrueFalse
2015-01-04FalseFalseTrueFalseTrueTrueTrueTrueTrueTrueFalseTrueTrueTrueTrueTrueFalse
2015-01-05FalseFalseTrueFalseTrueTrueTrueTrueTrueTrueFalseTrueTrueTrueTrueTrueFalse

122 rows × 17 columns

また、sumメソッドを利用すると、各カラムごとにNullの個数をカウントする事が出来ます。

df.isnull().sum()
Day                      0
Cases_Guinea            29
Cases_Liberia           39
Cases_SierraLeone       35
Cases_Nigeria           84
Cases_Senegal           97
Cases_UnitedStates     104
Cases_Spain            106
Cases_Mali             110
Deaths_Liberia          41
Deaths_SierraLeone      35
Deaths_Nigeria          84
Deaths_Senegal         100
Deaths_UnitedStates    104
Deaths_Spain           106
Deaths_Mali            110
season                   0
dtype: int64

同様に、meanメソッドで平均を出すことが出来ます。

df.isnull().mean()
Day                    0.000000
Cases_Guinea           0.237705
Cases_Liberia          0.319672
Cases_SierraLeone      0.286885
Cases_Nigeria          0.688525
Cases_Senegal          0.795082
Cases_UnitedStates     0.852459
Cases_Spain            0.868852
Cases_Mali             0.901639
Deaths_Liberia         0.336066
Deaths_SierraLeone     0.286885
Deaths_Nigeria         0.688525
Deaths_Senegal         0.819672
Deaths_UnitedStates    0.852459
Deaths_Spain           0.868852
Deaths_Mali            0.901639
season                 0.000000
dtype: float64

Nullのデータを書き換えます。fillnaというメソッドを利用します。

df.fillna(value={'Cases_Liberia': 0.0, 'Deaths_Liberia': 0.0}, inplace=True)
df.isnull().sum()
Day                      0
Cases_Guinea            29
Cases_Liberia            0
Cases_SierraLeone       35
Cases_Nigeria           84
Cases_Senegal           97
Cases_UnitedStates     104
Cases_Spain            106
Cases_Mali             110
Deaths_Liberia           0
Deaths_SierraLeone      35
Deaths_Nigeria          84
Deaths_Senegal         100
Deaths_UnitedStates    104
Deaths_Spain           106
Deaths_Mali            110
season                   0
dtype: int64

これで確かにCases_LiberiaとDeath_Liberiaのnullの数が0になりました。

また、ある列にNullがある行を削除することが出来ます。dropnaを適用した前後のデータ数を比較してみるとわかります。削除前は、

df.shape
(122, 17)

となります。削除後は以下の通りで、確かに削除されていることがわかります。

df.dropna(subset=['Cases_Nigeria'], axis=0).shape
(38, 17)
df.dropna(subset=['Cases_Nigeria'], axis=0).isnull().sum()
Day                     0
Cases_Guinea            2
Cases_Liberia           0
Cases_SierraLeone       0
Cases_Nigeria           0
Cases_Senegal          13
Cases_UnitedStates     21
Cases_Spain            23
Cases_Mali             27
Deaths_Liberia          0
Deaths_SierraLeone      0
Deaths_Nigeria          0
Deaths_Senegal         16
Deaths_UnitedStates    21
Deaths_Spain           23
Deaths_Mali            27
season                  0
dtype: int64
df.dropna(subset=['Cases_Nigeria'], axis=0).head()

DayCases_GuineaCases_LiberiaCases_SierraLeoneCases_NigeriaCases_SenegalCases_UnitedStatesCases_SpainCases_MaliDeaths_LiberiaDeaths_SierraLeoneDeaths_NigeriaDeaths_SenegalDeaths_UnitedStatesDeaths_SpainDeaths_Maliseason
YYYYMMDD
2014-07-23123427.0249.0525.00.0NaNNaNNaNNaN129.0224.00.0NaNNaNNaNNaN
2014-07-27126460.0329.0533.01.0NaNNaNNaNNaN156.0233.01.0NaNNaNNaNNaN
2014-07-30129472.0391.0574.03.0NaNNaNNaNNaN227.0252.01.0NaNNaNNaNNaN
2014-08-01132485.0468.0646.04.0NaNNaNNaNNaN255.0273.01.0NaNNaNNaNNaN
2014-08-04135495.0516.0691.09.0NaNNaNNaNNaN282.0286.01.0NaNNaNNaNNaN

列名やインデックス名の変更

上で既に出てきていますが、列名やインデックスの名前を変更したい場合はよくあります。renameメソッドを使います。

df.rename(columns={'before': 'after'}, inplace=True)
df.rename(index={'before': 'after'}, inplace=True)

SQL likeなメソッド

SQLおなじみのgroupbyがpandasで利用できます。こちらは個人的にはよく利用しますね。

df.groupby(['season'])['season'].count()
season
春    12
夏    35
秋    33
冬    42
Name: season, dtype: int64

CSVへ出力

メモリに格納されているすべてのデータを出力します。

df.to_csv('./out.csv')
!head  -n 10 out.csv
YYYYMMDD,Day,Cases_Guinea,Cases_Liberia,Cases_SierraLeone,Cases_Nigeria,Cases_Senegal,Cases_UnitedStates,Cases_Spain,Cases_Mali,Deaths_Liberia,Deaths_SierraLeone,Deaths_Nigeria,Deaths_Senegal,Deaths_UnitedStates,Deaths_Spain,Deaths_Mali,season
2014-03-22,0,49.0,0.0,,,,,,,0.0,,,,,,,春
2014-03-24,2,86.0,0.0,,,,,,,0.0,,,,,,,春
2014-03-25,3,86.0,0.0,,,,,,,0.0,,,,,,,春
2014-03-26,4,86.0,0.0,,,,,,,0.0,,,,,,,春
2014-03-27,5,103.0,8.0,6.0,,,,,,6.0,5.0,,,,,,春
2014-03-28,6,112.0,3.0,2.0,,,,,,3.0,2.0,,,,,,春
2014-03-29,7,112.0,7.0,,,,,,,2.0,,,,,,,春
2014-03-31,9,122.0,8.0,2.0,,,,,,4.0,2.0,,,,,,春
2014-04-01,10,127.0,8.0,2.0,,,,,,5.0,2.0,,,,,,夏
df.to_csv('./out.csv', columns=['Deaths_Liberia'])
!head  -n 10 out.csv
YYYYMMDD,Deaths_Liberia
2014-03-22,0.0
2014-03-24,0.0
2014-03-25,0.0
2014-03-26,0.0
2014-03-27,6.0
2014-03-28,3.0
2014-03-29,2.0
2014-03-31,4.0
2014-04-01,5.0

ヘッダーとインデックスを記述しないように出来ます。

df.to_csv('./out.csv', header=False, index=False)
!head  -n 10 out.csv
0,49.0,0.0,,,,,,,0.0,,,,,,,春
2,86.0,0.0,,,,,,,0.0,,,,,,,春
3,86.0,0.0,,,,,,,0.0,,,,,,,春
4,86.0,0.0,,,,,,,0.0,,,,,,,春
5,103.0,8.0,6.0,,,,,,6.0,5.0,,,,,,春
6,112.0,3.0,2.0,,,,,,3.0,2.0,,,,,,春
7,112.0,7.0,,,,,,,2.0,,,,,,,春
9,122.0,8.0,2.0,,,,,,4.0,2.0,,,,,,春
10,127.0,8.0,2.0,,,,,,5.0,2.0,,,,,,夏
13,143.0,18.0,2.0,,,,,,7.0,2.0,,,,,,夏

その他追記

型変換

型を指定して上書きします。一括の変換の表記方法です。inplaceがなく、少し時間を使ってしまい、メモしておきます。

test = pd.DataFrame({'max':[1], 'min':[2], 'mean':[1.5]})

test.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1 entries, 0 to 0
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   max     1 non-null      int64
 1   min     1 non-null      int64
 2   mean    1 non-null      float64
dtypes: float64(1), int64(2)
memory usage: 152.0 bytes
test = test.astype({'max': float, 'min': float, 'mean': float})

test.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1 entries, 0 to 0
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   max     1 non-null      float64
 1   min     1 non-null      float64
 2   mean    1 non-null      float64
dtypes: float64(3)
memory usage: 152.0 bytes

よく使う関数

最後のまとめとして、良く使う関数をまとめておきます。個人的なsnipetみたいなものです。

インデックスの変更(既存のカラム名に変更)

df.set_index('xxxx')

カラム名の変更

df.rename(columns={'before': 'after'}, inplace=True)
df.rename(index={'before': 'after'}, inplace=True)

あるカラムでソートする

df.sort_values(by='xxx', ascending=True)

インデックスでソートする

df.sort_index()

datetime型の型変換

df.to_datetime()

NaNのカラムごとの個数

df.isnull().sum()

参考文献