pandas.Timestamp.round

2025-05-16

pandas.Timestamp は、Pythonの datetime オブジェクトを拡張したもので、タイムゾーン情報やナノ秒単位の精度など、より強力な時間処理機能を提供します。round() メソッドは、この Timestamp オブジェクトを、指定された頻度(例:日、時間、分、秒など)に最も近い時刻に丸めます。

使い方 (Usage)

基本的な使い方は以下の通りです。

import pandas as pd

# Timestamp オブジェクトを作成
ts = pd.Timestamp('2023-10-26 14:32:55.123456')
print(f"元のTimestamp: {ts}")

# 時間単位で丸める (例: 時間)
rounded_to_hour = ts.round(freq='H')
print(f"時間に丸めたTimestamp: {rounded_to_hour}")

# 分単位で丸める (例: 10分)
rounded_to_10min = ts.round(freq='10min')
print(f"10分に丸めたTimestamp: {rounded_to_10min}")

# 秒単位で丸める
rounded_to_second = ts.round(freq='S')
print(f"秒に丸めたTimestamp: {rounded_to_second}")

# ミリ秒単位で丸める
rounded_to_millisecond = ts.round(freq='ms')
print(f"ミリ秒に丸めたTimestamp: {rounded_to_millisecond}")

パラメータ (Parameters)

  • nonexistent: (タイムゾーンがある場合のみ関連)
    • : str または timedelta (デフォルト: 'raise')
    • 説明: サマータイム(DST)の移行期間などで、存在しない時刻(例:時計が1時間進むときにスキップされる時間帯)に丸めようとした場合にどう処理するかを指定します。
      • 'raise': NonExistentTimeError を発生させます。
      • 'shift_forward': 最も近い将来の存在する時刻にシフトします。
      • 'shift_backward': 最も近い過去の存在する時刻にシフトします。
      • 'NaT': NaT (Not a Time) を返します。
      • timedelta オブジェクト: 指定された timedelta だけ時刻をシフトします。
  • ambiguous: (タイムゾーンがある場合のみ関連)
    • : bool または {'raise', 'NaT'} (デフォルト: 'raise')
    • 説明: サマータイム(DST)の移行期間などで、時刻が曖昧になる場合にどう処理するかを指定します。
      • 'raise': AmbiguousTimeError を発生させます。
      • 'NaT': NaT (Not a Time) を返します。
      • True / False: その時刻がDSTかどうかを明示的に指定します。

丸めの挙動 (Rounding Behavior)

round() メソッドは、指定された頻度の「最も近い」値に丸めます。例えば、freq='H' (時間単位) で丸める場合、ある時刻がXX時30分00秒より前の場合はXX時00分00秒に、XX時30分00秒以降の場合はXX時59分59秒から次のXX+1時00分00秒に丸められます。

例: 2023-10-26 14:29:59'H' (時間) で丸めると 2023-10-26 14:00:00 2023-10-26 14:30:00'H' (時間) で丸めると 2023-10-26 15:00:00

注意点 (Notes)

  • floor()ceil(): round() の他に、常に切り下げる floor() や常に切り上げる ceil() メソッドもあります。これらも同様に freq パラメータを取ります。
  • NaT (Not a Time): pd.NaTround() すると、結果も pd.NaT になります。
  • タイムゾーン (Timezone): Timestamp がタイムゾーンを持つ場合、丸めはローカル時刻(壁時計の時刻)に基づいて行われ、その後同じタイムゾーンに再ローカライズされます。DSTの移行期間では、ambiguousnonexistent パラメータが重要になります。
import pandas as pd

# 例1: 秒単位で丸める
ts = pd.Timestamp('2024-01-15 10:30:45.789')
rounded_s = ts.round('S')
print(f"秒に丸めた: {rounded_s}") # 出力: 2024-01-15 10:30:46

