akiyoko blog

akiyoko の IT技術系ブログです

pandas.DataFrame で時系列データの手習い

前回・前々回と、pandas.DataFrame の基礎編についての記事を書きましたが、

その応用編として、時系列データを扱うための手習いとして pandas.DataFrame をいろいろ弄ってみようと思っています。


なおこれまで同様、ローカルの実行環境は以下の通りとなっています。

  • MacOS Sierra 10.12.3
  • Python 2.7.12 (Anaconda 4.2.0)
  • Jupyter Notebook 4.2.0
  • pandas 0.18.1



まずは、import などの諸設定。

import glob

import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline

pd.options.display.max_rows = 10

plt.style.use('ggplot')
font = {'family': 'IPAexGothic'}
matplotlib.rc('font', **font)

 

CSVファイルからデータを読み込む

例えば、1日分のデータとして以下のような CSVデータを想定。
一列目(date)が日付なのですが、「yymmdd」形式で出力されています。

date,expected_value,preliminary_value,name,confirmed_value
160301,,,,
160301,0,-100,銀行券要因,
160301,-7200,-10000,財政等要因,
160301,-7200,-10100,資金過不足,
160301,6000,6000, ,
160301,7500,7500, ,
160301,,, ,
160301,,, ,
160301,,, ,
160301,,, ,
160301,,, ,
160301,,, ,
160301,,, ,
160301,-300,-300, ,
160301,-100,-100, ,
160301,,, ,
160301,,, ,
160301,,, ,
160301,,, ,
160301,,-200, ,
160301,+13100,+12900, ,
160301,,, ,
160301,,, ,
160301,+0,+0, ,
160301,+13100,+12900,合計,
160301,+5900,+2800,当座預金増減,
160301,,2592700,当座預金残高,
160301,,2336500, ,
160301,,1896900, ,
160301,,1896900, ,
160301,,256200, ,
160301,,,マネタリーベース,
160301,,積み期間(2/16~3/15日)の所要準備額(積数),積み期間(2/16~3/15日)の所要準備額(積数),1917300
160301,,積み期間(2/16~3/15日)の所要準備額(1日平均),積み期間(2/16~3/15日)の所要準備額(1日平均),66100
160301,,3/2日以降の残り要積立額(積数),3/2日以降の残り要積立額(積数),3300
160301,,3/2日以降の残り要積立額(1日平均),3/2日以降の残り要積立額(1日平均),200


まずは、CSVデータの読み込み。

CSV データの一列目の日付文字列を DatetimeIndex オブジェクトに変換して、index 列として読み込んでいます。

(参考)pandasで様々な日付フォーマットを取り扱う - Qiita

date_parser = lambda d: pd.datetime.strptime(d, '%y%m%d')
df = pd.read_csv(
    '/Users/akiyoko/PycharmProjects/marketstat/downloads/boj/2016/160301.csv',
    index_col='date', parse_dates=True, date_parser=date_parser,
    na_values=' ',  # たまに「name」列に ' ' が入っているので NaN に変換
)
df

f:id:akiyoko:20170409232420p:plain:w400


na_values オプションは、CSV 読み込み時に特定の値(今回の場合は半角スペース)を NaN に変換してくれるので便利です。



 

特定の列が NaN の行を除外

「name」列や「preliminary_value」列が NaN の行を除外します(不要なので)。

(参考)http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.dropna.html

df = df.dropna(subset=['name', 'preliminary_value'])
df

f:id:akiyoko:20170409232651p:plain:w400


 

特定条件の行だけを抽出

「name」列が「当座預金残高」になっている行だけを抽出します。

df = df[df['name'] == '当座預金残高']
df

f:id:akiyoko:20170409232706p:plain:w400


 

特定の列だけを抽出

「expected_value」列と「confirmed_value」列を除外します(不要なので)。

(参考)http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.drop.html

df = df.drop(['expected_value', 'confirmed_value', 'name'], axis=1)
df

# 以下のように残すカラムだけ指定しても、同じ結果が得られる
# df = df[['preliminary_value']]

f:id:akiyoko:20170409232719p:plain:w150



 

ラベル名を変更

