note.nkmk.me

pandasで日付・時間の列を処理(文字列変換、年月日抽出など)

Date: 2018-05-10 / tags: Python, pandas
このエントリーをはてなブックマークに追加

pandas.DataFrameの日時(日付・時間)を表した列を操作する方法を説明する。文字列とdatetime64型との相互変換、年月日、時刻を数値として抽出する方法など。

datetime64型をインデックスに指定し、時系列データとして処理する方法およびその活用法は以下の記事を参照。

以下のcsvファイルを読み込んだpandas.DataFrameを例とする。なお、datetimeは説明のためにインポートしているだけなので、処理自体には不要。

import pandas as pd
import datetime

df = pd.read_csv('data/src/sample_datetime_multi.csv')

print(df)
#                   A                   B
# 0  2017-11-01 12:24   2017年11月1日 12時24分
# 1  2017-11-18 23:00  2017年11月18日 23時00分
# 2   2017-12-05 5:05    2017年12月5日 5時05分
# 3   2017-12-22 8:54   2017年12月22日 8時54分
# 4  2018-01-08 14:20    2018年1月8日 14時20分
# 5  2018-01-19 20:01   2018年1月19日 20時01分

いずれの列もobject型(各要素は文字列str型)。

print(df.dtypes)
# A    object
# B    object
# dtype: object

print(type(df['A'][0]))
# <class 'str'>

以下の内容について説明する。

  • 文字列をdatetime64型(Timestamp型)に変換: to_datetime()
  • Timestamp型の属性・メソッド
  • dt属性で列全体を一括処理
    • 年月日、曜日などを抽出
    • 任意のフォーマットで日時を文字列に変換
    • Pythonのdataframe型、NumPyのdatetime64型の配列に変換
    • dt属性に用意されていないメソッドの場合
  • DatetimeIndexの場合
  • ファイルからの読み込み時に文字列をdatetime64型に変換
スポンサーリンク

文字列をdatetime64型(Timestamp型)に変換: to_datetime()

pandas.to_datetime()関数を使うと、日時(日付・時間)を表した文字列の列pandas.Seriesdatetime64[ns]型に変換できる。

print(pd.to_datetime(df['A']))
# 0   2017-11-01 12:24:00
# 1   2017-11-18 23:00:00
# 2   2017-12-05 05:05:00
# 3   2017-12-22 08:54:00
# 4   2018-01-08 14:20:00
# 5   2018-01-19 20:01:00
# Name: A, dtype: datetime64[ns]

標準的な書式でない場合は、引数formatに書式文字列を指定する。使用できる書式化コードは以下の公式ドキュメントを参照。

print(pd.to_datetime(df['B'], format='%Y年%m月%d日 %H時%M分'))
# 0   2017-11-01 12:24:00
# 1   2017-11-18 23:00:00
# 2   2017-12-05 05:05:00
# 3   2017-12-22 08:54:00
# 4   2018-01-08 14:20:00
# 5   2018-01-19 20:01:00
# Name: B, dtype: datetime64[ns]

元の書式が違っていても指し示す日時が同じであればdatetime64[ns]型の値は等価。

print(pd.to_datetime(df['A']) == pd.to_datetime(df['B'], format='%Y年%m月%d日 %H時%M分'))
# 0    True
# 1    True
# 2    True
# 3    True
# 4    True
# 5    True
# dtype: bool

datetime64[ns]型に変換した列をpandas.DataFrameに新たな列として追加したい場合は、新たな列名を指定して代入すればよい。元の列名を指定すると上書きされる。

df['X'] = pd.to_datetime(df['A'])