# 例2: 30分単位で丸める
ts2 = pd.Timestamp('2024-01-15 10:12:34')
rounded_30min = ts2.round('30min')
print(f"30分に丸めた (10:12:34): {rounded_30min}") # 出力: 2024-01-15 10:00:00

ts3 = pd.Timestamp('2024-01-15 10:45:00')
rounded_30min_2 = ts3.round('30min')
print(f"30分に丸めた (10:45:00): {rounded_30min_2}") # 出力: 2024-01-15 11:00:00

# 例3: タイムゾーン付きのTimestampを丸める
ts_tz = pd.Timestamp("2023-03-12 02:15:00", tz="US/Eastern")
# 2023年3月12日はDST移行日 (2時がスキップされる)
# 2023-03-12 02:00:00 US/Eastern は存在しない時刻
# round('h') で時間に丸めると、存在しない時刻になる可能性があるため、nonexistentを指定
try:
    # デフォルトの'raise'の場合、エラーが発生
    rounded_tz = ts_tz.round("h")
except Exception as e:
    print(f"エラーが発生: {e}")

# nonexistent='shift_forward'で、存在しない時刻を将来にシフト
rounded_tz_shifted = ts_tz.round("h", nonexistent='shift_forward')
print(f"タイムゾーン付き時間を丸めた (シフト): {rounded_tz_shifted}") # 出力: 2023-03-12 03:00:00-04:00 (EDT)


無効な頻度文字列 (freq)

エラー (Error)

ValueError: Invalid frequency: 'invalid_freq_string' のようなエラーが発生します。

原因 (Cause)

round メソッドの freq 引数に、Pandasが認識できない頻度文字列(Offset Alias)を指定した場合に発生します。

トラブルシューティング (Troubleshooting)

  • 一般的な頻度文字列の例:
    • 'D' (日)
    • 'H' (時間)
    • 'T' または 'min' (分)
    • 'S' (秒)
    • 'ms' (ミリ秒)
    • 'us' (マイクロ秒)
    • 'ns' (ナノ秒)
    • '5min' (5分), '3H' (3時間) など、数値と組み合わせる場合。

タイムゾーンの曖昧さ (ambiguous)

AmbiguousTimeError: Cannot infer timezone from datetime, try using the 'ambiguous' argument のようなエラーが発生します。

タイムゾーン情報を持つ Timestamp がサマータイム(DST)の切り替わり期間にある場合、特定のローカル時刻が2回現れる(例:秋のDST終了時、時計が1時間戻る)ことがあります。この曖昧さを解決できない場合に発生します。デフォルトの ambiguous='raise' が原因です。

ambiguous 引数を明示的に指定します。

  • ambiguous='NaT': 曖昧な時刻を NaT (Not a Time) にします。

<!-- end list -->

import pandas as pd
ts_ambiguous = pd.Timestamp("2023-11-05 01:30:00", tz="US/Eastern") # DST終了日、この時刻は2回現れる
rounded_nat = ts_ambiguous.round("H", ambiguous='NaT')
print(f"曖昧な時刻をNaTに: {rounded_nat}")
  • ambiguous=True または False: 曖昧な時刻を、DSTが有効な時刻とそうでない時刻のどちらとして解釈するかを明示的に指定します。
# 例: 2023-11-05 01:30:00 US/Eastern は、EDT (GMT-4) と EST (GMT-5) の両方で存在しうる
# True はEDTとして解釈 (DSTが有効)
rounded_true = ts_ambiguous.round("H", ambiguous=True)
print(f"曖昧な時刻をTrueで: {rounded_true}") # 01:00-04:00 (EDT) or 02:00-05:00 (EST) depending on behavior

# False はESTとして解釈 (DSTが無効)
rounded_false = ts_ambiguous.round("H", ambiguous=False)
print(f"曖昧な時刻をFalseで: {rounded_false}") # 01:00-05:00 (EST)

存在しない時刻 (nonexistent)

