pandasで時系列データを扱う(pandas.Seriesとpandas.DataFrameを使って時系列データを扱う)1

話題は変わるが、「pandasによるStackingとUnstacking」の続き。

時系列データについては、これまで以下で扱ったので重複する部分もあると思うが反復練習ということで気にしない。

Pythonでの時系列データの扱い1 〜 文字列とdatetimeの変換
Pythonでの時系列データの扱い2 〜 時系列データの作成および選択
Pythonでの時系列データの扱い3 〜 時系列データの頻度設定
Pythonでの時系列データの扱い4 〜 「祝日の取得」および「祝日を考慮した営業日の取得」
pandas_datareader.dataのDataReaderを使用して株価を取得する 〜 pandas.Panel型で取得したデータ構造からpandas.Panel.minor_xsを使用して特定の銘柄のDataFrameを取得する

扱うテーマは次。

1.時系列データとDatetimeIndex
2.指定した頻度の時系列データを作成する方法
3.日付のoffsetsを使用して新しい日付を計算する方法
4.指定した期間で時系列データを扱う方法
5.時系列データのlagを取る方法
6.時系列データの頻度を変更する方法
7.時系列データのresampling(upsamplingとdownsampling)

1.時系列データとDatetimeIndex

◎特定の日付や時刻の扱い
・pandasでは、特定の日付や時刻は「Timestamp」クラスを使用する。
・TimestampはNumPyの「dtype datetime64」を元に作成されていて、Pythonに元々備わっているdatetimeオブジェクトよりも高精度。

◎時系列データ(timestampオブジェクトのシーケンス)の扱い
・時系列データ(timestampオブジェクトのシーケンス)はDatetimeIndexとして扱う。
・DatetimeIndexはpandasのindexの一種で、日付や時刻によるindexの作成に最適化されている。

さて、DatetimeIndexの作成方法は幾つかある。

1−1.datetimeオブジェクトの配列からDatetimeIndexを作成する
datetime(YYYY,MM,dd)を使用してdatetimeオブジェクトを作成する。次に、それをリストにしてから、pandas.DatetimeIndexを作用させるとDatetimeIndexとなる。

#coding:utf-8
import pandas as pd
import datetime as dt

# datetimeオブジェクトからDatetimeIndexを作成する
dates = [dt.datetime(2014, 8, 1), dt.datetime(2014, 8, 2)]
dti = pd.DatetimeIndex(dates)

print ""
print "print dti"
print dti

実行結果は次。

print dti
DatetimeIndex(['2014-08-01', '2014-08-02'], dtype='datetime64[ns]', freq=None)


1−2.pandas.SeriesからDatetimeIndexを作成する
◎1−2−1.datetimeオブジェクトから作成する
datetimeオブジェクトを作成してリストにするところまでは同じ。
それをpandas.Seriesに変換すると、pandas.DatetimeIndexを作用させなくともDatetimeIndexが作成される。

#coding:utf-8
import pandas as pd
import datetime as dt
import numpy as np

# Timestamp
dates = [
    dt.datetime(2014, 8, 1),
    dt.datetime(2014, 8, 2)
]

## frome series to DatetimeIndex
# データ作成
np.random.seed(123456)

ts = pd.Series(
    data = np.random.randn(2),
    index = dates
)
print ts

実行結果は次。

2014-08-01 -1.509059
2014-08-02 -1.135632

◎1−2−2.文字列オブジェクトから作成する

#coding:utf-8
import pandas as pd
import datetime as dt
import numpy as np

# stringからDatetimeIndexを作成する
dates = [
    '2014-08-01',
    '2014-08-02'
]

ts = pd.Series(
    data = np.random.randn(2),
    index = dates
)
print ts

実行結果は次。

2014-08-01 -1.509059
2014-08-02 -1.135632

◎1−2−3.pandas.to_datetimeを使用する
pandas.to_datetimeを使えば、異なる形式の日付表現からDatetimeIndexを作成することができる。
次のように異なる形式の日付表現でもDatetimeIndexを作成できる。

#coding:utf-8
import pandas as pd
import datetime as dt
import numpy as np

# to_datetimeからDatetimeIndexを作成する
dti = pd.to_datetime(
    [
        'Aug 1, 2014',
        '2014-08-02',
        '2014.8.3',
        None
    ]
)
print "# to_datetimeからDatetimeIndexを作成する"
print "print dti"
print dti

実行結果は次。

# to_datetimeからDatetimeIndexを作成する
print dti
DatetimeIndex(['2014-08-01', '2014-08-02', '2014-08-03', 'NaT'], dtype='datetime64[ns]', freq=None)