print(df)
#                   A                   B                   X
# 0  2017-11-01 12:24   2017年11月1日 12時24分 2017-11-01 12:24:00
# 1  2017-11-18 23:00  2017年11月18日 23時00分 2017-11-18 23:00:00
# 2   2017-12-05 5:05    2017年12月5日 5時05分 2017-12-05 05:05:00
# 3   2017-12-22 8:54   2017年12月22日 8時54分 2017-12-22 08:54:00
# 4  2018-01-08 14:20    2018年1月8日 14時20分 2018-01-08 14:20:00
# 5  2018-01-19 20:01   2018年1月19日 20時01分 2018-01-19 20:01:00

Timestamp型の属性・メソッド

pandas.to_datetime()関数で変換した列のdtypedatetime64[ns]型で、各要素はTimestamp型となる。

print(df)
#                   A                   B                   X
# 0  2017-11-01 12:24   2017年11月1日 12時24分 2017-11-01 12:24:00
# 1  2017-11-18 23:00  2017年11月18日 23時00分 2017-11-18 23:00:00
# 2   2017-12-05 5:05    2017年12月5日 5時05分 2017-12-05 05:05:00
# 3   2017-12-22 8:54   2017年12月22日 8時54分 2017-12-22 08:54:00
# 4  2018-01-08 14:20    2018年1月8日 14時20分 2018-01-08 14:20:00
# 5  2018-01-19 20:01   2018年1月19日 20時01分 2018-01-19 20:01:00

print(df.dtypes)
# A            object
# B            object
# X    datetime64[ns]
# dtype: object

print(df['X'][0])
# 2017-11-01 12:24:00

print(type(df['X'][0]))
# <class 'pandas._libs.tslib.Timestamp'>

Timestamp型はPythonの標準ライブラリdatetimedatetime型を継承し拡張した型。

print(issubclass(pd.Timestamp, datetime.datetime))
# True

年月日(year, month, day)、時分秒(hour, minute, second)、曜日(文字列: weekday_name, 数値: dayofweek)などを属性として取得できる。

print(df['X'][0].year)
# 2017

print(df['X'][0].weekday_name)
# Wednesday

また、to_pydatetime()でPython標準ライブラリのdatetime型、to_datetime64()でNumPyのdatetime64型に変換したりもできる。

py_dt = df['X'][0].to_pydatetime()
print(type(py_dt))
# <class 'datetime.datetime'>

dt64 = df['X'][0].to_datetime64()
print(type(dt64))
# <class 'numpy.datetime64'>

timestamp()はUNIX時間(エポック秒 = 1970年1月1日0時0分0秒からの秒数)を浮動小数点float型で返すメソッド。整数にしたいときはint()を使う。

print(df['X'][0].timestamp())
# 1509539040.0

print(pd.to_datetime('1970-01-01 00:00:00').timestamp())
# 0.0

print(int(df['X'][0].timestamp()))
# 1509539040

Python標準ライブラリのdatetime型と同様にstrftime()で任意のフォーマットの文字列に変換することも可能。列の要素すべてに適用する方法は後述。

print(df['X'][0].strftime('%Y/%m/%d'))
# 2017/11/01

dt属性で列全体を一括処理

pandas.Series全体に文字列処理を適用するstr属性がある。

同様に、pandas.Series全体に日時処理を適用するdt属性がある。

年月日、曜日などを抽出

Timestamp型と同様に、年月日(year, month, day)、時分秒(hour, minute, second)、曜日(文字列: weekday_name, 数値: dayofweek)などを属性として取得できる。

dtのあとに各属性名を記述する。

pandas.Seriesの各要素が処理され、pandas.Seriesが返る。

print(df['X'].dt.year)
# 0    2017
# 1    2017
# 2    2017
# 3    2017
# 4    2018
# 5    2018
# Name: X, dtype: int64

print(df['X'].dt.hour)
# 0    12
# 1    23
# 2     5
# 3     8
# 4    14
# 5    20
# Name: X, dtype: int64

dayofweek(月曜が0, 日曜が6)を利用して、特定の曜日の行のみを抽出したりすることも可能。