NonExistentTimeError: Cannot convert 2023-03-12 02:30:00 to US/Eastern, there is no such time. のようなエラーが発生します。

タイムゾーン情報を持つ Timestamp がサマータイム(DST)の切り替わり期間にある場合、特定のローカル時刻が存在しない(例:春のDST開始時、時計が1時間進む)ことがあります。round メソッドがこの存在しない時刻に丸めようとした場合に発生します。デフォルトの nonexistent='raise' が原因です。

nonexistent 引数を明示的に指定します。

  • nonexistent='NaT': 存在しない時刻を NaT にします。
import pandas as pd
ts_nonexistent = pd.Timestamp("2023-03-12 02:15:00", tz="US/Eastern") # DST開始日、この時刻はスキップされる
rounded_nat = ts_nonexistent.round("H", nonexistent='NaT')
print(f"存在しない時刻をNaTに: {rounded_nat}")
  • nonexistent='shift_forward': 存在しない時刻を、その時刻から最も近い将来の存在する時刻にシフトします。
rounded_forward = ts_nonexistent.round("H", nonexistent='shift_forward')
print(f"存在しない時刻を前方にシフト: {rounded_forward}") # 2023-03-12 03:00:00-04:00 (EDT)
  • nonexistent='shift_backward': 存在しない時刻を、その時刻から最も近い過去の存在する時刻にシフトします。
rounded_backward = ts_nonexistent.round("H", nonexistent='shift_backward')
print(f"存在しない時刻を後方にシフト: {rounded_backward}") # 2023-03-12 01:00:00-05:00 (EST)
  • nonexistent=timedelta: 特定の timedelta だけ時刻をシフトします。
from datetime import timedelta
rounded_timedelta = ts_nonexistent.round("H", nonexistent=timedelta(hours=1))
print(f"存在しない時刻をtimedeltaでシフト: {rounded_timedelta}")

問題 (Problem)

特にマイクロ秒やナノ秒レベルで非常に細かい丸めを行う場合、浮動小数点数演算の性質上、期待通りの丸め結果にならないことがあります。これはPandas内部での処理に起因することがあります。

  • 手動での丸め処理を検討する: 非常に特殊な要件があり、round() メソッドの挙動が期待通りでない場合は、Timestamp オブジェクトの .value (ナノ秒単位の整数) を直接操作し、数学的な丸め関数(np.round など)を使用してから再度 Timestamp に変換するなどの方法も考えられます。ただし、これは複雑で、通常は推奨されません。
  • Pandasのバージョンを確認する: Pandasは継続的に改善されており、過去のバージョンで報告された特定の丸めバグが修正されている可能性があります。最新の安定版を使用することを検討してください。
  • 問題が本当に精度によるものか確認する: 非常に細かい時間単位(例:'ns')で丸めを行う必要がある場合にのみ問題となることが多いです。通常の分析ではあまり遭遇しません。


基本的な丸め処理

最も一般的な使い方です。指定した時間単位で最も近い時刻に丸めます。

import pandas as pd

# 元のTimestampオブジェクト
ts = pd.Timestamp('2023-10-26 14:32:55.123456')
print(f"元のTimestamp: {ts}")

# 1. 時間単位 ('H') で丸める
# 32分は30分以上なので、次の時間 (15時) に丸められる
rounded_to_hour = ts.round(freq='H')
print(f"時間に丸めた: {rounded_to_hour}") # 2023-10-26 15:00:00

# 2. 10分単位 ('10min' または '10T') で丸める
# 32分は30分に近く、かつ35分(次の10分単位)よりも32分(30分に丸まる)が近い
# 32分は25分(30分の半分)以上なので、30分に丸められる
rounded_to_10min = ts.round(freq='10min')
print(f"10分に丸めた: {rounded_to_10min}") # 2023-10-26 14:30:00

