【Python】日付の揺れを統一したい
・お題:PandasのDataframeで日付を含む表を入手したものの、日付の表記が様々ある。統一したい。
・DataFrameを作成する。
import pandas as pd
a1=["2022/1/1","2022/01/01","2022.1.1","2022.01.01","2022-1-1","2022-01-01"]
df1=pd.DataFrame(a1,columns=["a1"])
・4桁年月日になっている。
・pd.to_datetimeを使う。DataFrameを放り込むのは難しく、SeriesならOK見たい。
pd.to_datetime(df1["a1"])
で、以下が返ってくる。
0 2022-01-01
1 2022-01-01
2 2022-01-01
3 2022-01-01
4 2022-01-01
5 2022-01-01
Name: a1, dtype: datetime64[ns]
・df1を統一したいなら、以下のような感じ。
df1["a1"]=pd.to_datetime(df1["a1"])
・一応はこれでうまくいった。だが、実際にデータを眺めてみると、日付の表記にもっとバリエーションがあった。
import pandas as pd
a2=["2022/1/1","2022/01/01","2022.1.1","2022.01.01","2022-1-1","2022-01-01","2022年1月1日","2022年01月01日","2022年1月1日","2022年01月01日","22/1/1","22/01/01","22.1.1","22.01.01","22-1-1","22-01-01","22年1月1日","22年01月01日","22年1月1日","22年01月01日","20220101","220101"]
df2=pd.DataFrame(a2,columns=["a2"])
・4桁年月日、だけではなく、2桁年月日、全角半角のバリエーションも想定される。
・まず、これを先ほどの関数に入れると、エラーが返ってくる。
ParserError: Unknown string format: 2022年1月1日
・年、月、日がエラーになる。いったんエラーを無視する。
pd.to_datetime(df2["a2"],errors="coerce")
0 2022-01-01
1 2022-01-01
2 2022-01-01
3 2022-01-01
4 2022-01-01
5 2022-01-01
6 NaT
7 NaT
8 NaT
9 NaT
10 2001-01-22
11 2001-01-22
12 2001-01-22
13 2001-01-22
14 2001-01-22
15 2001-01-22
16 NaT
17 NaT
18 NaT
19 NaT
20 2022-01-01
21 2001-01-22
Name: a2, dtype: datetime64[ns]
・先ほどうまくいったものは今回もうまくいった。先ほどエラーになりそうなものは、NaTになった。一見うまくいきそうでうまくいかなかったのが、2桁年月日のパターンで、日付が変わってしまった。これは2桁年月日が年月日の順番で読み込まれていないことが原因ぽい。
pd.to_datetime(df2["a2"],yearfirst=True,errors='coerce')
0 2022-01-01
1 2022-01-01
2 2022-01-01
3 2022-01-01
4 2022-01-01
5 2022-01-01
6 NaT
7 NaT
8 NaT
9 NaT
10 2022-01-01
11 2022-01-01
12 2022-01-01
13 2022-01-01
14 2022-01-01
15 2022-01-01
16 NaT
17 NaT
18 NaT
19 NaT
20 2022-01-01
21 2022-01-01
Name: a2, dtype: datetime64[ns]
・次に、NaTを処理する。
pd.to_datetime(df2["a2"],format="%Y年%m月%d日",errors='coerce')
0 NaT
1 NaT
2 NaT
3 NaT
4 NaT
5 NaT
6 2022-01-01
7 2022-01-01
8 NaT
9 NaT
10 NaT
11 NaT
12 NaT
13 NaT
14 NaT
15 NaT
16 NaT
17 NaT
18 NaT
19 NaT
20 NaT
21 NaT
Name: a2, dtype: datetime64[ns]
・日付が全角になっている場合はうまくいかない。全角を半角に変換したい。以下の記事を参考にさせて頂いた。
・全角英数字を半角にする。
ZenHan=lambda x:x.translate(str.maketrans({chr(0xFF01 + i): chr(0x21 + i) for i in range(94)}))
df2["a2"]=df2["a2"].apply(ZenHan)
・これで、NaTの4桁年月日は解決しそう。まだ2桁年月日は解決していない。2桁年月日を処理したい場合は、以下のようにformatの年のYをyに変える。
pd.to_datetime(df2["a2"],format="%y年%m月%d日",errors='coerce')
・以上を組み合わせて処理してみる。方針としては、①とりあえず半角に変換、②デフォルトのフォーマットで処理できそうなところは処理、③エラーで弾かれた4桁年月日を処理、④さらにエラーで弾かれた2桁年月日を処理、という感じでいってみた。
#全角英数を半角にする。
ZenHan=lambda x:x.translate(str.maketrans({chr(0xFF01 + i): chr(0x21 + i) for i in range(94)}))
df2["a2"]=df2["a2"].apply(ZenHan)
#df3を変換後のDataFrameとして利用
df3=df2.copy()
#デフォルトの処理
df3["a2"]=pd.to_datetime(df2["a2"],yearfirst=True,errors='coerce')
#df3でエラーが出ているところに、df2のフォーマットを指定して処理した結果を挿入×2回
df3["a2"]=df3["a2"].fillna(pd.to_datetime(df2["a2"],format="%Y年%m月%d日", errors="coerce"))
df3["a2"]=df3["a2"].fillna(pd.to_datetime(df2["a2"],format="%y年%m月%d日", errors="coerce"))
#df3をdf2に代入
df2=df3.copy()
・どうやらうまくいったらしい。
おわり。