column名を「preliminary_value」から「当座預金残高」に変更します。

(参考)https://nkmk.github.io/blog/python-pandas-dataframe-rename/

df = df.rename(columns={'preliminary_value': u'当座預金残高'})


次に、index名を「Date」に変更します。

df.index.names = ['Date']
df

f:id:akiyoko:20170409232732p:plain:w150


 

値を int型に変換

ここで、「当座預金残高」列のデータの値は object 型になっています。

df[u'当座預金残高']
Date
2016-03-01    2592700
Name: 当座預金残高, dtype: object


object型のままだとグラフにできないので、int型に変換します。

(参考)Python pandas strアクセサによる文字列処理 - StatsFragments

df[u'当座預金残高'] = df[u'当座預金残高'].astype(int)
df[u'当座預金残高']
Date
2016-03-01    2592700
Name: 当座預金残高, dtype: int64



 

実践編

次に、一定期間分(2016〜2017年分)の CSVデータを読み込んでみます。

# 時系列データ(日銀の当座預金残高)を読み込む
df_boj = pd.DataFrame()
date_parser = lambda d: pd.datetime.strptime(d, '%y%m%d')
paths = glob.glob('/Users/akiyoko/PycharmProjects/marketstat/downloads/boj/201[67]/*.csv')
for path in paths:
    df = pd.read_csv(path,
                     index_col='date', parse_dates=True, date_parser=date_parser,
                     na_values=' ')
    # 「name」列や「preliminary_value」列が NaN の行を除外
    df = df.dropna(subset=['name', 'preliminary_value'])
    # 「name」列が '当座預金残高' になっている行だけに絞る
    df = df[df['name'] == '当座預金残高']
    # 「expected_value」列と「confirmed_value」列を除外
    df = df.drop(['expected_value', 'confirmed_value', 'name'], axis=1)
    # column名を「preliminary_value」から「当座預金残高」に変更
    df = df.rename(columns={'preliminary_value': u'当座預金残高'})
    # index名を「Date」に変更する
    df.index.names = ['Date']
    # object型のままだとグラフにできないので int型に変換
    df[u'当座預金残高'] = df[u'当座預金残高'].astype(int)
    # 行を末尾に追加
    df_boj = df_boj.append(df)

# なお、column数が1つだけの場合はyオプションは不要
df_boj.plot()
df_boj

f:id:akiyoko:20170409232752p:plain:w150

f:id:akiyoko:20170409232810p:plain



ここで、2016年の時系列データ(日経平均株価)を読み込んでみます。

# 時系列データ(日経平均株価)を読み込む
df_n225 = pd.read_csv('n225.csv', index_col='Date', parse_dates=True, usecols=['Date', 'Adj Close'])
df_n225 = df_n225.sort_index()
df_n225 = df_n225.rename(columns={'Adj Close': u'日経平均株価(終値)'})
df_n225.plot()
df_n225

f:id:akiyoko:20170409232828p:plain:w180

f:id:akiyoko:20170409232857p:plain



単純に、二つのグラフを同じ図上に表示してみます。
日経平均株価の値が相対的に小さすぎて、地を這うようなグラフになってしまいました。

ax = df_boj.plot()
df_n225.plot(ax=ax)

f:id:akiyoko:20170409232940p:plain



見やすいように倍率を調整してみます。

adj = df_boj[u'当座預金残高'].mean() / df_n225[u'日経平均株価(終値)'].mean()
ax = df_boj.plot()
(df_n225 * adj).plot(ax=ax)

f:id:akiyoko:20170409232953p:plain



最後に、期間を合わせてみます。

start_date = '2016-01-01'
end_date = '2016-12-31'
span = pd.date_range(start_date, end_date)
df_2016 = pd.DataFrame(index=span)
df_2016 = df_2016.join(df_boj[start_date:end_date])
df_2016 = df_2016.join(df_n225[start_date:end_date] * adj)
df_2016.plot()
df_2016

f:id:akiyoko:20170409233012p:plain:w250

f:id:akiyoko:20170409233028p:plain




 

参考

Pandas について本格的に勉強するならこちらの本を。


Jypyter Notebook の勉強をするならこの本を。