# 3. 秒単位 ('S') で丸める
# 55.123456秒は55秒に丸められる (0.5秒未満のため)
rounded_to_second = ts.round(freq='S')
print(f"秒に丸めた: {rounded_to_second}") # 2023-10-26 14:32:55

# 4. ミリ秒単位 ('ms') で丸める
# 123456マイクロ秒は123ミリ秒に丸められる (0.5ミリ秒未満のため)
rounded_to_millisecond = ts.round(freq='ms')
print(f"ミリ秒に丸めた: {rounded_to_millisecond}") # 2023-10-26 14:32:55.123000

データフレームの列に適用する

データフレームのDatetimeIndexやTimestamp型の列に対して dt アクセサを通じて round を適用することもよくあります。

import pandas as pd

# データフレームの作成
df = pd.DataFrame({
    'timestamp': [
        pd.Timestamp('2023-01-01 10:05:23'),
        pd.Timestamp('2023-01-01 10:14:59'),
        pd.Timestamp('2023-01-01 10:15:00'),
        pd.Timestamp('2023-01-01 10:29:59'),
        pd.Timestamp('2023-01-01 10:30:00')
    ]
})
print("元のDataFrame:\n", df)

# 'timestamp' 列を15分単位で丸める
# 10:05:23 -> 10:00:00 (15分の半分(7.5分)以下)
# 10:14:59 -> 10:15:00 (15分の半分(7.5分)以上)
# 10:15:00 -> 10:15:00
# 10:29:59 -> 10:30:00
# 10:30:00 -> 10:30:00
df['rounded_timestamp'] = df['timestamp'].dt.round('15min')
print("\n15分単位で丸めたDataFrame:\n", df)

タイムゾーン(tz)を持つTimestampを扱う場合、特に夏時間(DST)の切り替わり期間では ambiguousnonexistent パラメータが重要になります。

例3-1: 曖昧な時刻 (ambiguous)

夏時間が終了し、時計が1時間戻る日には、特定の時間が2回現れます。この場合、どちらの時間として解釈するかを指定します。

import pandas as pd

# 例: 2023年11月5日午前2時 (US/Eastern)
# この日は2時に時計が戻るため、01:00-02:00の時間が繰り返されます
# 01:30:00は、EDT (GMT-4) と EST (GMT-5) の両方で存在しうる

# 曖昧な時刻を作成
ts_ambiguous = pd.Timestamp("2023-11-05 01:30:00", tz="US/Eastern")
print(f"元の曖昧なTimestamp: {ts_ambiguous}")

# 1. デフォルト (`ambiguous='raise'`) - エラーが発生する
try:
    ts_ambiguous.round("H")
except Exception as e:
    print(f"\nエラーが発生 (ambiguous='raise' のため): {e}")

# 2. `ambiguous='NaT'` で `NaT` を返す
rounded_nat = ts_ambiguous.round("H", ambiguous='NaT')
print(f"曖昧な時刻をNaTに丸めた: {rounded_nat}") # NaT

# 3. `ambiguous=True` でDSTが有効な方として解釈し丸める
# 01:30:00がEDT (GMT-4) として解釈され、丸められる
rounded_true = ts_ambiguous.round("H", ambiguous=True)
print(f"ambiguous=True で丸めた: {rounded_true}") # 2023-11-05 02:00:00-05:00 (EST) (DST終了後に変換されるため)

# 4. `ambiguous=False` でDSTが無効な方として解釈し丸める
# 01:30:00がEST (GMT-5) として解釈され、丸められる
rounded_false = ts_ambiguous.round("H", ambiguous=False)
print(f"ambiguous=False で丸めた: {rounded_false}") # 2023-11-05 02:00:00-05:00 (EST)

例3-2: 存在しない時刻 (nonexistent)

夏時間が始まり、時計が1時間進む日には、特定の時間がスキップされ、存在しなくなります。この場合、どのように処理するかを指定します。

import pandas as pd
from datetime import timedelta

