Python Pandasで時間差を扱うならこれ!to_timedeltaの全て

2025-05-26

Timedeltaとは?

まず、Timedeltaについて簡単に説明します。Timedeltaは、日付や時刻間の差、つまり「期間」を表すデータ型です。例えば、「5日間」、「3時間20分」、「1週間」といった期間を表現するのに使われます。Python標準ライブラリのdatetime.timedeltaに相当し、Pandasでは時系列データの処理において非常に重要な役割を果たします。

pandas.to_timedeltaの役割

pandas.to_timedelta関数は、以下のような様々な入力値をTimedeltaオブジェクト(またはTimedeltaIndexTimedelta型のSeries)に変換します。

  1. 文字列 (String)
    "1 day 06:05:01" や "15.5us" のような、時間差を表す文字列を直接Timedeltaに変換できます。Pandasが自動的に文字列を解析し、適切な期間として認識します。

    import pandas as pd
    
    td = pd.to_timedelta('1 day 2 hours 30 minutes')
    print(td)
    # 出力: 1 days 02:30:00
    
  2. 数値 (Integer/Float)
    数値と単位を組み合わせて期間を表現できます。例えば、unit='s'を指定すれば秒として、unit='d'を指定すれば日として扱われます。


    import pandas as pd
    
    td_seconds = pd.to_timedelta(10, unit='s') # 10秒
    print(td_seconds)
    # 出力: 0 days 00:00:10
    
    td_days = pd.to_timedelta(5, unit='d') # 5日
    print(td_days)
    # 出力: 5 days 00:00:00
    
  3. リスト、NumPy配列、Series
    複数の期間を一括で変換したい場合、文字列や数値のリスト、NumPy配列、またはPandas Seriesを渡すことができます。この場合、結果はTimedeltaIndexまたはTimedelta型のSeriesとして返されます。


    import pandas as pd
    
    # 文字列のリスト
    td_list = pd.to_timedelta(['1 hour', '30 minutes', '2 days'])
    print(td_list)
    # 出力:
    # TimedeltaIndex(['0 days 01:00:00', '0 days 00:30:00', '2 days 00:00:00'], dtype='timedelta64[ns]', freq=None)
    
    # 数値のSeries
    s = pd.Series([10, 20, 30])
    td_series = pd.to_timedelta(s, unit='m') # 分単位
    print(td_series)
    # 出力:
    # 0   0 days 00:10:00
    # 1   0 days 00:20:00
    # 2   0 days 00:30:00
    # dtype: timedelta64[ns]
    

主な引数

  • errors (オプション): 変換エラーが発生した場合の挙動を指定します。

    • 'raise' (デフォルト): エラーを発生させます。
    • 'coerce': 無効な解析をNaT (Not a Time)に設定します。
    • 'ignore': 無効な解析をそのまま入力値として返します。

    例 (errors='coerce')

    import pandas as pd
    
    td_error = pd.to_timedelta(['1 hour', 'invalid_duration', '2 days'], errors='coerce')
    print(td_error)
    # 出力:
    # TimedeltaIndex(['0 days 01:00:00', NaT, '2 days 00:00:00'], dtype='timedelta64[ns]', freq=None)
    
  • unit (オプション): argが数値の場合に、その数値の単位を指定します。'D' (日), 'h' (時間), 'm' (分), 's' (秒), 'ms' (ミリ秒), 'us' (マイクロ秒), 'ns' (ナノ秒) などが指定可能です。デフォルトは'ns'(ナノ秒)です。

  • arg: 変換したい値(文字列、数値、リスト、Seriesなど)。

  • 堅牢な解析
    不明瞭な時間表現や、異なる単位の期間も適切に解析しようとします。
  • 計算の容易さ
    Timedelta型に変換することで、datetime型との加算・減算、Timedelta型同士の加算・減算、数値との乗算・除算といった時間計算を直感的に行えるようになります。
  • データ型の一貫性
    様々な形式で表現された期間データを、Pandasが扱いやすい統一されたTimedelta型に変換することで、データ操作や計算を容易にします。


無効な文字列形式 (Invalid String Format)

最も一般的なエラーの一つは、to_timedeltaが解析できない文字列形式を渡すことです。Pandasは柔軟な解析を試みますが、認識できないパターンや矛盾した表現があるとエラーになります。