次のようにして、DataFrameからDatetimeIndexを作成することもできる。

#coding:utf-8
import pandas as pd
import datetime as dt
import numpy as np

df = pd.DataFrame(
    {'year': [2015, 2016],
     'month': [2, 3],
     'day': [4, 5]
     }
)
print "元のデータフレーム"
print "print df"
print df

print "# to_datetimeからDatetimeIndexを作成する"
print "print pd.to_datetime(df)"
print pd.to_datetime(df)

実行結果は次。

元のデータフレーム
print df
day month year
0 4 2 2015
1 5 3 2016
# to_datetimeからDatetimeIndexを作成する
print pd.to_datetime(df)
0 2015-02-04
1 2016-03-05
dtype: datetime64[ns]

注意する点として、変換に失敗した場合はデフォルトでNumPy配列が戻されること。
これを強制的にDatetimeIndexを戻すようにするには、「coerce=True」とする。
仕様が変わったようで、pandas0.19.1のドキュメントを参照すると、デフォルトではエラーを返すようになっているようだ。

実際、次のように書くと「ValueError: unknown string format」となった。

dtiNG = pd.to_datetime(
    [
        'Aug 1, 2014',
        'foo'
    ]
)

このようなケースのハンドリングにはerrorsオプションを使用する。
デフォルトは「errors=‘raise’」となっているので例外がスローされる。
エラーを無視するには次のように「errors=‘false’」とする。そうすると戻り値としてNumPy配列が戻される

#coding:utf-8
import pandas as pd
import datetime as dt
import numpy as np

# エラーを無視する
dtiNG = pd.to_datetime(
    [
        'Aug 1, 2014',
        'foo'
    ],
    errors='ignore'
)

print "dtiNG"
print dtiNG

実行結果は次。

dtiNG
['Aug 1, 2014' 'foo']

これを、エラーを無視するのではなく、日付変換できないデータを「NaT」に置き換えてDatetimeIndexを戻すには「errors=‘coerce’」を指定する。

#coding:utf-8
import pandas as pd
import datetime as dt
import numpy as np

# エラーを無視するのではなく、日付変換できないデータを「NaT」に置き換え
dtiNG_coerce = pd.to_datetime(
    [
        'Aug 1, 2014',
        'YYYY-MM-dd'
    ],
    errors='coerce'
)

print "dtiNG_coerce"
print dtiNG_coerce

実行結果は次。

dtiNG_coerce
DatetimeIndex(['2014-08-01', 'NaT'], dtype='datetime64[ns]', freq=None)

◎1−2−4.pandas.date_rangeを使用する
pandas.date_rangeは指定日付(startもしくはend)から指定期間(period)のDatetimeIndexを返す。

#coding:utf-8
import pandas as pd
import datetime as dt
import numpy as np

# 一定範囲のデータ
dates = pd.date_range(
    start = '8/1/2014',
    periods=10
)
print "dates"
print dates

結果は次。

dates
DatetimeIndex(['2014-08-01', '2014-08-02', '2014-08-03', '2014-08-04',
'2014-08-05', '2014-08-06', '2014-08-07', '2014-08-08',
'2014-08-09', '2014-08-10'],
dtype='datetime64[ns]', freq='D')

指定日付から過去分を取得するにはendオプションを明記する。
※)periodsに指定する値を負にしても駄目

#coding:utf-8
import pandas as pd
import datetime as dt
import numpy as np

dates = pd.date_range(
    end='8/1/2014',
    periods=10
)

print "dates"
print dates

結果は次。

dates
DatetimeIndex(['2014-07-23', '2014-07-24', '2014-07-25', '2014-07-26',
'2014-07-27', '2014-07-28', '2014-07-29', '2014-07-30',
'2014-07-31', '2014-08-01'],
dtype='datetime64[ns]', freq='D')

◎1−2−5.株価データのindexとしてのDatetimeIndex
例によってAAPLの株価をyahoo financeから取得する。

#coding:utf-8
import pandas as pd
import datetime as dt
import numpy as np
import pandas_datareader.data as pdd

# 期間設定
date_from = dt.date(2016, 9, 24)
date_to = dt.date(2016, 10, 6)

# 株価取得
aapl = pdd.DataReader('AAPL', "yahoo", date_from, date_to)

ここで取得したデータに対して幾つか操作を行う。
・カラムを指定してSeriesデータを受け取る

print aapl['Volume']

実行結果は次。