# 例: 2023年3月12日午前2時 (US/Eastern)
# この日は2時に時計が3時に進むため、02:00-02:59:59の時間が存在しない

# 存在しない時刻を作成
ts_nonexistent = pd.Timestamp("2023-03-12 02:15:00", tz="US/Eastern")
print(f"元の存在しないTimestamp: {ts_nonexistent}")

# 1. デフォルト (`nonexistent='raise'`) - エラーが発生する
try:
    ts_nonexistent.round("H")
except Exception as e:
    print(f"\nエラーが発生 (nonexistent='raise' のため): {e}")

# 2. `nonexistent='NaT'` で `NaT` を返す
rounded_nat = ts_nonexistent.round("H", nonexistent='NaT')
print(f"存在しない時刻をNaTに丸めた: {rounded_nat}") # NaT

# 3. `nonexistent='shift_forward'` で最も近い将来の存在する時刻にシフトして丸める
# 02:15:00は存在しないので、最も近い将来の03:00:00に丸められる
rounded_forward = ts_nonexistent.round("H", nonexistent='shift_forward')
print(f"存在しない時刻を前方にシフトして丸めた: {rounded_forward}") # 2023-03-12 03:00:00-04:00 (EDT)

# 4. `nonexistent='shift_backward'` で最も近い過去の存在する時刻にシフトして丸める
# 02:15:00は存在しないので、最も近い過去の01:00:00に丸められる
rounded_backward = ts_nonexistent.round("H", nonexistent='shift_backward')
print(f"存在しない時刻を後方にシフトして丸めた: {rounded_backward}") # 2023-03-12 01:00:00-05:00 (EST)

# 5. `nonexistent=timedelta` で指定した時間だけシフトして丸める
# 例: 1時間進める
rounded_timedelta = ts_nonexistent.round("H", nonexistent=timedelta(hours=1))
print(f"存在しない時刻を1時間シフトして丸めた: {rounded_timedelta}") # 2023-03-12 03:00:00-04:00 (EDT)


pandas.Timestamp.floor() および pandas.Timestamp.ceil()

round() は「最も近い」時間単位に丸めますが、floor() は常に切り捨て(過去方向へ丸め)、ceil() は常に切り上げ(未来方向へ丸め)を行います。これらは round() と同じように freq 引数を取ります。

例: 時間単位での切り捨て/切り上げ

import pandas as pd

ts = pd.Timestamp('2023-10-26 14:32:55.123456')
print(f"元のTimestamp: {ts}")

# floor(): 時間単位で切り捨て
# 14:32:55 は 14:00:00 になる
floored_to_hour = ts.floor(freq='H')
print(f"時間に切り捨て: {floored_to_hour}")

# ceil(): 時間単位で切り上げ
# 14:32:55 は 15:00:00 になる
ceiled_to_hour = ts.ceil(freq='H')
print(f"時間に切り上げ: {ceiled_to_hour}")

# round(): 比較のため
# 14:32:55 は 14:30:00 を超えているため 15:00:00 になる
rounded_to_hour = ts.round(freq='H')
print(f"時間に丸めた (round): {rounded_to_hour}")

ユースケース:

  • ceil(): データを特定の時間ブロックの終了時点に揃えたい場合 (例: 各日の終了時刻、各時間の終了時刻)。期間の終了点を厳密に定義する際に便利です。
  • floor(): データを特定の時間ブロックの開始時点に揃えたい場合 (例: 各日の開始時刻、各時間の開始時刻)。期間の開始点を厳密に定義する際に便利です。

dt.normalize() (日付のみに丸める)

タイムスタンプの時刻部分をゼロに設定し、日付のみにしたい場合に便利です。これは round(freq='D') と同じ結果になりますが、意図がより明確になります。

例: 日付のみに正規化

import pandas as pd

ts = pd.Timestamp('2023-10-26 14:32:55.123456')
print(f"元のTimestamp: {ts}")

