⏱️

Timedelta型データのsort時の注意

に公開

Timedelta型とは,Timestamp型のデータで差をとった時に出てくる型.(他のケースもあるだろうけど)
この型を要素に持つリストをsortしたい時に欠損(NaT)があると予想外の挙動が起きる.
これの具体的な例と改善策を示す.

timedelta型が出てくるシーン

Timestamp型のデータを作成し,差をとってみる.
date_lateの型はTiemstamp型で,差をとったdeltaの型はTimedelta型.
Timedeltaはこんな感じで登場する.

from datetime import timedelta
import pandas as pd

date_late = pd.to_datetime('2025-08-24')
delta = date_late - pd.to_datetime('2025-08-20')
print(type(date_late))
print(type(delta))
print(delta)
<class 'pandas._libs.tslibs.timestamps.Timestamp'>
<class 'pandas._libs.tslibs.timedeltas.Timedelta'>
Timedelta('4 days 00:00:00')

Timedeltaをlistに格納してsortしてみる

次にTimedelta型を要素に持つlistを作成し,sortしてみる.
Timedelta型は比較可能であるため,普通にsortできる.

delta_list = [
    timedelta(days=1),
    timedelta(days=3),
    timedelta(days=2),
]
print(sorted(delta_list))
[datetime.timedelta(days=1),
 datetime.timedelta(days=2),
 datetime.timedelta(days=3)]

ちゃんと昇順になっている.

問題のケース

こんな感じに,途中にNaTが入ったデータを想定する.

delta_list = [
    timedelta(days=1),
    timedelta(days=3),
    timedelta(days=2),
    pd.NaT,
    timedelta(days=4),
    timedelta(days=5),
    timedelta(days=0),
    timedelta(days=6)
]
display(delta_list)
[datetime.timedelta(days=1),
 datetime.timedelta(days=3),
 datetime.timedelta(days=2),
 NaT,
 datetime.timedelta(days=4),
 datetime.timedelta(days=5),
 datetime.timedelta(0),
 datetime.timedelta(days=6)]

これをsortしてみる.

display(sorted(delta_list))
[datetime.timedelta(days=1),
 datetime.timedelta(days=2),
 datetime.timedelta(days=3),
 NaT,
 datetime.timedelta(0),
 datetime.timedelta(days=4),
 datetime.timedelta(days=5),
 datetime.timedelta(days=6)]

エラーになると思いきや,欠損値を区切ってsortしたような挙動になる.
これはNaTがTimedelta型との比較が可能なオブジェクトであるために起きる.

ちなみに,NaTは何と比較してもFalseを返す.

display(pd.NaT < timedelta(days=1))
display(pd.NaT < timedelta(days=0))
display(pd.NaT < timedelta(days=-1))
False
False
False

欠損値であれば比較不可能な方がいいんじゃないと思うが,なぜかこうなっている.
(なんでこのような仕様なのかご存知の方がいればご教授お願いします.)

解決策

以下に示すようにすればOK
最初に欠損値かどうかを判定して...というように,二段階でsortするようにしている.(gemini先生ありがとね.)

sorted(delta_list, key=lambda x: (pd.isna(x), x))
[datetime.timedelta(0),
 datetime.timedelta(days=1),
 datetime.timedelta(days=2),
 datetime.timedelta(days=3),
 datetime.timedelta(days=4),
 datetime.timedelta(days=5),
 datetime.timedelta(days=6),
 NaT]

要は,最初に要素が欠損であるかどうかでsortをする.
そうすると,NaTが一番最後に来るようになる.

sorted(delta_list, key=lambda x: pd.isna(x))
[datetime.timedelta(days=1),
 datetime.timedelta(days=3),
 datetime.timedelta(days=2),
 datetime.timedelta(days=4),
 datetime.timedelta(days=5),
 datetime.timedelta(0),
 datetime.timedelta(days=6),
 NaT]

その後に全体でsortをかける.
するとNaTは一番最後に来ているので,実質Timedelta型の要素だけでsortがかかる.

sorted_first = sorted(delta_list, key=lambda x: pd.isna(x)) # NaTを最後に持ってくる
sorted(sorted_first, key=lambda x: x) # 実質NaT以外でsort
[datetime.timedelta(0),
 datetime.timedelta(days=1),
 datetime.timedelta(days=2),
 datetime.timedelta(days=3),
 datetime.timedelta(days=4),
 datetime.timedelta(days=5),
 datetime.timedelta(days=6),
 NaT]

以上!!!

Discussion