print(df['X'].dt.dayofweek)
# 0    2
# 1    5
# 2    1
# 3    4
# 4    0
# 5    4
# Name: X, dtype: int64

print(df[df['X'].dt.dayofweek == 4])
#                   A                  B                   X
# 3   2017-12-22 8:54  2017年12月22日 8時54分 2017-12-22 08:54:00
# 5  2018-01-19 20:01  2018年1月19日 20時01分 2018-01-19 20:01:00

任意のフォーマットで日時を文字列に変換

datetime64[ns]型の列をastype()メソッドで文字列str型に変換すると、標準的な書式で文字列に変換される。

print(df['X'].astype(str))
# 0    2017-11-01 12:24:00
# 1    2017-11-18 23:00:00
# 2    2017-12-05 05:05:00
# 3    2017-12-22 08:54:00
# 4    2018-01-08 14:20:00
# 5    2018-01-19 20:01:00
# Name: X, dtype: object

dt.strftime()で列を一括で任意のフォーマットの文字列に変換できる。日付のみや時刻のみの文字列にすることも可能。

print(df['X'].dt.strftime('%A, %B %d, %Y'))
# 0    Wednesday, November 01, 2017
# 1     Saturday, November 18, 2017
# 2      Tuesday, December 05, 2017
# 3       Friday, December 22, 2017
# 4        Monday, January 08, 2018
# 5        Friday, January 19, 2018
# Name: X, dtype: object

print(df['X'].dt.strftime('%Y年%m月%d日'))
# 0    2017年11月01日
# 1    2017年11月18日
# 2    2017年12月05日
# 3    2017年12月22日
# 4    2018年01月08日
# 5    2018年01月19日
# Name: X, dtype: object

文字列に変換した列をpandas.DataFrameに新たな列として追加したい場合は、新たな列名を指定して代入すればよい。元の列名を指定すれば上書きされる。

df['en'] = df['X'].dt.strftime('%A, %B %d, %Y')
df['jp'] = df['X'].dt.strftime('%Y年%m月%d日')

print(df)
#                   A                   B                   X  \
# 0  2017-11-01 12:24   2017年11月1日 12時24分 2017-11-01 12:24:00   
# 1  2017-11-18 23:00  2017年11月18日 23時00分 2017-11-18 23:00:00   
# 2   2017-12-05 5:05    2017年12月5日 5時05分 2017-12-05 05:05:00   
# 3   2017-12-22 8:54   2017年12月22日 8時54分 2017-12-22 08:54:00   
# 4  2018-01-08 14:20    2018年1月8日 14時20分 2018-01-08 14:20:00   
# 5  2018-01-19 20:01   2018年1月19日 20時01分 2018-01-19 20:01:00   
#                              en           jp  
# 0  Wednesday, November 01, 2017  2017年11月01日  
# 1   Saturday, November 18, 2017  2017年11月18日  
# 2    Tuesday, December 05, 2017  2017年12月05日  
# 3     Friday, December 22, 2017  2017年12月22日  
# 4      Monday, January 08, 2018  2018年01月08日  
# 5      Friday, January 19, 2018  2018年01月19日  

Pythonのdataframe型、NumPyのdatetime64型の配列に変換

dt.to_pydatetime()でPython標準ライブラリのdatetime型のオブジェクトを要素とするNumPy配列ndarrayを取得できる。

print(df['X'].dt.to_pydatetime())
# [datetime.datetime(2017, 11, 1, 12, 24)
#  datetime.datetime(2017, 11, 18, 23, 0)
#  datetime.datetime(2017, 12, 5, 5, 5)
#  datetime.datetime(2017, 12, 22, 8, 54)
#  datetime.datetime(2018, 1, 8, 14, 20)
#  datetime.datetime(2018, 1, 19, 20, 1)]

print(type(df['X'].dt.to_pydatetime()))
print(type(df['X'].dt.to_pydatetime()[0]))
# <class 'numpy.ndarray'>
# <class 'datetime.datetime'>