# normalize(): 時刻部分をゼロに設定し、日付のみにする
normalized_date = ts.normalize()
print(f"日付のみに正規化: {normalized_date}") # 2023-10-26 00:00:00

# round('D') と比較
rounded_to_day = ts.round('D')
print(f"日に丸めた (round('D')): {rounded_to_day}") # 2023-10-26 00:00:00
  • データ分析で、タイムスタンプの時刻情報が必要なく、日付レベルでの集計を行いたい場合。

astype(str) や dt.strftime() (文字列変換による丸め)

厳密には丸めではありませんが、表示目的や特定の粒度でグループ化するために、日時を文字列としてフォーマットする方法です。

例: 特定のフォーマットでの文字列変換

import pandas as pd

ts = pd.Timestamp('2023-10-26 14:32:55.123456')
print(f"元のTimestamp: {ts}")

# dt.strftime() を使って、指定したフォーマットで文字列化
# 時分秒を切り捨てて「年-月-日 時:00」の形式にする
formatted_str = ts.strftime('%Y-%m-%d %H:00')
print(f"文字列としてフォーマット (時単位): {formatted_str}") # 2023-10-26 14:00

# データフレームの列に適用する場合
df = pd.DataFrame({
    'timestamp': [
        pd.Timestamp('2023-01-01 10:05:23'),
        pd.Timestamp('2023-01-01 10:14:59'),
        pd.Timestamp('2023-01-01 10:15:00')
    ]
})
df['formatted_hour'] = df['timestamp'].dt.strftime('%Y-%m-%d %H:00')
print("\n文字列としてフォーマットしたDataFrame:\n", df)
  • ただし、この方法は結果が文字列になるため、その後の日時演算には適していません。
  • 日時を文字列としてグループ化したいが、それ以上の日時演算は不要な場合。
  • 日時の表示形式を厳密に制御したい場合。

resample() メソッドは Timestamp オブジェクト単体ではなく、DatetimeIndex を持つSeriesやDataFrameに対して使用されます。これは指定した頻度でデータをリサンプリング(再サンプリング)し、通常は集計操作(平均、合計など)と組み合わせて使用されます。実質的に、特定の時間ブロックにデータを丸めて集計するようなものです。

例: 5分ごとにデータを集計

import pandas as pd
import numpy as np

# DatetimeIndexを持つSeriesを作成
rng = pd.date_range('2023-01-01 00:00', periods=10, freq='3min')
data = np.random.randint(1, 10, size=10)
s = pd.Series(data, index=rng)
print("元のSeries:\n", s)

# 5分ごとにリサンプリングし、平均を計算
# 例えば、00:00, 00:03, 00:06 は 00:00-00:04 のブロックに属し、00:05, 00:08 は 00:05-00:09 のブロックに属する
resampled_s = s.resample('5min').mean()
print("\n5分ごとにリサンプリングして平均:\n", resampled_s)
  • データの時間ブロックに対する集計が必要な場合。resample()floor() と似た挙動で時間ブロックの開始点を基準にしますが、labelloffset オプションで調整可能です。
  • 時系列データを異なる時間粒度で集計したい場合(例: 分データから時間データへの変換、日次データから週次データへの変換)。
  • resample(): DatetimeIndex を持つSeries/DataFrameの時系列データを異なる時間粒度で集計する。時系列分析で必須。
  • dt.strftime(): 日時を文字列としてフォーマットする。表示目的や、厳密な日時演算が不要な場合のグループ化に。
  • normalize(): 日付部分のみを残し、時刻をゼロにする。日付レベルでの集計や表示に最適。
  • floor() / ceil(): それぞれ常に切り捨て/切り上げを行う。時間ブロックの開始点や終了点を厳密に定義したい場合に便利。
  • round(): 最も近い時間単位に丸める。汎用的な日時丸めに最適。