pandas.DataFrame, Seriesを時系列データとして処理
pandas.DataFrame
, Series
のインデックスをdatetime64[ns]
型にするとDatetimeIndex
となり、時系列データとして様々な機能が使えるようになる。
ここでは、以下の内容について説明する。
- CSV読み込み時に
DatetimeIndex
を設定する方法 - 既存データの列を
DatetimeIndex
として指定する方法 - 時系列データの行を年・月・日などで選択して抽出
最後に説明するように、時系列データとして扱うと年や月で行を指定したりスライスで期間を抽出したりできて便利。
曜日や年・月ごとの合計や平均を算出するのも簡単にできるようになる。
文字列とdatetime64[ns]
型との相互変換や年月日・時刻を数値として抽出する方法、タイムゾーンの扱いについては以下の記事を参照。
例として以下のCSVを使う。中身はリンクを参照されたいが、sample_date.csv
はYYYY-MM-DD
の形式、sample_date_jp.csv
はYYYY年MM月DD日
の形式で日時情報が書かれている。
CSV読み込み時にDatetimeIndexを設定する方法
CSVファイルをpd.read_csv()
で読み込む際にDatetimeIndex
を設定できる。
pd.read_csv()
の引数index_col
にインデックスとして使う日時データの列名(または0始まりの列番号)を指定し、引数parse_dates
をTrue
とする。
index
がDatetimeIndex
として読み込まれる。インデックスの各要素はTimestamp
型。
import pandas as pd
print(pd.__version__)
# 1.2.2
df = pd.read_csv('data/src/sample_date.csv',
index_col='date', parse_dates=True).head(3)
print(df)
# val_1 val_2
# date
# 2017-11-01 65 76
# 2017-11-07 26 66
# 2017-11-18 47 47
print(type(df.index))
# <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
print(df.index[0])
# 2017-11-01 00:00:00
print(type(df.index[0]))
# <class 'pandas._libs.tslibs.timestamps.Timestamp'>
なお、便宜上、head()
で先頭3行のみを使っている。スペースを省略するためで処理自体には関係ない。以降のサンプルコードでも同様。
引数parse_dates
のデフォルト値はFalse
なので、省略するとIndex
として読み込まれ各要素はただの文字列str
となってしまう。print()
での出力では見分けがつかない場合があるので注意。
df_default = pd.read_csv('data/src/sample_date.csv', index_col='date').head(3)
print(df_default)
# val_1 val_2
# date
# 2017-11-01 65 76
# 2017-11-07 26 66
# 2017-11-18 47 47
print(type(df_default.index))
# <class 'pandas.core.indexes.base.Index'>
print(df_default.index[0])
# 2017-11-01
print(type(df_default.index[0]))
# <class 'str'>
日付文字列がYYYY-MM-DD
のような標準的な形式の場合は上の例のように引数parse_dates
をTrue
とするだけでよいが、YYYY年MM月DD日
のような非標準の形式の場合はDatetimeIndex
には変換されない。このとき、エラーにはならないので注意。
df_jp_ng = pd.read_csv('data/src/sample_date_jp.csv',
index_col='date', parse_dates=True).head(3)
print(df_jp_ng)
# val_1 val_2
# date
# 2017年11月1日 65 76
# 2017年11月7日 26 66
# 2017年11月18日 47 47
print(type(df_jp_ng.index))
# <class 'pandas.core.indexes.base.Index'>
非標準形式の日付文字列の場合は、引数date_parser
にパーサーを指定する。ここでは、引数format
に書式を指定したpd.to_datetime()
を利用している。
df_jp = pd.read_csv('data/src/sample_date_jp.csv',
index_col='date', parse_dates=True,
date_parser=lambda x: pd.to_datetime(x, format='%Y年%m月%d日')).head(3)
print(df_jp)
# val_1 val_2
# date
# 2017-11-01 65 76
# 2017-11-07 26 66
# 2017-11-18 47 47
print(type(df_jp.index))
# <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
既存データの列をDatetimeIndexとして指定する方法
上の例のように読み込み時にDatetimeIndex
を設定できればそちらのほうが楽だが、既存の列をDatetimeIndex
として指定することもできる。
set_index()
説明のためにindex
を指定せずにCSVファイルを読み込む。連番のインデックスRangeIndex
として扱われる。
df = pd.read_csv('data/src/sample_date.csv').head(3)
print(df)
# date val_1 val_2
# 0 2017-11-01 65 76
# 1 2017-11-07 26 66
# 2 2017-11-18 47 47
print(type(df.index))
# <class 'pandas.core.indexes.range.RangeIndex'>
日付の文字列の列に対してpd.to_datetime()
を適用し、datetime64
型に変換する。非標準形式の日付文字列の場合は上の例のようにpd.to_datetime()
の引数format
を指定すればよい。
df['date'] = pd.to_datetime(df['date'])
print(df['date'].dtype)
# datetime64[ns]
set_index()
メソッドでdatetime64
型の列をインデックスに指定する。
これでインデックスがDatetimeIndex
となる。
df.set_index('date', inplace=True)
print(df)
# val_1 val_2
# date
# 2017-11-01 65 76
# 2017-11-07 26 66
# 2017-11-18 47 47
print(type(df.index))
# <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
set_axis()
pd.to_datetime()
で変換した列をset_axis()
でindex
に指定する方法もある。
df = pd.read_csv('data/src/sample_date.csv').head(3)
df.set_axis(pd.to_datetime(df['date']), axis='index', inplace=True)
print(df)
# date val_1 val_2
# date
# 2017-11-01 2017-11-01 65 76
# 2017-11-07 2017-11-07 26 66
# 2017-11-18 2017-11-18 47 47
print(type(df.index))
# <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
このままだと元の日付文字列の列が残ったままなので、drop()
で削除する。
df.drop(['date'], axis='columns', inplace=True)
print(df)
# val_1 val_2
# date
# 2017-11-01 65 76
# 2017-11-07 26 66
# 2017-11-18 47 47
index属性を更新
set_axis()
を使わずに、index
属性を更新することも可能。この場合も元の日付文字列の列をdrop()
で削除する。
df = pd.read_csv('data/src/sample_date.csv').head(3)
df.index = pd.to_datetime(df['date'])
df.drop(['date'], axis='columns', inplace=True)
print(df)
# val_1 val_2
# date
# 2017-11-01 65 76
# 2017-11-07 26 66
# 2017-11-18 47 47
print(type(df.index))
# <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
時系列データの行を年・月・日などで選択して抽出
以下のpandas.DataFrame
を例とする。
df = pd.read_csv('data/src/sample_date.csv', index_col='date', parse_dates=True)
print(df)
# val_1 val_2
# date
# 2017-11-01 65 76
# 2017-11-07 26 66
# 2017-11-18 47 47
# 2017-11-27 20 38
# 2017-12-05 65 85
# 2017-12-12 4 29
# 2017-12-22 31 54
# 2017-12-29 21 8
# 2018-01-03 98 76
# 2018-01-08 48 64
# 2018-01-19 18 48
# 2018-01-23 86 70
print(type(df.index))
# <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
index
がDatetimeIndex
のpandas.DataFrame
, Series
は時系列データとして扱われ、loc
やat
で年・月・日を指定して行を抽出できる。
print(df.loc['2018'])
# val_1 val_2
# date
# 2018-01-03 98 76
# 2018-01-08 48 64
# 2018-01-19 18 48
# 2018-01-23 86 70
print(df.loc['2017-11'])
# val_1 val_2
# date
# 2017-11-01 65 76
# 2017-11-07 26 66
# 2017-11-18 47 47
# 2017-11-27 20 38
スライスで期間を指定して抽出することもできる。
print(df.loc['2017-12-15':'2018-01-15'])
# val_1 val_2
# date
# 2017-12-22 31 54
# 2017-12-29 21 8
# 2018-01-03 98 76
# 2018-01-08 48 64
日時は様々な書式で指定可能。
print(df.at['01/19/2018', 'val_1'])
# 18
print(df.at['20180103', 'val_2'])
# 76
なお、以前のバージョンではdf['2018']
のように指定できていたが、バージョン1.2.0からはDeprecated(非推奨)となり、df.loc['2018']
のようにloc
の使用が推奨されるようになった。
バージョン1.2.1時点ではdf['2018']
でも警告(FutureWarning
)のみで使用可能だが将来的には使えなくなるので注意。
Deprecated indexing DataFrame rows with a single datetime-like string as
df[string]
(given the ambiguity whether it is indexing the rows or selecting a column), usedf.loc[string]
instead. - What’s new in 1.2.0 (December 26, 2020) - Deprecations — pandas 1.2.3 documentation
そのほか、asfreq()
で周期的な日時(5日ごと、毎週水曜など)の行を抽出したり、at_time()
で決まった時刻の行を抽出したりすることもできる。以下の記事を参照。