エラーの例

import pandas as pd

# 認識できない単位
try:
    pd.to_timedelta('1 year') # 'year'は直接Timedeltaの単位として認識されない
except Exception as e:
    print(f"Error: {e}")

# 不明瞭な形式
try:
    pd.to_timedelta('5h 30m 10s') # スペースがないと認識されにくい
except Exception as e:
    print(f"Error: {e}")

トラブルシューティング

  • 入力データを前処理する
    必要に応じて、正規表現などを使って文字列をto_timedeltaが解析しやすい形式に事前に変換します。
  • errors='coerce' を使用する
    これが最も一般的な解決策です。無効な形式の文字列はNaT (Not a Time) に変換されるため、エラーでプログラムが停止することなく、無効な値を特定して後で処理できます。
    import pandas as pd
    
    data = ['1 day', '2 hours', 'invalid_duration', '30 minutes']
    td_result = pd.to_timedelta(data, errors='coerce')
    print(td_result)
    # 出力:
    # TimedeltaIndex(['1 days 00:00:00', '0 days 02:00:00', NaT, '0 days 00:30:00'], dtype='timedelta64[ns]', freq=None)
    
  • より明確な文字列形式を使用する
    特に複数の単位を組み合わせる場合は、"1 day 06:05:01" のように日付と時刻の標準的な形式に近づけるか、"1d 6h 5m 1s" のように略語とスペースで明確に区切ると良いでしょう。
  • Pandasがサポートする形式を確認する
    一般的に、"1 day 2 hours 30 minutes" や "1h 30m"、"10s" のような形式はうまく機能します。数値と単位の組み合わせ(例: "10 seconds", "5 days")も有効です。

数値とunit引数の不一致 (Mismatch between Numeric Value and unit Argument)

数値をto_timedeltaに渡す場合、unit引数を正しく指定することが重要です。指定しない場合、デフォルトの'ns' (ナノ秒) が使用され、意図しない結果になることがあります。

エラー/意図しない結果の例

import pandas as pd

# unitを指定しない場合、デフォルトの'ns'が使われる
td_default = pd.to_timedelta(5)
print(td_default)
# 出力: 0 days 00:00:00.000000005 (5ナノ秒) - 5日を期待していた場合、これは意図しない結果

# 不適切な単位の指定
try:
    pd.to_timedelta('5', unit='day') # 文字列にunitは通常適用されない
except Exception as e:
    print(f"Error: {e}")

トラブルシューティング

  • 文字列と数値の使い分けを理解する
    文字列はPandasが解析し、単位を自動的に推測します。数値はunit引数で単位を明確にする必要があります。
  • 数値には必ずunitを指定する
    数値を期間として扱う場合、その数値が何を表すのか(秒、分、日など)をunit引数で明示的に指定します。
    import pandas as pd
    
    td_days = pd.to_timedelta(5, unit='d') # 5日
    print(td_days)
    # 出力: 5 days 00:00:00
    
    td_hours = pd.to_timedelta(120, unit='h') # 120時間
    print(td_hours)
    # 出力: 5 days 00:00:00
    

オーバーフロー (OverflowError / OutOfBoundsTimedelta)

非常に大きな数値をunit='ns'(デフォルト)でto_timedeltaに渡すと、Timedeltaの表現可能な最大値を超えてしまい、オーバーフローエラーが発生することがあります。

エラーの例

import pandas as pd

# 非常に大きな数値をナノ秒として変換しようとする
try:
    pd.to_timedelta(10**19, unit='ns') # 10の19乗ナノ秒は大きすぎる
except Exception as e:
    print(f"Error: {e}")

トラブルシューティング

  • 値を分割して計算する
    極端に大きな期間を扱う場合は、複数回to_timedeltaを呼び出して加算するなど、処理を分割することも検討できます。
  • より大きな単位を使用する
    期間が非常に長い場合は、unit's' (秒)、'm' (分)、'h' (時間)、'd' (日) のように大きな単位に変更することを検討してください。
    import pandas as pd
    
    # 10の10乗秒として変換
    td_large = pd.to_timedelta(10**10, unit='s')
    print(td_large)
    # 出力: 115740 days 16:26:40
    

