🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

3回答

931閲覧

Pythonの日付・時刻データ(datetime型)の時刻部分(日付は無視)とある固定時刻の差を効率的に計算したい

tkhk

総合スコア9

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

1クリップ

投稿2021/01/14 19:01

編集2021/01/15 07:41

前提・実現したいこと

pythonでの日付・時刻に関する演算に関する質問です。
datetimeをdt、pandasをpdと書きます。

具体的には、dt.datetime型の値(年、月、日、時、分、秒)を持つpd.Seriesがあるとします。
それらの日付・時刻の値の時刻部分(時、分、秒)に対して、それらの時刻より遅いある決まった時刻(例えば、22時0分0秒)からの差分を計算し、秒単位で求めたいです。

一旦差分を算出できれば、.total_seconds()で秒換算すれば良いと思うのですが、効率的に差分を計算するやり方が分かりません。

試したこと

例えば、
endtime = (22,0,0)
date = pd.Seriesの日付データ
として forループで
tmp = dt.datetime.combine(date_[i], dt.time(*endtime)) - data[i]
などとdatetime型の差として計算する関数を作って計算してみましたが、非常に時間がかかるので途中で計算を中止させました。

以下のようにできれば楽なのですが、dt.datetimeはpd.Seriesを引数に取れないようです

Python

1dt.datetime(date.dt.year, date.dt.month, date.dt.day, *endtime) - date

(追記)
昨日は、時間がかかって途中でやめてしまったのですが、プログラムを一部修正して改めて実施したところ、一応、計算結果が出ました。

Python

1def remtime(data, date_column, endtime): 2 """ 3 data: pd.DataFrameのデータ 4 date_column: dataの中のdt.datetime型の値(年、月、日、時、分、秒)を持つカラム 5 endtime: date_columnの時刻の値と比較したい固定した時刻(dt.time型)を作成するためのパラメータのタプル(時、分、秒) 6 return: dataの中に、endtimeとdate_columnの差分を秒換算した値を持つ新しいカラム'ddelta'を加えて返す 7 """ 8 date = data[date_column].dt.date 9 data['ddelta'] = 0 10 for i in range(data.shape[0]): 11 tmp = dt.datetime.combine(date.iloc[i], dt.time(*endtime)) - data[date_column].iloc[i] 12 data['ddelta'].iloc[i] = tmp.total_seconds() 13 return data

行数390万件程度のデータで上記関数で計算させた際の計算時間は、以下の通りでした:
CPU times: user 1min 49s, sys: 1.89 s, total: 1min 51s
Wall time: 1min 49s

補足情報(FW/ツールのバージョンなど)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

y_waiwai

2021/01/14 23:22

ふつーにdatetime通しの差をもとめればいいかとおもいますが、 非常に時間がかかるというのはどんだけの時間がかかるんでしょう
tkhk

2021/01/15 07:03 編集

質問ありがとうございます。 昨日は数分間結果が返ってこなかったような気がしますが、プログラムを修正して改めて実施したところ、1分程度で返ってきました。行数390万件程度のデータです。 投稿した質問に、説明を補足し、新しく試したプログラムも追記させていただいきました。
guest

回答3

0

ベストアンサー

最終的に欲しいのが秒とのことなので、シリーズの時刻部分を秒に変換して計算してしまうのもよいと思います。

python

1endtime = (22, 0, 0) 2endtime_sec = endtime[0] * 3600 + endtime[1] * 60 + endtime[2] 3 4diff_sec = endtime_sec - (data.dt.hour * 3600 + data.dt.minute * 60 + data.dt.second)

投稿2021/01/15 03:55

bsdfan

総合スコア4794

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tkhk

2021/01/15 05:00 編集

ありがとうございます。 投稿した質問に、説明を補足し、新しく試したプログラムも追記させていただいきました。 いただいたコード例も試してみます。後ほどまた追記します。
tkhk

2021/01/15 07:11

いただいたコード例を参考に、以下の関数を実装しました: def remtime2(data, date_column, endtime): endtime_sec = endtime[0] * 3600 + endtime[1] * 60 + endtime[2] diff_sec = endtime_sec - (data[date_column].dt.hour * 3600 + data[date_column].dt.minute * 60 + data[date_column].dt.second) return diff_sec こちらの関数は、質問に追記した関数に比べ、DataFrameの処理をしない、forループを使わない、秒換算差分の計算のみを返す、といった要因のせいと思いますが、結果がかなり早く返ってきます。以下は390万件ほどのデータに対する計算時間の結果です: CPU times: user 154 ms, sys: 48 µs, total: 154 ms Wall time: 147 ms 貴方のコメントをベストアンサーにさせていただきたいと思います。 ありがとうございました。
guest

0

こういうことでしょうか……

python

1In [11]: s = pd.Series(pd._testing.makeDateIndex() + pd._testing.makeTimedeltaIndex(freq='H')) 2 3In [12]: s 4Out[12]: 50 2000-01-04 00:00:00 61 2000-01-05 01:00:00 72 2000-01-06 02:00:00 83 2000-01-07 03:00:00 94 2000-01-08 04:00:00 105 2000-01-11 05:00:00 116 2000-01-12 06:00:00 127 2000-01-13 07:00:00 138 2000-01-14 08:00:00 149 2000-01-15 09:00:00 15dtype: datetime64[ns] 16 17In [13]: endtime = (22, 0, 0) 18 ...: c = 24*60*60 19 ...: (dt.datetime(2021, 1, 15, *endtime) - s).dt.total_seconds() % c 20Out[13]: 210 79200.0 221 75600.0 232 72000.0 243 68400.0 254 64800.0 265 61200.0 276 57600.0 287 54000.0 298 50400.0 309 46800.0 31dtype: float64

投稿2021/01/15 03:00

編集2021/01/15 04:01
kirara0048

総合スコア1399

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tkhk

2021/01/15 07:19 編集

回答ありがとうございます。 質問の説明が不十分で申し訳ありませんでした。 endtimeで指定する時刻も、差分を取る時系列の日付に応じて日付を変化させて、差分を取りたいと考えています。 質問の投稿に説明の補足や新しく試した内容について、追記させていただきました。
guest

0

差分の秒数は以下のように求められます。

python

1>>> a = dt.datetime(2021, 1, 15, 16, 15, 20) 2>>> s = (dt.timedelta(hours=22,minutes=0,seconds=0)-(a-dt.datetime.fromordinal(a.toordinal()))).seconds 3>>> print(s) 420680

投稿2021/01/14 23:58

ppaul

総合スコア24670

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tkhk

2021/01/15 04:43

ありがとうございます。 コード例が一見して分からないので、試して理解した後に改めて追記させていただきます。
tkhk

2021/01/15 06:52 編集

いただいたコメントを参考に以下の関数を作って計算させたのですが、2〜3分程度待っても終わらないので途中でやめてしまいました。しかし、1個のレコードについては、欲しい計算ができることを確認しています。ありがとうございました。 def remtime3(data, date_column, endtime): endtime_ = dt.timedelta(hours=endtime[0], minutes=endtime[1], seconds=endtime[2]) data['ddelta'] = 0 for i in range(data.shape[0]): ddelta = endtime_ - (data[date_column].iloc[i] - dt.datetime.fromordinal(data[date_column].iloc[i].toordinal())) data['ddelta'].iloc[i] = ddelta.total_seconds return data (追記) 上記の当方作成コードは不具合があったようです。エラーが出ていました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問