NumPyのdatetime64型の配列はメソッドではなくvalues属性で取得できる。

print(df['X'].values)
# ['2017-11-01T12:24:00.000000000' '2017-11-18T23:00:00.000000000'
#  '2017-12-05T05:05:00.000000000' '2017-12-22T08:54:00.000000000'
#  '2018-01-08T14:20:00.000000000' '2018-01-19T20:01:00.000000000']

print(type(df['X'].values))
print(type(df['X'].values[0]))
# <class 'numpy.ndarray'>
# <class 'numpy.datetime64'>

dt属性に用意されていないメソッドの場合

例えば、UNIX時間(エポック秒)を返すメソッド(timestamp())はTimestamp型にはあるが、dt属性には用意されていない。そのような場合はmap()を使えばOK。

print(df['X'].map(pd.Timestamp.timestamp))
# 0    1.509539e+09
# 1    1.511046e+09
# 2    1.512450e+09
# 3    1.513933e+09
# 4    1.515421e+09
# 5    1.516392e+09
# Name: X, dtype: float64

整数int型に変換したい場合はastype()メソッドを使う。

print(df['X'].map(pd.Timestamp.timestamp).astype(int))
# 0    1509539040
# 1    1511046000
# 2    1512450300
# 3    1513932840
# 4    1515421200
# 5    1516392060
# Name: X, dtype: int64

DatetimeIndexの場合

datetime64型の列をインデックスに指定すると、そのインデックスはDatetimeIndex型となる。

時系列データを処理する場合にはとても便利。詳細は以下の記事を参照。

例では、set_index()で既存の列をインデックスに指定し、便宜上、余分な列をdrop()メソッドで削除している。

df_i = df.set_index('X').drop(['en', 'jp'], axis=1)

print(df_i)
#                                     A                   B
# X                                                        
# 2017-11-01 12:24:00  2017-11-01 12:24   2017年11月1日 12時24分
# 2017-11-18 23:00:00  2017-11-18 23:00  2017年11月18日 23時00分
# 2017-12-05 05:05:00   2017-12-05 5:05    2017年12月5日 5時05分
# 2017-12-22 08:54:00   2017-12-22 8:54   2017年12月22日 8時54分
# 2018-01-08 14:20:00  2018-01-08 14:20    2018年1月8日 14時20分
# 2018-01-19 20:01:00  2018-01-19 20:01   2018年1月19日 20時01分

print(df_i.index)
# DatetimeIndex(['2017-11-01 12:24:00', '2017-11-18 23:00:00',
#                '2017-12-05 05:05:00', '2017-12-22 08:54:00',
#                '2018-01-08 14:20:00', '2018-01-19 20:01:00'],
#               dtype='datetime64[ns]', name='X', freq=None)

DatetimeIndex型のインデックスには年月日(year, month, day)、時分秒(hour, minute, second)、曜日(文字列: weekday_name, 数値: dayofweek)などの属性や、strftime()などのメソッドが用意されているため、dt属性を介さずにインデックスの要素を一括で処理できる。

返る型はpandas.Seriesでなく属性やメソッドによって異なるが、pandas.DataFrameに新たな列として追加する場合は、新たな列名を指定して代入すればよい。

print(df_i.index.minute)
# Int64Index([24, 0, 5, 54, 20, 1], dtype='int64', name='X')

print(df_i.index.strftime('%y/%m/%d'))
# ['17/11/01' '17/11/18' '17/12/05' '17/12/22' '18/01/08' '18/01/19']

df_i['min'] = df_i.index.minute
df_i['str'] = df_i.index.strftime('%y/%m/%d')