NaT (Not a Time) の扱いの問題

errors='coerce' を使用した場合、無効な値はNaTに変換されます。NaTは時間計算においてNaN (Not a Number) と同様の挙動を示すため、後続の計算で予期しない結果(NaTが伝播するなど)を引き起こす可能性があります。

問題の例

import pandas as pd

data = ['1 hour', 'invalid', '3 hours']
td_series = pd.to_timedelta(data, errors='coerce')
print(td_series)

# NaTを含むSeriesでの加算
result = td_series + pd.to_timedelta('10 minutes')
print(result)
# 出力:
# 0   0 days 01:10:00
# 1               NaT # NaTが伝播している
# 2   0 days 03:10:00
# dtype: timedelta64[ns]

トラブルシューティング

  • NaTを特定し、適切に処理する
    • isna()notna()を使ってNaTを含む行を特定し、削除するか、他の値で埋める(fillna())などの処理を検討します。
    import pandas as pd
    
    data = ['1 hour', 'invalid', '3 hours']
    td_series = pd.to_timedelta(data, errors='coerce')
    
    # NaTの行を削除
    td_cleaned = td_series.dropna()
    print("dropna:\n", td_cleaned)
    
    # NaTを0秒で埋める(状況による)
    td_filled = td_series.fillna(pd.Timedelta(seconds=0))
    print("fillna:\n", td_filled)
    

Seriesやリストに異なるデータ型(文字列と数値など)が混在している場合、to_timedeltaは期待通りに動作しないか、エラーを発生させることがあります。

問題の例

import pandas as pd

# 文字列と数値が混在
data = ['1 hour', 60, '2 days'] # 60が秒として扱われるか、エラーになるか不明確
try:
    pd.to_timedelta(data)
except Exception as e:
    print(f"Error: {e}") # TypeErrorになる可能性が高い
  • applyメソッドと組み合わせる
    複雑な変換が必要な場合は、Seriesのapplyメソッドを使って各要素にカスタムの変換ロジックを適用することも可能です。
  • 入力データを事前に統一する
    to_timedeltaに渡す前に、すべての要素が同じ型(文字列または数値)になるようにデータを前処理します。
    • 文字列に変換する:
      data_str = ['1 hour', '60s', '2 days'] # 60を'60s'のように文字列にする
      td = pd.to_timedelta(data_str)
      print(td)
      
    • 数値に変換し、単位を統一する:
      # 全てを秒に変換する例
      data_seconds = [3600, 60, 172800] # 各要素を秒に変換
      td = pd.to_timedelta(data_seconds, unit='s')
      print(td)
      


基本的な使用法: 単一の文字列からの変換

最も基本的な使い方です。期間を表す文字列をTimedeltaオブジェクトに変換します。

import pandas as pd

# 1. シンプルな時間差の文字列
td1 = pd.to_timedelta('1 day')
print("--- 1. シンプルな時間差の文字列 ---")
print(f"入力: '1 day'")
print(f"出力: {td1}")
print(f"型: {type(td1)}\n")

# 2. 複数の単位を含む文字列
td2 = pd.to_timedelta('2 hours 30 minutes 15 seconds')
print("--- 2. 複数の単位を含む文字列 ---")
print(f"入力: '2 hours 30 minutes 15 seconds'")
print(f"出力: {td2}\n")

# 3. 短縮形と小数を含む文字列
td3 = pd.to_timedelta('1.5h') # 1.5時間
print("--- 3. 短縮形と小数を含む文字列 ---")
print(f"入力: '1.5h'")
print(f"出力: {td3}\n")

# 4. マイクロ秒やナノ秒
td4 = pd.to_timedelta('100ms') # 100ミリ秒
td5 = pd.to_timedelta('5000us') # 5000マイクロ秒
print("--- 4. マイクロ秒やナノ秒 ---")
print(f"入力: '100ms'")
print(f"出力: {td4}")
print(f"入力: '5000us'")
print(f"出力: {td5}\n")

実行結果

--- 1. シンプルな時間差の文字列 ---
入力: '1 day'
出力: 1 days 00:00:00
型: <class 'pandas.Timedelta'>

