SE_BOKUのまとめノート的ブログ

SE_BOKUが知ってること・勉強したこと・考えたことetc

EXCELのシリアル値(日付・時刻)をPythonのdatetimeにする

目次

EXCELのシリアル値(日付・時刻)をPythonのdatetimeにする

データ分析を頼まれて、CSVファイルを受け取ったところ、日付・時刻がEXCEL形式の数値のままはいってた::というケースです。

例えば、文字列で「2023/10/27  17:15:10」の日付・時刻は「45226.71887」です。

少ない量なら、EXCELで開いて書式設定→値のみコピーしてCSVを上書きする・・みたいにコツコツやる手もありますが、数百ファイルもあると、やる気になりません。

EXCELの日付・時刻

EXCELの日付時刻は

  • 整数部(シリアル値)=日付
  • 少数分=時刻

を表します。

上記の「45226.71887」であれば「45226 = 2023/10/27」です。

そして「0.71887 = 17:15:10」を表します。

日付のシリアル値は「1900/1/0」を「0」とする日数です。

時刻は24時間(86400秒)で、経過秒数を割った値です。

上記の「17:15:10」は「(17*3600)+(15*60)+10 = 62110秒」なので、

62110 / 86400 = 0.718865740 。

これを、小数点以下5桁にまるめて「0.71887」となってます。

Pythonのdatetime

データ分析をするには、上記のシリアル日付・時刻を、Pythonのdatetimeに変換する必要があります。

まずはシリアル値をPythonのdatetimeで扱える差分形式に変換します。

例は上記の「45226.71887」を使います。

import datetime

td = datetime.timedelta(45226.71887)

datetime.timedeltaは、date, time, あるいは datetime クラスの二つのインスタンス間の時間差をマイクロ秒精度で表す経過時間値です。

Excelのシリアル日付・時刻を引数に渡して、datetime.timedeltaに変換します。

これを「print」で表示すると、こんな感じです。

45226 days, 17:15:10.368000

日付の整数部と時刻の小数部がわけて管理されてます。

でもって、EXCELのシリアル値が「1900/1/1」が1となっているので、これに経過時間地を足せばよさそうなものですが、実際にやってみると。

dt = (datetime.datetime(1900,1,1) + td).strftime('%Y/%m/%d %H:%M:%S')
print(dt)

2023/10/29 17:15:10

正解は「2023/10/27 17:15:10」なので2日間ずれてます。

なので、基準の日を2日前にずらしてみると

dt = (datetime.datetime(1899,12,30) + td).strftime('%Y/%m/%d %H:%M:%S')
print(dt)

2023/10/27 17:15:10

ばっちりです。

pandasだとめんどいので無理にやろうとしない

実は「pandas」と、このExcelのシリアル日付・時刻の値は相性が悪いです。

例えば、もらったCSVデータの「datetime」がこのシリアル日付・時刻になっていたとします。

import pandas as pd
import datetime as dt

tran = pd.read_csv("./work.csv")
tran.head()

この場合、形式的には少数値なので、dtypesでみると

みたいに「float64」になりますが、こうなると、to_datetimeみたいな、pandasの便利関数で「エラー」になってしまうし、エラーを回避するために「int64」なんかに変換すると時刻の情報が消えてしまうので非常にイライラします。

なので、こういうデータをもらうと、まず、元ファイルを上記の方法を使って「2023/10/27 17:15:10」のような日付時刻文字列に書き直してしまいます。

こんな感じに。

こうすれば、普通に「object」になるので「datetime」に変換するなりして、どうとでもなりますし、何より、一回プログラムを作っておけば、ちょこっと書き換えて、フォルダまるごと一括返還かけるだけなので、500個くらいあってもすぐですしね。

なんでもpandasでこねくりまわすより効率的・・だと個人的には思ってます。

ではでは。