print(df_i)
#                                     A                   B  min       str
# X                                                                       
# 2017-11-01 12:24:00  2017-11-01 12:24   2017年11月1日 12時24分   24  17/11/01
# 2017-11-18 23:00:00  2017-11-18 23:00  2017年11月18日 23時00分    0  17/11/18
# 2017-12-05 05:05:00   2017-12-05 5:05    2017年12月5日 5時05分    5  17/12/05
# 2017-12-22 08:54:00   2017-12-22 8:54   2017年12月22日 8時54分   54  17/12/22
# 2018-01-08 14:20:00  2018-01-08 14:20    2018年1月8日 14時20分   20  18/01/08
# 2018-01-19 20:01:00  2018-01-19 20:01   2018年1月19日 20時01分    1  18/01/19

ファイルからの読み込み時に文字列をdatetime64型に変換

データをファイルから読み込む場合は、読み込み時に文字列をdatetime64型に変換できる。

pandas.read_csv()関数の場合は引数parse_datesdatetime64型に変換したい列番号をリストで指定する。一つだけの場合もリストにする必要があるので注意。

df_csv = pd.read_csv('data/src/sample_datetime_multi.csv', parse_dates=[0])

print(df_csv)
#                     A                   B
# 0 2017-11-01 12:24:00   2017年11月1日 12時24分
# 1 2017-11-18 23:00:00  2017年11月18日 23時00分
# 2 2017-12-05 05:05:00    2017年12月5日 5時05分
# 3 2017-12-22 08:54:00   2017年12月22日 8時54分
# 4 2018-01-08 14:20:00    2018年1月8日 14時20分
# 5 2018-01-19 20:01:00   2018年1月19日 20時01分

print(df_csv.dtypes)
# A    datetime64[ns]
# B            object
# dtype: object

標準的な書式ではない場合は引数date_parserに変換する関数を指定する。ここでは無名関数(ラムダ式)で定義している。

df_csv_jp = pd.read_csv('data/src/sample_datetime_multi.csv',
                        parse_dates=[1],
                        date_parser=lambda date: pd.to_datetime(date, format='%Y年%m月%d日 %H時%M分'))

print(df_csv_jp)
#                   A                   B
# 0  2017-11-01 12:24 2017-11-01 12:24:00
# 1  2017-11-18 23:00 2017-11-18 23:00:00
# 2   2017-12-05 5:05 2017-12-05 05:05:00
# 3   2017-12-22 8:54 2017-12-22 08:54:00
# 4  2018-01-08 14:20 2018-01-08 14:20:00
# 5  2018-01-19 20:01 2018-01-19 20:01:00

print(df_csv_jp.dtypes)
# A            object
# B    datetime64[ns]
# dtype: object

引数index_colでインデックスとする列を指定できる。

その場合、引数parse_dates=Trueとするとインデックスの列がdatetime64型に変換される。

df_csv_jp_i = pd.read_csv('data/src/sample_datetime_multi.csv',
                          index_col=1,
                          parse_dates=True,
                          date_parser=lambda date: pd.to_datetime(date, format='%Y年%m月%d日 %H時%M分'))

print(df_csv_jp_i)
#                                     A
# B                                    
# 2017-11-01 12:24:00  2017-11-01 12:24
# 2017-11-18 23:00:00  2017-11-18 23:00
# 2017-12-05 05:05:00   2017-12-05 5:05
# 2017-12-22 08:54:00   2017-12-22 8:54
# 2018-01-08 14:20:00  2018-01-08 14:20
# 2018-01-19 20:01:00  2018-01-19 20:01

print(df_csv_jp_i.index)
# DatetimeIndex(['2017-11-01 12:24:00', '2017-11-18 23:00:00',
#                '2017-12-05 05:05:00', '2017-12-22 08:54:00',
#                '2018-01-08 14:20:00', '2018-01-19 20:01:00'],
#               dtype='datetime64[ns]', name='B', freq=None)

エクセルファイルを読み込むpandas.read_excel()関数にも引数parse_dates, date_parser, index_colがあるので、同様に読み込み時に変換できる。pandas.read_excel()関数については以下の記事を参照。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事