--- 2. 複数の単位を含む文字列 ---
入力: '2 hours 30 minutes 15 seconds'
出力: 0 days 02:30:15

--- 3. 短縮形と小数を含む文字列 ---
入力: '1.5h'
出力: 0 days 01:30:00

--- 4. マイクロ秒やナノ秒 ---
入力: '100ms'
出力: 0 days 00:00:00.100000
入力: '5000us'
出力: 0 days 00:00:00.005000

数値とunit引数を使用した変換

数値だけを渡す場合、unit引数でその数値の単位を明示的に指定します。

import pandas as pd

# 1. 秒単位の数値
td_s = pd.to_timedelta(60, unit='s') # 60秒
print("--- 1. 秒単位の数値 ---")
print(f"入力: 60, unit='s'")
print(f"出力: {td_s}\n")

# 2. 分単位の数値
td_m = pd.to_timedelta(90, unit='m') # 90分
print("--- 2. 分単位の数値 ---")
print(f"入力: 90, unit='m'")
print(f"出力: {td_m}\n")

# 3. 日単位の数値
td_d = pd.to_timedelta(7, unit='D') # 7日
print("--- 3. 日単位の数値 ---")
print(f"入力: 7, unit='D'")
print(f"出力: {td_d}\n")

# 4. デフォルトの単位(ナノ秒)
# unitを指定しない場合、非常に小さな時間になることに注意
td_default_unit = pd.to_timedelta(100) # 100ナノ秒
print("--- 4. デフォルトの単位(ナノ秒) ---")
print(f"入力: 100 (unitなし)")
print(f"出力: {td_default_unit}\n")

実行結果

--- 1. 秒単位の数値 ---
入力: 60, unit='s'
出力: 0 days 00:01:00

--- 2. 分単位の数値 ---
入力: 90, unit='m'
出力: 0 days 01:30:00

--- 3. 日単位の数値 ---
入力: 7, unit='D'
出力: 7 days 00:00:00

--- 4. デフォルトの単位(ナノ秒) ---
入力: 100 (unitなし)
出力: 0 days 00:00:00.000000100

リストやSeriesからの変換

複数の期間データを一括で変換し、TimedeltaIndexまたはTimedelta型のSeriesを生成します。

import pandas as pd

# 1. 文字列のリストから変換
duration_strings = ['1 day', '5 hours', '30 minutes', '2 seconds']
td_index = pd.to_timedelta(duration_strings)
print("--- 1. 文字列のリストから変換 ---")
print(f"入力リスト: {duration_strings}")
print(f"出力:\n{td_index}")
print(f"型: {type(td_index)}\n")

# 2. 数値のSeriesとunit引数
s_minutes = pd.Series([10, 25, 60, 120])
td_series = pd.to_timedelta(s_minutes, unit='m') # 分単位
print("--- 2. 数値のSeriesとunit引数 ---")
print(f"入力Series:\n{s_minutes}")
print(f"出力:\n{td_series}")
print(f"型: {type(td_series)}\n")

# 3. 日付と時刻の差を計算してTimedeltaIndexを作成
start_dates = pd.to_datetime(['2023-01-01', '2023-01-05', '2023-01-10'])
end_dates = pd.to_datetime(['2023-01-03', '2023-01-08', '2023-01-12'])
time_diff = end_dates - start_dates
print("--- 3. 日付と時刻の差を計算 ---")
print(f"開始日:\n{start_dates}")
print(f"終了日:\n{end_dates}")
print(f"差 (TimedeltaIndex):\n{time_diff}")
print(f"型: {type(time_diff)}\n")

実行結果

--- 1. 文字列のリストから変換 ---
入力リスト: ['1 day', '5 hours', '30 minutes', '2 seconds']
出力:
TimedeltaIndex(['1 days 00:00:00', '0 days 05:00:00', '0 days 00:30:00',
                '0 days 00:00:02'],
               dtype='timedelta64[ns]', freq=None)
型: <class 'pandas.core.indexes.timedeltas.TimedeltaIndex'>

--- 2. 数値のSeriesとunit引数 ---
入力Series:
0     10
1     25
2     60
3    120
dtype: int64
出力:
0   0 days 00:10:00
1   0 days 00:25:00
2   0 days 01:00:00
3   0 days 02:00:00
dtype: timedelta64[ns]
型: <class 'pandas.core.series.Series'>