Date
2016-09-26 29869400
2016-09-27 24607400
2016-09-28 29641100
2016-09-29 35887000
2016-09-30 36379100
2016-10-03 21701800
2016-10-04 29736800
2016-10-05 21453100
2016-10-06 28779300
Name: Volume, dtype: int64

・DatetimeIndexの範囲指定で指定範囲のデータを取得する

print aapl['2016-08':'2016-09']

実行結果は次。

Open High Low Close Volume \
Date
2016-09-26 111.639999 113.389999 111.550003 112.879997 29869400
2016-09-27 113.000000 113.180000 112.339996 113.089996 24607400
2016-09-28 113.690002 114.639999 113.430000 113.949997 29641100
2016-09-29 113.160004 113.800003 111.800003 112.180000 35887000
2016-09-30 112.459999 113.370003 111.800003 113.050003 36379100

・# locで指定年月日の一定範囲のデータを取得する(DatetimeIndexの範囲指定と同じこと)
戻り値はDataFrame型。

print aapl.loc['2016-09-30':'2016-10-03']
print aapl['2016-09-30':'2016-10-03']

実行結果はいずれも次。

Open High Low Close Volume \
Date
2016-09-30 112.459999 113.370003 111.800003 113.050003 36379100
2016-10-03 112.709999 113.050003 112.279999 112.519997 21701800

・locで指定年月日の特定のデータを取得する
戻り値はSeries型。

print aapl.loc['2016-09-30']

実行結果は次。

Open 1.124600e+02
High 1.133700e+02
Low 1.118000e+02
Close 1.130500e+02
Volume 3.637910e+07
Adj Close 1.124725e+02
Name: 2016-09-30 00:00:00, dtype: float64

・年月だけを指定して、指定範囲のデータを取得する

# 年月だけを指定して、指定範囲のデータを取得する
print aapl['2016-09']

実行結果は次。

Open High Low Close Volume \
Date
2016-09-26 111.639999 113.389999 111.550003 112.879997 29869400
2016-09-27 113.000000 113.180000 112.339996 113.089996 24607400
2016-09-28 113.690002 114.639999 113.430000 113.949997 29641100
2016-09-29 113.160004 113.800003 111.800003 112.180000 35887000
2016-09-30 112.459999 113.370003 111.800003 113.050003 36379100

更に、次のようにして一部分を切り取ることも可能。

print aapl['2016-09'][:3] #スライスして特定部分に絞り込む

Open High Low Close Volume \
Date
2016-09-26 111.639999 113.389999 111.550003 112.879997 29869400
2016-09-27 113.000000 113.180000 112.339996 113.089996 24607400
2016-09-28 113.690002 114.639999 113.430000 113.949997 29641100

2.指定した頻度の時系列データを作成する方法
pandas.date_rangeでfreqを指定すると良い。

#coding:utf-8
import pandas as pd
import datetime as dt
import numpy as np
import pandas_datareader.data as pdd

indexBy1min = pd.date_range(
        '2014-08-01',
        '2014-10-29 23:59:00',
        freq='T')
print indexBy1min

実行結果は次。

DatetimeIndex(['2014-08-01 00:00:00', '2014-08-01 00:01:00',
'2014-08-01 00:02:00', '2014-08-01 00:03:00',
'2014-08-01 00:04:00', '2014-08-01 00:05:00',
'2014-08-01 00:06:00', '2014-08-01 00:07:00',
'2014-08-01 00:08:00', '2014-08-01 00:09:00',
...
'2014-10-29 23:50:00', '2014-10-29 23:51:00',
'2014-10-29 23:52:00', '2014-10-29 23:53:00',
'2014-10-29 23:54:00', '2014-10-29 23:55:00',
'2014-10-29 23:56:00', '2014-10-29 23:57:00',
'2014-10-29 23:58:00', '2014-10-29 23:59:00'],
dtype='datetime64[ns]', length=129600, freq='T')

これをindexとしたpandas.Seriesデータを作成するには次のように出来る。

bymin = pd.Series(
    data = np.arange(0, 90*60*24),
    index = indexBy1min
)

printで出力した結果は次。

2014-08-01 00:00:00 0
2014-08-01 00:01:00 1
2014-08-01 00:02:00 2
2014-08-01 00:03:00 3
2014-08-01 00:04:00 4
・・・・・・・・

今回はここまで。
以下は続きで。
3.日付のoffsetsを使用して新しい日付を計算する方法
4.指定した期間で時系列データを扱う方法
5.時系列データのlagを取る方法
6.時系列データの頻度を変更する方法
7.時系列データのresampling(upsamplingとdownsampling)