Python – 4-5. pandas(DataFrame)Date

5. 日付操作

pandasにおける日付型についての理解

 dataframeでは、日付型項目をあくまでもobject型として保持している。
 dtypesメソッドで全列の型を確認(hiredate列に注目)

df_emp.dtypes
    empno         int64
    ename        object
    job          object
    manager     float64
    hiredate     object ※
    sal           int64
    comm        float64
    dept        float64
    dtype: object

列単体としてはSeries型であることを確認

type(df_emp['hiredate'])
→    <class 'pandas.core.series.Series'>

このSeriesのdtypeを取ると、「O」となっている。

df_emp['hiredate'].dtype
    dtype('O')

empnoだと「int64」というデータ型らしき値が戻る

df_emp['empno'].dtype
→    dtype('int64')

行・列を指定した、empno=7369(index=0)の要素 hiredate=1980/12/17 単体のデータ型は、、、

df_emp.loc[0,'hiredate'].dtype
→    AttributeError: 'str' object has no attribute 'dtype'

エラーになってしまう。
empnoだと、この方法でも「int64」が取れる

df_emp.loc[0,'empno'].dtype
→    dtype('int64')

type()を取ってみると、

type(df_emp.loc[0,'hiredate'])
→    <class 'str'>

⇒文字列

pd.to_datetime で明示的にdatetime/Timestamp型に変換する

pd.to_datetime(df_emp['hiredate'])
0    1980-12-17
:
14   1985-03-17
Name: hiredate, dtype: datetime64[ns]
 
pd.to_datetime(df_emp.loc[0,'hiredate'])
Timestamp('1980-12-17 00:00:00')

読込時、データ型にdatetime64型を指定することもできる。
(parse_dates=[4]で5番目の列を日付型として解析させている)

df_emp = pd.read_csv('emp.csv', encoding='utf-8', parse_dates=[4])
 
# データ型を確認(dtype)
df_emp['hiredate'].dtype
>> dtype('<M8[ns]')
 
# データ型を確認(type)
type(df_emp.loc[0,'hiredate'])
>> <class 'pandas._libs.tslibs.timestamps.Timestamp'>

文字列による期間指定での抽出(1)

hiredateが 1981/5/1 以降を抽出

SQL

SELECT * FROM emp WHERE hiredate >= TO_DATE('1981/05/01','YYYY/MM/DD')

pandas

df_emp.query('hiredate >= "1981/05/01"')

文字列による期間指定での抽出(2)

hiredateが 1981/5/1 以降、1981/12/3より前を抽出

SQL

SELECT * FROM emp
WHERE hiredate >= TO_DATE('1981/05/01','YYYY/MM/DD')
  AND hiredate < TO_DATE('1981/12/03','YYYY/MM/DD')

pandas

df_emp.query('hiredate >= "1981/05/01" and hiredate < "1981/12/03"')

dtアクセサを使用し、年を条件に抽出

SQL

SELECT * FROM emp WHERE TO_CHAR(hiredate, 'YYYY') = '1985'

pandas

df_emp[pd.to_datetime(df_emp.loc[:,'hiredate']).dt.year == 1985]

dtアクセサを使用した月を条件にした抽出

SQL

SELECT * FROM emp WHERE TO_CHAR(hiredate, 'MM') = '4'

pandas

df_emp[pd.to_datetime(df_emp.loc[:,'hiredate']).dt.month == 4]

dtアクセサを使用した日を条件にした抽出

SQL

SELECT * FROM emp WHERE TO_CHAR(hiredate, 'DD') = '17'

pandas

df_emp[pd.to_datetime(df_emp.loc[:,'hiredate']).dt.day == 17]

dtアクセサを使用した曜日を条件にした抽出

SQL

SELECT * FROM emp WHERE TO_CHAR(hiredate, 'D') = 1
※ 1:日曜日~7:土曜日

pandas

df_emp[pd.to_datetime(df_emp.loc[:,'hiredate']).dt.dayofweek == 1]
※Monday=0, Sunday=6
※isoweekday() を使うと、Monday=1, Sunday=7

dtアクセサを使用して、その月の日数を取得

SQL

SELECT emp, TO_CHAR(LASTDAY(hiredate), 'DD') FROM emp

pandas

pd.to_datetime(df_emp.loc[:,'hiredate']).dt.daysinmonth

dtアクセサを使用して、その月の1日の日付を取得

SQL

SELECT emp, TRUNC(hiredate, 'Day') FROM emp;

pandas

pd.to_datetime(df_emp.loc[:,'hiredate']).dt.round('D') # ==> OK
 
日付以外は固定的な頻度を表す周期パラメータを指定可能
pd.to_datetime(df_emp.loc[:,'hiredate']).dt.round('H') # ==> OK(時)
pd.to_datetime(df_emp.loc[:,'hiredate']).dt.round('T') # ==> OK(分)
pd.to_datetime(df_emp.loc[:,'hiredate']).dt.round('S') # ==> OK(秒)
※以下は、エラーになってしまう。
pd.to_datetime(df_emp.loc[:,'hiredate']).dt.round('MS') # ==> ng(月の開始)
pd.to_datetime(df_emp.loc[:,'hiredate']).dt.round('Y') # ==> ng(年)

