LoginSignup
6
4

More than 5 years have passed since last update.

Pandas Period の挙動まとめ (freq編)

Last updated at Posted at 2018-09-27

Pandas Period の freq

Pandasでは日付を Period として扱うことができる。
メリットをあげると例えば以下がある。
- 期間を扱えるため、四半期後 とか 半年後 などの計算が簡単になる。( + 1 とかすればいい)
- 期中の中途半端な日付を情報を保存しつつ丸めることが可能

PandasのPeriodは使いこなせれば便利だけど、datetime との行き来がめんどくさかったり、期待している挙動をしないことが多いためメモしておく。 今回は freq に関して。 (そのうちdatetime 変換のところもまとめる。)

Periodを使う際に期間の切り方に関するパラメータが freq
Q (クォーター) のデフォルトでは Q-DEC になっているため、年末くぎり。 日本の年度は4月からなので、それらを指定したいときとかに使う。

パラメータに関する本家リンク

freq で指定できるパラメータはこの辺。 どこで使える使えないの情報は細かく記載されていない。

https://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases
https://pandas.pydata.org/pandas-docs/stable/timeseries.html#anchored-offsets

まとめ

パラメータの指定が全部動くのは date_range だけど、それだと Period の恩恵を受けられない。
とりあえずの対処法としては期間指定とかを諦めてデフォルトで変換して処理を行い、処理結果を表示するときは datetime などに戻すのがよさそう。

特にPeriodの QS はBug(enhancement?)っぽいので issue あげるなり、原因特定してコミットしたいところ(ちょっとみたところpandas内の tslib はCythonだからしんどそう…)。

対応表

Q 2Q QS Q-APR 2QS 2Q-APR QS-APR 2QS-APR
Period × × × ×
period_range × × × ×
date_range
date_range.to_period × ※1 × × ※1 ×

※1: エラーははかれないが freq がデフォルト( Q-DEC )になる

出力結果 (エラーなど)

Pandas のバージョン

In [82]: pd.__version__
Out[82]: '0.23.4'

Period

2Q

In [56]: pd.Period('2018-04-01', freq='2Q')
Out[56]: Period('2018Q2', '2Q-DEC')

QS

In [37]: pd.Period('2018-04-01', freq='QS')
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
pandas/_libs/tslibs/frequencies.pyx in pandas._libs.tslibs.frequencies._period_str_to_code()

KeyError: 'QS-JAN'

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-37-670d308b00be> in <module>()
----> 1 pd.Period('2018-04-01', freq='QS')

pandas/_libs/tslibs/period.pyx in pandas._libs.tslibs.period.Period.__new__()

pandas/_libs/tslibs/frequencies.pyx in pandas._libs.tslibs.frequencies.get_freq_code()

pandas/_libs/tslibs/frequencies.pyx in pandas._libs.tslibs.frequencies.get_freq_code()

pandas/_libs/tslibs/frequencies.pyx in pandas._libs.tslibs.frequencies._period_str_to_code()

ValueError: Invalid frequency: QS-JAN

period_range

2Q

In [26]: pd.period_range('2018-04-01', '2020-01-01', freq='2Q')
Out[26]: PeriodIndex(['2018Q2', '2018Q4', '2019Q2', '2019Q4'], dtype='period[2Q-DEC]', freq='2Q-DEC')

2Q + 1

In [27]: pd.period_range('2018-04-01', '2020-01-01', freq='2Q') + 1
Out[27]: PeriodIndex(['2018Q4', '2019Q2', '2019Q4', '2020Q2'], dtype='period[2Q-DEC]', freq='2Q-DEC')

QS

In [33]: pd.period_range('2018-04-01', '2020-01-01', freq='QS')
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
pandas/_libs/tslibs/frequencies.pyx in pandas._libs.tslibs.frequencies._period_str_to_code()

KeyError: 'QS-JAN'

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-33-25a035e5f517> in <module>()
----> 1 pd.period_range('2018-04-01', '2020-01-01', freq='QS')

~/.pyenv/versions/miniconda-latest/envs/test/lib/python3.6/site-packages/pandas/core/indexes/period.py in period_range(start, end, periods, freq, name)
   1295
   1296     return PeriodIndex(start=start, end=end, periods=periods,
-> 1297                        freq=freq, name=name)

~/.pyenv/versions/miniconda-latest/envs/test/lib/python3.6/site-packages/pandas/core/indexes/period.py in __new__(cls, data, ordinal, freq, start, end, periods, tz, dtype, copy, name, **fields)
    275             else:
    276                 data, freq = cls._generate_range(start, end, periods,
--> 277                                                  freq, fields)
    278             return cls._from_ordinals(data, name=name, freq=freq)
    279

~/.pyenv/versions/miniconda-latest/envs/test/lib/python3.6/site-packages/pandas/core/indexes/period.py in _generate_range(cls, start, end, periods, freq, fields)
    331                 raise ValueError('Can either instantiate from fields '
    332                                  'or endpoints, but not both')
--> 333             subarr, freq = _get_ordinal_range(start, end, periods, freq)
    334         elif field_count > 0:
    335             subarr, freq = _range_from_fields(freq=freq, **fields)

