pandas.DataFrame, Seriesを時系列データとして処理

Modified: | Tags: Python, pandas, 時系列データ

pandas.DataFrame, Seriesのインデックスをdatetime64[ns]型にするとDatetimeIndexとなり、時系列データとして様々な機能が使えるようになる。

ここでは、以下の内容について説明する。

  • CSV読み込み時にDatetimeIndexを設定する方法
  • 既存データの列をDatetimeIndexとして指定する方法
  • 時系列データの行を年・月・日などで選択して抽出

最後に説明するように、時系列データとして扱うと年や月で行を指定したりスライスで期間を抽出したりできて便利。

曜日や年・月ごとの合計や平均を算出するのも簡単にできるようになる。

文字列とdatetime64[ns]型との相互変換や年月日・時刻を数値として抽出する方法、タイムゾーンの扱いについては以下の記事を参照。

例として以下のCSVを使う。中身はリンクを参照されたいが、sample_date.csvYYYY-MM-DDの形式、sample_date_jp.csvYYYY年MM月DD日の形式で日時情報が書かれている。

CSV読み込み時にDatetimeIndexを設定する方法

CSVファイルをpd.read_csv()で読み込む際にDatetimeIndexを設定できる。

pd.read_csv()の引数index_colにインデックスとして使う日時データの列名(または0始まりの列番号)を指定し、引数parse_datesTrueとする。

indexDatetimeIndexとして読み込まれる。インデックスの各要素は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_datesTrueとするだけでよいが、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'>

indexDatetimeIndexpandas.DataFrame, Seriesは時系列データとして扱われ、locatで年・月・日を指定して行を抽出できる。

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), use df.loc[string] instead. - What’s new in 1.2.0 (December 26, 2020) - Deprecations — pandas 1.2.3 documentation

そのほか、asfreq()で周期的な日時(5日ごと、毎週水曜など)の行を抽出したり、at_time()で決まった時刻の行を抽出したりすることもできる。以下の記事を参照。

関連カテゴリー

関連記事