日付の引き算(社員Aのhiredate-社員Bのhiredate)

SQL

SELECT emp1.hiredate - emp0.hiredate
  FROM (SELECT ,empno, hiredate FROM emp WHERE empno = 7499) as emp1
      ,(SELECT ,empno, hiredate FROM emp WHERE empno = 7369) as emp0

pandas

pd.to_datetime(df_emp.loc[1,'hiredate'])-pd.to_datetime(df_emp.loc[0,'hiredate'])

日付の計算(1日加算)

SQL

SELECT hiredate + 1 FROM emp;

pandas

datetime.timedeltaを使用する。days=1を指定して「1日間」を表す。

import datetime as dt
diffdate = dt.timedelta(days=1)
pd.to_datetime(df_emp.loc[:,'hiredate']) + diffdate

日付の計算(1ヶ月加算)

SQL

SELECT ADD_MONTHS(hiredate, 1) FROM emp;

pandas

from dateutil import relativedelta
diffdate = relativedelta.relativedelta(months=1)
pd.to_datetime(df_emp.loc[:,'hiredate']).apply(lambda x: x + diffdate)

<< 参考サイト >>
・Python で前月同日・前月末日を求める
 https://blaue-fuchs.hatenadiary.org/entry/20110814/1313310157
・Pythonで日付の加算、特にnヶ月後やn年後の日付を求める方法
 https://analytics-note.xyz/programming/relativedelta/

日付の計算(当月1日)

SQL

SELECT TRUNC(hiredate, 'MONTHS') FROM emp;

pandas

pd.to_datetime(df_emp.loc[:,'hiredate']).apply(lambda x: x.replace(day=1))

日付の計算(月末)

SQL

SELECT LASTDAY(hiredate) FROM emp;

pandas

dateutil.relativedelta を使用する。

※翌月同日を算出し、その月の1日を取得。更にその前日を算出する。
from dateutil import relativedelta
 
addmonth1 = relativedelta.relativedelta(months=1)
diffdate = relativedelta.relativedelta(days=1)
 
pd.to_datetime(df_emp.loc[:,'hiredate']) \
    .apply(lambda x:(x+addmonth1).replace(day=1) - diffdate)

日付の計算(年末)

SQL

SELECT TRUNC(ADD_MONTHS(hiredate, 12), 'YEAR')-1 FROM emp;

pandas

※翌年同日を算出し、その年の1月1日に置換。更にその前日を算出する。
from dateutil import relativedelta
 
addmonth12 = relativedelta.relativedelta(years=1)
diffdate = relativedelta.relativedelta(days=1)
 
pd.to_datetime(df_emp.loc[:,'hiredate']) \
    .apply(lambda x: (x + addmonth12) \
    .replace(month=1).replace(day=1)-diffdate)
 
※別の方式。当月1日に変換し、それに13-月数を加算して、翌年1月1日を算出。その前日で年末を求める。
pd.to_datetime(df_emp.loc[:,'hiredate']) \
   .apply(lambda x: x.replace(day=1) \
          + relativedelta.relativedelta(months=13-x.month)-diffdate)

strftime() で文字列型に変換

SQL

SELECT emp, TO_CHAR(hiredate, 'YYYY-MM-DD') FROM emp;

pandas

pd.to_datetime(df_emp.loc[:,'hiredate']).dt.strftime('%Y-%m-%d')
pd.to_datetime(df_emp.loc[:,'hiredate']).dt.strftime('%Y // %m // %d //')
pd.to_datetime(df_emp.loc[:,'hiredate']).dt.strftime('%Y年%m月%d日')
 
## ↓年月日の表示が上手くいかない場合(Windows)
pd.to_datetime(df_emp.loc[:,'hiredate']).dt.strftime('%Y年%m月%d日'.encode('unicode-escape').decode()) \
    .str.encode('cp932').str.decode("unicode-escape")

Windows環境での注意事項

Windowsの場合は、書式文字列に年月日等のマルチバイト文字列を使用した場合に、
 UnicodeEncodeError: 'locale’ codec can’t encode character '\u5e74’
が出る場合があります(’\u5e74’は「年」の文字コード表示)。
下記のような対応で解決する場合がありますが、詳細は環境等々の状況により異なりますので個別に確認ください。

chcp 65001    # Windowsの場合にコンソールのエンコーディングを指定する(戻すときはchcp 932)
python -X  utf8   # python起動時にコンソールの文字コードを指定
 
import datetime as dt
import locale
 
# localeモジュールで時間のロケールを'ja_JP.UTF-8'に変更する
locale.setlocale(locale.LC_TIME, 'ja_JP.UTF-8')
 
d = dt.date(2022, 12, 31)
print(d.strftime('%A'))  # => '土曜日'
print(d.strftime('%Y年%m月%d日'))   # ==> 2022年12月31日
# これでもダメな場合は、
print(d.strftime('%Y年%m月%d日'.encode('unicode-escape').decode()).encode().decode("unicode-escape"))

<< 参考サイト >>
【Python】曜日を取得する(英語&日本語)
 https://qiita.com/_masa_u/items/e104d42bd6f200d3b959
Windows 上の Python で UTF-8 をデフォルトにする
 https://qiita.com/methane/items/9a19ddf615089b071e71


2022-06-06

Posted by tfurukaw