~/.pyenv/versions/miniconda-latest/envs/test/lib/python3.6/site-packages/pandas/core/indexes/period.py in _get_ordinal_range(start, end, periods, freq, mult)
   1144
   1145     if freq is not None:
-> 1146         _, mult = _gfc(freq)
   1147
   1148     if start is not None:

pandas/_libs/tslibs/frequencies.pyx in pandas._libs.tslibs.frequencies.get_freq_code()

pandas/_libs/tslibs/frequencies.pyx in pandas._libs.tslibs.frequencies.get_freq_code()

pandas/_libs/tslibs/frequencies.pyx in pandas._libs.tslibs.frequencies.get_freq_code()

pandas/_libs/tslibs/frequencies.pyx in pandas._libs.tslibs.frequencies._period_str_to_code()

ValueError: Invalid frequency: QS-JAN

date_range

QS-APR

In [55]: pd.date_range('2018-04-01', '2020-01-01', freq='QS-APR')
Out[55]:
DatetimeIndex(['2018-04-01', '2018-07-01', '2018-10-01', '2019-01-01',
               '2019-04-01', '2019-07-01', '2019-10-01', '2020-01-01'],
              dtype='datetime64[ns]', freq='QS-APR')

2QS

In [29]: pd.date_range('2018-04-01', '2020-01-01', freq='2QS')
Out[29]: DatetimeIndex(['2018-04-01', '2018-10-01', '2019-04-01', '2019-10-01'], dtype='datetime64[ns]', freq='2QS-JAN')

2QS-APR

In [30]: pd.date_range('2018-04-01', '2020-01-01', freq='2QS-APR')
Out[30]: DatetimeIndex(['2018-04-01', '2018-10-01', '2019-04-01', '2019-10-01'], dtype='datetime64[ns]', freq='2QS-APR')

2QS-APR + 1

In [31]: pd.date_range('2018-04-01', '2020-01-01', freq='2QS-APR') + 1
Out[31]: DatetimeIndex(['2018-10-01', '2019-04-01', '2019-10-01', '2020-04-01'], dtype='datetime64[ns]', freq='2QS-APR')

date_range.to_period

Q

In [48]: pd.date_range('2018-04-01', '2020-01-01', freq='Q').to_period()
Out[48]:
PeriodIndex(['2018Q2', '2018Q3', '2018Q4', '2019Q1', '2019Q2', '2019Q3',
             '2019Q4'],
            dtype='period[Q-DEC]', freq='Q-DEC')

QS

QS の部分はperiodに変換したときに無視されている

In [49]: pd.date_range('2018-04-01', '2020-01-01', freq='QS').to_period()
Out[49]:
PeriodIndex(['2018Q2', '2018Q3', '2018Q4', '2019Q1', '2019Q2', '2019Q3',
             '2019Q4', '2020Q1'],
            dtype='period[Q-DEC]', freq='Q-DEC')

Q-APR

Q-APR は保存される

In [53]: pd.date_range('2018-04-01', '2020-01-01', freq='Q-APR').to_period()
Out[53]:
PeriodIndex(['2018Q4', '2019Q1', '2019Q2', '2019Q3', '2019Q4', '2020Q1',
             '2020Q2'],
            dtype='period[Q-APR]', freq='Q-APR')

QS-APR

APRも無視される

In [54]: pd.date_range('2018-04-01', '2020-01-01', freq='QS-APR').to_period()
Out[54]:
PeriodIndex(['2018Q2', '2018Q3', '2018Q4', '2019Q1', '2019Q2', '2019Q3',
             '2019Q4', '2020Q1'],
            dtype='period[Q-DEC]', freq='Q-DEC')

2Q

なぜかエラー

In [42]: pd.date_range('2018-04-01', '2020-01-01', freq='2Q').to_period()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-42-ab52acd3f100> in <module>()
----> 1 pd.date_range('2018-04-01', '2020-01-01', freq='2Q').to_period()

~/.pyenv/versions/miniconda-latest/envs/test/lib/python3.6/site-packages/pandas/core/indexes/datetimes.py in to_period(self, freq)
   1117             freq = get_period_alias(freq)
   1118
-> 1119         return PeriodIndex(self.values, name=self.name, freq=freq, tz=self.tz)
   1120
   1121     def snap(self, freq='S'):

~/.pyenv/versions/miniconda-latest/envs/test/lib/python3.6/site-packages/pandas/core/indexes/period.py in __new__(cls, data, ordinal, freq, start, end, periods, tz, dtype, copy, name, **fields)
    303         # datetime other than period
    304         if is_datetime64_dtype(data.dtype):
--> 305             data = dt64arr_to_periodarr(data, freq, tz)
    306             return cls._from_ordinals(data, name=name, freq=freq)
    307

~/.pyenv/versions/miniconda-latest/envs/test/lib/python3.6/site-packages/pandas/core/indexes/period.py in dt64arr_to_periodarr(data, freq, tz)
     65         raise ValueError('Wrong dtype: %s' % data.dtype)
     66
---> 67     freq = Period._maybe_convert_freq(freq)
     68     base, mult = _gfc(freq)
     69     return period.dt64arr_to_periodarr(data.view('i8'), base, tz)

pandas/_libs/tslibs/period.pyx in pandas._libs.tslibs.period._Period._maybe_convert_freq()

AttributeError: 'NoneType' object has no attribute 'n'
6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4