時系列 / 日付機能#
pandas には、あらゆるドメインの時系列データを操作するための広範な機能と特徴が含まれています。 NumPy の datetime64 および timedelta64 dtypes を使用して、pandas は scikits.timeseries などの他の Python ライブラリの多数の機能を統合し、時系列データを操作するための膨大な量の新しい機能を作成しました。
例えば、pandas は以下をサポートします。
様々なソースや形式からの時系列情報の解析
In [1]: import datetime
In [2]: dti = pd.to_datetime(
...: ["1/1/2018", np.datetime64("2018-01-01"), datetime.datetime(2018, 1, 1)]
...: )
...:
In [3]: dti
Out[3]: DatetimeIndex(['2018-01-01', '2018-01-01', '2018-01-01'], dtype='datetime64[ns]', freq=None)
固定周波数で日付と時間範囲のシーケンスを生成
In [4]: dti = pd.date_range("2018-01-01", periods=3, freq="h")
In [5]: dti
Out[5]:
DatetimeIndex(['2018-01-01 00:00:00', '2018-01-01 01:00:00',
'2018-01-01 02:00:00'],
dtype='datetime64[ns]', freq='h')
タイムゾーン情報を含む日時の操作と変換
In [6]: dti = dti.tz_localize("UTC")
In [7]: dti
Out[7]:
DatetimeIndex(['2018-01-01 00:00:00+00:00', '2018-01-01 01:00:00+00:00',
'2018-01-01 02:00:00+00:00'],
dtype='datetime64[ns, UTC]', freq='h')
In [8]: dti.tz_convert("US/Pacific")
Out[8]:
DatetimeIndex(['2017-12-31 16:00:00-08:00', '2017-12-31 17:00:00-08:00',
'2017-12-31 18:00:00-08:00'],
dtype='datetime64[ns, US/Pacific]', freq='h')
時系列を特定の周波数にリサンプリングまたは変換
In [9]: idx = pd.date_range("2018-01-01", periods=5, freq="h")
In [10]: ts = pd.Series(range(len(idx)), index=idx)
In [11]: ts
Out[11]:
2018-01-01 00:00:00 0
2018-01-01 01:00:00 1
2018-01-01 02:00:00 2
2018-01-01 03:00:00 3
2018-01-01 04:00:00 4
Freq: h, dtype: int64
In [12]: ts.resample("2h").mean()
Out[12]:
2018-01-01 00:00:00 0.5
2018-01-01 02:00:00 2.5
2018-01-01 04:00:00 4.0
Freq: 2h, dtype: float64
絶対的または相対的な時間増分を用いた日付と時刻の算術演算の実行
In [13]: friday = pd.Timestamp("2018-01-05")
In [14]: friday.day_name()
Out[14]: 'Friday'
# Add 1 day
In [15]: saturday = friday + pd.Timedelta("1 day")
In [16]: saturday.day_name()
Out[16]: 'Saturday'
# Add 1 business day (Friday --> Monday)
In [17]: monday = friday + pd.offsets.BDay()
In [18]: monday.day_name()
Out[18]: 'Monday'
pandas は、上記のタスクなどを実行するための比較的コンパクトで自己完結型のツールセットを提供します。
概要#
pandas は、時間に関連する4つの一般的な概念を捉えます。
日時: タイムゾーンサポート付きの特定の日付と時刻。標準ライブラリの
datetime.datetimeに類似。時間差: 絶対的な時間期間。標準ライブラリの
datetime.timedeltaに類似。時間範囲: 特定の時点とその関連する頻度によって定義される時間の範囲。
日付オフセット: カレンダーの算術演算に従う相対的な時間期間。
dateutilパッケージのdateutil.relativedelta.relativedeltaに類似。
概念 |
スカラクラス |
配列クラス |
pandas データ型 |
主要な作成方法 |
|---|---|---|---|---|
日時 |
|
|
|
|
時間差 |
|
|
|
|
時間範囲 |
|
|
|
|
日付オフセット |
|
|
|
|
時系列データの場合、操作を時間要素に関して実行できるように、Series または DataFrame のインデックスに時間コンポーネントを表現するのが慣例です。
In [19]: pd.Series(range(3), index=pd.date_range("2000", freq="D", periods=3))
Out[19]:
2000-01-01 0
2000-01-02 1
2000-01-03 2
Freq: D, dtype: int64
ただし、Series および DataFrame は、時間コンポーネントをデータ自体としても直接サポートできます。
In [20]: pd.Series(pd.date_range("2000", freq="D", periods=3))
Out[20]:
0 2000-01-01
1 2000-01-02
2 2000-01-03
dtype: datetime64[ns]
Series と DataFrame は、datetime、timedelta、Period データをこれらのコンストラクタに渡した場合、拡張されたデータ型サポートと機能を提供します。ただし、DateOffset データは object データとして保存されます。
In [21]: pd.Series(pd.period_range("1/1/2011", freq="M", periods=3))
Out[21]:
0 2011-01
1 2011-02
2 2011-03
dtype: period[M]
In [22]: pd.Series([pd.DateOffset(1), pd.DateOffset(2)])
Out[22]:
0 <DateOffset>
1 <2 * DateOffsets>
dtype: object
In [23]: pd.Series(pd.date_range("1/1/2011", freq="ME", periods=3))
Out[23]:
0 2011-01-31
1 2011-02-28
2 2011-03-31
dtype: datetime64[ns]
最後に、pandas は null の日時、時間差、時間範囲を NaT として表現します。これは、欠損または null の日付のような値を表現するのに役立ち、浮動小数点データに対する np.nan と同様に動作します。
In [24]: pd.Timestamp(pd.NaT)
Out[24]: NaT
In [25]: pd.Timedelta(pd.NaT)
Out[25]: NaT
In [26]: pd.Period(pd.NaT)
Out[26]: NaT
# Equality acts as np.nan would
In [27]: pd.NaT == pd.NaT
Out[27]: False
タイムスタンプとタイムスパン#
タイムスタンプ付きデータは、値を時点に関連付ける最も基本的なタイプの時系列データです。 pandas オブジェクトの場合、それは時点を使用することを意味します。
In [28]: import datetime
In [29]: pd.Timestamp(datetime.datetime(2012, 5, 1))
Out[29]: Timestamp('2012-05-01 00:00:00')
In [30]: pd.Timestamp("2012-05-01")
Out[30]: Timestamp('2012-05-01 00:00:00')
In [31]: pd.Timestamp(2012, 5, 1)
Out[31]: Timestamp('2012-05-01 00:00:00')
しかし、多くの場合、変化変数のようなものを時間範囲に関連付ける方が自然です。Period によって表される範囲は、明示的に指定することも、日時文字列の形式から推論することもできます。
例えば
In [32]: pd.Period("2011-01")
Out[32]: Period('2011-01', 'M')
In [33]: pd.Period("2012-05", freq="D")
Out[33]: Period('2012-05-01', 'D')
Timestamp と Period はインデックスとして機能できます。Timestamp と Period のリストは、それぞれ自動的に DatetimeIndex と PeriodIndex に型強制されます。
In [34]: dates = [
....: pd.Timestamp("2012-05-01"),
....: pd.Timestamp("2012-05-02"),
....: pd.Timestamp("2012-05-03"),
....: ]
....:
In [35]: ts = pd.Series(np.random.randn(3), dates)
In [36]: type(ts.index)
Out[36]: pandas.core.indexes.datetimes.DatetimeIndex
In [37]: ts.index
Out[37]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)
In [38]: ts
Out[38]:
2012-05-01 0.469112
2012-05-02 -0.282863
2012-05-03 -1.509059
dtype: float64
In [39]: periods = [pd.Period("2012-01"), pd.Period("2012-02"), pd.Period("2012-03")]
In [40]: ts = pd.Series(np.random.randn(3), periods)
In [41]: type(ts.index)
Out[41]: pandas.core.indexes.period.PeriodIndex
In [42]: ts.index
Out[42]: PeriodIndex(['2012-01', '2012-02', '2012-03'], dtype='period[M]')
In [43]: ts
Out[43]:
2012-01 -1.135632
2012-02 1.212112
2012-03 -0.173215
Freq: M, dtype: float64
pandas は両方の表現を捉え、それらの間で変換することを可能にします。内部的には、pandas は Timestamp のインスタンスを使用してタイムスタンプを表現し、DatetimeIndex のインスタンスを使用してタイムスタンプのシーケンスを表現します。通常の時間範囲の場合、pandas はスカラ値には Period オブジェクトを使用し、範囲のシーケンスには PeriodIndex を使用します。任意開始点と終了点を持つ不規則な間隔のより良いサポートは、将来のリリースで提供される予定です。
タイムスタンプへの変換#
日付のようなオブジェクトの Series またはリストのようなオブジェクト (例: 文字列、エポック、またはその混合) を変換するには、to_datetime 関数を使用できます。Series を渡すと、これは Series (同じインデックスを持つ) を返し、リストのようなオブジェクトは DatetimeIndex に変換されます。
In [44]: pd.to_datetime(pd.Series(["Jul 31, 2009", "Jan 10, 2010", None]))
Out[44]:
0 2009-07-31
1 2010-01-10
2 NaT
dtype: datetime64[ns]
In [45]: pd.to_datetime(["2005/11/23", "2010/12/31"])
Out[45]: DatetimeIndex(['2005-11-23', '2010-12-31'], dtype='datetime64[ns]', freq=None)
日付が日を最初に始める (つまり、ヨーロッパ式) 場合、dayfirst フラグを渡すことができます。
In [46]: pd.to_datetime(["04-01-2012 10:00"], dayfirst=True)
Out[46]: DatetimeIndex(['2012-01-04 10:00:00'], dtype='datetime64[ns]', freq=None)
In [47]: pd.to_datetime(["04-14-2012 10:00"], dayfirst=True)
Out[47]: DatetimeIndex(['2012-04-14 10:00:00'], dtype='datetime64[ns]', freq=None)
警告
上記の例では、dayfirst は厳密ではないことがわかります。日付が最初の日として解析できない場合、dayfirst が False であるかのように解析され、警告も発生します。
to_datetime に単一の文字列を渡すと、単一の Timestamp が返されます。Timestamp も文字列入力を受け入れますが、dayfirst や format のような文字列解析オプションは受け入れないため、これらが必要な場合は to_datetime を使用してください。
In [48]: pd.to_datetime("2010/11/12")
Out[48]: Timestamp('2010-11-12 00:00:00')
In [49]: pd.Timestamp("2010/11/12")
Out[49]: Timestamp('2010-11-12 00:00:00')
DatetimeIndex コンストラクタを直接使用することもできます。
In [50]: pd.DatetimeIndex(["2018-01-01", "2018-01-03", "2018-01-05"])
Out[50]: DatetimeIndex(['2018-01-01', '2018-01-03', '2018-01-05'], dtype='datetime64[ns]', freq=None)
インデックスの周波数を推論された周波数として作成時に設定するために、文字列 'infer' を渡すことができます。
In [51]: pd.DatetimeIndex(["2018-01-01", "2018-01-03", "2018-01-05"], freq="infer")
Out[51]: DatetimeIndex(['2018-01-01', '2018-01-03', '2018-01-05'], dtype='datetime64[ns]', freq='2D')
format 引数の提供#
必要な日時文字列に加えて、特定の解析を確実にするために format 引数を渡すことができます。これにより、変換が大幅に高速化される可能性もあります。
In [52]: pd.to_datetime("2010/11/12", format="%Y/%m/%d")
Out[52]: Timestamp('2010-11-12 00:00:00')
In [53]: pd.to_datetime("12-11-2010 00:00", format="%d-%m-%Y %H:%M")
Out[53]: Timestamp('2010-11-12 00:00:00')
format オプションを指定する際に利用できる選択肢の詳細については、Python のdatetime ドキュメントを参照してください。
複数の DataFrame 列からの日時の組み立て#
整数または文字列列の DataFrame を渡して、Timestamps の Series に組み立てることもできます。
In [54]: df = pd.DataFrame(
....: {"year": [2015, 2016], "month": [2, 3], "day": [4, 5], "hour": [2, 3]}
....: )
....:
In [55]: pd.to_datetime(df)
Out[55]:
0 2015-02-04 02:00:00
1 2016-03-05 03:00:00
dtype: datetime64[ns]
組み立てに必要な列のみを渡すことができます。
In [56]: pd.to_datetime(df[["year", "month", "day"]])
Out[56]:
0 2015-02-04
1 2016-03-05
dtype: datetime64[ns]
pd.to_datetime は、列名に日時コンポーネントの標準的な指定子を探します。
必須:
year,month,dayオプション:
hour,minute,second,millisecond,microsecond,nanosecond
無効なデータ#
デフォルトの動作である errors='raise' は、解析できない場合にエラーを発生させます。
In [57]: pd.to_datetime(['2009/07/31', 'asd'], errors='raise')
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[57], line 1
----> 1 pd.to_datetime(['2009/07/31', 'asd'], errors='raise')
File ~/work/pandas/pandas/pandas/core/tools/datetimes.py:1104, in to_datetime(arg, errors, dayfirst, yearfirst, utc, format, exact, unit, infer_datetime_format, origin, cache)
1102 result = _convert_and_box_cache(argc, cache_array)
1103 else:
-> 1104 result = convert_listlike(argc, format)
1105 else:
1106 result = convert_listlike(np.array([arg]), format)[0]
File ~/work/pandas/pandas/pandas/core/tools/datetimes.py:435, in _convert_listlike_datetimes(arg, format, name, utc, unit, errors, dayfirst, yearfirst, exact)
433 # `format` could be inferred, or user didn't ask for mixed-format parsing.
434 if format is not None and format != "mixed":
--> 435 return _array_strptime_with_fallback(arg, name, utc, format, exact, errors)
437 result, tz_parsed = objects_to_datetime64(
438 arg,
439 dayfirst=dayfirst,
(...)
443 allow_object=True,
444 )
446 if tz_parsed is not None:
447 # We can take a shortcut since the datetime64 numpy array
448 # is in UTC
File ~/work/pandas/pandas/pandas/core/tools/datetimes.py:469, in _array_strptime_with_fallback(arg, name, utc, fmt, exact, errors)
458 def _array_strptime_with_fallback(
459 arg,
460 name,
(...)
464 errors: str,
465 ) -> Index:
466 """
467 Call array_strptime, with fallback behavior depending on 'errors'.
468 """
--> 469 result, tz_out = array_strptime(arg, fmt, exact=exact, errors=errors, utc=utc)
470 if tz_out is not None:
471 unit = np.datetime_data(result.dtype)[0]
File ~/work/pandas/pandas/pandas/_libs/tslibs/strptime.pyx:501, in pandas._libs.tslibs.strptime.array_strptime()
File ~/work/pandas/pandas/pandas/_libs/tslibs/strptime.pyx:451, in pandas._libs.tslibs.strptime.array_strptime()
File ~/work/pandas/pandas/pandas/_libs/tslibs/strptime.pyx:583, in pandas._libs.tslibs.strptime._parse_with_format()
ValueError: time data "asd" doesn't match format "%Y/%m/%d", at position 1. You might want to try:
- passing `format` if your strings have a consistent format;
- passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
- passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
解析できないデータを NaT (not a time) に変換するには errors='coerce' を渡します。
In [58]: pd.to_datetime(["2009/07/31", "asd"], errors="coerce")
Out[58]: DatetimeIndex(['2009-07-31', 'NaT'], dtype='datetime64[ns]', freq=None)
エポックタイムスタンプ#
pandas は、整数または浮動小数点のエポック時刻を Timestamp および DatetimeIndex に変換する機能をサポートしています。デフォルトの単位はナノ秒ですが、これは Timestamp オブジェクトが内部でどのように保存されているかによるものです。ただし、エポックは別の unit で保存されていることが多く、これを指定できます。これらは、origin パラメータで指定された開始点から計算されます。
In [59]: pd.to_datetime(
....: [1349720105, 1349806505, 1349892905, 1349979305, 1350065705], unit="s"
....: )
....:
Out[59]:
DatetimeIndex(['2012-10-08 18:15:05', '2012-10-09 18:15:05',
'2012-10-10 18:15:05', '2012-10-11 18:15:05',
'2012-10-12 18:15:05'],
dtype='datetime64[ns]', freq=None)
In [60]: pd.to_datetime(
....: [1349720105100, 1349720105200, 1349720105300, 1349720105400, 1349720105500],
....: unit="ms",
....: )
....:
Out[60]:
DatetimeIndex(['2012-10-08 18:15:05.100000', '2012-10-08 18:15:05.200000',
'2012-10-08 18:15:05.300000', '2012-10-08 18:15:05.400000',
'2012-10-08 18:15:05.500000'],
dtype='datetime64[ns]', freq=None)
注
unit パラメータは、上記で説明した format パラメータと同じ文字列を使用しません。利用可能な単位は、pandas.to_datetime() のドキュメントに記載されています。
tz 引数を指定して、エポックタイムスタンプを持つ Timestamp または DatetimeIndex を構築すると、ValueError が発生します。別のタイムゾーンのウォールタイムにエポックがある場合、エポックをタイムゾーン非対応のタイムスタンプとして読み込み、適切なタイムゾーンにローカライズできます。
In [61]: pd.Timestamp(1262347200000000000).tz_localize("US/Pacific")
Out[61]: Timestamp('2010-01-01 12:00:00-0800', tz='US/Pacific')
In [62]: pd.DatetimeIndex([1262347200000000000]).tz_localize("US/Pacific")
Out[62]: DatetimeIndex(['2010-01-01 12:00:00-08:00'], dtype='datetime64[ns, US/Pacific]', freq=None)
注
エポック時刻は、最も近いナノ秒に丸められます。
警告
浮動小数点エポック時刻の変換は、不正確で予期せぬ結果につながる可能性があります。Python の浮動小数点数は、10進数で約15桁の精度を持ちます。浮動小数点数から高精度の Timestamp への変換における丸めは避けられません。正確な精度を達成する唯一の方法は、固定幅型 (例: int64) を使用することです。
In [63]: pd.to_datetime([1490195805.433, 1490195805.433502912], unit="s")
Out[63]: DatetimeIndex(['2017-03-22 15:16:45.433000088', '2017-03-22 15:16:45.433502913'], dtype='datetime64[ns]', freq=None)
In [64]: pd.to_datetime(1490195805433502912, unit="ns")
Out[64]: Timestamp('2017-03-22 15:16:45.433502912')
タイムスタンプからエポックへ#
上記の操作を逆にする、つまり Timestamp から「Unix」エポックに変換するには、
In [65]: stamps = pd.date_range("2012-10-08 18:15:05", periods=4, freq="D")
In [66]: stamps
Out[66]:
DatetimeIndex(['2012-10-08 18:15:05', '2012-10-09 18:15:05',
'2012-10-10 18:15:05', '2012-10-11 18:15:05'],
dtype='datetime64[ns]', freq='D')
エポック (1970年1月1日UTCの真夜中) を減算し、その後、「単位」(1秒) で切り捨て除算します。
In [67]: (stamps - pd.Timestamp("1970-01-01")) // pd.Timedelta("1s")
Out[67]: Index([1349720105, 1349806505, 1349892905, 1349979305], dtype='int64')
origin パラメーターの使用#
origin パラメータを使用すると、DatetimeIndex を作成するための代替開始点を指定できます。たとえば、1960-01-01 を開始日として使用するには、
In [68]: pd.to_datetime([1, 2, 3], unit="D", origin=pd.Timestamp("1960-01-01"))
Out[68]: DatetimeIndex(['1960-01-02', '1960-01-03', '1960-01-04'], dtype='datetime64[ns]', freq=None)
デフォルトは origin='unix' に設定されており、これは 1970-01-01 00:00:00 をデフォルトとします。これは一般的に「Unixエポック」またはPOSIX時間と呼ばれます。
In [69]: pd.to_datetime([1, 2, 3], unit="D")
Out[69]: DatetimeIndex(['1970-01-02', '1970-01-03', '1970-01-04'], dtype='datetime64[ns]', freq=None)
タイムスタンプの範囲の生成#
タイムスタンプを持つインデックスを生成するには、DatetimeIndex または Index コンストラクタを使用し、datetime オブジェクトのリストを渡します。
In [70]: dates = [
....: datetime.datetime(2012, 5, 1),
....: datetime.datetime(2012, 5, 2),
....: datetime.datetime(2012, 5, 3),
....: ]
....:
# Note the frequency information
In [71]: index = pd.DatetimeIndex(dates)
In [72]: index
Out[72]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)
# Automatically converted to DatetimeIndex
In [73]: index = pd.Index(dates)
In [74]: index
Out[74]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)
実際には、非常に長いタイムスタンプを多数含むインデックスが必要になることが多いため、これは非常に面倒になります。規則的な頻度でタイムスタンプが必要な場合は、date_range() 関数と bdate_range() 関数を使用して DatetimeIndex を作成できます。date_range のデフォルトの頻度は暦日ですが、bdate_range のデフォルトは営業日です。
In [75]: start = datetime.datetime(2011, 1, 1)
In [76]: end = datetime.datetime(2012, 1, 1)
In [77]: index = pd.date_range(start, end)
In [78]: index
Out[78]:
DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04',
'2011-01-05', '2011-01-06', '2011-01-07', '2011-01-08',
'2011-01-09', '2011-01-10',
...
'2011-12-23', '2011-12-24', '2011-12-25', '2011-12-26',
'2011-12-27', '2011-12-28', '2011-12-29', '2011-12-30',
'2011-12-31', '2012-01-01'],
dtype='datetime64[ns]', length=366, freq='D')
In [79]: index = pd.bdate_range(start, end)
In [80]: index
Out[80]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07', '2011-01-10', '2011-01-11', '2011-01-12',
'2011-01-13', '2011-01-14',
...
'2011-12-19', '2011-12-20', '2011-12-21', '2011-12-22',
'2011-12-23', '2011-12-26', '2011-12-27', '2011-12-28',
'2011-12-29', '2011-12-30'],
dtype='datetime64[ns]', length=260, freq='B')
date_range や bdate_range のような便利な関数は、様々なオフセットエイリアスを利用できます。
In [81]: pd.date_range(start, periods=1000, freq="ME")
Out[81]:
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-30',
'2011-05-31', '2011-06-30', '2011-07-31', '2011-08-31',
'2011-09-30', '2011-10-31',
...
'2093-07-31', '2093-08-31', '2093-09-30', '2093-10-31',
'2093-11-30', '2093-12-31', '2094-01-31', '2094-02-28',
'2094-03-31', '2094-04-30'],
dtype='datetime64[ns]', length=1000, freq='ME')
In [82]: pd.bdate_range(start, periods=250, freq="BQS")
Out[82]:
DatetimeIndex(['2011-01-03', '2011-04-01', '2011-07-01', '2011-10-03',
'2012-01-02', '2012-04-02', '2012-07-02', '2012-10-01',
'2013-01-01', '2013-04-01',
...
'2071-01-01', '2071-04-01', '2071-07-01', '2071-10-01',
'2072-01-01', '2072-04-01', '2072-07-01', '2072-10-03',
'2073-01-02', '2073-04-03'],
dtype='datetime64[ns]', length=250, freq='BQS-JAN')
date_range と bdate_range を使用すると、start、end、periods、freq などのさまざまなパラメータの組み合わせを使用して、日付の範囲を簡単に生成できます。開始日と終了日は厳密に含まれるため、指定された範囲外の日付は生成されません。
In [83]: pd.date_range(start, end, freq="BME")
Out[83]:
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29',
'2011-05-31', '2011-06-30', '2011-07-29', '2011-08-31',
'2011-09-30', '2011-10-31', '2011-11-30', '2011-12-30'],
dtype='datetime64[ns]', freq='BME')
In [84]: pd.date_range(start, end, freq="W")
Out[84]:
DatetimeIndex(['2011-01-02', '2011-01-09', '2011-01-16', '2011-01-23',
'2011-01-30', '2011-02-06', '2011-02-13', '2011-02-20',
'2011-02-27', '2011-03-06', '2011-03-13', '2011-03-20',
'2011-03-27', '2011-04-03', '2011-04-10', '2011-04-17',
'2011-04-24', '2011-05-01', '2011-05-08', '2011-05-15',
'2011-05-22', '2011-05-29', '2011-06-05', '2011-06-12',
'2011-06-19', '2011-06-26', '2011-07-03', '2011-07-10',
'2011-07-17', '2011-07-24', '2011-07-31', '2011-08-07',
'2011-08-14', '2011-08-21', '2011-08-28', '2011-09-04',
'2011-09-11', '2011-09-18', '2011-09-25', '2011-10-02',
'2011-10-09', '2011-10-16', '2011-10-23', '2011-10-30',
'2011-11-06', '2011-11-13', '2011-11-20', '2011-11-27',
'2011-12-04', '2011-12-11', '2011-12-18', '2011-12-25',
'2012-01-01'],
dtype='datetime64[ns]', freq='W-SUN')
In [85]: pd.bdate_range(end=end, periods=20)
Out[85]:
DatetimeIndex(['2011-12-05', '2011-12-06', '2011-12-07', '2011-12-08',
'2011-12-09', '2011-12-12', '2011-12-13', '2011-12-14',
'2011-12-15', '2011-12-16', '2011-12-19', '2011-12-20',
'2011-12-21', '2011-12-22', '2011-12-23', '2011-12-26',
'2011-12-27', '2011-12-28', '2011-12-29', '2011-12-30'],
dtype='datetime64[ns]', freq='B')
In [86]: pd.bdate_range(start=start, periods=20)
Out[86]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07', '2011-01-10', '2011-01-11', '2011-01-12',
'2011-01-13', '2011-01-14', '2011-01-17', '2011-01-18',
'2011-01-19', '2011-01-20', '2011-01-21', '2011-01-24',
'2011-01-25', '2011-01-26', '2011-01-27', '2011-01-28'],
dtype='datetime64[ns]', freq='B')
start、end、periods を指定すると、start から end まで均等に間隔が空けられた日付の範囲が生成され、結果の DatetimeIndex には periods 個の要素が含まれます。
In [87]: pd.date_range("2018-01-01", "2018-01-05", periods=5)
Out[87]:
DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
'2018-01-05'],
dtype='datetime64[ns]', freq=None)
In [88]: pd.date_range("2018-01-01", "2018-01-05", periods=10)
Out[88]:
DatetimeIndex(['2018-01-01 00:00:00', '2018-01-01 10:40:00',
'2018-01-01 21:20:00', '2018-01-02 08:00:00',
'2018-01-02 18:40:00', '2018-01-03 05:20:00',
'2018-01-03 16:00:00', '2018-01-04 02:40:00',
'2018-01-04 13:20:00', '2018-01-05 00:00:00'],
dtype='datetime64[ns]', freq=None)
カスタム周波数範囲#
bdate_range は、weekmask と holidays パラメータを使用して、カスタム周波数日付の範囲も生成できます。これらのパラメータは、カスタム周波数文字列が渡された場合にのみ使用されます。
In [89]: weekmask = "Mon Wed Fri"
In [90]: holidays = [datetime.datetime(2011, 1, 5), datetime.datetime(2011, 3, 14)]
In [91]: pd.bdate_range(start, end, freq="C", weekmask=weekmask, holidays=holidays)
Out[91]:
DatetimeIndex(['2011-01-03', '2011-01-07', '2011-01-10', '2011-01-12',
'2011-01-14', '2011-01-17', '2011-01-19', '2011-01-21',
'2011-01-24', '2011-01-26',
...
'2011-12-09', '2011-12-12', '2011-12-14', '2011-12-16',
'2011-12-19', '2011-12-21', '2011-12-23', '2011-12-26',
'2011-12-28', '2011-12-30'],
dtype='datetime64[ns]', length=154, freq='C')
In [92]: pd.bdate_range(start, end, freq="CBMS", weekmask=weekmask)
Out[92]:
DatetimeIndex(['2011-01-03', '2011-02-02', '2011-03-02', '2011-04-01',
'2011-05-02', '2011-06-01', '2011-07-01', '2011-08-01',
'2011-09-02', '2011-10-03', '2011-11-02', '2011-12-02'],
dtype='datetime64[ns]', freq='CBMS')
参照
タイムスタンプの制限#
タイムスタンプ表現の限界は、選択された解像度に依存します。ナノ秒解像度の場合、64ビット整数を使用して表現できる時間範囲は約584年に制限されます。
In [93]: pd.Timestamp.min
Out[93]: Timestamp('1677-09-21 00:12:43.145224193')
In [94]: pd.Timestamp.max
Out[94]: Timestamp('2262-04-11 23:47:16.854775807')
秒分解能を選択すると、利用可能な範囲は +/- 2.9e11 年 に拡大します。異なる分解能は as_unit を介して相互に変換できます。
参照
インデックス付け#
DatetimeIndex の主な用途の1つは、pandas オブジェクトのインデックスとして使用することです。DatetimeIndex クラスには、時系列関連の多くの最適化が含まれています。
様々なオフセットに対する広範囲の日付は、後続の日付範囲の生成を非常に高速にするために (単にスライスを取得するだけで済むように)、内部で事前に計算されキャッシュされています。
pandas オブジェクトの
shiftメソッドを使用した高速なシフト。同じ周波数の重なり合う
DatetimeIndexオブジェクトの結合は非常に高速です (高速なデータアラインメントに重要)。year、monthなどのプロパティを介した日付フィールドへの迅速なアクセス。snapのような正規化関数や非常に高速なasofロジック。
DatetimeIndex オブジェクトには、通常の Index オブジェクトのすべての基本機能と、簡単な周波数処理のための高度な時系列固有のメソッドが豊富に備わっています。
参照
注
pandas はソートされた日付インデックスを強制しませんが、日付がソートされていない場合、これらのメソッドの一部は予期しない、または誤った動作をする可能性があります。
DatetimeIndex は通常のインデックスのように使用でき、選択、スライスなどのすべてのインテリジェントな機能を提供します。
In [95]: rng = pd.date_range(start, end, freq="BME")
In [96]: ts = pd.Series(np.random.randn(len(rng)), index=rng)
In [97]: ts.index
Out[97]:
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29',
'2011-05-31', '2011-06-30', '2011-07-29', '2011-08-31',
'2011-09-30', '2011-10-31', '2011-11-30', '2011-12-30'],
dtype='datetime64[ns]', freq='BME')
In [98]: ts[:5].index
Out[98]:
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29',
'2011-05-31'],
dtype='datetime64[ns]', freq='BME')
In [99]: ts[::2].index
Out[99]:
DatetimeIndex(['2011-01-31', '2011-03-31', '2011-05-31', '2011-07-29',
'2011-09-30', '2011-11-30'],
dtype='datetime64[ns]', freq='2BME')
部分文字列インデックス付け#
タイムスタンプとして解析される日付と文字列をインデックス付けパラメータとして渡すことができます。
In [100]: ts["1/31/2011"]
Out[100]: 0.11920871129693428
In [101]: ts[datetime.datetime(2011, 12, 25):]
Out[101]:
2011-12-30 0.56702
Freq: BME, dtype: float64
In [102]: ts["10/31/2011":"12/31/2011"]
Out[102]:
2011-10-31 0.271860
2011-11-30 -0.424972
2011-12-30 0.567020
Freq: BME, dtype: float64
より長い時系列にアクセスする便宜を図るため、年または年月を文字列として渡すこともできます。
In [103]: ts["2011"]
Out[103]:
2011-01-31 0.119209
2011-02-28 -1.044236
2011-03-31 -0.861849
2011-04-29 -2.104569
2011-05-31 -0.494929
2011-06-30 1.071804
2011-07-29 0.721555
2011-08-31 -0.706771
2011-09-30 -1.039575
2011-10-31 0.271860
2011-11-30 -0.424972
2011-12-30 0.567020
Freq: BME, dtype: float64
In [104]: ts["2011-6"]
Out[104]:
2011-06-30 1.071804
Freq: BME, dtype: float64
このタイプのスライスは、DatetimeIndex を持つ DataFrame でも機能します。部分文字列選択はラベルスライスの一種であるため、終点も含まれます。これには、含まれる日付の対応する時間も含まれます。
警告
DataFrame の行を getitem (例: frame[dtstring]) で単一の文字列でインデックス付けすることは、pandas 1.2.0 から非推奨となり (行をインデックス付けしているのか列を選択しているのかの曖昧さのため)、将来のバージョンで削除されます。.loc を使用した同等の操作 (例: frame.loc[dtstring]) は引き続きサポートされます。
In [105]: dft = pd.DataFrame(
.....: np.random.randn(100000, 1),
.....: columns=["A"],
.....: index=pd.date_range("20130101", periods=100000, freq="min"),
.....: )
.....:
In [106]: dft
Out[106]:
A
2013-01-01 00:00:00 0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00 0.113648
2013-01-01 00:04:00 -1.478427
... ...
2013-03-11 10:35:00 -0.747967
2013-03-11 10:36:00 -0.034523
2013-03-11 10:37:00 -0.201754
2013-03-11 10:38:00 -1.509067
2013-03-11 10:39:00 -1.693043
[100000 rows x 1 columns]
In [107]: dft.loc["2013"]
Out[107]:
A
2013-01-01 00:00:00 0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00 0.113648
2013-01-01 00:04:00 -1.478427
... ...
2013-03-11 10:35:00 -0.747967
2013-03-11 10:36:00 -0.034523
2013-03-11 10:37:00 -0.201754
2013-03-11 10:38:00 -1.509067
2013-03-11 10:39:00 -1.693043
[100000 rows x 1 columns]
これは月の最初の時点から始まり、月の最後の日と時間を含みます。
In [108]: dft["2013-1":"2013-2"]
Out[108]:
A
2013-01-01 00:00:00 0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00 0.113648
2013-01-01 00:04:00 -1.478427
... ...
2013-02-28 23:55:00 0.850929
2013-02-28 23:56:00 0.976712
2013-02-28 23:57:00 -2.693884
2013-02-28 23:58:00 -1.575535
2013-02-28 23:59:00 -1.573517
[84960 rows x 1 columns]
これは最終日のすべての時間を含む終了時刻を指定します
In [109]: dft["2013-1":"2013-2-28"]
Out[109]:
A
2013-01-01 00:00:00 0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00 0.113648
2013-01-01 00:04:00 -1.478427
... ...
2013-02-28 23:55:00 0.850929
2013-02-28 23:56:00 0.976712
2013-02-28 23:57:00 -2.693884
2013-02-28 23:58:00 -1.575535
2013-02-28 23:59:00 -1.573517
[84960 rows x 1 columns]
これは正確な停止時刻を指定します (上記とは異なります)。
In [110]: dft["2013-1":"2013-2-28 00:00:00"]
Out[110]:
A
2013-01-01 00:00:00 0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00 0.113648
2013-01-01 00:04:00 -1.478427
... ...
2013-02-27 23:56:00 1.197749
2013-02-27 23:57:00 0.720521
2013-02-27 23:58:00 -0.072718
2013-02-27 23:59:00 -0.681192
2013-02-28 00:00:00 -0.557501
[83521 rows x 1 columns]
インデックスの一部であるため、含まれる終点で停止しています。
In [111]: dft["2013-1-15":"2013-1-15 12:30:00"]
Out[111]:
A
2013-01-15 00:00:00 -0.984810
2013-01-15 00:01:00 0.941451
2013-01-15 00:02:00 1.559365
2013-01-15 00:03:00 1.034374
2013-01-15 00:04:00 -1.480656
... ...
2013-01-15 12:26:00 0.371454
2013-01-15 12:27:00 -0.930806
2013-01-15 12:28:00 -0.069177
2013-01-15 12:29:00 0.066510
2013-01-15 12:30:00 -0.003945
[751 rows x 1 columns]
DatetimeIndex の部分文字列インデックス付けは、MultiIndex を持つ DataFrame でも機能します。
In [112]: dft2 = pd.DataFrame(
.....: np.random.randn(20, 1),
.....: columns=["A"],
.....: index=pd.MultiIndex.from_product(
.....: [pd.date_range("20130101", periods=10, freq="12h"), ["a", "b"]]
.....: ),
.....: )
.....:
In [113]: dft2
Out[113]:
A
2013-01-01 00:00:00 a -0.298694
b 0.823553
2013-01-01 12:00:00 a 0.943285
b -1.479399
2013-01-02 00:00:00 a -1.643342
... ...
2013-01-04 12:00:00 b 0.069036
2013-01-05 00:00:00 a 0.122297
b 1.422060
2013-01-05 12:00:00 a 0.370079
b 1.016331
[20 rows x 1 columns]
In [114]: dft2.loc["2013-01-05"]
Out[114]:
A
2013-01-05 00:00:00 a 0.122297
b 1.422060
2013-01-05 12:00:00 a 0.370079
b 1.016331
In [115]: idx = pd.IndexSlice
In [116]: dft2 = dft2.swaplevel(0, 1).sort_index()
In [117]: dft2.loc[idx[:, "2013-01-05"], :]
Out[117]:
A
a 2013-01-05 00:00:00 0.122297
2013-01-05 12:00:00 0.370079
b 2013-01-05 00:00:00 1.422060
2013-01-05 12:00:00 1.016331
文字列インデックスによるスライスも UTC オフセットを尊重します。
In [118]: df = pd.DataFrame([0], index=pd.DatetimeIndex(["2019-01-01"], tz="US/Pacific"))
In [119]: df
Out[119]:
0
2019-01-01 00:00:00-08:00 0
In [120]: df["2019-01-01 12:00:00+04:00":"2019-01-01 13:00:00+04:00"]
Out[120]:
0
2019-01-01 00:00:00-08:00 0
スライス vs. 完全一致#
インデックスの解像度に応じて、インデックスパラメータとして使用される同じ文字列は、スライスとして、または完全一致として扱われる可能性があります。文字列がインデックスよりも精度が低い場合、スライスとして扱われ、そうでない場合は完全一致として扱われます。
分単位の解像度を持つインデックスを持つ Series オブジェクトを考えてみましょう。
In [121]: series_minute = pd.Series(
.....: [1, 2, 3],
.....: pd.DatetimeIndex(
.....: ["2011-12-31 23:59:00", "2012-01-01 00:00:00", "2012-01-01 00:02:00"]
.....: ),
.....: )
.....:
In [122]: series_minute.index.resolution
Out[122]: 'minute'
分よりも精度の低いタイムスタンプ文字列は、Series オブジェクトを返します。
In [123]: series_minute["2011-12-31 23"]
Out[123]:
2011-12-31 23:59:00 1
dtype: int64
分単位の解像度 (またはそれ以上の精度) のタイムスタンプ文字列は、代わりにスカラー値を返します。つまり、スライスにキャストされません。
In [124]: series_minute["2011-12-31 23:59"]
Out[124]: 1
In [125]: series_minute["2011-12-31 23:59:00"]
Out[125]: 1
インデックスの解像度が秒の場合、分単位のタイムスタンプは Series を返します。
In [126]: series_second = pd.Series(
.....: [1, 2, 3],
.....: pd.DatetimeIndex(
.....: ["2011-12-31 23:59:59", "2012-01-01 00:00:00", "2012-01-01 00:00:01"]
.....: ),
.....: )
.....:
In [127]: series_second.index.resolution
Out[127]: 'second'
In [128]: series_second["2011-12-31 23:59"]
Out[128]:
2011-12-31 23:59:59 1
dtype: int64
タイムスタンプ文字列がスライスとして扱われる場合、.loc[] を使用して DataFrame をインデックス付けすることもできます。
In [129]: dft_minute = pd.DataFrame(
.....: {"a": [1, 2, 3], "b": [4, 5, 6]}, index=series_minute.index
.....: )
.....:
In [130]: dft_minute.loc["2011-12-31 23"]
Out[130]:
a b
2011-12-31 23:59:00 1 4
警告
ただし、文字列が完全一致として扱われる場合、DataFrame の [] での選択は行単位ではなく列単位になります。インデックス付けの基本を参照してください。たとえば、dft_minute['2011-12-31 23:59'] は、'2012-12-31 23:59' がインデックスと同じ解像度を持ち、そのような名前の列がないため、KeyError を発生させます。
行がスライスとして扱われるか単一の選択として扱われるかにかかわらず、常に明確な選択を行うには、.loc を使用します。
In [131]: dft_minute.loc["2011-12-31 23:59"]
Out[131]:
a 1
b 4
Name: 2011-12-31 23:59:00, dtype: int64
また、DatetimeIndex の解像度は日よりも粗くすることはできません。
In [132]: series_monthly = pd.Series(
.....: [1, 2, 3], pd.DatetimeIndex(["2011-12", "2012-01", "2012-02"])
.....: )
.....:
In [133]: series_monthly.index.resolution
Out[133]: 'day'
In [134]: series_monthly["2011-12"] # returns Series
Out[134]:
2011-12-01 1
dtype: int64
正確なインデックス付け#
前のセクションで説明したように、部分文字列による DatetimeIndex のインデックス付けは、期間の「精度」、つまり、インデックスの解像度に対する間隔の具体性に依存します。対照的に、Timestamp または datetime オブジェクトによるインデックス付けは正確です。これは、オブジェクトが正確な意味を持つためです。これらは、両方の終点を含むという意味論にも従います。
これらの Timestamp と datetime オブジェクトは、明示的に指定されていなくても (0 です)、正確な hours, minutes, seconds を持ちます。
In [135]: dft[datetime.datetime(2013, 1, 1): datetime.datetime(2013, 2, 28)]
Out[135]:
A
2013-01-01 00:00:00 0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00 0.113648
2013-01-01 00:04:00 -1.478427
... ...
2013-02-27 23:56:00 1.197749
2013-02-27 23:57:00 0.720521
2013-02-27 23:58:00 -0.072718
2013-02-27 23:59:00 -0.681192
2013-02-28 00:00:00 -0.557501
[83521 rows x 1 columns]
デフォルトなし。
In [136]: dft[
.....: datetime.datetime(2013, 1, 1, 10, 12, 0): datetime.datetime(
.....: 2013, 2, 28, 10, 12, 0
.....: )
.....: ]
.....:
Out[136]:
A
2013-01-01 10:12:00 0.565375
2013-01-01 10:13:00 0.068184
2013-01-01 10:14:00 0.788871
2013-01-01 10:15:00 -0.280343
2013-01-01 10:16:00 0.931536
... ...
2013-02-28 10:08:00 0.148098
2013-02-28 10:09:00 -0.388138
2013-02-28 10:10:00 0.139348
2013-02-28 10:11:00 0.085288
2013-02-28 10:12:00 0.950146
[83521 rows x 1 columns]
切り捨てと高度なインデックス付け#
スライスに似た便利な truncate() 関数が提供されています。truncate は、スライスが部分的に一致する日付を返すのとは対照的に、DatetimeIndex で指定されていない日付コンポーネントには 0 の値を想定することに注意してください。
In [137]: rng2 = pd.date_range("2011-01-01", "2012-01-01", freq="W")
In [138]: ts2 = pd.Series(np.random.randn(len(rng2)), index=rng2)
In [139]: ts2.truncate(before="2011-11", after="2011-12")
Out[139]:
2011-11-06 0.437823
2011-11-13 -0.293083
2011-11-20 -0.059881
2011-11-27 1.252450
Freq: W-SUN, dtype: float64
In [140]: ts2["2011-11":"2011-12"]
Out[140]:
2011-11-06 0.437823
2011-11-13 -0.293083
2011-11-20 -0.059881
2011-11-27 1.252450
2011-12-04 0.046611
2011-12-11 0.059478
2011-12-18 -0.286539
2011-12-25 0.841669
Freq: W-SUN, dtype: float64
DatetimeIndex の周波数規則性を破るような複雑な高度なインデックス付けでも、周波数は失われますが、DatetimeIndex が結果として返されます。
In [141]: ts2.iloc[[0, 2, 6]].index
Out[141]: DatetimeIndex(['2011-01-02', '2011-01-16', '2011-02-13'], dtype='datetime64[ns]', freq=None)
時刻/日付コンポーネント#
Timestamp または DatetimeIndex のようなタイムスタンプのコレクションからアクセスできるいくつかの時刻/日付プロパティがあります。
プロパティ |
説明 |
|---|---|
year |
日付の年 |
month |
日付の月 |
day |
日付の日 |
hour |
日付の時 |
minute |
日付の分 |
second |
日付の秒 |
microsecond |
日付のマイクロ秒 |
nanosecond |
日付のナノ秒 |
date |
datetime.date を返します (タイムゾーン情報は含まれません)。 |
time |
datetime.time を返します (タイムゾーン情報は含まれません)。 |
timetz |
タイムゾーン情報付きのローカル時刻として datetime.time を返します。 |
dayofyear |
年内の通算日 |
day_of_year |
年内の通算日 |
weekofyear |
年内の週の序数 |
week |
年内の週の序数 |
dayofweek |
曜日の番号。月曜日=0、日曜日=6 |
day_of_week |
曜日の番号。月曜日=0、日曜日=6 |
weekday |
曜日の番号。月曜日=0、日曜日=6 |
quarter |
日付の四半期: 1月~3月=1、4月~6月=2 など。 |
days_in_month |
日付の月の日数 |
is_month_start |
月の最初の日であるかどうかを示す論理値 (周波数によって定義されます) |
is_month_end |
月の最後の日であるかどうかを示す論理値 (周波数によって定義されます) |
is_quarter_start |
四半期の最初の日であるかどうかを示す論理値 (周波数によって定義されます) |
is_quarter_end |
四半期の最後の日であるかどうかを示す論理値 (周波数によって定義されます) |
is_year_start |
年の最初の日であるかどうかを示す論理値 (周波数によって定義されます) |
is_year_end |
年の最後の日であるかどうかを示す論理値 (周波数によって定義されます) |
is_leap_year |
日付が閏年に属するかどうかを示す論理値 |
さらに、日時のような値を持つ Series がある場合、.dt アクセサのセクションで詳しく説明されているように、.dt アクセサを介してこれらのプロパティにアクセスできます。
ISO 8601 標準から ISO 年の年、週、日コンポーネントを取得できます。
In [142]: idx = pd.date_range(start="2019-12-29", freq="D", periods=4)
In [143]: idx.isocalendar()
Out[143]:
year week day
2019-12-29 2019 52 7
2019-12-30 2020 1 1
2019-12-31 2020 1 2
2020-01-01 2020 1 3
In [144]: idx.to_series().dt.isocalendar()
Out[144]:
year week day
2019-12-29 2019 52 7
2019-12-30 2020 1 1
2019-12-31 2020 1 2
2020-01-01 2020 1 3
DateOffset オブジェクト#
以前の例では、周波数文字列 (例: 'D') が、
date_range()を使用する際にDatetimeIndex内の日時がどのように間隔を空けて配置されるかを定義する周波数を指定するために使用されました。PeriodまたはPeriodIndexの頻度
これらの周波数文字列は、DateOffset オブジェクトとそのサブクラスにマッピングされます。DateOffset は、時間期間を表す Timedelta に似ていますが、特定の日付期間ルールに従います。たとえば、Timedelta の日数は常に datetimes を24時間増加させますが、DateOffset の日数は、夏時間のために1日が23時間、24時間、または25時間のいずれであっても、datetimes を翌日の同じ時刻に増加させます。ただし、1時間以下 (Hour、Minute、Second、Milli、Micro、Nano) のすべての DateOffset サブクラスは Timedelta と同様に動作し、絶対時間を尊重します。
基本的な DateOffset は、対応するカレンダー期間によって日時をシフトする dateutil.relativedelta (relativedelta ドキュメント) と同様に動作します。算術演算子 (+) を使用してシフトを実行できます。
# This particular day contains a day light savings time transition
In [145]: ts = pd.Timestamp("2016-10-30 00:00:00", tz="Europe/Helsinki")
# Respects absolute time
In [146]: ts + pd.Timedelta(days=1)
Out[146]: Timestamp('2016-10-30 23:00:00+0200', tz='Europe/Helsinki')
# Respects calendar time
In [147]: ts + pd.DateOffset(days=1)
Out[147]: Timestamp('2016-10-31 00:00:00+0200', tz='Europe/Helsinki')
In [148]: friday = pd.Timestamp("2018-01-05")
In [149]: friday.day_name()
Out[149]: 'Friday'
# Add 2 business days (Friday --> Tuesday)
In [150]: two_business_days = 2 * pd.offsets.BDay()
In [151]: friday + two_business_days
Out[151]: Timestamp('2018-01-09 00:00:00')
In [152]: (friday + two_business_days).day_name()
Out[152]: 'Tuesday'
ほとんどの DateOffset には、関連する周波数文字列、またはオフセットエイリアスがあり、これらは freq キーワード引数に渡すことができます。利用可能な日付オフセットと関連する周波数文字列は以下で見つけることができます。
日付オフセット |
周波数文字列 |
説明 |
|---|---|---|
なし |
一般的なオフセットクラス、デフォルトは絶対24時間 |
|
|
|
営業日 (平日) |
|
カスタム営業日 |
|
|
1週間、オプションで曜日に固定 |
|
|
毎月のy週目のx日 |
|
|
毎月の最終週のx日 |
|
|
暦月末 |
|
|
暦月初め |
|
|
営業月末 |
|
|
営業月初 |
|
|
カスタム営業月末 |
|
|
カスタム営業月初 |
|
|
15日 (または他の日) と暦月末 |
|
|
15日 (または他の日) と暦月初 |
|
|
暦四半期末 |
|
|
暦四半期始め |
|
|
営業四半期末 |
|
|
営業四半期初め |
|
|
小売 (52-53週) 四半期 |
|
|
暦年末 |
|
|
暦年始め |
|
|
営業年末 |
|
|
営業年初め |
|
|
小売 (52-53週) 年 |
|
なし |
イースター休暇 |
|
|
営業時間 |
|
|
カスタム営業時間 |
|
|
1絶対日 |
|
|
1時間 |
|
|
1分 |
|
|
1秒 |
|
|
1ミリ秒 |
|
|
1マイクロ秒 |
|
|
1ナノ秒 |
DateOffsets には、日付をオフセットに対して有効な日付にそれぞれ進める、または戻すための rollforward() メソッドと rollback() メソッドも備わっています。たとえば、ビジネスオフセットは、週末 (土曜日と日曜日) に当たる日付を月曜日に繰り越します。これは、ビジネスオフセットが平日に運用されるためです。
In [153]: ts = pd.Timestamp("2018-01-06 00:00:00")
In [154]: ts.day_name()
Out[154]: 'Saturday'
# BusinessHour's valid offset dates are Monday through Friday
In [155]: offset = pd.offsets.BusinessHour(start="09:00")
# Bring the date to the closest offset date (Monday)
In [156]: offset.rollforward(ts)
Out[156]: Timestamp('2018-01-08 09:00:00')
# Date is brought to the closest offset date first and then the hour is added
In [157]: ts + offset
Out[157]: Timestamp('2018-01-08 10:00:00')
これらの操作は、デフォルトで時間 (時、分など) 情報を保持します。時間を深夜にリセットするには、操作を適用する前または後に normalize() を使用します (時間情報を操作に含めるかどうかに応じて)。
In [158]: ts = pd.Timestamp("2014-01-01 09:00")
In [159]: day = pd.offsets.Day()
In [160]: day + ts
Out[160]: Timestamp('2014-01-02 09:00:00')
In [161]: (day + ts).normalize()
Out[161]: Timestamp('2014-01-02 00:00:00')
In [162]: ts = pd.Timestamp("2014-01-01 22:00")
In [163]: hour = pd.offsets.Hour()
In [164]: hour + ts
Out[164]: Timestamp('2014-01-01 23:00:00')
In [165]: (hour + ts).normalize()
Out[165]: Timestamp('2014-01-01 00:00:00')
In [166]: (hour + pd.Timestamp("2014-01-01 23:30")).normalize()
Out[166]: Timestamp('2014-01-02 00:00:00')
パラメトリックオフセット#
一部のオフセットは、作成時に「パラメーター化」して異なる動作をさせることができます。たとえば、週次データを生成するための Week オフセットは、weekday パラメーターを受け入れ、生成される日付が常に特定の曜日に該当するようにします。
In [167]: d = datetime.datetime(2008, 8, 18, 9, 0)
In [168]: d
Out[168]: datetime.datetime(2008, 8, 18, 9, 0)
In [169]: d + pd.offsets.Week()
Out[169]: Timestamp('2008-08-25 09:00:00')
In [170]: d + pd.offsets.Week(weekday=4)
Out[170]: Timestamp('2008-08-22 09:00:00')
In [171]: (d + pd.offsets.Week(weekday=4)).weekday()
Out[171]: 4
In [172]: d - pd.offsets.Week()
Out[172]: Timestamp('2008-08-11 09:00:00')
normalize オプションは加算と減算に有効です。
In [173]: d + pd.offsets.Week(normalize=True)
Out[173]: Timestamp('2008-08-25 00:00:00')
In [174]: d - pd.offsets.Week(normalize=True)
Out[174]: Timestamp('2008-08-11 00:00:00')
もう1つの例は、YearEnd を特定の終了月でパラメーター化することです。
In [175]: d + pd.offsets.YearEnd()
Out[175]: Timestamp('2008-12-31 09:00:00')
In [176]: d + pd.offsets.YearEnd(month=6)
Out[176]: Timestamp('2009-06-30 09:00:00')
Series / DatetimeIndex とのオフセットの使用#
オフセットは、Series または DatetimeIndex と組み合わせて、各要素にオフセットを適用することができます。
In [177]: rng = pd.date_range("2012-01-01", "2012-01-03")
In [178]: s = pd.Series(rng)
In [179]: rng
Out[179]: DatetimeIndex(['2012-01-01', '2012-01-02', '2012-01-03'], dtype='datetime64[ns]', freq='D')
In [180]: rng + pd.DateOffset(months=2)
Out[180]: DatetimeIndex(['2012-03-01', '2012-03-02', '2012-03-03'], dtype='datetime64[ns]', freq=None)
In [181]: s + pd.DateOffset(months=2)
Out[181]:
0 2012-03-01
1 2012-03-02
2 2012-03-03
dtype: datetime64[ns]
In [182]: s - pd.DateOffset(months=2)
Out[182]:
0 2011-11-01
1 2011-11-02
2 2011-11-03
dtype: datetime64[ns]
オフセットクラスが Timedelta (Day、Hour、Minute、Second、Micro、Milli、Nano) に直接マップされる場合、Timedelta とまったく同じように使用できます。詳細な例については、Timedelta セクションを参照してください。
In [183]: s - pd.offsets.Day(2)
Out[183]:
0 2011-12-30
1 2011-12-31
2 2012-01-01
dtype: datetime64[ns]
In [184]: td = s - pd.Series(pd.date_range("2011-12-29", "2011-12-31"))
In [185]: td
Out[185]:
0 3 days
1 3 days
2 3 days
dtype: timedelta64[ns]
In [186]: td + pd.offsets.Minute(15)
Out[186]:
0 3 days 00:15:00
1 3 days 00:15:00
2 3 days 00:15:00
dtype: timedelta64[ns]
一部のオフセット (例: BQuarterEnd) にはベクトル化された実装がないことに注意してください。それらは引き続き使用できますが、計算が著しく遅くなり、PerformanceWarning が表示される場合があります。
In [187]: rng + pd.offsets.BQuarterEnd()
Out[187]: DatetimeIndex(['2012-03-30', '2012-03-30', '2012-03-30'], dtype='datetime64[ns]', freq=None)
カスタム営業日#
CDay または CustomBusinessDay クラスは、パラメトリックな BusinessDay クラスを提供し、地方の休日や週末の慣習を考慮したカスタマイズされた営業日カレンダーを作成するために使用できます。
興味深い例として、金曜日と土曜日が週末であるエジプトを見てみましょう。
In [188]: weekmask_egypt = "Sun Mon Tue Wed Thu"
# They also observe International Workers' Day so let's
# add that for a couple of years
In [189]: holidays = [
.....: "2012-05-01",
.....: datetime.datetime(2013, 5, 1),
.....: np.datetime64("2014-05-01"),
.....: ]
.....:
In [190]: bday_egypt = pd.offsets.CustomBusinessDay(
.....: holidays=holidays,
.....: weekmask=weekmask_egypt,
.....: )
.....:
In [191]: dt = datetime.datetime(2013, 4, 30)
In [192]: dt + 2 * bday_egypt
Out[192]: Timestamp('2013-05-05 00:00:00')
曜日名にマッピングしてみましょう。
In [193]: dts = pd.date_range(dt, periods=5, freq=bday_egypt)
In [194]: pd.Series(dts.weekday, dts).map(pd.Series("Mon Tue Wed Thu Fri Sat Sun".split()))
Out[194]:
2013-04-30 Tue
2013-05-02 Thu
2013-05-05 Sun
2013-05-06 Mon
2013-05-07 Tue
Freq: C, dtype: object
祝日カレンダーを使用して、祝日のリストを提供できます。詳細については、祝日カレンダーのセクションを参照してください。
In [195]: from pandas.tseries.holiday import USFederalHolidayCalendar
In [196]: bday_us = pd.offsets.CustomBusinessDay(calendar=USFederalHolidayCalendar())
# Friday before MLK Day
In [197]: dt = datetime.datetime(2014, 1, 17)
# Tuesday after MLK Day (Monday is skipped because it's a holiday)
In [198]: dt + bday_us
Out[198]: Timestamp('2014-01-21 00:00:00')
特定の祝日カレンダーを尊重する月次オフセットは、通常の方法で定義できます。
In [199]: bmth_us = pd.offsets.CustomBusinessMonthBegin(calendar=USFederalHolidayCalendar())
# Skip new years
In [200]: dt = datetime.datetime(2013, 12, 17)
In [201]: dt + bmth_us
Out[201]: Timestamp('2014-01-02 00:00:00')
# Define date index with custom offset
In [202]: pd.date_range(start="20100101", end="20120101", freq=bmth_us)
Out[202]:
DatetimeIndex(['2010-01-04', '2010-02-01', '2010-03-01', '2010-04-01',
'2010-05-03', '2010-06-01', '2010-07-01', '2010-08-02',
'2010-09-01', '2010-10-01', '2010-11-01', '2010-12-01',
'2011-01-03', '2011-02-01', '2011-03-01', '2011-04-01',
'2011-05-02', '2011-06-01', '2011-07-01', '2011-08-01',
'2011-09-01', '2011-10-03', '2011-11-01', '2011-12-01'],
dtype='datetime64[ns]', freq='CBMS')
注
周波数文字列「C」はCustomBusinessDay DateOffsetが使用されていることを示します。CustomBusinessDayはパラメータ化されたタイプであるため、CustomBusinessDayのインスタンスが異なる可能性があり、これは「C」周波数文字列からは検出できないことに注意することが重要です。したがって、ユーザーは、ユーザーのアプリケーション内で「C」周波数文字列が常に一貫して使用されるようにする必要があります。
営業時間#
BusinessHour クラスは、BusinessDay での営業時間を表現し、特定の開始時刻と終了時刻を使用できるようにします。
デフォルトでは、BusinessHour は 9:00 - 17:00 を営業時間として使用します。BusinessHour を追加すると、Timestamp は時間単位で増分されます。ターゲットの Timestamp が営業時間外の場合、次の営業時間に移動し、その後増分されます。結果が営業時間の終了を超えた場合、残りの時間は次の営業日に加算されます。
In [203]: bh = pd.offsets.BusinessHour()
In [204]: bh
Out[204]: <BusinessHour: bh=09:00-17:00>
# 2014-08-01 is Friday
In [205]: pd.Timestamp("2014-08-01 10:00").weekday()
Out[205]: 4
In [206]: pd.Timestamp("2014-08-01 10:00") + bh
Out[206]: Timestamp('2014-08-01 11:00:00')
# Below example is the same as: pd.Timestamp('2014-08-01 09:00') + bh
In [207]: pd.Timestamp("2014-08-01 08:00") + bh
Out[207]: Timestamp('2014-08-01 10:00:00')
# If the results is on the end time, move to the next business day
In [208]: pd.Timestamp("2014-08-01 16:00") + bh
Out[208]: Timestamp('2014-08-04 09:00:00')
# Remainings are added to the next day
In [209]: pd.Timestamp("2014-08-01 16:30") + bh
Out[209]: Timestamp('2014-08-04 09:30:00')
# Adding 2 business hours
In [210]: pd.Timestamp("2014-08-01 10:00") + pd.offsets.BusinessHour(2)
Out[210]: Timestamp('2014-08-01 12:00:00')
# Subtracting 3 business hours
In [211]: pd.Timestamp("2014-08-01 10:00") + pd.offsets.BusinessHour(-3)
Out[211]: Timestamp('2014-07-31 15:00:00')
start と end 時刻をキーワードで指定することもできます。引数は hour:minute 表現の str、または datetime.time インスタンスである必要があります。秒、マイクロ秒、ナノ秒を営業時間として指定すると、ValueError が発生します。
In [212]: bh = pd.offsets.BusinessHour(start="11:00", end=datetime.time(20, 0))
In [213]: bh
Out[213]: <BusinessHour: bh=11:00-20:00>
In [214]: pd.Timestamp("2014-08-01 13:00") + bh
Out[214]: Timestamp('2014-08-01 14:00:00')
In [215]: pd.Timestamp("2014-08-01 09:00") + bh
Out[215]: Timestamp('2014-08-01 12:00:00')
In [216]: pd.Timestamp("2014-08-01 18:00") + bh
Out[216]: Timestamp('2014-08-01 19:00:00')
start 時刻が end 時刻よりも遅い場合、深夜の営業時間を表します。この場合、営業時間は深夜を越えて翌日まで重なります。有効な営業時間は、有効な BusinessDay から開始されたかどうかによって区別されます。
In [217]: bh = pd.offsets.BusinessHour(start="17:00", end="09:00")
In [218]: bh
Out[218]: <BusinessHour: bh=17:00-09:00>
In [219]: pd.Timestamp("2014-08-01 17:00") + bh
Out[219]: Timestamp('2014-08-01 18:00:00')
In [220]: pd.Timestamp("2014-08-01 23:00") + bh
Out[220]: Timestamp('2014-08-02 00:00:00')
# Although 2014-08-02 is Saturday,
# it is valid because it starts from 08-01 (Friday).
In [221]: pd.Timestamp("2014-08-02 04:00") + bh
Out[221]: Timestamp('2014-08-02 05:00:00')
# Although 2014-08-04 is Monday,
# it is out of business hours because it starts from 08-03 (Sunday).
In [222]: pd.Timestamp("2014-08-04 04:00") + bh
Out[222]: Timestamp('2014-08-04 18:00:00')
BusinessHour.rollforward と rollback を営業時間外に適用すると、次の営業時間開始または前日の終了になります。他のオフセットとは異なり、BusinessHour.rollforward は定義上 apply とは異なる結果を出力する可能性があります。
これは、ある日の営業終了時刻が翌日の営業開始時刻と等しいためです。例えば、デフォルトの営業時間 (9:00 - 17:00) の場合、2014-08-01 17:00 と 2014-08-04 09:00 の間にはギャップ (0分) がありません。
# This adjusts a Timestamp to business hour edge
In [223]: pd.offsets.BusinessHour().rollback(pd.Timestamp("2014-08-02 15:00"))
Out[223]: Timestamp('2014-08-01 17:00:00')
In [224]: pd.offsets.BusinessHour().rollforward(pd.Timestamp("2014-08-02 15:00"))
Out[224]: Timestamp('2014-08-04 09:00:00')
# It is the same as BusinessHour() + pd.Timestamp('2014-08-01 17:00').
# And it is the same as BusinessHour() + pd.Timestamp('2014-08-04 09:00')
In [225]: pd.offsets.BusinessHour() + pd.Timestamp("2014-08-02 15:00")
Out[225]: Timestamp('2014-08-04 10:00:00')
# BusinessDay results (for reference)
In [226]: pd.offsets.BusinessHour().rollforward(pd.Timestamp("2014-08-02"))
Out[226]: Timestamp('2014-08-04 09:00:00')
# It is the same as BusinessDay() + pd.Timestamp('2014-08-01')
# The result is the same as rollworward because BusinessDay never overlap.
In [227]: pd.offsets.BusinessHour() + pd.Timestamp("2014-08-02")
Out[227]: Timestamp('2014-08-04 10:00:00')
BusinessHour は土曜日と日曜日を休日とみなします。任意の休日を使用するには、次のサブセクションで説明するように、CustomBusinessHour オフセットを使用できます。
カスタム営業時間#
CustomBusinessHour は BusinessHour と CustomBusinessDay の混合であり、任意の休日を指定できます。CustomBusinessHour は、指定されたカスタム休日をスキップすることを除いて、BusinessHour と同じように動作します。
In [228]: from pandas.tseries.holiday import USFederalHolidayCalendar
In [229]: bhour_us = pd.offsets.CustomBusinessHour(calendar=USFederalHolidayCalendar())
# Friday before MLK Day
In [230]: dt = datetime.datetime(2014, 1, 17, 15)
In [231]: dt + bhour_us
Out[231]: Timestamp('2014-01-17 16:00:00')
# Tuesday after MLK Day (Monday is skipped because it's a holiday)
In [232]: dt + bhour_us * 2
Out[232]: Timestamp('2014-01-21 09:00:00')
BusinessHour と CustomBusinessDay の両方がサポートするキーワード引数を使用できます。
In [233]: bhour_mon = pd.offsets.CustomBusinessHour(start="10:00", weekmask="Tue Wed Thu Fri")
# Monday is skipped because it's a holiday, business hour starts from 10:00
In [234]: dt + bhour_mon * 2
Out[234]: Timestamp('2014-01-21 10:00:00')
オフセットエイリアス#
便利な一般的な時系列周波数には、多数の文字列エイリアスが与えられています。これらのエイリアスをオフセットエイリアスと呼びます。
エイリアス |
説明 |
|---|---|
B |
営業日頻度 |
C |
カスタム営業日頻度 |
D |
暦日頻度 |
W |
週次頻度 |
ME |
月末頻度 |
SME |
半月頻度 (15日と月末) |
BME |
営業月末頻度 |
CBME |
カスタム営業月末頻度 |
MS |
月初頻度 |
SMS |
半月月初頻度 (1日と15日) |
BMS |
営業月初頻度 |
CBMS |
カスタム営業月初頻度 |
QE |
四半期末頻度 |
BQE |
営業四半期末頻度 |
QS |
四半期開始頻度 |
BQS |
営業四半期開始頻度 |
YE |
年末頻度 |
BYE |
営業年末頻度 |
YS |
年始頻度 |
BYS |
営業年始頻度 |
h |
時間頻度 |
bh |
営業時間頻度 |
cbh |
カスタム営業時間頻度 |
min |
分単位頻度 |
s |
秒単位頻度 |
ms |
ミリ秒 |
us |
マイクロ秒 |
ns |
ナノ秒 |
バージョン2.2.0から非推奨: エイリアス H, BH, CBH, T, S, L, U, N は、エイリアス h, bh, cbh, min, s, ms, us, ns を支持して非推奨になりました。
注
上記のオフセットエイリアスを使用する場合、
date_range()、bdate_range()などの関数は、start_dateとend_dateで定義された間隔内のタイムスタンプのみを返すことに注意する必要があります。start_dateが周波数に対応しない場合、返されるタイムスタンプは次の有効なタイムスタンプから開始し、end_dateの場合も同様に、返されるタイムスタンプは前の有効なタイムスタンプで停止します。
例えば、オフセット MS の場合、start_date が月の初日でない場合、返されるタイムスタンプは翌月の初日から始まります。end_date が月の初日でない場合、最後に返されるタイムスタンプは対応する月の初日になります。
In [235]: dates_lst_1 = pd.date_range("2020-01-06", "2020-04-03", freq="MS")
In [236]: dates_lst_1
Out[236]: DatetimeIndex(['2020-02-01', '2020-03-01', '2020-04-01'], dtype='datetime64[ns]', freq='MS')
In [237]: dates_lst_2 = pd.date_range("2020-01-01", "2020-04-01", freq="MS")
In [238]: dates_lst_2
Out[238]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01', '2020-04-01'], dtype='datetime64[ns]', freq='MS')
上記の例では、date_range() と bdate_range() が start_date と end_date の間の有効なタイムスタンプのみを返すことがわかります。これらが指定された頻度にとって有効なタイムスタンプでない場合、start_date の場合は次の値に (それぞれ end_date の場合は前の値に) 繰り越されます。
期間エイリアス#
便利な一般的な時系列周波数には、多数の文字列エイリアスが与えられています。これらのエイリアスを期間エイリアスと呼びます。
エイリアス |
説明 |
|---|---|
B |
営業日頻度 |
D |
暦日頻度 |
W |
週次頻度 |
M |
月次頻度 |
Q |
四半期頻度 |
Y |
年次頻度 |
h |
時間頻度 |
min |
分単位頻度 |
s |
秒単位頻度 |
ms |
ミリ秒 |
us |
マイクロ秒 |
ns |
ナノ秒 |
バージョン2.2.0から非推奨: エイリアス A, H, T, S, L, U, N は、エイリアス Y, h, min, s, ms, us, ns を支持して非推奨になりました。
エイリアスの組み合わせ#
これまで見てきたように、エイリアスとオフセットインスタンスはほとんどの関数で交換可能です。
In [239]: pd.date_range(start, periods=5, freq="B")
Out[239]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07'],
dtype='datetime64[ns]', freq='B')
In [240]: pd.date_range(start, periods=5, freq=pd.offsets.BDay())
Out[240]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07'],
dtype='datetime64[ns]', freq='B')
日と日中のオフセットを組み合わせることができます。
In [241]: pd.date_range(start, periods=10, freq="2h20min")
Out[241]:
DatetimeIndex(['2011-01-01 00:00:00', '2011-01-01 02:20:00',
'2011-01-01 04:40:00', '2011-01-01 07:00:00',
'2011-01-01 09:20:00', '2011-01-01 11:40:00',
'2011-01-01 14:00:00', '2011-01-01 16:20:00',
'2011-01-01 18:40:00', '2011-01-01 21:00:00'],
dtype='datetime64[ns]', freq='140min')
In [242]: pd.date_range(start, periods=10, freq="1D10us")
Out[242]:
DatetimeIndex([ '2011-01-01 00:00:00', '2011-01-02 00:00:00.000010',
'2011-01-03 00:00:00.000020', '2011-01-04 00:00:00.000030',
'2011-01-05 00:00:00.000040', '2011-01-06 00:00:00.000050',
'2011-01-07 00:00:00.000060', '2011-01-08 00:00:00.000070',
'2011-01-09 00:00:00.000080', '2011-01-10 00:00:00.000090'],
dtype='datetime64[ns]', freq='86400000010us')
固定オフセット#
一部の周波数については、固定サフィックスを指定できます。
エイリアス |
説明 |
|---|---|
W-SUN |
週次頻度 (日曜日)。'W' と同じ |
W-MON |
週次頻度 (月曜日) |
W-TUE |
週次頻度 (火曜日) |
W-WED |
週次頻度 (水曜日) |
W-THU |
週次頻度 (木曜日) |
W-FRI |
週次頻度 (金曜日) |
W-SAT |
週次頻度 (土曜日) |
(B)Q(E)(S)-DEC |
四半期頻度、年末が12月。'QE' と同じ。 |
(B)Q(E)(S)-JAN |
四半期頻度、年末が1月 |
(B)Q(E)(S)-FEB |
四半期頻度、年末が2月 |
(B)Q(E)(S)-MAR |
四半期頻度、年末が3月 |
(B)Q(E)(S)-APR |
四半期頻度、年末が4月 |
(B)Q(E)(S)-MAY |
四半期頻度、年末が5月 |
(B)Q(E)(S)-JUN |
四半期頻度、年末が6月 |
(B)Q(E)(S)-JUL |
四半期頻度、年末が7月 |
(B)Q(E)(S)-AUG |
四半期頻度、年末が8月 |
(B)Q(E)(S)-SEP |
四半期頻度、年末が9月 |
(B)Q(E)(S)-OCT |
四半期頻度、年末が10月 |
(B)Q(E)(S)-NOV |
四半期頻度、年末が11月 |
(B)Y(E)(S)-DEC |
年次頻度、12月末固定。'YE' と同じ。 |
(B)Y(E)(S)-JAN |
年次頻度、1月末固定 |
(B)Y(E)(S)-FEB |
年次頻度、2月末固定 |
(B)Y(E)(S)-MAR |
年次頻度、3月末固定 |
(B)Y(E)(S)-APR |
年次頻度、4月末固定 |
(B)Y(E)(S)-MAY |
年次頻度、5月末固定 |
(B)Y(E)(S)-JUN |
年次頻度、6月末固定 |
(B)Y(E)(S)-JUL |
年次頻度、7月末固定 |
(B)Y(E)(S)-AUG |
年次頻度、8月末固定 |
(B)Y(E)(S)-SEP |
年次頻度、9月末固定 |
(B)Y(E)(S)-OCT |
年次頻度、10月末固定 |
(B)Y(E)(S)-NOV |
年次頻度、11月末固定 |
これらは、date_range、bdate_range、DatetimeIndex のコンストラクタ、および pandas のその他の時系列関連関数への引数として使用できます。
固定オフセットのセマンティクス#
特定の頻度の開始または終了に固定されているオフセット (MonthEnd、MonthBegin、WeekEnd など) については、順方向および逆方向へのロールに以下のルールが適用されます。
n が 0 でない場合、指定された日付がアンカーポイントにない場合、次の (前の) アンカーポイントにスナップされ、さらに |n|-1 ステップ進むか戻るかします。
In [243]: pd.Timestamp("2014-01-02") + pd.offsets.MonthBegin(n=1)
Out[243]: Timestamp('2014-02-01 00:00:00')
In [244]: pd.Timestamp("2014-01-02") + pd.offsets.MonthEnd(n=1)
Out[244]: Timestamp('2014-01-31 00:00:00')
In [245]: pd.Timestamp("2014-01-02") - pd.offsets.MonthBegin(n=1)
Out[245]: Timestamp('2014-01-01 00:00:00')
In [246]: pd.Timestamp("2014-01-02") - pd.offsets.MonthEnd(n=1)
Out[246]: Timestamp('2013-12-31 00:00:00')
In [247]: pd.Timestamp("2014-01-02") + pd.offsets.MonthBegin(n=4)
Out[247]: Timestamp('2014-05-01 00:00:00')
In [248]: pd.Timestamp("2014-01-02") - pd.offsets.MonthBegin(n=4)
Out[248]: Timestamp('2013-10-01 00:00:00')
指定された日付がアンカーポイントにある場合、それは |n| ポイント前方または後方に移動されます。
In [249]: pd.Timestamp("2014-01-01") + pd.offsets.MonthBegin(n=1)
Out[249]: Timestamp('2014-02-01 00:00:00')
In [250]: pd.Timestamp("2014-01-31") + pd.offsets.MonthEnd(n=1)
Out[250]: Timestamp('2014-02-28 00:00:00')
In [251]: pd.Timestamp("2014-01-01") - pd.offsets.MonthBegin(n=1)
Out[251]: Timestamp('2013-12-01 00:00:00')
In [252]: pd.Timestamp("2014-01-31") - pd.offsets.MonthEnd(n=1)
Out[252]: Timestamp('2013-12-31 00:00:00')
In [253]: pd.Timestamp("2014-01-01") + pd.offsets.MonthBegin(n=4)
Out[253]: Timestamp('2014-05-01 00:00:00')
In [254]: pd.Timestamp("2014-01-31") - pd.offsets.MonthBegin(n=4)
Out[254]: Timestamp('2013-10-01 00:00:00')
n=0 の場合、日付がアンカーポイントにあれば移動せず、そうでなければ次のアンカーポイントに繰り上げられます。
In [255]: pd.Timestamp("2014-01-02") + pd.offsets.MonthBegin(n=0)
Out[255]: Timestamp('2014-02-01 00:00:00')
In [256]: pd.Timestamp("2014-01-02") + pd.offsets.MonthEnd(n=0)
Out[256]: Timestamp('2014-01-31 00:00:00')
In [257]: pd.Timestamp("2014-01-01") + pd.offsets.MonthBegin(n=0)
Out[257]: Timestamp('2014-01-01 00:00:00')
In [258]: pd.Timestamp("2014-01-31") + pd.offsets.MonthEnd(n=0)
Out[258]: Timestamp('2014-01-31 00:00:00')
休日 / 祝日カレンダー#
休日とカレンダーは、CustomBusinessDay とともに使用したり、事前に定義された休日のセットを必要とする他の分析で使用したりするための休日ルールを定義する簡単な方法を提供します。AbstractHolidayCalendar クラスは、休日のリストを返すために必要なすべてのメソッドを提供し、特定の祝日カレンダー クラスで rules のみを定義する必要があります。さらに、start_date および end_date クラス属性は、休日が生成される日付範囲を決定します。これらは、範囲をすべてのカレンダーサブクラスに適用するために AbstractHolidayCalendar クラスで上書きする必要があります。USFederalHolidayCalendar は存在する唯一のカレンダーであり、主に他のカレンダーを開発するための例として機能します。
固定日 (例: 米国の戦没将兵追悼記念日または7月4日) に発生する休日については、その日が週末またはその他の非観察日に当たる場合に、いつその休日を観察するかを決定する遵守規則が定義されています。定義されている遵守規則は以下の通りです。
ルール |
説明 |
|---|---|
nearest_workday |
土曜日を金曜日に、日曜日を月曜日に移動する |
sunday_to_monday |
日曜日を次の月曜日に移動する |
next_monday_or_tuesday |
土曜日を月曜日に、日曜日/月曜日を火曜日に移動する |
previous_friday |
土曜日と日曜日を前の金曜日に移動する |
next_monday |
土曜日と日曜日を次の月曜日に移動する |
休日と祝日カレンダーがどのように定義されているかの例
In [259]: from pandas.tseries.holiday import (
.....: Holiday,
.....: USMemorialDay,
.....: AbstractHolidayCalendar,
.....: nearest_workday,
.....: MO,
.....: )
.....:
In [260]: class ExampleCalendar(AbstractHolidayCalendar):
.....: rules = [
.....: USMemorialDay,
.....: Holiday("July 4th", month=7, day=4, observance=nearest_workday),
.....: Holiday(
.....: "Columbus Day",
.....: month=10,
.....: day=1,
.....: offset=pd.DateOffset(weekday=MO(2)),
.....: ),
.....: ]
.....:
In [261]: cal = ExampleCalendar()
In [262]: cal.holidays(datetime.datetime(2012, 1, 1), datetime.datetime(2012, 12, 31))
Out[262]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None)
- ヒント:
weekday=MO(2) は 2 * Week(weekday=2) と同じです。
このカレンダーを使用すると、インデックスを作成したり、オフセット演算を実行したりする際に、週末や休日 (例: 戦没将兵追悼記念日/独立記念日) がスキップされます。例えば、以下では ExampleCalendar を使用してカスタム営業日オフセットを定義しています。他のオフセットと同様に、DatetimeIndex を作成したり、datetime または Timestamp オブジェクトに追加したりすることができます。
In [263]: pd.date_range(
.....: start="7/1/2012", end="7/10/2012", freq=pd.offsets.CDay(calendar=cal)
.....: ).to_pydatetime()
.....:
Out[263]:
array([datetime.datetime(2012, 7, 2, 0, 0),
datetime.datetime(2012, 7, 3, 0, 0),
datetime.datetime(2012, 7, 5, 0, 0),
datetime.datetime(2012, 7, 6, 0, 0),
datetime.datetime(2012, 7, 9, 0, 0),
datetime.datetime(2012, 7, 10, 0, 0)], dtype=object)
In [264]: offset = pd.offsets.CustomBusinessDay(calendar=cal)
In [265]: datetime.datetime(2012, 5, 25) + offset
Out[265]: Timestamp('2012-05-29 00:00:00')
In [266]: datetime.datetime(2012, 7, 3) + offset
Out[266]: Timestamp('2012-07-05 00:00:00')
In [267]: datetime.datetime(2012, 7, 3) + 2 * offset
Out[267]: Timestamp('2012-07-06 00:00:00')
In [268]: datetime.datetime(2012, 7, 6) + offset
Out[268]: Timestamp('2012-07-09 00:00:00')
範囲は AbstractHolidayCalendar の start_date および end_date クラス属性によって定義されます。デフォルトは以下の通りです。
In [269]: AbstractHolidayCalendar.start_date
Out[269]: Timestamp('1970-01-01 00:00:00')
In [270]: AbstractHolidayCalendar.end_date
Out[270]: Timestamp('2200-12-31 00:00:00')
これらの日付は、属性を datetime/Timestamp/文字列として設定することで上書きできます。
In [271]: AbstractHolidayCalendar.start_date = datetime.datetime(2012, 1, 1)
In [272]: AbstractHolidayCalendar.end_date = datetime.datetime(2012, 12, 31)
In [273]: cal.holidays()
Out[273]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None)
すべてのカレンダー クラスは、get_calendar 関数を使用して名前でアクセスでき、この関数は祝日クラスのインスタンスを返します。インポートされたカレンダー クラスはすべて、この関数によって自動的に利用可能になります。また、HolidayCalendarFactory は、カレンダーの組み合わせや追加ルールを持つカレンダーを簡単に作成できるインターフェースを提供します。
In [274]: from pandas.tseries.holiday import get_calendar, HolidayCalendarFactory, USLaborDay
In [275]: cal = get_calendar("ExampleCalendar")
In [276]: cal.rules
Out[276]:
[Holiday: Memorial Day (month=5, day=31, offset=<DateOffset: weekday=MO(-1)>),
Holiday: July 4th (month=7, day=4, observance=<function nearest_workday at 0x7f9491497c70>),
Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: weekday=MO(+2)>)]
In [277]: new_cal = HolidayCalendarFactory("NewExampleCalendar", cal, USLaborDay)
In [278]: new_cal.rules
Out[278]:
[Holiday: Labor Day (month=9, day=1, offset=<DateOffset: weekday=MO(+1)>),
Holiday: Memorial Day (month=5, day=31, offset=<DateOffset: weekday=MO(-1)>),
Holiday: July 4th (month=7, day=4, observance=<function nearest_workday at 0x7f9491497c70>),
Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: weekday=MO(+2)>)]
リサンプリング#
pandas には、頻度変換(例:秒単位のデータを 5 分単位のデータに変換)の際のリサンプリング操作を実行するための、シンプルで強力かつ効率的な機能があります。これは、金融アプリケーションに限らず、非常によく使われます。
resample() は、時間ベースの groupby の後に、各グループに対して集計メソッドを適用するものです。高度な戦略については、いくつかの クックブックの例 を参照してください。
resample() メソッドは、DataFrameGroupBy オブジェクトから直接使用できます。groupby のドキュメント を参照してください。
基本#
In [290]: rng = pd.date_range("1/1/2012", periods=100, freq="s")
In [291]: ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)
In [292]: ts.resample("5Min").sum()
Out[292]:
2012-01-01 25103
Freq: 5min, dtype: int64
resample 関数は非常に柔軟で、頻度変換とリサンプリング操作を制御するために多くの異なるパラメータを指定できます。
GroupBy 経由で利用可能な組み込みメソッドはすべて、返されるオブジェクトのメソッドとして利用できます。sum、mean、std、sem、max、min、median、first、last、ohlc などが含まれます。
In [293]: ts.resample("5Min").mean()
Out[293]:
2012-01-01 251.03
Freq: 5min, dtype: float64
In [294]: ts.resample("5Min").ohlc()
Out[294]:
open high low close
2012-01-01 308 460 9 205
In [295]: ts.resample("5Min").max()
Out[295]:
2012-01-01 460
Freq: 5min, dtype: int64
ダウンサンプリングの場合、インターバルのどちらの端を閉じるかを指定するために、closed を 'left' または 'right' に設定できます。
In [296]: ts.resample("5Min", closed="right").mean()
Out[296]:
2011-12-31 23:55:00 308.000000
2012-01-01 00:00:00 250.454545
Freq: 5min, dtype: float64
In [297]: ts.resample("5Min", closed="left").mean()
Out[297]:
2012-01-01 251.03
Freq: 5min, dtype: float64
label のようなパラメータは、結果のラベルを操作するために使用されます。label は、結果がインターバルの開始でラベル付けされるか、終了でラベル付けされるかを指定します。
In [298]: ts.resample("5Min").mean() # by default label='left'
Out[298]:
2012-01-01 251.03
Freq: 5min, dtype: float64
In [299]: ts.resample("5Min", label="left").mean()
Out[299]:
2012-01-01 251.03
Freq: 5min, dtype: float64
警告
label と closed のデフォルト値は、'ME'、'YE'、'QE'、'BME'、'BYE'、'BQE'、および 'W' を除くすべての頻度オフセットで 'left' です。これらすべてはデフォルトで 'right' です。
これにより、意図せず将来の値を以前の時点に引き戻すことにつながる可能性があります。次の例では、BusinessDay 頻度でそれが示されています。
In [300]: s = pd.date_range("2000-01-01", "2000-01-05").to_series()
In [301]: s.iloc[2] = pd.NaT
In [302]: s.dt.day_name()
Out[302]:
2000-01-01 Saturday
2000-01-02 Sunday
2000-01-03 NaN
2000-01-04 Tuesday
2000-01-05 Wednesday
Freq: D, dtype: object
# default: label='left', closed='left'
In [303]: s.resample("B").last().dt.day_name()
Out[303]:
1999-12-31 Sunday
2000-01-03 NaN
2000-01-04 Tuesday
2000-01-05 Wednesday
Freq: B, dtype: object
日曜日の値が前の金曜日に引き戻されていることに注目してください。日曜日の値が月曜日に押し出されるような動作を得るには、代わりに以下を使用してください。
In [304]: s.resample("B", label="right", closed="right").last().dt.day_name()
Out[304]:
2000-01-03 Sunday
2000-01-04 Tuesday
2000-01-05 Wednesday
2000-01-06 NaN
Freq: B, dtype: object
axis パラメータは 0 または 1 に設定でき、DataFrame の指定された軸をリサンプリングできます。
kind は 'timestamp' または 'period' に設定でき、結果のインデックスをタイムスタンプ表現とタイムスパン表現の間で変換できます。デフォルトでは、resample は入力表現を保持します。
期間データをリサンプリングする際(詳細は下記参照)、convention は 'start' または 'end' に設定できます。これは、低い頻度の期間が高い頻度の期間にどのように変換されるかを指定します。
アップサンプリング#
アップサンプリングの場合、アップサンプリングの方法と、作成されるギャップを補間するための limit パラメータを指定できます。
# from secondly to every 250 milliseconds
In [305]: ts[:2].resample("250ms").asfreq()
Out[305]:
2012-01-01 00:00:00.000 308.0
2012-01-01 00:00:00.250 NaN
2012-01-01 00:00:00.500 NaN
2012-01-01 00:00:00.750 NaN
2012-01-01 00:00:01.000 204.0
Freq: 250ms, dtype: float64
In [306]: ts[:2].resample("250ms").ffill()
Out[306]:
2012-01-01 00:00:00.000 308
2012-01-01 00:00:00.250 308
2012-01-01 00:00:00.500 308
2012-01-01 00:00:00.750 308
2012-01-01 00:00:01.000 204
Freq: 250ms, dtype: int64
In [307]: ts[:2].resample("250ms").ffill(limit=2)
Out[307]:
2012-01-01 00:00:00.000 308.0
2012-01-01 00:00:00.250 308.0
2012-01-01 00:00:00.500 308.0
2012-01-01 00:00:00.750 NaN
2012-01-01 00:00:01.000 204.0
Freq: 250ms, dtype: float64
スパースなリサンプリング#
スパースな時系列とは、リサンプリングしたい時間量に対してデータポイントが非常に少ないものです。スパースな時系列を単純にアップサンプリングすると、大量の中間値が生成される可能性があります。これらの値を埋めるメソッド(例: fill_method が None の場合)を使用しない場合、中間値は NaN で埋められます。
resample は時間ベースの groupby であるため、以下の方法は、すべてが NaN ではないグループのみを効率的にリサンプリングするためのものです。
In [308]: rng = pd.date_range("2014-1-1", periods=100, freq="D") + pd.Timedelta("1s")
In [309]: ts = pd.Series(range(100), index=rng)
系列の全範囲にリサンプリングしたい場合
In [310]: ts.resample("3min").sum()
Out[310]:
2014-01-01 00:00:00 0
2014-01-01 00:03:00 0
2014-01-01 00:06:00 0
2014-01-01 00:09:00 0
2014-01-01 00:12:00 0
..
2014-04-09 23:48:00 0
2014-04-09 23:51:00 0
2014-04-09 23:54:00 0
2014-04-09 23:57:00 0
2014-04-10 00:00:00 99
Freq: 3min, Length: 47521, dtype: int64
代わりに、次のようにデータポイントがあるグループのみをリサンプリングできます。
In [311]: from functools import partial
In [312]: from pandas.tseries.frequencies import to_offset
In [313]: def round(t, freq):
.....: freq = to_offset(freq)
.....: td = pd.Timedelta(freq)
.....: return pd.Timestamp((t.value // td.value) * td.value)
.....:
In [314]: ts.groupby(partial(round, freq="3min")).sum()
Out[314]:
2014-01-01 0
2014-01-02 1
2014-01-03 2
2014-01-04 3
2014-01-05 4
..
2014-04-06 95
2014-04-07 96
2014-04-08 97
2014-04-09 98
2014-04-10 99
Length: 100, dtype: int64
集計#
resample() メソッドは pandas.api.typing.Resampler インスタンスを返します。aggregating API、groupby API、および window API と同様に、Resampler は選択的にリサンプリングできます。
DataFrame をリサンプリングすると、デフォルトではすべての列に対して同じ関数が適用されます。
In [315]: df = pd.DataFrame(
.....: np.random.randn(1000, 3),
.....: index=pd.date_range("1/1/2012", freq="s", periods=1000),
.....: columns=["A", "B", "C"],
.....: )
.....:
In [316]: r = df.resample("3min")
In [317]: r.mean()
Out[317]:
A B C
2012-01-01 00:00:00 -0.033823 -0.121514 -0.081447
2012-01-01 00:03:00 0.056909 0.146731 -0.024320
2012-01-01 00:06:00 -0.058837 0.047046 -0.052021
2012-01-01 00:09:00 0.063123 -0.026158 -0.066533
2012-01-01 00:12:00 0.186340 -0.003144 0.074752
2012-01-01 00:15:00 -0.085954 -0.016287 -0.050046
標準の getitem を使用して、特定の列または複数の列を選択できます。
In [318]: r["A"].mean()
Out[318]:
2012-01-01 00:00:00 -0.033823
2012-01-01 00:03:00 0.056909
2012-01-01 00:06:00 -0.058837
2012-01-01 00:09:00 0.063123
2012-01-01 00:12:00 0.186340
2012-01-01 00:15:00 -0.085954
Freq: 3min, Name: A, dtype: float64
In [319]: r[["A", "B"]].mean()
Out[319]:
A B
2012-01-01 00:00:00 -0.033823 -0.121514
2012-01-01 00:03:00 0.056909 0.146731
2012-01-01 00:06:00 -0.058837 0.047046
2012-01-01 00:09:00 0.063123 -0.026158
2012-01-01 00:12:00 0.186340 -0.003144
2012-01-01 00:15:00 -0.085954 -0.016287
集計を行う関数のリストまたは辞書を渡すことができ、DataFrame が出力されます。
In [320]: r["A"].agg(["sum", "mean", "std"])
Out[320]:
sum mean std
2012-01-01 00:00:00 -6.088060 -0.033823 1.043263
2012-01-01 00:03:00 10.243678 0.056909 1.058534
2012-01-01 00:06:00 -10.590584 -0.058837 0.949264
2012-01-01 00:09:00 11.362228 0.063123 1.028096
2012-01-01 00:12:00 33.541257 0.186340 0.884586
2012-01-01 00:15:00 -8.595393 -0.085954 1.035476
リサンプリングされた DataFrame に対して、各列に適用する関数のリストを渡すことができ、階層インデックスを持つ集計結果が生成されます。
In [321]: r.agg(["sum", "mean"])
Out[321]:
A ... C
sum mean ... sum mean
2012-01-01 00:00:00 -6.088060 -0.033823 ... -14.660515 -0.081447
2012-01-01 00:03:00 10.243678 0.056909 ... -4.377642 -0.024320
2012-01-01 00:06:00 -10.590584 -0.058837 ... -9.363825 -0.052021
2012-01-01 00:09:00 11.362228 0.063123 ... -11.975895 -0.066533
2012-01-01 00:12:00 33.541257 0.186340 ... 13.455299 0.074752
2012-01-01 00:15:00 -8.595393 -0.085954 ... -5.004580 -0.050046
[6 rows x 6 columns]
aggregate に辞書を渡すことで、DataFrame の列ごとに異なる集計を適用できます。
In [322]: r.agg({"A": "sum", "B": lambda x: np.std(x, ddof=1)})
Out[322]:
A B
2012-01-01 00:00:00 -6.088060 1.001294
2012-01-01 00:03:00 10.243678 1.074597
2012-01-01 00:06:00 -10.590584 0.987309
2012-01-01 00:09:00 11.362228 0.944953
2012-01-01 00:12:00 33.541257 1.095025
2012-01-01 00:15:00 -8.595393 1.035312
関数名は文字列にすることもできます。文字列が有効であるためには、リサンプリングされたオブジェクトに実装されている必要があります。
In [323]: r.agg({"A": "sum", "B": "std"})
Out[323]:
A B
2012-01-01 00:00:00 -6.088060 1.001294
2012-01-01 00:03:00 10.243678 1.074597
2012-01-01 00:06:00 -10.590584 0.987309
2012-01-01 00:09:00 11.362228 0.944953
2012-01-01 00:12:00 33.541257 1.095025
2012-01-01 00:15:00 -8.595393 1.035312
さらに、各列に複数の集計関数を個別に指定することもできます。
In [324]: r.agg({"A": ["sum", "std"], "B": ["mean", "std"]})
Out[324]:
A B
sum std mean std
2012-01-01 00:00:00 -6.088060 1.043263 -0.121514 1.001294
2012-01-01 00:03:00 10.243678 1.058534 0.146731 1.074597
2012-01-01 00:06:00 -10.590584 0.949264 0.047046 0.987309
2012-01-01 00:09:00 11.362228 1.028096 -0.026158 0.944953
2012-01-01 00:12:00 33.541257 0.884586 -0.003144 1.095025
2012-01-01 00:15:00 -8.595393 1.035476 -0.016287 1.035312
DataFrame が datetime-like インデックスを持たず、代わりにフレーム内の datetime-like 列に基づいてリサンプリングしたい場合は、on キーワードに渡すことができます。
In [325]: df = pd.DataFrame(
.....: {"date": pd.date_range("2015-01-01", freq="W", periods=5), "a": np.arange(5)},
.....: index=pd.MultiIndex.from_arrays(
.....: [[1, 2, 3, 4, 5], pd.date_range("2015-01-01", freq="W", periods=5)],
.....: names=["v", "d"],
.....: ),
.....: )
.....:
In [326]: df
Out[326]:
date a
v d
1 2015-01-04 2015-01-04 0
2 2015-01-11 2015-01-11 1
3 2015-01-18 2015-01-18 2
4 2015-01-25 2015-01-25 3
5 2015-02-01 2015-02-01 4
In [327]: df.resample("ME", on="date")[["a"]].sum()
Out[327]:
a
date
2015-01-31 6
2015-02-28 4
同様に、代わりに MultiIndex の datetime-like レベルでリサンプリングしたい場合は、その名前または位置を level キーワードに渡すことができます。
In [328]: df.resample("ME", level="d")[["a"]].sum()
Out[328]:
a
d
2015-01-31 6
2015-02-28 4
グループの繰り返し処理#
Resampler オブジェクトを手に入れれば、グループ化されたデータの繰り返し処理は非常に自然で、itertools.groupby() と同様に機能します。
In [329]: small = pd.Series(
.....: range(6),
.....: index=pd.to_datetime(
.....: [
.....: "2017-01-01T00:00:00",
.....: "2017-01-01T00:30:00",
.....: "2017-01-01T00:31:00",
.....: "2017-01-01T01:00:00",
.....: "2017-01-01T03:00:00",
.....: "2017-01-01T03:05:00",
.....: ]
.....: ),
.....: )
.....:
In [330]: resampled = small.resample("h")
In [331]: for name, group in resampled:
.....: print("Group: ", name)
.....: print("-" * 27)
.....: print(group, end="\n\n")
.....:
Group: 2017-01-01 00:00:00
---------------------------
2017-01-01 00:00:00 0
2017-01-01 00:30:00 1
2017-01-01 00:31:00 2
dtype: int64
Group: 2017-01-01 01:00:00
---------------------------
2017-01-01 01:00:00 3
dtype: int64
Group: 2017-01-01 02:00:00
---------------------------
Series([], dtype: int64)
Group: 2017-01-01 03:00:00
---------------------------
2017-01-01 03:00:00 4
2017-01-01 03:05:00 5
dtype: int64
詳細については、グループの繰り返し処理 または Resampler.__iter__ を参照してください。
origin または offset を使用してビンの開始を調整する#
グルーピングのビンは、時系列の開始点の日の始まりに基づいて調整されます。これは、日の倍数(例:30D)または日を均等に分割する(例:90s または 1min)頻度でうまく機能します。この基準を満たさない一部の頻度では、不整合が生じる可能性があります。この動作を変更するには、引数 origin で固定の Timestamp を指定できます。
例えば
In [332]: start, end = "2000-10-01 23:30:00", "2000-10-02 00:30:00"
In [333]: middle = "2000-10-02 00:00:00"
In [334]: rng = pd.date_range(start, end, freq="7min")
In [335]: ts = pd.Series(np.arange(len(rng)) * 3, index=rng)
In [336]: ts
Out[336]:
2000-10-01 23:30:00 0
2000-10-01 23:37:00 3
2000-10-01 23:44:00 6
2000-10-01 23:51:00 9
2000-10-01 23:58:00 12
2000-10-02 00:05:00 15
2000-10-02 00:12:00 18
2000-10-02 00:19:00 21
2000-10-02 00:26:00 24
Freq: 7min, dtype: int64
ここでわかるように、origin をデフォルト値('start_day')で使用すると、'2000-10-02 00:00:00' 以降の結果は時系列の開始によって同一ではありません。
In [337]: ts.resample("17min", origin="start_day").sum()
Out[337]:
2000-10-01 23:14:00 0
2000-10-01 23:31:00 9
2000-10-01 23:48:00 21
2000-10-02 00:05:00 54
2000-10-02 00:22:00 24
Freq: 17min, dtype: int64
In [338]: ts[middle:end].resample("17min", origin="start_day").sum()
Out[338]:
2000-10-02 00:00:00 33
2000-10-02 00:17:00 45
Freq: 17min, dtype: int64
ここでわかるように、origin を 'epoch' に設定すると、'2000-10-02 00:00:00' 以降の結果は時系列の開始によって同一になります。
In [339]: ts.resample("17min", origin="epoch").sum()
Out[339]:
2000-10-01 23:18:00 0
2000-10-01 23:35:00 18
2000-10-01 23:52:00 27
2000-10-02 00:09:00 39
2000-10-02 00:26:00 24
Freq: 17min, dtype: int64
In [340]: ts[middle:end].resample("17min", origin="epoch").sum()
Out[340]:
2000-10-01 23:52:00 15
2000-10-02 00:09:00 39
2000-10-02 00:26:00 24
Freq: 17min, dtype: int64
必要に応じて、origin にカスタムのタイムスタンプを使用できます。
In [341]: ts.resample("17min", origin="2001-01-01").sum()
Out[341]:
2000-10-01 23:30:00 9
2000-10-01 23:47:00 21
2000-10-02 00:04:00 54
2000-10-02 00:21:00 24
Freq: 17min, dtype: int64
In [342]: ts[middle:end].resample("17min", origin=pd.Timestamp("2001-01-01")).sum()
Out[342]:
2000-10-02 00:04:00 54
2000-10-02 00:21:00 24
Freq: 17min, dtype: int64
必要に応じて、デフォルトの origin に追加される offset Timedelta でビンを調整できます。この時系列の場合、これら2つの例は同等です。
In [343]: ts.resample("17min", origin="start").sum()
Out[343]:
2000-10-01 23:30:00 9
2000-10-01 23:47:00 21
2000-10-02 00:04:00 54
2000-10-02 00:21:00 24
Freq: 17min, dtype: int64
In [344]: ts.resample("17min", offset="23h30min").sum()
Out[344]:
2000-10-01 23:30:00 9
2000-10-01 23:47:00 21
2000-10-02 00:04:00 54
2000-10-02 00:21:00 24
Freq: 17min, dtype: int64
最後の例では origin に 'start' を使用していることに注意してください。この場合、origin は時系列の最初の値に設定されます。
後方リサンプリング#
バージョン 1.3.0 で追加。
ビンの開始を調整する代わりに、与えられた freq で後方リサンプリングを行うためにビンの終了を固定する必要がある場合があります。後方リサンプリングでは、最後の値が最後のビンの境界点として考慮されるべきであるため、デフォルトで closed を 'right' に設定します。
origin を 'end' に設定できます。特定の Timestamp インデックスの値は、現在の Timestamp から freq を引いた期間から現在の Timestamp までの右閉じのリサンプリング結果を表します。
In [345]: ts.resample('17min', origin='end').sum()
Out[345]:
2000-10-01 23:35:00 0
2000-10-01 23:52:00 18
2000-10-02 00:09:00 27
2000-10-02 00:26:00 63
Freq: 17min, dtype: int64
さらに、'start_day' オプションとは対照的に、end_day がサポートされています。これは、最大の Timestamp の翌日の深夜を起点として設定します。
In [346]: ts.resample('17min', origin='end_day').sum()
Out[346]:
2000-10-01 23:38:00 3
2000-10-01 23:55:00 15
2000-10-02 00:12:00 45
2000-10-02 00:29:00 45
Freq: 17min, dtype: int64
上記の結果は、以下の計算により 2000-10-02 00:29:00 を最後のビンの右端として使用しています。
In [347]: ceil_mid = rng.max().ceil('D')
In [348]: freq = pd.offsets.Minute(17)
In [349]: bin_res = ceil_mid - freq * ((ceil_mid - rng.max()) // freq)
In [350]: bin_res
Out[350]: Timestamp('2000-10-02 00:29:00')
タイムスパン表現#
時間の規則的な間隔は pandas では Period オブジェクトで表され、Period オブジェクトのシーケンスは PeriodIndex に収集されます。PeriodIndex は便利な関数 period_range で作成できます。
期間#
Period は時間のスパン(例:1日、1ヶ月、1四半期など)を表します。freq キーワードを使用して、以下のように頻度エイリアスを使ってスパンを指定できます。freq は Period のスパンを表すため、「-3D」のように負の値にすることはできません。
In [351]: pd.Period("2012", freq="Y-DEC")
Out[351]: Period('2012', 'Y-DEC')
In [352]: pd.Period("2012-1-1", freq="D")
Out[352]: Period('2012-01-01', 'D')
In [353]: pd.Period("2012-1-1 19:00", freq="h")
Out[353]: Period('2012-01-01 19:00', 'h')
In [354]: pd.Period("2012-1-1 19:00", freq="5h")
Out[354]: Period('2012-01-01 19:00', '5h')
ピリオドに整数を加算・減算すると、ピリオドが自身の頻度でシフトします。異なる freq (スパン) を持つ Period 間では算術演算は許可されません。
In [355]: p = pd.Period("2012", freq="Y-DEC")
In [356]: p + 1
Out[356]: Period('2013', 'Y-DEC')
In [357]: p - 3
Out[357]: Period('2009', 'Y-DEC')
In [358]: p = pd.Period("2012-01", freq="2M")
In [359]: p + 2
Out[359]: Period('2012-05', '2M')
In [360]: p - 1
Out[360]: Period('2011-11', '2M')
In [361]: p == pd.Period("2012-01", freq="3M")
Out[361]: False
Period の頻度が日単位以上(D、h、min、s、ms、us、ns)の場合、結果が同じ頻度を持つことができる限り、offsets および timedelta 類似のものを加算できます。それ以外の場合、ValueError が発生します。
In [362]: p = pd.Period("2014-07-01 09:00", freq="h")
In [363]: p + pd.offsets.Hour(2)
Out[363]: Period('2014-07-01 11:00', 'h')
In [364]: p + datetime.timedelta(minutes=120)
Out[364]: Period('2014-07-01 11:00', 'h')
In [365]: p + np.timedelta64(7200, "s")
Out[365]: Period('2014-07-01 11:00', 'h')
In [366]: p + pd.offsets.Minute(5)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
File ~/work/pandas/pandas/pandas/_libs/tslibs/period.pyx:1824, in pandas._libs.tslibs.period._Period._add_timedeltalike_scalar()
File ~/work/pandas/pandas/pandas/_libs/tslibs/timedeltas.pyx:278, in pandas._libs.tslibs.timedeltas.delta_to_nanoseconds()
File ~/work/pandas/pandas/pandas/_libs/tslibs/np_datetime.pyx:661, in pandas._libs.tslibs.np_datetime.convert_reso()
ValueError: Cannot losslessly convert units
The above exception was the direct cause of the following exception:
IncompatibleFrequency Traceback (most recent call last)
Cell In[366], line 1
----> 1 p + pd.offsets.Minute(5)
File ~/work/pandas/pandas/pandas/_libs/tslibs/period.pyx:1845, in pandas._libs.tslibs.period._Period.__add__()
File ~/work/pandas/pandas/pandas/_libs/tslibs/period.pyx:1826, in pandas._libs.tslibs.period._Period._add_timedeltalike_scalar()
IncompatibleFrequency: Input cannot be converted to Period(freq=h)
Period が他の頻度を持つ場合、同じ offsets のみを追加できます。それ以外の場合、ValueError が発生します。
In [367]: p = pd.Period("2014-07", freq="M")
In [368]: p + pd.offsets.MonthEnd(3)
Out[368]: Period('2014-10', 'M')
In [369]: p + pd.offsets.MonthBegin(3)
---------------------------------------------------------------------------
IncompatibleFrequency Traceback (most recent call last)
Cell In[369], line 1
----> 1 p + pd.offsets.MonthBegin(3)
File ~/work/pandas/pandas/pandas/_libs/tslibs/period.pyx:1847, in pandas._libs.tslibs.period._Period.__add__()
File ~/work/pandas/pandas/pandas/_libs/tslibs/period.pyx:1837, in pandas._libs.tslibs.period._Period._add_offset()
File ~/work/pandas/pandas/pandas/_libs/tslibs/period.pyx:1732, in pandas._libs.tslibs.period.PeriodMixin._require_matching_freq()
IncompatibleFrequency: Input has different freq=3M from Period(freq=M)
同じ頻度を持つ Period インスタンスの差を取ると、それらの間の頻度単位の数が返されます。
In [370]: pd.Period("2012", freq="Y-DEC") - pd.Period("2002", freq="Y-DEC")
Out[370]: <10 * YearEnds: month=12>
PeriodIndex と period_range#
Period オブジェクトの規則的なシーケンスは PeriodIndex に収集できます。これは period_range の便利な関数を使って構築できます。
In [371]: prng = pd.period_range("1/1/2011", "1/1/2012", freq="M")
In [372]: prng
Out[372]:
PeriodIndex(['2011-01', '2011-02', '2011-03', '2011-04', '2011-05', '2011-06',
'2011-07', '2011-08', '2011-09', '2011-10', '2011-11', '2011-12',
'2012-01'],
dtype='period[M]')
PeriodIndex コンストラクターを直接使用することもできます。
In [373]: pd.PeriodIndex(["2011-1", "2011-2", "2011-3"], freq="M")
Out[373]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]')
乗算された頻度を渡すと、乗算されたスパンを持つ Period のシーケンスが出力されます。
In [374]: pd.period_range(start="2014-01", freq="3M", periods=4)
Out[374]: PeriodIndex(['2014-01', '2014-04', '2014-07', '2014-10'], dtype='period[3M]')
start または end が Period オブジェクトの場合、それらは PeriodIndex コンストラクタの頻度と一致する頻度を持つ PeriodIndex のアンカーエンドポイントとして使用されます。
In [375]: pd.period_range(
.....: start=pd.Period("2017Q1", freq="Q"), end=pd.Period("2017Q2", freq="Q"), freq="M"
.....: )
.....:
Out[375]: PeriodIndex(['2017-03', '2017-04', '2017-05', '2017-06'], dtype='period[M]')
DatetimeIndex と同様に、PeriodIndex も pandas オブジェクトのインデックスとして使用できます。
In [376]: ps = pd.Series(np.random.randn(len(prng)), prng)
In [377]: ps
Out[377]:
2011-01 -2.916901
2011-02 0.514474
2011-03 1.346470
2011-04 0.816397
2011-05 2.258648
2011-06 0.494789
2011-07 0.301239
2011-08 0.464776
2011-09 -1.393581
2011-10 0.056780
2011-11 0.197035
2011-12 2.261385
2012-01 -0.329583
Freq: M, dtype: float64
PeriodIndex は Period と同じ規則で加算と減算をサポートします。
In [378]: idx = pd.period_range("2014-07-01 09:00", periods=5, freq="h")
In [379]: idx
Out[379]:
PeriodIndex(['2014-07-01 09:00', '2014-07-01 10:00', '2014-07-01 11:00',
'2014-07-01 12:00', '2014-07-01 13:00'],
dtype='period[h]')
In [380]: idx + pd.offsets.Hour(2)
Out[380]:
PeriodIndex(['2014-07-01 11:00', '2014-07-01 12:00', '2014-07-01 13:00',
'2014-07-01 14:00', '2014-07-01 15:00'],
dtype='period[h]')
In [381]: idx = pd.period_range("2014-07", periods=5, freq="M")
In [382]: idx
Out[382]: PeriodIndex(['2014-07', '2014-08', '2014-09', '2014-10', '2014-11'], dtype='period[M]')
In [383]: idx + pd.offsets.MonthEnd(3)
Out[383]: PeriodIndex(['2014-10', '2014-11', '2014-12', '2015-01', '2015-02'], dtype='period[M]')
PeriodIndex には period という独自の dtype があります。期間のデータ型 を参照してください。
期間のデータ型#
PeriodIndex にはカスタムの period dtype があります。これは、タイムゾーンを考慮した dtype (datetime64[ns, tz]) に似た pandas 拡張 dtype です。
period dtypeはfreq属性を保持し、頻度文字列を用いてperiod[D]やperiod[M]のようにperiod[freq]と表記されます。
In [384]: pi = pd.period_range("2016-01-01", periods=3, freq="M")
In [385]: pi
Out[385]: PeriodIndex(['2016-01', '2016-02', '2016-03'], dtype='period[M]')
In [386]: pi.dtype
Out[386]: period[M]
period dtype は .astype(...) で使用できます。.asfreq() のように PeriodIndex の freq を変更したり、to_period() のように DatetimeIndex を PeriodIndex に変換したりできます。
# change monthly freq to daily freq
In [387]: pi.astype("period[D]")
Out[387]: PeriodIndex(['2016-01-31', '2016-02-29', '2016-03-31'], dtype='period[D]')
# convert to DatetimeIndex
In [388]: pi.astype("datetime64[ns]")
Out[388]: DatetimeIndex(['2016-01-01', '2016-02-01', '2016-03-01'], dtype='datetime64[ns]', freq='MS')
# convert to PeriodIndex
In [389]: dti = pd.date_range("2011-01-01", freq="ME", periods=3)
In [390]: dti
Out[390]: DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31'], dtype='datetime64[ns]', freq='ME')
In [391]: dti.astype("period[M]")
Out[391]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]')
PeriodIndex の部分文字列インデックス付け#
PeriodIndex は、非単調なインデックスを持つ部分文字列スライスをサポートするようになりました。
DatetimeIndex と同様に、PeriodIndex を持つ Series および DataFrame に日付と文字列を渡すことができます。詳細については、DatetimeIndex の部分文字列インデックス付け を参照してください。
In [392]: ps["2011-01"]
Out[392]: -2.9169013294054507
In [393]: ps[datetime.datetime(2011, 12, 25):]
Out[393]:
2011-12 2.261385
2012-01 -0.329583
Freq: M, dtype: float64
In [394]: ps["10/31/2011":"12/31/2011"]
Out[394]:
2011-10 0.056780
2011-11 0.197035
2011-12 2.261385
Freq: M, dtype: float64
PeriodIndex よりも低い頻度を表す文字列を渡すと、部分的にスライスされたデータが返されます。
In [395]: ps["2011"]
Out[395]:
2011-01 -2.916901
2011-02 0.514474
2011-03 1.346470
2011-04 0.816397
2011-05 2.258648
2011-06 0.494789
2011-07 0.301239
2011-08 0.464776
2011-09 -1.393581
2011-10 0.056780
2011-11 0.197035
2011-12 2.261385
Freq: M, dtype: float64
In [396]: dfp = pd.DataFrame(
.....: np.random.randn(600, 1),
.....: columns=["A"],
.....: index=pd.period_range("2013-01-01 9:00", periods=600, freq="min"),
.....: )
.....:
In [397]: dfp
Out[397]:
A
2013-01-01 09:00 -0.538468
2013-01-01 09:01 -1.365819
2013-01-01 09:02 -0.969051
2013-01-01 09:03 -0.331152
2013-01-01 09:04 -0.245334
... ...
2013-01-01 18:55 0.522460
2013-01-01 18:56 0.118710
2013-01-01 18:57 0.167517
2013-01-01 18:58 0.922883
2013-01-01 18:59 1.721104
[600 rows x 1 columns]
In [398]: dfp.loc["2013-01-01 10h"]
Out[398]:
A
2013-01-01 10:00 -0.308975
2013-01-01 10:01 0.542520
2013-01-01 10:02 1.061068
2013-01-01 10:03 0.754005
2013-01-01 10:04 0.352933
... ...
2013-01-01 10:55 -0.865621
2013-01-01 10:56 -1.167818
2013-01-01 10:57 -2.081748
2013-01-01 10:58 -0.527146
2013-01-01 10:59 0.802298
[60 rows x 1 columns]
DatetimeIndex と同様に、エンドポイントは結果に含まれます。以下の例では、10:00 から 11:59 までのデータをスライスしています。
In [399]: dfp["2013-01-01 10h":"2013-01-01 11h"]
Out[399]:
A
2013-01-01 10:00 -0.308975
2013-01-01 10:01 0.542520
2013-01-01 10:02 1.061068
2013-01-01 10:03 0.754005
2013-01-01 10:04 0.352933
... ...
2013-01-01 11:55 -0.590204
2013-01-01 11:56 1.539990
2013-01-01 11:57 -1.224826
2013-01-01 11:58 0.578798
2013-01-01 11:59 -0.685496
[120 rows x 1 columns]
PeriodIndex を使用した頻度変換とリサンプリング#
Period と PeriodIndex の頻度は asfreq メソッドで変換できます。まず、12月に終了する2011会計年度から始めましょう。
In [400]: p = pd.Period("2011", freq="Y-DEC")
In [401]: p
Out[401]: Period('2011', 'Y-DEC')
これを月次頻度に変換できます。how パラメータを使用して、開始月を返すか終了月を返すかを指定できます。
In [402]: p.asfreq("M", how="start")
Out[402]: Period('2011-01', 'M')
In [403]: p.asfreq("M", how="end")
Out[403]: Period('2011-12', 'M')
利便性のために 's' と 'e' の短縮形が提供されています。
In [404]: p.asfreq("M", "s")
Out[404]: Period('2011-01', 'M')
In [405]: p.asfreq("M", "e")
Out[405]: Period('2011-12', 'M')
「スーパーピリオド」(例:年次頻度は四半期頻度のスーパーピリオド)に変換すると、入力ピリオドを含むスーパーピリオドが自動的に返されます。
In [406]: p = pd.Period("2011-12", freq="M")
In [407]: p.asfreq("Y-NOV")
Out[407]: Period('2012', 'Y-NOV')
11月に年度が終了する年次頻度に変換したため、2011年12月の月次期間は実際には2012 Y-NOV期間に含まれることに注意してください。
固定された頻度での期間変換は、経済学、ビジネス、その他の分野で一般的なさまざまな四半期データを扱うのに特に便利です。多くの組織は、会計年度が開始および終了する月に対して四半期を定義します。したがって、2011年の第1四半期は2010年に開始することも、2011年の数ヶ月後に開始することもできます。固定された頻度により、pandas は Q-JAN から Q-DEC までのすべての四半期頻度で機能します。
Q-DEC は通常のカレンダー四半期を定義します。
In [408]: p = pd.Period("2012Q1", freq="Q-DEC")
In [409]: p.asfreq("D", "s")
Out[409]: Period('2012-01-01', 'D')
In [410]: p.asfreq("D", "e")
Out[410]: Period('2012-03-31', 'D')
Q-MAR は 3 月に会計年度末を定義します。
In [411]: p = pd.Period("2011Q4", freq="Q-MAR")
In [412]: p.asfreq("D", "s")
Out[412]: Period('2011-01-01', 'D')
In [413]: p.asfreq("D", "e")
Out[413]: Period('2011-03-31', 'D')
表現間の変換#
タイムスタンプデータは to_period を使用して PeriodIndex 化されたデータに変換でき、逆に to_timestamp を使用して変換できます。
In [414]: rng = pd.date_range("1/1/2012", periods=5, freq="ME")
In [415]: ts = pd.Series(np.random.randn(len(rng)), index=rng)
In [416]: ts
Out[416]:
2012-01-31 1.931253
2012-02-29 -0.184594
2012-03-31 0.249656
2012-04-30 -0.978151
2012-05-31 -0.873389
Freq: ME, dtype: float64
In [417]: ps = ts.to_period()
In [418]: ps
Out[418]:
2012-01 1.931253
2012-02 -0.184594
2012-03 0.249656
2012-04 -0.978151
2012-05 -0.873389
Freq: M, dtype: float64
In [419]: ps.to_timestamp()
Out[419]:
2012-01-01 1.931253
2012-02-01 -0.184594
2012-03-01 0.249656
2012-04-01 -0.978151
2012-05-01 -0.873389
Freq: MS, dtype: float64
's'と'e'を使うと、期間の開始または終了のタイムスタンプを返すことができることを覚えておいてください。
In [420]: ps.to_timestamp("D", how="s")
Out[420]:
2012-01-01 1.931253
2012-02-01 -0.184594
2012-03-01 0.249656
2012-04-01 -0.978151
2012-05-01 -0.873389
Freq: MS, dtype: float64
ピリオドとタイムスタンプ間の変換により、いくつかの便利な算術関数を使用できます。次の例では、11月に年度末を迎える四半期頻度を、四半期末の翌月の午前9時に変換しています。
In [421]: prng = pd.period_range("1990Q1", "2000Q4", freq="Q-NOV")
In [422]: ts = pd.Series(np.random.randn(len(prng)), prng)
In [423]: ts.index = (prng.asfreq("M", "e") + 1).asfreq("h", "s") + 9
In [424]: ts.head()
Out[424]:
1990-03-01 09:00 -0.109291
1990-06-01 09:00 -0.637235
1990-09-01 09:00 -1.735925
1990-12-01 09:00 2.096946
1991-03-01 09:00 -1.039926
Freq: h, dtype: float64
範囲外のスパンの表現#
Timestamp の範囲外のデータがある場合(タイムスタンプの制限 を参照)、PeriodIndex や Period の Series を使用して計算を行うことができます。
In [425]: span = pd.period_range("1215-01-01", "1381-01-01", freq="D")
In [426]: span
Out[426]:
PeriodIndex(['1215-01-01', '1215-01-02', '1215-01-03', '1215-01-04',
'1215-01-05', '1215-01-06', '1215-01-07', '1215-01-08',
'1215-01-09', '1215-01-10',
...
'1380-12-23', '1380-12-24', '1380-12-25', '1380-12-26',
'1380-12-27', '1380-12-28', '1380-12-29', '1380-12-30',
'1380-12-31', '1381-01-01'],
dtype='period[D]', length=60632)
int64 ベースの YYYYMMDD 表現から変換するには。
In [427]: s = pd.Series([20121231, 20141130, 99991231])
In [428]: s
Out[428]:
0 20121231
1 20141130
2 99991231
dtype: int64
In [429]: def conv(x):
.....: return pd.Period(year=x // 10000, month=x // 100 % 100, day=x % 100, freq="D")
.....:
In [430]: s.apply(conv)
Out[430]:
0 2012-12-31
1 2014-11-30
2 9999-12-31
dtype: period[D]
In [431]: s.apply(conv)[2]
Out[431]: Period('9999-12-31', 'D')
これらは簡単に PeriodIndex に変換できます。
In [432]: span = pd.PeriodIndex(s.apply(conv))
In [433]: span
Out[433]: PeriodIndex(['2012-12-31', '2014-11-30', '9999-12-31'], dtype='period[D]')
タイムゾーンの扱い#
pandas は、pytz および dateutil ライブラリ、または標準ライブラリの datetime.timezone オブジェクトを使用して、異なるタイムゾーンのタイムスタンプを扱うための豊富なサポートを提供します。
タイムゾーンの操作#
デフォルトでは、pandas オブジェクトはタイムゾーンを認識しません。
In [434]: rng = pd.date_range("3/6/2012 00:00", periods=15, freq="D")
In [435]: rng.tz is None
Out[435]: True
これらの日付をタイムゾーンにローカライズ(特定のタイムゾーンをネイティブな日付に割り当てる)するには、tz_localize メソッド、または date_range()、Timestamp、または DatetimeIndex の tz キーワード引数を使用できます。pytz または dateutil のタイムゾーンオブジェクト、または Olson タイムゾーンデータベース文字列を渡すことができます。Olson タイムゾーン文字列はデフォルトで pytz タイムゾーンオブジェクトを返します。dateutil タイムゾーンオブジェクトを返すには、文字列の前に dateutil/ を追加します。
pytzでは、from pytz import common_timezones, all_timezonesを使用して、一般的(およびあまり一般的ではない)なタイムゾーンのリストを見つけることができます。dateutilは OS のタイムゾーンを使用するため、固定されたリストは利用できません。一般的なゾーンの場合、名前はpytzと同じです。
In [436]: import dateutil
# pytz
In [437]: rng_pytz = pd.date_range("3/6/2012 00:00", periods=3, freq="D", tz="Europe/London")
In [438]: rng_pytz.tz
Out[438]: <DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD>
# dateutil
In [439]: rng_dateutil = pd.date_range("3/6/2012 00:00", periods=3, freq="D")
In [440]: rng_dateutil = rng_dateutil.tz_localize("dateutil/Europe/London")
In [441]: rng_dateutil.tz
Out[441]: tzfile('/usr/share/zoneinfo/Europe/London')
# dateutil - utc special case
In [442]: rng_utc = pd.date_range(
.....: "3/6/2012 00:00",
.....: periods=3,
.....: freq="D",
.....: tz=dateutil.tz.tzutc(),
.....: )
.....:
In [443]: rng_utc.tz
Out[443]: tzutc()
# datetime.timezone
In [444]: rng_utc = pd.date_range(
.....: "3/6/2012 00:00",
.....: periods=3,
.....: freq="D",
.....: tz=datetime.timezone.utc,
.....: )
.....:
In [445]: rng_utc.tz
Out[445]: datetime.timezone.utc
UTC タイムゾーンは dateutil の特殊なケースであり、dateutil.tz.tzutc のインスタンスとして明示的に構築する必要があることに注意してください。他のタイムゾーンオブジェクトも最初に明示的に構築できます。
In [446]: import pytz
# pytz
In [447]: tz_pytz = pytz.timezone("Europe/London")
In [448]: rng_pytz = pd.date_range("3/6/2012 00:00", periods=3, freq="D")
In [449]: rng_pytz = rng_pytz.tz_localize(tz_pytz)
In [450]: rng_pytz.tz == tz_pytz
Out[450]: True
# dateutil
In [451]: tz_dateutil = dateutil.tz.gettz("Europe/London")
In [452]: rng_dateutil = pd.date_range("3/6/2012 00:00", periods=3, freq="D", tz=tz_dateutil)
In [453]: rng_dateutil.tz == tz_dateutil
Out[453]: True
タイムゾーンを認識する pandas オブジェクトをあるタイムゾーンから別のタイムゾーンに変換するには、tz_convert メソッドを使用できます。
In [454]: rng_pytz.tz_convert("US/Eastern")
Out[454]:
DatetimeIndex(['2012-03-05 19:00:00-05:00', '2012-03-06 19:00:00-05:00',
'2012-03-07 19:00:00-05:00'],
dtype='datetime64[ns, US/Eastern]', freq=None)
注
pytz タイムゾーンを使用する場合、DatetimeIndex は同じタイムゾーン入力に対して Timestamp とは異なるタイムゾーンオブジェクトを構築します。DatetimeIndex は、異なる UTC オフセットを持つ可能性のある Timestamp オブジェクトのコレクションを保持でき、1つの pytz タイムゾーンインスタンスによって簡潔に表現することはできません。一方、1つの Timestamp は、特定の UTC オフセットを持つ1つの時点を表します。
In [455]: dti = pd.date_range("2019-01-01", periods=3, freq="D", tz="US/Pacific")
In [456]: dti.tz
Out[456]: <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>
In [457]: ts = pd.Timestamp("2019-01-01", tz="US/Pacific")
In [458]: ts.tz
Out[458]: <DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD>
警告
ライブラリ間の変換には注意が必要です。一部のタイムゾーンでは、pytz と dateutil ではゾーンの定義が異なります。これは、US/Eastern のような「標準」ゾーンよりも、珍しいタイムゾーンで問題になります。
警告
タイムゾーンライブラリのバージョン間でのタイムゾーン定義は、同じと見なされない場合があることに注意してください。これにより、あるバージョンを使用してローカライズされた保存データが、別のバージョンで操作された場合に問題が発生する可能性があります。このような状況への対処方法については、こちら を参照してください。
警告
pytz タイムゾーンの場合、タイムゾーンオブジェクトを datetime.datetime コンストラクターに直接渡すことは誤りです(例:datetime.datetime(2011, 1, 1, tzinfo=pytz.timezone('US/Eastern')))。代わりに、pytz タイムゾーンオブジェクトの localize メソッドを使用して datetime をローカライズする必要があります。
警告
将来の時刻については、タイムゾーン間の正確な変換(および UTC への変換)は、各政府によってタイムゾーンの UTC からのオフセットが変更される可能性があるため、どのタイムゾーンライブラリでも保証できないことに注意してください。
警告
2038年1月18日以降の日付を使用する場合、2038年問題によって引き起こされる基盤ライブラリの現在の不備のため、タイムゾーンを考慮した日付への夏時間(DST)調整は適用されません。基盤ライブラリが修正された場合、DST移行は適用されます。
例えば、英国夏時間(通常は GMT+1)である2つの日付の場合、以下の両方のアサートは真と評価されます。
In [459]: d_2037 = "2037-03-31T010101"
In [460]: d_2038 = "2038-03-31T010101"
In [461]: DST = "Europe/London"
In [462]: assert pd.Timestamp(d_2037, tz=DST) != pd.Timestamp(d_2037, tz="GMT")
In [463]: assert pd.Timestamp(d_2038, tz=DST) == pd.Timestamp(d_2038, tz="GMT")
内部的には、すべてのタイムスタンプは UTC で保存されます。タイムゾーンを認識する DatetimeIndex または Timestamp の値は、タイムゾーンにローカライズされたフィールド(日、時、分など)を持ちます。しかし、同じ UTC 値を持つタイムスタンプは、異なるタイムゾーンであっても等しいと見なされます。
In [464]: rng_eastern = rng_utc.tz_convert("US/Eastern")
In [465]: rng_berlin = rng_utc.tz_convert("Europe/Berlin")
In [466]: rng_eastern[2]
Out[466]: Timestamp('2012-03-07 19:00:00-0500', tz='US/Eastern')
In [467]: rng_berlin[2]
Out[467]: Timestamp('2012-03-08 01:00:00+0100', tz='Europe/Berlin')
In [468]: rng_eastern[2] == rng_berlin[2]
Out[468]: True
異なるタイムゾーンの Series 間の操作は、UTC タイムスタンプでデータを整列させ、UTC Series を生成します。
In [469]: ts_utc = pd.Series(range(3), pd.date_range("20130101", periods=3, tz="UTC"))
In [470]: eastern = ts_utc.tz_convert("US/Eastern")
In [471]: berlin = ts_utc.tz_convert("Europe/Berlin")
In [472]: result = eastern + berlin
In [473]: result
Out[473]:
2013-01-01 00:00:00+00:00 0
2013-01-02 00:00:00+00:00 2
2013-01-03 00:00:00+00:00 4
Freq: D, dtype: int64
In [474]: result.index
Out[474]:
DatetimeIndex(['2013-01-01 00:00:00+00:00', '2013-01-02 00:00:00+00:00',
'2013-01-03 00:00:00+00:00'],
dtype='datetime64[ns, UTC]', freq='D')
タイムゾーン情報を削除するには、tz_localize(None) または tz_convert(None) を使用します。tz_localize(None) はタイムゾーンを削除し、ローカル時刻表現を返します。tz_convert(None) は UTC 時刻に変換した後でタイムゾーンを削除します。
In [475]: didx = pd.date_range(start="2014-08-01 09:00", freq="h", periods=3, tz="US/Eastern")
In [476]: didx
Out[476]:
DatetimeIndex(['2014-08-01 09:00:00-04:00', '2014-08-01 10:00:00-04:00',
'2014-08-01 11:00:00-04:00'],
dtype='datetime64[ns, US/Eastern]', freq='h')
In [477]: didx.tz_localize(None)
Out[477]:
DatetimeIndex(['2014-08-01 09:00:00', '2014-08-01 10:00:00',
'2014-08-01 11:00:00'],
dtype='datetime64[ns]', freq=None)
In [478]: didx.tz_convert(None)
Out[478]:
DatetimeIndex(['2014-08-01 13:00:00', '2014-08-01 14:00:00',
'2014-08-01 15:00:00'],
dtype='datetime64[ns]', freq='h')
# tz_convert(None) is identical to tz_convert('UTC').tz_localize(None)
In [479]: didx.tz_convert("UTC").tz_localize(None)
Out[479]:
DatetimeIndex(['2014-08-01 13:00:00', '2014-08-01 14:00:00',
'2014-08-01 15:00:00'],
dtype='datetime64[ns]', freq=None)
フォールド#
曖昧な時刻の場合、pandas はキーワード引数のみの fold 引数を明示的に指定することをサポートしています。夏時間から冬時間への移行時、夏時間によって1つの壁時計時刻が2回発生することがあります。fold は、datetime-like が曖昧な時刻に壁時計が最初に到達する時刻(0)または2番目に到達する時刻(1)に対応するかどうかを記述します。fold は、ネイティブな datetime.datetime からの構築(詳細は datetime ドキュメント を参照)または Timestamp からの構築、またはコンポーネントからの構築(下記参照)の場合にのみサポートされます。pytz タイムゾーンは fold をサポートしないため(曖昧な datetime の処理方法の詳細は pytz ドキュメント を参照)、dateutil タイムゾーンのみがサポートされます(曖昧な datetime を扱う dateutil メソッドの詳細は dateutil ドキュメント を参照)。pytz を使用して曖昧な datetime をローカライズするには、Timestamp.tz_localize() を使用してください。一般的に、曖昧な datetime をローカライズする際に、それらの処理方法を直接制御する必要がある場合は、Timestamp.tz_localize() に依存することをお勧めします。
In [480]: pd.Timestamp(
.....: datetime.datetime(2019, 10, 27, 1, 30, 0, 0),
.....: tz="dateutil/Europe/London",
.....: fold=0,
.....: )
.....:
Out[480]: Timestamp('2019-10-27 01:30:00+0100', tz='dateutil//usr/share/zoneinfo/Europe/London')
In [481]: pd.Timestamp(
.....: year=2019,
.....: month=10,
.....: day=27,
.....: hour=1,
.....: minute=30,
.....: tz="dateutil/Europe/London",
.....: fold=1,
.....: )
.....:
Out[481]: Timestamp('2019-10-27 01:30:00+0000', tz='dateutil//usr/share/zoneinfo/Europe/London')
ローカライズ時の曖昧な時間#
tz_localize は、タイムスタンプの UTC オフセットを決定できない場合があります。これは、現地時間の夏時間(DST)によって、一部の時刻が1日のうちに2回発生するためです(「時計を戻す」)。以下のオプションが利用可能です。
'raise':pytz.AmbiguousTimeErrorを発生させます(デフォルトの動作)。'infer': タイムスタンプの単調性に基づいて正しいオフセットを決定しようとします。'NaT': 曖昧な時刻をNaTで置き換えます。bool:Trueは DST 時刻を、Falseは非 DST 時刻を表します。時系列のシーケンスにはbool値の配列がサポートされています。
In [482]: rng_hourly = pd.DatetimeIndex(
.....: ["11/06/2011 00:00", "11/06/2011 01:00", "11/06/2011 01:00", "11/06/2011 02:00"]
.....: )
.....:
曖昧な時間('11/06/2011 01:00')があるため、これは失敗します。
In [483]: rng_hourly.tz_localize('US/Eastern')
---------------------------------------------------------------------------
AmbiguousTimeError Traceback (most recent call last)
Cell In[483], line 1
----> 1 rng_hourly.tz_localize('US/Eastern')
File ~/work/pandas/pandas/pandas/core/indexes/datetimes.py:293, in DatetimeIndex.tz_localize(self, tz, ambiguous, nonexistent)
286 @doc(DatetimeArray.tz_localize)
287 def tz_localize(
288 self,
(...)
291 nonexistent: TimeNonexistent = "raise",
292 ) -> Self:
--> 293 arr = self._data.tz_localize(tz, ambiguous, nonexistent)
294 return type(self)._simple_new(arr, name=self.name)
File ~/work/pandas/pandas/pandas/core/arrays/_mixins.py:81, in ravel_compat.<locals>.method(self, *args, **kwargs)
78 @wraps(meth)
79 def method(self, *args, **kwargs):
80 if self.ndim == 1:
---> 81 return meth(self, *args, **kwargs)
83 flags = self._ndarray.flags
84 flat = self.ravel("K")
File ~/work/pandas/pandas/pandas/core/arrays/datetimes.py:1090, in DatetimeArray.tz_localize(self, tz, ambiguous, nonexistent)
1087 tz = timezones.maybe_get_tz(tz)
1088 # Convert to UTC
-> 1090 new_dates = tzconversion.tz_localize_to_utc(
1091 self.asi8,
1092 tz,
1093 ambiguous=ambiguous,
1094 nonexistent=nonexistent,
1095 creso=self._creso,
1096 )
1097 new_dates_dt64 = new_dates.view(f"M8[{self.unit}]")
1098 dtype = tz_to_dtype(tz, unit=self.unit)
File ~/work/pandas/pandas/pandas/_libs/tslibs/tzconversion.pyx:371, in pandas._libs.tslibs.tzconversion.tz_localize_to_utc()
AmbiguousTimeError: Cannot infer dst time from 2011-11-06 01:00:00, try using the 'ambiguous' argument
これらの曖昧な時間を次のように指定して処理します。
In [484]: rng_hourly.tz_localize("US/Eastern", ambiguous="infer")
Out[484]:
DatetimeIndex(['2011-11-06 00:00:00-04:00', '2011-11-06 01:00:00-04:00',
'2011-11-06 01:00:00-05:00', '2011-11-06 02:00:00-05:00'],
dtype='datetime64[ns, US/Eastern]', freq=None)
In [485]: rng_hourly.tz_localize("US/Eastern", ambiguous="NaT")
Out[485]:
DatetimeIndex(['2011-11-06 00:00:00-04:00', 'NaT', 'NaT',
'2011-11-06 02:00:00-05:00'],
dtype='datetime64[ns, US/Eastern]', freq=None)
In [486]: rng_hourly.tz_localize("US/Eastern", ambiguous=[True, True, False, False])
Out[486]:
DatetimeIndex(['2011-11-06 00:00:00-04:00', '2011-11-06 01:00:00-04:00',
'2011-11-06 01:00:00-05:00', '2011-11-06 02:00:00-05:00'],
dtype='datetime64[ns, US/Eastern]', freq=None)
ローカライズ時の存在しない時間#
DST の移行により、現地時間が1時間進み、存在しない現地時間が作成されることもあります(「時計が春に進む」)。存在しない時間で時系列をローカライズする動作は、nonexistent 引数で制御できます。以下のオプションが利用可能です。
'raise':pytz.NonExistentTimeErrorを発生させます(デフォルトの動作)。'NaT': 存在しない時間をNaTで置き換えます。'shift_forward': 存在しない時間を最も近い実際の時間に前方にシフトします。'shift_backward': 存在しない時間を最も近い実際の時間に後方にシフトします。Timedelta オブジェクト: 存在しない時間を timedelta の期間だけシフトします。
In [487]: dti = pd.date_range(start="2015-03-29 02:30:00", periods=3, freq="h")
# 2:30 is a nonexistent time
存在しない時刻のローカライズは、デフォルトでエラーを発生させます。
In [488]: dti.tz_localize('Europe/Warsaw')
---------------------------------------------------------------------------
NonExistentTimeError Traceback (most recent call last)
Cell In[488], line 1
----> 1 dti.tz_localize('Europe/Warsaw')
File ~/work/pandas/pandas/pandas/core/indexes/datetimes.py:293, in DatetimeIndex.tz_localize(self, tz, ambiguous, nonexistent)
286 @doc(DatetimeArray.tz_localize)
287 def tz_localize(
288 self,
(...)
291 nonexistent: TimeNonexistent = "raise",
292 ) -> Self:
--> 293 arr = self._data.tz_localize(tz, ambiguous, nonexistent)
294 return type(self)._simple_new(arr, name=self.name)
File ~/work/pandas/pandas/pandas/core/arrays/_mixins.py:81, in ravel_compat.<locals>.method(self, *args, **kwargs)
78 @wraps(meth)
79 def method(self, *args, **kwargs):
80 if self.ndim == 1:
---> 81 return meth(self, *args, **kwargs)
83 flags = self._ndarray.flags
84 flat = self.ravel("K")
File ~/work/pandas/pandas/pandas/core/arrays/datetimes.py:1090, in DatetimeArray.tz_localize(self, tz, ambiguous, nonexistent)
1087 tz = timezones.maybe_get_tz(tz)
1088 # Convert to UTC
-> 1090 new_dates = tzconversion.tz_localize_to_utc(
1091 self.asi8,
1092 tz,
1093 ambiguous=ambiguous,
1094 nonexistent=nonexistent,
1095 creso=self._creso,
1096 )
1097 new_dates_dt64 = new_dates.view(f"M8[{self.unit}]")
1098 dtype = tz_to_dtype(tz, unit=self.unit)
File ~/work/pandas/pandas/pandas/_libs/tslibs/tzconversion.pyx:431, in pandas._libs.tslibs.tzconversion.tz_localize_to_utc()
NonExistentTimeError: 2015-03-29 02:30:00
存在しない時間を NaT に変換するか、時間をシフトします。
In [489]: dti
Out[489]:
DatetimeIndex(['2015-03-29 02:30:00', '2015-03-29 03:30:00',
'2015-03-29 04:30:00'],
dtype='datetime64[ns]', freq='h')
In [490]: dti.tz_localize("Europe/Warsaw", nonexistent="shift_forward")
Out[490]:
DatetimeIndex(['2015-03-29 03:00:00+02:00', '2015-03-29 03:30:00+02:00',
'2015-03-29 04:30:00+02:00'],
dtype='datetime64[ns, Europe/Warsaw]', freq=None)
In [491]: dti.tz_localize("Europe/Warsaw", nonexistent="shift_backward")
Out[491]:
DatetimeIndex(['2015-03-29 01:59:59.999999999+01:00',
'2015-03-29 03:30:00+02:00',
'2015-03-29 04:30:00+02:00'],
dtype='datetime64[ns, Europe/Warsaw]', freq=None)
In [492]: dti.tz_localize("Europe/Warsaw", nonexistent=pd.Timedelta(1, unit="h"))
Out[492]:
DatetimeIndex(['2015-03-29 03:30:00+02:00', '2015-03-29 03:30:00+02:00',
'2015-03-29 04:30:00+02:00'],
dtype='datetime64[ns, Europe/Warsaw]', freq=None)
In [493]: dti.tz_localize("Europe/Warsaw", nonexistent="NaT")
Out[493]:
DatetimeIndex(['NaT', '2015-03-29 03:30:00+02:00',
'2015-03-29 04:30:00+02:00'],
dtype='datetime64[ns, Europe/Warsaw]', freq=None)
タイムゾーン Series 演算#
タイムゾーン**を認識しない**値を持つ Series は datetime64[ns] の dtype で表されます。
In [494]: s_naive = pd.Series(pd.date_range("20130101", periods=3))
In [495]: s_naive
Out[495]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
dtype: datetime64[ns]
タイムゾーン**を認識する**値を持つ Series は datetime64[ns, tz] の dtype で表され、ここで tz はタイムゾーンです。
In [496]: s_aware = pd.Series(pd.date_range("20130101", periods=3, tz="US/Eastern"))
In [497]: s_aware
Out[497]:
0 2013-01-01 00:00:00-05:00
1 2013-01-02 00:00:00-05:00
2 2013-01-03 00:00:00-05:00
dtype: datetime64[ns, US/Eastern]
これらの Series の両方のタイムゾーン情報は、.dt アクセサーを介して操作できます。dt アクセサーセクション を参照してください。
例えば、ネイティブなタイムスタンプをローカライズしてタイムゾーン対応に変換するには、次のようにします。
In [498]: s_naive.dt.tz_localize("UTC").dt.tz_convert("US/Eastern")
Out[498]:
0 2012-12-31 19:00:00-05:00
1 2013-01-01 19:00:00-05:00
2 2013-01-02 19:00:00-05:00
dtype: datetime64[ns, US/Eastern]
タイムゾーン情報は astype メソッドを使って操作することもできます。このメソッドは、異なるタイムゾーンを認識する dtype 間で変換できます。
# convert to a new time zone
In [499]: s_aware.astype("datetime64[ns, CET]")
Out[499]:
0 2013-01-01 06:00:00+01:00
1 2013-01-02 06:00:00+01:00
2 2013-01-03 06:00:00+01:00
dtype: datetime64[ns, CET]
注
Series に対して Series.to_numpy() を使用すると、データの NumPy 配列が返されます。NumPy は現在タイムゾーンをサポートしていないため(ただしローカルタイムゾーンで**出力**されています!)、タイムゾーン対応データに対しては Timestamp のオブジェクト配列が返されます。
In [500]: s_naive.to_numpy()
Out[500]:
array(['2013-01-01T00:00:00.000000000', '2013-01-02T00:00:00.000000000',
'2013-01-03T00:00:00.000000000'], dtype='datetime64[ns]')
In [501]: s_aware.to_numpy()
Out[501]:
array([Timestamp('2013-01-01 00:00:00-0500', tz='US/Eastern'),
Timestamp('2013-01-02 00:00:00-0500', tz='US/Eastern'),
Timestamp('2013-01-03 00:00:00-0500', tz='US/Eastern')],
dtype=object)
Timestamp のオブジェクト配列に変換することで、タイムゾーン情報が保持されます。たとえば、Series に変換し直す場合。
In [502]: pd.Series(s_aware.to_numpy())
Out[502]:
0 2013-01-01 00:00:00-05:00
1 2013-01-02 00:00:00-05:00
2 2013-01-03 00:00:00-05:00
dtype: datetime64[ns, US/Eastern]
しかし、オブジェクト配列ではなく(UTC に変換された値を持つ)実際の NumPy datetime64[ns] 配列が必要な場合は、dtype 引数を指定できます。
In [503]: s_aware.to_numpy(dtype="datetime64[ns]")
Out[503]:
array(['2013-01-01T05:00:00.000000000', '2013-01-02T05:00:00.000000000',
'2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')