--- 3. 日付と時刻の差を計算 ---
開始日:
DatetimeIndex(['2023-01-01', '2023-01-05', '2023-01-10'], dtype='datetime64[ns]', freq=None)
終了日:
DatetimeIndex(['2023-01-03', '2023-01-08', '2023-01-12'], dtype='datetime64[ns]', freq=None)
差 (TimedeltaIndex):
TimedeltaIndex(['2 days', '3 days', '2 days'], dtype='timedelta64[ns]', freq=None)
型: <class 'pandas.core.indexes.timedeltas.TimedeltaIndex'>

エラーハンドリング (errors引数)

無効な入力があった場合にどう振る舞うかをerrors引数で制御できます。

import pandas as pd

invalid_data = ['1 hour', 'invalid_duration', '3 days', '20 minutes']

# 1. errors='raise' (デフォルト): 無効な入力でエラーを発生させる
print("--- 1. errors='raise' (デフォルト) ---")
try:
    td_raise = pd.to_timedelta(invalid_data, errors='raise')
    print(td_raise)
except ValueError as e:
    print(f"エラーが発生しました: {e}\n")

# 2. errors='coerce': 無効な入力をNaTに変換する
print("--- 2. errors='coerce' ---")
td_coerce = pd.to_timedelta(invalid_data, errors='coerce')
print(f"出力:\n{td_coerce}\n")

# 3. errors='ignore': 無効な入力をそのまま返す (通常は避けるべき)
print("--- 3. errors='ignore' ---")
td_ignore = pd.to_timedelta(invalid_data, errors='ignore')
print(f"出力:\n{td_ignore}\n")

実行結果

--- 1. errors='raise' (デフォルト) ---
エラーが発生しました: Unable to parse 25 strings, at least one is not a timedelta

--- 2. errors='coerce' ---
出力:
TimedeltaIndex(['0 days 01:00:00', NaT, '3 days 00:00:00',
                '0 days 00:20:00'],
               dtype='timedelta64[ns]', freq=None)

--- 3. errors='ignore' ---
出力:
Index(['1 hour', 'invalid_duration', '3 days', '20 minutes'], dtype='object')

to_timedeltaで作成したTimedeltaオブジェクトは、時間に関する計算に非常に便利です。

import pandas as pd
from datetime import datetime, timedelta

# DatetimeオブジェクトとTimedeltaの加算
start_time = pd.to_datetime('2023-05-20 10:00:00')
duration = pd.to_timedelta('3 days 12 hours')
end_time = start_time + duration
print("--- DatetimeとTimedeltaの加算 ---")
print(f"開始時刻: {start_time}")
print(f"期間: {duration}")
print(f"終了時刻: {end_time}\n")

# Timedelta同士の加算・減算
td_a = pd.to_timedelta('1 day')
td_b = pd.to_timedelta('5 hours')
td_sum = td_a + td_b
td_diff = td_a - td_b
print("--- Timedelta同士の演算 ---")
print(f"1日 + 5時間 = {td_sum}")
print(f"1日 - 5時間 = {td_diff}\n")

# Timedeltaと数値の乗算・除算
td_base = pd.to_timedelta('1 hour')
td_multiplied = td_base * 3.5 # 3.5時間
td_divided = td_base / 2 # 0.5時間
print("--- Timedeltaと数値の乗算・除算 ---")
print(f"1時間 * 3.5 = {td_multiplied}")
print(f"1時間 / 2 = {td_divided}\n")

# DataFrameでの利用例
df = pd.DataFrame({
    'start_datetime': pd.to_datetime(['2023-01-01', '2023-01-02', '2023-01-03']),
    'duration_str': ['1 day', '2 hours', '30 minutes']
})

# duration_strカラムをTimedelta型に変換し、新しいカラムを作成
df['duration_td'] = pd.to_timedelta(df['duration_str'])
# 開始時刻に期間を加算して終了時刻を計算
df['end_datetime'] = df['start_datetime'] + df['duration_td']

print("--- DataFrameでの利用例 ---")
print(df)
--- DatetimeとTimedeltaの加算 ---
開始時刻: 2023-05-20 10:00:00
期間: 3 days 12:00:00
終了時刻: 2023-05-23 22:00:00

--- Timedelta同士の演算 ---
1日 + 5時間 = 1 days 05:00:00
1日 - 5時間 = 0 days 19:00:00

--- Timedeltaと数値の乗算・除算 ---
1時間 * 3.5 = 0 days 03:30:00
1時間 / 2 = 0 days 00:30:00

--- DataFrameでの利用例 ---
  start_datetime duration_str duration_td          end_datetime
0     2023-01-01        1 day  1 days 00:00:00 2023-01-02 00:00:00
1     2023-01-02      2 hours  0 days 02:00:00 2023-01-02 02:00:00
2     2023-01-03   30 minutes  0 days 00:30:00 2023-01-03 00:30:00


datetime.timedelta オブジェクトを直接作成する

Python 標準ライブラリの datetime モジュールには、期間を表す timedelta クラスが用意されています。個々の期間データを作成する場合には、これを直接利用することができます。

メリット

  • Pythonの標準的な振る舞いを理解しやすい。
  • Pandasに依存しないため、軽量なスクリプトやPandasをインポートしたくない場合に便利。

デメリット

  • errors='coerce'のような柔軟なエラーハンドリングがない。
  • PandasのSeriesやDataFrameの列全体に適用するには、ループ処理やapplyメソッドが必要になるため、大規模データでは効率が悪い可能性がある。

コード例

from datetime import timedelta
import pandas as pd

# 単一の timedelta オブジェクトを作成
td_std_days = timedelta(days=5)
td_std_hours = timedelta(hours=3, minutes=45)

print(f"5日間: {td_std_days}")
print(f"3時間45分: {td_std_hours}\n")

# リストやSeriesに適用する場合(applyメソッドを使用)
duration_strings = ['1 day', '2 hours', '30 minutes']

# 文字列を timedelta に変換する関数
def parse_duration_string(s):
    parts = s.split()
    value = int(parts[0])
    unit = parts[1]

    if 'day' in unit:
        return timedelta(days=value)
    elif 'hour' in unit:
        return timedelta(hours=value)
    elif 'minute' in unit:
        return timedelta(minutes=value)
    else:
        # 例外処理やエラーハンドリングはここで行う
        return None # またはエラーを発生させる

s = pd.Series(duration_strings)
# to_timedeltaを使わない場合、このようにカスタム関数を適用する必要がある
s_timedelta = s.apply(parse_duration_string)

print("--- Seriesに適用した場合 ---")
print(s_timedelta)
print(f"型: {type(s_timedelta.iloc[0])}\n") # pandas.Timedeltaではなく datetime.timedelta になる
print(f"Seriesのdtype: {s_timedelta.dtype}") # object 型になることが多い

出力例

5日間: 5 days, 0:00:00
3時間45分: 3:45:00

--- Seriesに適用した場合 ---
0   1 days 00:00:00
1   0 days 02:00:00
2   0 days 00:30:00
dtype: object
型: <class 'datetime.timedelta'>
Seriesのdtype: object

注釈: datetime.timedeltaオブジェクトのSeriesは、Pandas内部ではobject型として扱われるため、pandas.Timedelta型のSeriesと比較して、パフォーマンスや一部の操作で違いが出ることがあります。

数値型を直接使用し、単位を保持する (推奨されないが可能性として)

これは一般的な方法ではありませんし、ほとんどのケースで推奨されませんが、時間差を秒やミリ秒などの単一の数値として保持し、単位を別途管理することも理論的には可能です。

メリット

  • Pandasやdatetimeモジュールに依存しない。
  • メモリ使用量が少ない場合がある(ただし、大きな数値になるとNumPyの型でも限界がある)。

デメリット

  • ほとんどの場合、Timedelta型の方が優れている。
  • 可読性が低い。
  • 「日」「時間」「分」といった複合的な期間の表現が困難。
  • 時間計算(加算、減算、日付への適用など)が非常に面倒でエラーを起こしやすい。

コード例

import pandas as pd

# 時間を秒単位の数値として管理
df_raw = pd.DataFrame({
    'event_id': [1, 2, 3],
    'duration_seconds': [3600, 7200, 1800] # 全て秒単位
})

print("--- 秒単位の数値として管理 ---")
print(df_raw)

# この形式で時間計算を行うのは非常に手動になる
# 例: 最初のイベントに30分(1800秒)加算
df_raw.loc[0, 'duration_seconds'] += 1800
print("\n--- 最初のイベントに30分(1800秒)加算後 ---")
print(df_raw)

# DatetimeIndexに適用する場合も手動での計算が必要
start_dt = pd.to_datetime('2023-01-01 00:00:00')
# end_dt = start_dt + df_raw['duration_seconds'] # これは直接は動作しない
# end_dt = start_dt + pd.to_timedelta(df_raw['duration_seconds'], unit='s') # 結局to_timedeltaを使うのが自然

PandasのTimedeltaTimedeltaIndexは、内部的にNumPyのtimedelta64データ型に基づいています。そのため、NumPyを使って直接timedelta64配列を作成することも可能です。

メリット

  • PandasのTimedeltaオブジェクトと互換性があるため、PandasのSeriesやDataFrameに容易に変換できる。
  • 一部のNumPy演算を直接適用できる。
  • Pandasの内部動作を理解するのに役立つ。

デメリット

  • NaTのようなPandas固有の機能は直接利用できない。
  • 文字列解析の柔軟性はない(Pandasが提供する高度な解析機能は利用できない)。

コード例

import numpy as np
import pandas as pd

# 1. 単一の timedelta64 オブジェクトを作成
# 'D': 日, 'h': 時間, 'm': 分, 's': 秒, 'ms': ミリ秒, 'us': マイクロ秒, 'ns': ナノ秒
td_np_days = np.timedelta64(2, 'D') # 2日間
td_np_hours = np.timedelta64(4, 'h') # 4時間

print("--- NumPyのtimedelta64オブジェクト ---")
print(f"2日間: {td_np_days}")
print(f"型: {type(td_np_days)}")
print(f"4時間: {td_np_hours}\n")

# 2. 配列として作成
durations_np = np.array([10, 20, 30], dtype='timedelta64[m]') # 分単位の配列
print("--- NumPy配列として作成 ---")
print(f"出力:\n{durations_np}")
print(f"型: {type(durations_np)}\n")

# Pandas Seriesへの変換は容易
s_from_np = pd.Series(durations_np)
print("--- Pandas Seriesへの変換 ---")
print(f"出力:\n{s_from_np}")
print(f"Seriesのdtype: {s_from_np.dtype}")
print(f"型 (Seriesの要素): {type(s_from_np.iloc[0])}\n")

# 文字列からの変換は、NumPy単体では面倒
# 例: '1 day' を変換するには、Pandasのto_timedeltaを使用するのが最も簡単
# np.timedelta64('1 day') は可能だが、複数形式の文字列解析はできない
--- NumPyのtimedelta64オブジェクト ---
2日間: 2 days
型: <class 'numpy.timedelta64'>
4時間: 4 hours

--- NumPy配列として作成 ---
出力:
[10 minutes 20 minutes 30 minutes]
型: <class 'numpy.ndarray'>

--- Pandas Seriesへの変換 ---
出力:
0   0 days 00:10:00
1   0 days 00:20:00
2   0 days 00:30:00
dtype: timedelta64[ns]
Seriesのdtype: timedelta64[ns]
型 (Seriesの要素): <class 'pandas.core.timedeltas.Timedelta'>
  • 数値として管理
    ほとんどの場合に推奨されません。時間計算が複雑になり、エラーを引き起こしやすいためです。
  • NumPy timedelta64
    Pandasの内部を理解するのに役立ちますが、Pandasが提供する高レベルな機能(特に文字列解析)がないため、直接使うケースは限られます。Pandas SeriesやDataFrameの特定のNumPy演算が必要な場合に検討されることがあります。
  • datetime.timedelta
    個々のシンプルな期間を作成する場合や、Pandasに依存しないコードが必要な場合に有用です。ただし、大規模データや多様な形式の解析には不向きです。
  • ほとんどの場合、pandas.to_timedeltaを使用することが最も推奨されます。 豊富な機能、柔軟な文字列解析、エラーハンドリング、Pandasのデータ構造とのシームレスな統合を提供するためです。