1676 lines
		
	
	
		
			58 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1676 lines
		
	
	
		
			58 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Arithmetic tests for DataFrame/Series/Index/Array classes that should
 | |
| # behave identically.
 | |
| # Specifically for Period dtype
 | |
| import operator
 | |
| 
 | |
| import numpy as np
 | |
| import pytest
 | |
| 
 | |
| from pandas._libs.tslibs import (
 | |
|     IncompatibleFrequency,
 | |
|     Period,
 | |
|     Timestamp,
 | |
|     to_offset,
 | |
| )
 | |
| from pandas.errors import PerformanceWarning
 | |
| 
 | |
| import pandas as pd
 | |
| from pandas import (
 | |
|     PeriodIndex,
 | |
|     Series,
 | |
|     Timedelta,
 | |
|     TimedeltaIndex,
 | |
|     period_range,
 | |
| )
 | |
| import pandas._testing as tm
 | |
| from pandas.core import ops
 | |
| from pandas.core.arrays import TimedeltaArray
 | |
| from pandas.tests.arithmetic.common import (
 | |
|     assert_invalid_addsub_type,
 | |
|     assert_invalid_comparison,
 | |
|     get_upcast_box,
 | |
| )
 | |
| 
 | |
| _common_mismatch = [
 | |
|     pd.offsets.YearBegin(2),
 | |
|     pd.offsets.MonthBegin(1),
 | |
|     pd.offsets.Minute(),
 | |
| ]
 | |
| 
 | |
| 
 | |
| @pytest.fixture(
 | |
|     params=[
 | |
|         Timedelta(minutes=30).to_pytimedelta(),
 | |
|         np.timedelta64(30, "s"),
 | |
|         Timedelta(seconds=30),
 | |
|     ]
 | |
|     + _common_mismatch
 | |
| )
 | |
| def not_hourly(request):
 | |
|     """
 | |
|     Several timedelta-like and DateOffset instances that are _not_
 | |
|     compatible with Hourly frequencies.
 | |
|     """
 | |
|     return request.param
 | |
| 
 | |
| 
 | |
| @pytest.fixture(
 | |
|     params=[
 | |
|         np.timedelta64(365, "D"),
 | |
|         Timedelta(days=365).to_pytimedelta(),
 | |
|         Timedelta(days=365),
 | |
|     ]
 | |
|     + _common_mismatch
 | |
| )
 | |
| def mismatched_freq(request):
 | |
|     """
 | |
|     Several timedelta-like and DateOffset instances that are _not_
 | |
|     compatible with Monthly or Annual frequencies.
 | |
|     """
 | |
|     return request.param
 | |
| 
 | |
| 
 | |
| # ------------------------------------------------------------------
 | |
| # Comparisons
 | |
| 
 | |
| 
 | |
| class TestPeriodArrayLikeComparisons:
 | |
|     # Comparison tests for PeriodDtype vectors fully parametrized over
 | |
|     #  DataFrame/Series/PeriodIndex/PeriodArray.  Ideally all comparison
 | |
|     #  tests will eventually end up here.
 | |
| 
 | |
|     @pytest.mark.parametrize("other", ["2017", Period("2017", freq="D")])
 | |
|     def test_eq_scalar(self, other, box_with_array):
 | |
|         idx = PeriodIndex(["2017", "2017", "2018"], freq="D")
 | |
|         idx = tm.box_expected(idx, box_with_array)
 | |
|         xbox = get_upcast_box(idx, other, True)
 | |
| 
 | |
|         expected = np.array([True, True, False])
 | |
|         expected = tm.box_expected(expected, xbox)
 | |
| 
 | |
|         result = idx == other
 | |
| 
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|     def test_compare_zerodim(self, box_with_array):
 | |
|         # GH#26689 make sure we unbox zero-dimensional arrays
 | |
| 
 | |
|         pi = period_range("2000", periods=4)
 | |
|         other = np.array(pi.to_numpy()[0])
 | |
| 
 | |
|         pi = tm.box_expected(pi, box_with_array)
 | |
|         xbox = get_upcast_box(pi, other, True)
 | |
| 
 | |
|         result = pi <= other
 | |
|         expected = np.array([True, False, False, False])
 | |
|         expected = tm.box_expected(expected, xbox)
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|     @pytest.mark.parametrize(
 | |
|         "scalar",
 | |
|         [
 | |
|             "foo",
 | |
|             Timestamp("2021-01-01"),
 | |
|             Timedelta(days=4),
 | |
|             9,
 | |
|             9.5,
 | |
|             2000,  # specifically don't consider 2000 to match Period("2000", "D")
 | |
|             False,
 | |
|             None,
 | |
|         ],
 | |
|     )
 | |
|     def test_compare_invalid_scalar(self, box_with_array, scalar):
 | |
|         # GH#28980
 | |
|         # comparison with scalar that cannot be interpreted as a Period
 | |
|         pi = period_range("2000", periods=4)
 | |
|         parr = tm.box_expected(pi, box_with_array)
 | |
|         assert_invalid_comparison(parr, scalar, box_with_array)
 | |
| 
 | |
|     @pytest.mark.parametrize(
 | |
|         "other",
 | |
|         [
 | |
|             pd.date_range("2000", periods=4).array,
 | |
|             pd.timedelta_range("1D", periods=4).array,
 | |
|             np.arange(4),
 | |
|             np.arange(4).astype(np.float64),
 | |
|             list(range(4)),
 | |
|             # match Period semantics by not treating integers as Periods
 | |
|             [2000, 2001, 2002, 2003],
 | |
|             np.arange(2000, 2004),
 | |
|             np.arange(2000, 2004).astype(object),
 | |
|             pd.Index([2000, 2001, 2002, 2003]),
 | |
|         ],
 | |
|     )
 | |
|     def test_compare_invalid_listlike(self, box_with_array, other):
 | |
|         pi = period_range("2000", periods=4)
 | |
|         parr = tm.box_expected(pi, box_with_array)
 | |
|         assert_invalid_comparison(parr, other, box_with_array)
 | |
| 
 | |
|     @pytest.mark.parametrize("other_box", [list, np.array, lambda x: x.astype(object)])
 | |
|     def test_compare_object_dtype(self, box_with_array, other_box):
 | |
|         pi = period_range("2000", periods=5)
 | |
|         parr = tm.box_expected(pi, box_with_array)
 | |
| 
 | |
|         other = other_box(pi)
 | |
|         xbox = get_upcast_box(parr, other, True)
 | |
| 
 | |
|         expected = np.array([True, True, True, True, True])
 | |
|         expected = tm.box_expected(expected, xbox)
 | |
| 
 | |
|         result = parr == other
 | |
|         tm.assert_equal(result, expected)
 | |
|         result = parr <= other
 | |
|         tm.assert_equal(result, expected)
 | |
|         result = parr >= other
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         result = parr != other
 | |
|         tm.assert_equal(result, ~expected)
 | |
|         result = parr < other
 | |
|         tm.assert_equal(result, ~expected)
 | |
|         result = parr > other
 | |
|         tm.assert_equal(result, ~expected)
 | |
| 
 | |
|         other = other_box(pi[::-1])
 | |
| 
 | |
|         expected = np.array([False, False, True, False, False])
 | |
|         expected = tm.box_expected(expected, xbox)
 | |
|         result = parr == other
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         expected = np.array([True, True, True, False, False])
 | |
|         expected = tm.box_expected(expected, xbox)
 | |
|         result = parr <= other
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         expected = np.array([False, False, True, True, True])
 | |
|         expected = tm.box_expected(expected, xbox)
 | |
|         result = parr >= other
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         expected = np.array([True, True, False, True, True])
 | |
|         expected = tm.box_expected(expected, xbox)
 | |
|         result = parr != other
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         expected = np.array([True, True, False, False, False])
 | |
|         expected = tm.box_expected(expected, xbox)
 | |
|         result = parr < other
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         expected = np.array([False, False, False, True, True])
 | |
|         expected = tm.box_expected(expected, xbox)
 | |
|         result = parr > other
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
| 
 | |
| class TestPeriodIndexComparisons:
 | |
|     # TODO: parameterize over boxes
 | |
| 
 | |
|     def test_pi_cmp_period(self):
 | |
|         idx = period_range("2007-01", periods=20, freq="M")
 | |
|         per = idx[10]
 | |
| 
 | |
|         result = idx < per
 | |
|         exp = idx.values < idx.values[10]
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
| 
 | |
|         # Tests Period.__richcmp__ against ndarray[object, ndim=2]
 | |
|         result = idx.values.reshape(10, 2) < per
 | |
|         tm.assert_numpy_array_equal(result, exp.reshape(10, 2))
 | |
| 
 | |
|         # Tests Period.__richcmp__ against ndarray[object, ndim=0]
 | |
|         result = idx < np.array(per)
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
| 
 | |
|     # TODO: moved from test_datetime64; de-duplicate with version below
 | |
|     def test_parr_cmp_period_scalar2(self, box_with_array):
 | |
|         pi = period_range("2000-01-01", periods=10, freq="D")
 | |
| 
 | |
|         val = pi[3]
 | |
|         expected = [x > val for x in pi]
 | |
| 
 | |
|         ser = tm.box_expected(pi, box_with_array)
 | |
|         xbox = get_upcast_box(ser, val, True)
 | |
| 
 | |
|         expected = tm.box_expected(expected, xbox)
 | |
|         result = ser > val
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         val = pi[5]
 | |
|         result = ser > val
 | |
|         expected = [x > val for x in pi]
 | |
|         expected = tm.box_expected(expected, xbox)
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|     @pytest.mark.parametrize("freq", ["M", "2M", "3M"])
 | |
|     def test_parr_cmp_period_scalar(self, freq, box_with_array):
 | |
|         # GH#13200
 | |
|         base = PeriodIndex(["2011-01", "2011-02", "2011-03", "2011-04"], freq=freq)
 | |
|         base = tm.box_expected(base, box_with_array)
 | |
|         per = Period("2011-02", freq=freq)
 | |
|         xbox = get_upcast_box(base, per, True)
 | |
| 
 | |
|         exp = np.array([False, True, False, False])
 | |
|         exp = tm.box_expected(exp, xbox)
 | |
|         tm.assert_equal(base == per, exp)
 | |
|         tm.assert_equal(per == base, exp)
 | |
| 
 | |
|         exp = np.array([True, False, True, True])
 | |
|         exp = tm.box_expected(exp, xbox)
 | |
|         tm.assert_equal(base != per, exp)
 | |
|         tm.assert_equal(per != base, exp)
 | |
| 
 | |
|         exp = np.array([False, False, True, True])
 | |
|         exp = tm.box_expected(exp, xbox)
 | |
|         tm.assert_equal(base > per, exp)
 | |
|         tm.assert_equal(per < base, exp)
 | |
| 
 | |
|         exp = np.array([True, False, False, False])
 | |
|         exp = tm.box_expected(exp, xbox)
 | |
|         tm.assert_equal(base < per, exp)
 | |
|         tm.assert_equal(per > base, exp)
 | |
| 
 | |
|         exp = np.array([False, True, True, True])
 | |
|         exp = tm.box_expected(exp, xbox)
 | |
|         tm.assert_equal(base >= per, exp)
 | |
|         tm.assert_equal(per <= base, exp)
 | |
| 
 | |
|         exp = np.array([True, True, False, False])
 | |
|         exp = tm.box_expected(exp, xbox)
 | |
|         tm.assert_equal(base <= per, exp)
 | |
|         tm.assert_equal(per >= base, exp)
 | |
| 
 | |
|     @pytest.mark.parametrize("freq", ["M", "2M", "3M"])
 | |
|     def test_parr_cmp_pi(self, freq, box_with_array):
 | |
|         # GH#13200
 | |
|         base = PeriodIndex(["2011-01", "2011-02", "2011-03", "2011-04"], freq=freq)
 | |
|         base = tm.box_expected(base, box_with_array)
 | |
| 
 | |
|         # TODO: could also box idx?
 | |
|         idx = PeriodIndex(["2011-02", "2011-01", "2011-03", "2011-05"], freq=freq)
 | |
| 
 | |
|         xbox = get_upcast_box(base, idx, True)
 | |
| 
 | |
|         exp = np.array([False, False, True, False])
 | |
|         exp = tm.box_expected(exp, xbox)
 | |
|         tm.assert_equal(base == idx, exp)
 | |
| 
 | |
|         exp = np.array([True, True, False, True])
 | |
|         exp = tm.box_expected(exp, xbox)
 | |
|         tm.assert_equal(base != idx, exp)
 | |
| 
 | |
|         exp = np.array([False, True, False, False])
 | |
|         exp = tm.box_expected(exp, xbox)
 | |
|         tm.assert_equal(base > idx, exp)
 | |
| 
 | |
|         exp = np.array([True, False, False, True])
 | |
|         exp = tm.box_expected(exp, xbox)
 | |
|         tm.assert_equal(base < idx, exp)
 | |
| 
 | |
|         exp = np.array([False, True, True, False])
 | |
|         exp = tm.box_expected(exp, xbox)
 | |
|         tm.assert_equal(base >= idx, exp)
 | |
| 
 | |
|         exp = np.array([True, False, True, True])
 | |
|         exp = tm.box_expected(exp, xbox)
 | |
|         tm.assert_equal(base <= idx, exp)
 | |
| 
 | |
|     @pytest.mark.parametrize("freq", ["M", "2M", "3M"])
 | |
|     def test_parr_cmp_pi_mismatched_freq(self, freq, box_with_array):
 | |
|         # GH#13200
 | |
|         # different base freq
 | |
|         base = PeriodIndex(["2011-01", "2011-02", "2011-03", "2011-04"], freq=freq)
 | |
|         base = tm.box_expected(base, box_with_array)
 | |
| 
 | |
|         msg = rf"Invalid comparison between dtype=period\[{freq}\] and Period"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             base <= Period("2011", freq="Y")
 | |
| 
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             Period("2011", freq="Y") >= base
 | |
| 
 | |
|         # TODO: Could parametrize over boxes for idx?
 | |
|         idx = PeriodIndex(["2011", "2012", "2013", "2014"], freq="Y")
 | |
|         rev_msg = r"Invalid comparison between dtype=period\[Y-DEC\] and PeriodArray"
 | |
|         idx_msg = rev_msg if box_with_array in [tm.to_array, pd.array] else msg
 | |
|         with pytest.raises(TypeError, match=idx_msg):
 | |
|             base <= idx
 | |
| 
 | |
|         # Different frequency
 | |
|         msg = rf"Invalid comparison between dtype=period\[{freq}\] and Period"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             base <= Period("2011", freq="4M")
 | |
| 
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             Period("2011", freq="4M") >= base
 | |
| 
 | |
|         idx = PeriodIndex(["2011", "2012", "2013", "2014"], freq="4M")
 | |
|         rev_msg = r"Invalid comparison between dtype=period\[4M\] and PeriodArray"
 | |
|         idx_msg = rev_msg if box_with_array in [tm.to_array, pd.array] else msg
 | |
|         with pytest.raises(TypeError, match=idx_msg):
 | |
|             base <= idx
 | |
| 
 | |
|     @pytest.mark.parametrize("freq", ["M", "2M", "3M"])
 | |
|     def test_pi_cmp_nat(self, freq):
 | |
|         idx1 = PeriodIndex(["2011-01", "2011-02", "NaT", "2011-05"], freq=freq)
 | |
|         per = idx1[1]
 | |
| 
 | |
|         result = idx1 > per
 | |
|         exp = np.array([False, False, False, True])
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
|         result = per < idx1
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
| 
 | |
|         result = idx1 == pd.NaT
 | |
|         exp = np.array([False, False, False, False])
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
|         result = pd.NaT == idx1
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
| 
 | |
|         result = idx1 != pd.NaT
 | |
|         exp = np.array([True, True, True, True])
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
|         result = pd.NaT != idx1
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
| 
 | |
|         idx2 = PeriodIndex(["2011-02", "2011-01", "2011-04", "NaT"], freq=freq)
 | |
|         result = idx1 < idx2
 | |
|         exp = np.array([True, False, False, False])
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
| 
 | |
|         result = idx1 == idx2
 | |
|         exp = np.array([False, False, False, False])
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
| 
 | |
|         result = idx1 != idx2
 | |
|         exp = np.array([True, True, True, True])
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
| 
 | |
|         result = idx1 == idx1
 | |
|         exp = np.array([True, True, False, True])
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
| 
 | |
|         result = idx1 != idx1
 | |
|         exp = np.array([False, False, True, False])
 | |
|         tm.assert_numpy_array_equal(result, exp)
 | |
| 
 | |
|     @pytest.mark.parametrize("freq", ["M", "2M", "3M"])
 | |
|     def test_pi_cmp_nat_mismatched_freq_raises(self, freq):
 | |
|         idx1 = PeriodIndex(["2011-01", "2011-02", "NaT", "2011-05"], freq=freq)
 | |
| 
 | |
|         diff = PeriodIndex(["2011-02", "2011-01", "2011-04", "NaT"], freq="4M")
 | |
|         msg = rf"Invalid comparison between dtype=period\[{freq}\] and PeriodArray"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             idx1 > diff
 | |
| 
 | |
|         result = idx1 == diff
 | |
|         expected = np.array([False, False, False, False], dtype=bool)
 | |
|         tm.assert_numpy_array_equal(result, expected)
 | |
| 
 | |
|     # TODO: De-duplicate with test_pi_cmp_nat
 | |
|     @pytest.mark.parametrize("dtype", [object, None])
 | |
|     def test_comp_nat(self, dtype):
 | |
|         left = PeriodIndex([Period("2011-01-01"), pd.NaT, Period("2011-01-03")])
 | |
|         right = PeriodIndex([pd.NaT, pd.NaT, Period("2011-01-03")])
 | |
| 
 | |
|         if dtype is not None:
 | |
|             left = left.astype(dtype)
 | |
|             right = right.astype(dtype)
 | |
| 
 | |
|         result = left == right
 | |
|         expected = np.array([False, False, True])
 | |
|         tm.assert_numpy_array_equal(result, expected)
 | |
| 
 | |
|         result = left != right
 | |
|         expected = np.array([True, True, False])
 | |
|         tm.assert_numpy_array_equal(result, expected)
 | |
| 
 | |
|         expected = np.array([False, False, False])
 | |
|         tm.assert_numpy_array_equal(left == pd.NaT, expected)
 | |
|         tm.assert_numpy_array_equal(pd.NaT == right, expected)
 | |
| 
 | |
|         expected = np.array([True, True, True])
 | |
|         tm.assert_numpy_array_equal(left != pd.NaT, expected)
 | |
|         tm.assert_numpy_array_equal(pd.NaT != left, expected)
 | |
| 
 | |
|         expected = np.array([False, False, False])
 | |
|         tm.assert_numpy_array_equal(left < pd.NaT, expected)
 | |
|         tm.assert_numpy_array_equal(pd.NaT > left, expected)
 | |
| 
 | |
| 
 | |
| class TestPeriodSeriesComparisons:
 | |
|     def test_cmp_series_period_series_mixed_freq(self):
 | |
|         # GH#13200
 | |
|         base = Series(
 | |
|             [
 | |
|                 Period("2011", freq="Y"),
 | |
|                 Period("2011-02", freq="M"),
 | |
|                 Period("2013", freq="Y"),
 | |
|                 Period("2011-04", freq="M"),
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         ser = Series(
 | |
|             [
 | |
|                 Period("2012", freq="Y"),
 | |
|                 Period("2011-01", freq="M"),
 | |
|                 Period("2013", freq="Y"),
 | |
|                 Period("2011-05", freq="M"),
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         exp = Series([False, False, True, False])
 | |
|         tm.assert_series_equal(base == ser, exp)
 | |
| 
 | |
|         exp = Series([True, True, False, True])
 | |
|         tm.assert_series_equal(base != ser, exp)
 | |
| 
 | |
|         exp = Series([False, True, False, False])
 | |
|         tm.assert_series_equal(base > ser, exp)
 | |
| 
 | |
|         exp = Series([True, False, False, True])
 | |
|         tm.assert_series_equal(base < ser, exp)
 | |
| 
 | |
|         exp = Series([False, True, True, False])
 | |
|         tm.assert_series_equal(base >= ser, exp)
 | |
| 
 | |
|         exp = Series([True, False, True, True])
 | |
|         tm.assert_series_equal(base <= ser, exp)
 | |
| 
 | |
| 
 | |
| class TestPeriodIndexSeriesComparisonConsistency:
 | |
|     """Test PeriodIndex and Period Series Ops consistency"""
 | |
| 
 | |
|     # TODO: needs parametrization+de-duplication
 | |
| 
 | |
|     def _check(self, values, func, expected):
 | |
|         # Test PeriodIndex and Period Series Ops consistency
 | |
| 
 | |
|         idx = PeriodIndex(values)
 | |
|         result = func(idx)
 | |
| 
 | |
|         # check that we don't pass an unwanted type to tm.assert_equal
 | |
|         assert isinstance(expected, (pd.Index, np.ndarray))
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         s = Series(values)
 | |
|         result = func(s)
 | |
| 
 | |
|         exp = Series(expected, name=values.name)
 | |
|         tm.assert_series_equal(result, exp)
 | |
| 
 | |
|     def test_pi_comp_period(self):
 | |
|         idx = PeriodIndex(
 | |
|             ["2011-01", "2011-02", "2011-03", "2011-04"], freq="M", name="idx"
 | |
|         )
 | |
|         per = idx[2]
 | |
| 
 | |
|         f = lambda x: x == per
 | |
|         exp = np.array([False, False, True, False], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
|         f = lambda x: per == x
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: x != per
 | |
|         exp = np.array([True, True, False, True], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
|         f = lambda x: per != x
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: per >= x
 | |
|         exp = np.array([True, True, True, False], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: x > per
 | |
|         exp = np.array([False, False, False, True], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: per >= x
 | |
|         exp = np.array([True, True, True, False], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|     def test_pi_comp_period_nat(self):
 | |
|         idx = PeriodIndex(
 | |
|             ["2011-01", "NaT", "2011-03", "2011-04"], freq="M", name="idx"
 | |
|         )
 | |
|         per = idx[2]
 | |
| 
 | |
|         f = lambda x: x == per
 | |
|         exp = np.array([False, False, True, False], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
|         f = lambda x: per == x
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: x == pd.NaT
 | |
|         exp = np.array([False, False, False, False], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
|         f = lambda x: pd.NaT == x
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: x != per
 | |
|         exp = np.array([True, True, False, True], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
|         f = lambda x: per != x
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: x != pd.NaT
 | |
|         exp = np.array([True, True, True, True], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
|         f = lambda x: pd.NaT != x
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: per >= x
 | |
|         exp = np.array([True, False, True, False], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: x < per
 | |
|         exp = np.array([True, False, False, False], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: x > pd.NaT
 | |
|         exp = np.array([False, False, False, False], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: pd.NaT >= x
 | |
|         exp = np.array([False, False, False, False], dtype=np.bool_)
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
| 
 | |
| # ------------------------------------------------------------------
 | |
| # Arithmetic
 | |
| 
 | |
| 
 | |
| class TestPeriodFrameArithmetic:
 | |
|     def test_ops_frame_period(self):
 | |
|         # GH#13043
 | |
|         df = pd.DataFrame(
 | |
|             {
 | |
|                 "A": [Period("2015-01", freq="M"), Period("2015-02", freq="M")],
 | |
|                 "B": [Period("2014-01", freq="M"), Period("2014-02", freq="M")],
 | |
|             }
 | |
|         )
 | |
|         assert df["A"].dtype == "Period[M]"
 | |
|         assert df["B"].dtype == "Period[M]"
 | |
| 
 | |
|         p = Period("2015-03", freq="M")
 | |
|         off = p.freq
 | |
|         # dtype will be object because of original dtype
 | |
|         exp = pd.DataFrame(
 | |
|             {
 | |
|                 "A": np.array([2 * off, 1 * off], dtype=object),
 | |
|                 "B": np.array([14 * off, 13 * off], dtype=object),
 | |
|             }
 | |
|         )
 | |
|         tm.assert_frame_equal(p - df, exp)
 | |
|         tm.assert_frame_equal(df - p, -1 * exp)
 | |
| 
 | |
|         df2 = pd.DataFrame(
 | |
|             {
 | |
|                 "A": [Period("2015-05", freq="M"), Period("2015-06", freq="M")],
 | |
|                 "B": [Period("2015-05", freq="M"), Period("2015-06", freq="M")],
 | |
|             }
 | |
|         )
 | |
|         assert df2["A"].dtype == "Period[M]"
 | |
|         assert df2["B"].dtype == "Period[M]"
 | |
| 
 | |
|         exp = pd.DataFrame(
 | |
|             {
 | |
|                 "A": np.array([4 * off, 4 * off], dtype=object),
 | |
|                 "B": np.array([16 * off, 16 * off], dtype=object),
 | |
|             }
 | |
|         )
 | |
|         tm.assert_frame_equal(df2 - df, exp)
 | |
|         tm.assert_frame_equal(df - df2, -1 * exp)
 | |
| 
 | |
| 
 | |
| class TestPeriodIndexArithmetic:
 | |
|     # ---------------------------------------------------------------
 | |
|     # __add__/__sub__ with PeriodIndex
 | |
|     # PeriodIndex + other is defined for integers and timedelta-like others
 | |
|     # PeriodIndex - other is defined for integers, timedelta-like others,
 | |
|     #   and PeriodIndex (with matching freq)
 | |
| 
 | |
|     def test_parr_add_iadd_parr_raises(self, box_with_array):
 | |
|         rng = period_range("1/1/2000", freq="D", periods=5)
 | |
|         other = period_range("1/6/2000", freq="D", periods=5)
 | |
|         # TODO: parametrize over boxes for other?
 | |
| 
 | |
|         rng = tm.box_expected(rng, box_with_array)
 | |
|         # An earlier implementation of PeriodIndex addition performed
 | |
|         # a set operation (union).  This has since been changed to
 | |
|         # raise a TypeError. See GH#14164 and GH#13077 for historical
 | |
|         # reference.
 | |
|         msg = r"unsupported operand type\(s\) for \+: .* and .*"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             rng + other
 | |
| 
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             rng += other
 | |
| 
 | |
|     def test_pi_sub_isub_pi(self):
 | |
|         # GH#20049
 | |
|         # For historical reference see GH#14164, GH#13077.
 | |
|         # PeriodIndex subtraction originally performed set difference,
 | |
|         # then changed to raise TypeError before being implemented in GH#20049
 | |
|         rng = period_range("1/1/2000", freq="D", periods=5)
 | |
|         other = period_range("1/6/2000", freq="D", periods=5)
 | |
| 
 | |
|         off = rng.freq
 | |
|         expected = pd.Index([-5 * off] * 5)
 | |
|         result = rng - other
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|         rng -= other
 | |
|         tm.assert_index_equal(rng, expected)
 | |
| 
 | |
|     def test_pi_sub_pi_with_nat(self):
 | |
|         rng = period_range("1/1/2000", freq="D", periods=5)
 | |
|         other = rng[1:].insert(0, pd.NaT)
 | |
|         assert other[1:].equals(rng[1:])
 | |
| 
 | |
|         result = rng - other
 | |
|         off = rng.freq
 | |
|         expected = pd.Index([pd.NaT, 0 * off, 0 * off, 0 * off, 0 * off])
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|     def test_parr_sub_pi_mismatched_freq(self, box_with_array, box_with_array2):
 | |
|         rng = period_range("1/1/2000", freq="D", periods=5)
 | |
|         other = period_range("1/6/2000", freq="h", periods=5)
 | |
| 
 | |
|         rng = tm.box_expected(rng, box_with_array)
 | |
|         other = tm.box_expected(other, box_with_array2)
 | |
|         msg = r"Input has different freq=[hD] from PeriodArray\(freq=[Dh]\)"
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng - other
 | |
| 
 | |
|     @pytest.mark.parametrize("n", [1, 2, 3, 4])
 | |
|     def test_sub_n_gt_1_ticks(self, tick_classes, n):
 | |
|         # GH 23878
 | |
|         p1_d = "19910905"
 | |
|         p2_d = "19920406"
 | |
|         p1 = PeriodIndex([p1_d], freq=tick_classes(n))
 | |
|         p2 = PeriodIndex([p2_d], freq=tick_classes(n))
 | |
| 
 | |
|         expected = PeriodIndex([p2_d], freq=p2.freq.base) - PeriodIndex(
 | |
|             [p1_d], freq=p1.freq.base
 | |
|         )
 | |
| 
 | |
|         tm.assert_index_equal((p2 - p1), expected)
 | |
| 
 | |
|     @pytest.mark.parametrize("n", [1, 2, 3, 4])
 | |
|     @pytest.mark.parametrize(
 | |
|         "offset, kwd_name",
 | |
|         [
 | |
|             (pd.offsets.YearEnd, "month"),
 | |
|             (pd.offsets.QuarterEnd, "startingMonth"),
 | |
|             (pd.offsets.MonthEnd, None),
 | |
|             (pd.offsets.Week, "weekday"),
 | |
|         ],
 | |
|     )
 | |
|     def test_sub_n_gt_1_offsets(self, offset, kwd_name, n):
 | |
|         # GH 23878
 | |
|         kwds = {kwd_name: 3} if kwd_name is not None else {}
 | |
|         p1_d = "19910905"
 | |
|         p2_d = "19920406"
 | |
|         freq = offset(n, normalize=False, **kwds)
 | |
|         p1 = PeriodIndex([p1_d], freq=freq)
 | |
|         p2 = PeriodIndex([p2_d], freq=freq)
 | |
| 
 | |
|         result = p2 - p1
 | |
|         expected = PeriodIndex([p2_d], freq=freq.base) - PeriodIndex(
 | |
|             [p1_d], freq=freq.base
 | |
|         )
 | |
| 
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|     # -------------------------------------------------------------
 | |
|     # Invalid Operations
 | |
| 
 | |
|     @pytest.mark.parametrize(
 | |
|         "other",
 | |
|         [
 | |
|             # datetime scalars
 | |
|             Timestamp("2016-01-01"),
 | |
|             Timestamp("2016-01-01").to_pydatetime(),
 | |
|             Timestamp("2016-01-01").to_datetime64(),
 | |
|             # datetime-like arrays
 | |
|             pd.date_range("2016-01-01", periods=3, freq="h"),
 | |
|             pd.date_range("2016-01-01", periods=3, tz="Europe/Brussels"),
 | |
|             pd.date_range("2016-01-01", periods=3, freq="s")._data,
 | |
|             pd.date_range("2016-01-01", periods=3, tz="Asia/Tokyo")._data,
 | |
|             # Miscellaneous invalid types
 | |
|             3.14,
 | |
|             np.array([2.0, 3.0, 4.0]),
 | |
|         ],
 | |
|     )
 | |
|     def test_parr_add_sub_invalid(self, other, box_with_array):
 | |
|         # GH#23215
 | |
|         rng = period_range("1/1/2000", freq="D", periods=3)
 | |
|         rng = tm.box_expected(rng, box_with_array)
 | |
| 
 | |
|         msg = "|".join(
 | |
|             [
 | |
|                 r"(:?cannot add PeriodArray and .*)",
 | |
|                 r"(:?cannot subtract .* from (:?a\s)?.*)",
 | |
|                 r"(:?unsupported operand type\(s\) for \+: .* and .*)",
 | |
|                 r"unsupported operand type\(s\) for [+-]: .* and .*",
 | |
|             ]
 | |
|         )
 | |
|         assert_invalid_addsub_type(rng, other, msg)
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             rng + other
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             other + rng
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             rng - other
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             other - rng
 | |
| 
 | |
|     # -----------------------------------------------------------------
 | |
|     # __add__/__sub__ with ndarray[datetime64] and ndarray[timedelta64]
 | |
| 
 | |
|     def test_pi_add_sub_td64_array_non_tick_raises(self):
 | |
|         rng = period_range("1/1/2000", freq="Q", periods=3)
 | |
|         tdi = TimedeltaIndex(["-1 Day", "-1 Day", "-1 Day"])
 | |
|         tdarr = tdi.values
 | |
| 
 | |
|         msg = r"Cannot add or subtract timedelta64\[ns\] dtype from period\[Q-DEC\]"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             rng + tdarr
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             tdarr + rng
 | |
| 
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             rng - tdarr
 | |
|         msg = r"cannot subtract PeriodArray from TimedeltaArray"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             tdarr - rng
 | |
| 
 | |
|     def test_pi_add_sub_td64_array_tick(self):
 | |
|         # PeriodIndex + Timedelta-like is allowed only with
 | |
|         #   tick-like frequencies
 | |
|         rng = period_range("1/1/2000", freq="90D", periods=3)
 | |
|         tdi = TimedeltaIndex(["-1 Day", "-1 Day", "-1 Day"])
 | |
|         tdarr = tdi.values
 | |
| 
 | |
|         expected = period_range("12/31/1999", freq="90D", periods=3)
 | |
|         result = rng + tdi
 | |
|         tm.assert_index_equal(result, expected)
 | |
|         result = rng + tdarr
 | |
|         tm.assert_index_equal(result, expected)
 | |
|         result = tdi + rng
 | |
|         tm.assert_index_equal(result, expected)
 | |
|         result = tdarr + rng
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|         expected = period_range("1/2/2000", freq="90D", periods=3)
 | |
| 
 | |
|         result = rng - tdi
 | |
|         tm.assert_index_equal(result, expected)
 | |
|         result = rng - tdarr
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|         msg = r"cannot subtract .* from .*"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             tdarr - rng
 | |
| 
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             tdi - rng
 | |
| 
 | |
|     @pytest.mark.parametrize("pi_freq", ["D", "W", "Q", "h"])
 | |
|     @pytest.mark.parametrize("tdi_freq", [None, "h"])
 | |
|     def test_parr_sub_td64array(self, box_with_array, tdi_freq, pi_freq):
 | |
|         box = box_with_array
 | |
|         xbox = box if box not in [pd.array, tm.to_array] else pd.Index
 | |
| 
 | |
|         tdi = TimedeltaIndex(["1 hours", "2 hours"], freq=tdi_freq)
 | |
|         dti = Timestamp("2018-03-07 17:16:40") + tdi
 | |
|         pi = dti.to_period(pi_freq)
 | |
| 
 | |
|         # TODO: parametrize over box for pi?
 | |
|         td64obj = tm.box_expected(tdi, box)
 | |
| 
 | |
|         if pi_freq == "h":
 | |
|             result = pi - td64obj
 | |
|             expected = (pi.to_timestamp("s") - tdi).to_period(pi_freq)
 | |
|             expected = tm.box_expected(expected, xbox)
 | |
|             tm.assert_equal(result, expected)
 | |
| 
 | |
|             # Subtract from scalar
 | |
|             result = pi[0] - td64obj
 | |
|             expected = (pi[0].to_timestamp("s") - tdi).to_period(pi_freq)
 | |
|             expected = tm.box_expected(expected, box)
 | |
|             tm.assert_equal(result, expected)
 | |
| 
 | |
|         elif pi_freq == "D":
 | |
|             # Tick, but non-compatible
 | |
|             msg = (
 | |
|                 "Cannot add/subtract timedelta-like from PeriodArray that is "
 | |
|                 "not an integer multiple of the PeriodArray's freq."
 | |
|             )
 | |
|             with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|                 pi - td64obj
 | |
| 
 | |
|             with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|                 pi[0] - td64obj
 | |
| 
 | |
|         else:
 | |
|             # With non-Tick freq, we could not add timedelta64 array regardless
 | |
|             #  of what its resolution is
 | |
|             msg = "Cannot add or subtract timedelta64"
 | |
|             with pytest.raises(TypeError, match=msg):
 | |
|                 pi - td64obj
 | |
|             with pytest.raises(TypeError, match=msg):
 | |
|                 pi[0] - td64obj
 | |
| 
 | |
|     # -----------------------------------------------------------------
 | |
|     # operations with array/Index of DateOffset objects
 | |
| 
 | |
|     @pytest.mark.parametrize("box", [np.array, pd.Index])
 | |
|     def test_pi_add_offset_array(self, box):
 | |
|         # GH#18849
 | |
|         pi = PeriodIndex([Period("2015Q1"), Period("2016Q2")])
 | |
|         offs = box(
 | |
|             [
 | |
|                 pd.offsets.QuarterEnd(n=1, startingMonth=12),
 | |
|                 pd.offsets.QuarterEnd(n=-2, startingMonth=12),
 | |
|             ]
 | |
|         )
 | |
|         expected = PeriodIndex([Period("2015Q2"), Period("2015Q4")]).astype(object)
 | |
| 
 | |
|         with tm.assert_produces_warning(PerformanceWarning):
 | |
|             res = pi + offs
 | |
|         tm.assert_index_equal(res, expected)
 | |
| 
 | |
|         with tm.assert_produces_warning(PerformanceWarning):
 | |
|             res2 = offs + pi
 | |
|         tm.assert_index_equal(res2, expected)
 | |
| 
 | |
|         unanchored = np.array([pd.offsets.Hour(n=1), pd.offsets.Minute(n=-2)])
 | |
|         # addition/subtraction ops with incompatible offsets should issue
 | |
|         # a PerformanceWarning and _then_ raise a TypeError.
 | |
|         msg = r"Input cannot be converted to Period\(freq=Q-DEC\)"
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             with tm.assert_produces_warning(PerformanceWarning):
 | |
|                 pi + unanchored
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             with tm.assert_produces_warning(PerformanceWarning):
 | |
|                 unanchored + pi
 | |
| 
 | |
|     @pytest.mark.parametrize("box", [np.array, pd.Index])
 | |
|     def test_pi_sub_offset_array(self, box):
 | |
|         # GH#18824
 | |
|         pi = PeriodIndex([Period("2015Q1"), Period("2016Q2")])
 | |
|         other = box(
 | |
|             [
 | |
|                 pd.offsets.QuarterEnd(n=1, startingMonth=12),
 | |
|                 pd.offsets.QuarterEnd(n=-2, startingMonth=12),
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         expected = PeriodIndex([pi[n] - other[n] for n in range(len(pi))])
 | |
|         expected = expected.astype(object)
 | |
| 
 | |
|         with tm.assert_produces_warning(PerformanceWarning):
 | |
|             res = pi - other
 | |
|         tm.assert_index_equal(res, expected)
 | |
| 
 | |
|         anchored = box([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)])
 | |
| 
 | |
|         # addition/subtraction ops with anchored offsets should issue
 | |
|         # a PerformanceWarning and _then_ raise a TypeError.
 | |
|         msg = r"Input has different freq=-1M from Period\(freq=Q-DEC\)"
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             with tm.assert_produces_warning(PerformanceWarning):
 | |
|                 pi - anchored
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             with tm.assert_produces_warning(PerformanceWarning):
 | |
|                 anchored - pi
 | |
| 
 | |
|     def test_pi_add_iadd_int(self, one):
 | |
|         # Variants of `one` for #19012
 | |
|         rng = period_range("2000-01-01 09:00", freq="h", periods=10)
 | |
|         result = rng + one
 | |
|         expected = period_range("2000-01-01 10:00", freq="h", periods=10)
 | |
|         tm.assert_index_equal(result, expected)
 | |
|         rng += one
 | |
|         tm.assert_index_equal(rng, expected)
 | |
| 
 | |
|     def test_pi_sub_isub_int(self, one):
 | |
|         """
 | |
|         PeriodIndex.__sub__ and __isub__ with several representations of
 | |
|         the integer 1, e.g. int, np.int64, np.uint8, ...
 | |
|         """
 | |
|         rng = period_range("2000-01-01 09:00", freq="h", periods=10)
 | |
|         result = rng - one
 | |
|         expected = period_range("2000-01-01 08:00", freq="h", periods=10)
 | |
|         tm.assert_index_equal(result, expected)
 | |
|         rng -= one
 | |
|         tm.assert_index_equal(rng, expected)
 | |
| 
 | |
|     @pytest.mark.parametrize("five", [5, np.array(5, dtype=np.int64)])
 | |
|     def test_pi_sub_intlike(self, five):
 | |
|         rng = period_range("2007-01", periods=50)
 | |
| 
 | |
|         result = rng - five
 | |
|         exp = rng + (-five)
 | |
|         tm.assert_index_equal(result, exp)
 | |
| 
 | |
|     def test_pi_add_sub_int_array_freqn_gt1(self):
 | |
|         # GH#47209 test adding array of ints when freq.n > 1 matches
 | |
|         #  scalar behavior
 | |
|         pi = period_range("2016-01-01", periods=10, freq="2D")
 | |
|         arr = np.arange(10)
 | |
|         result = pi + arr
 | |
|         expected = pd.Index([x + y for x, y in zip(pi, arr)])
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|         result = pi - arr
 | |
|         expected = pd.Index([x - y for x, y in zip(pi, arr)])
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|     def test_pi_sub_isub_offset(self):
 | |
|         # offset
 | |
|         # DateOffset
 | |
|         rng = period_range("2014", "2024", freq="Y")
 | |
|         result = rng - pd.offsets.YearEnd(5)
 | |
|         expected = period_range("2009", "2019", freq="Y")
 | |
|         tm.assert_index_equal(result, expected)
 | |
|         rng -= pd.offsets.YearEnd(5)
 | |
|         tm.assert_index_equal(rng, expected)
 | |
| 
 | |
|         rng = period_range("2014-01", "2016-12", freq="M")
 | |
|         result = rng - pd.offsets.MonthEnd(5)
 | |
|         expected = period_range("2013-08", "2016-07", freq="M")
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|         rng -= pd.offsets.MonthEnd(5)
 | |
|         tm.assert_index_equal(rng, expected)
 | |
| 
 | |
|     @pytest.mark.parametrize("transpose", [True, False])
 | |
|     def test_pi_add_offset_n_gt1(self, box_with_array, transpose):
 | |
|         # GH#23215
 | |
|         # add offset to PeriodIndex with freq.n > 1
 | |
| 
 | |
|         per = Period("2016-01", freq="2M")
 | |
|         pi = PeriodIndex([per])
 | |
| 
 | |
|         expected = PeriodIndex(["2016-03"], freq="2M")
 | |
| 
 | |
|         pi = tm.box_expected(pi, box_with_array, transpose=transpose)
 | |
|         expected = tm.box_expected(expected, box_with_array, transpose=transpose)
 | |
| 
 | |
|         result = pi + per.freq
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         result = per.freq + pi
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|     def test_pi_add_offset_n_gt1_not_divisible(self, box_with_array):
 | |
|         # GH#23215
 | |
|         # PeriodIndex with freq.n > 1 add offset with offset.n % freq.n != 0
 | |
|         pi = PeriodIndex(["2016-01"], freq="2M")
 | |
|         expected = PeriodIndex(["2016-04"], freq="2M")
 | |
| 
 | |
|         pi = tm.box_expected(pi, box_with_array)
 | |
|         expected = tm.box_expected(expected, box_with_array)
 | |
| 
 | |
|         result = pi + to_offset("3ME")
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         result = to_offset("3ME") + pi
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|     # ---------------------------------------------------------------
 | |
|     # __add__/__sub__ with integer arrays
 | |
| 
 | |
|     @pytest.mark.parametrize("int_holder", [np.array, pd.Index])
 | |
|     @pytest.mark.parametrize("op", [operator.add, ops.radd])
 | |
|     def test_pi_add_intarray(self, int_holder, op):
 | |
|         # GH#19959
 | |
|         pi = PeriodIndex([Period("2015Q1"), Period("NaT")])
 | |
|         other = int_holder([4, -1])
 | |
| 
 | |
|         result = op(pi, other)
 | |
|         expected = PeriodIndex([Period("2016Q1"), Period("NaT")])
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|     @pytest.mark.parametrize("int_holder", [np.array, pd.Index])
 | |
|     def test_pi_sub_intarray(self, int_holder):
 | |
|         # GH#19959
 | |
|         pi = PeriodIndex([Period("2015Q1"), Period("NaT")])
 | |
|         other = int_holder([4, -1])
 | |
| 
 | |
|         result = pi - other
 | |
|         expected = PeriodIndex([Period("2014Q1"), Period("NaT")])
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|         msg = r"bad operand type for unary -: 'PeriodArray'"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             other - pi
 | |
| 
 | |
|     # ---------------------------------------------------------------
 | |
|     # Timedelta-like (timedelta, timedelta64, Timedelta, Tick)
 | |
|     # TODO: Some of these are misnomers because of non-Tick DateOffsets
 | |
| 
 | |
|     def test_parr_add_timedeltalike_minute_gt1(self, three_days, box_with_array):
 | |
|         # GH#23031 adding a time-delta-like offset to a PeriodArray that has
 | |
|         # minute frequency with n != 1.  A more general case is tested below
 | |
|         # in test_pi_add_timedeltalike_tick_gt1, but here we write out the
 | |
|         # expected result more explicitly.
 | |
|         other = three_days
 | |
|         rng = period_range("2014-05-01", periods=3, freq="2D")
 | |
|         rng = tm.box_expected(rng, box_with_array)
 | |
| 
 | |
|         expected = PeriodIndex(["2014-05-04", "2014-05-06", "2014-05-08"], freq="2D")
 | |
|         expected = tm.box_expected(expected, box_with_array)
 | |
| 
 | |
|         result = rng + other
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         result = other + rng
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         # subtraction
 | |
|         expected = PeriodIndex(["2014-04-28", "2014-04-30", "2014-05-02"], freq="2D")
 | |
|         expected = tm.box_expected(expected, box_with_array)
 | |
|         result = rng - other
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         msg = "|".join(
 | |
|             [
 | |
|                 r"bad operand type for unary -: 'PeriodArray'",
 | |
|                 r"cannot subtract PeriodArray from timedelta64\[[hD]\]",
 | |
|             ]
 | |
|         )
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             other - rng
 | |
| 
 | |
|     @pytest.mark.parametrize("freqstr", ["5ns", "5us", "5ms", "5s", "5min", "5h", "5d"])
 | |
|     def test_parr_add_timedeltalike_tick_gt1(self, three_days, freqstr, box_with_array):
 | |
|         # GH#23031 adding a time-delta-like offset to a PeriodArray that has
 | |
|         # tick-like frequency with n != 1
 | |
|         other = three_days
 | |
|         rng = period_range("2014-05-01", periods=6, freq=freqstr)
 | |
|         first = rng[0]
 | |
|         rng = tm.box_expected(rng, box_with_array)
 | |
| 
 | |
|         expected = period_range(first + other, periods=6, freq=freqstr)
 | |
|         expected = tm.box_expected(expected, box_with_array)
 | |
| 
 | |
|         result = rng + other
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         result = other + rng
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         # subtraction
 | |
|         expected = period_range(first - other, periods=6, freq=freqstr)
 | |
|         expected = tm.box_expected(expected, box_with_array)
 | |
|         result = rng - other
 | |
|         tm.assert_equal(result, expected)
 | |
|         msg = "|".join(
 | |
|             [
 | |
|                 r"bad operand type for unary -: 'PeriodArray'",
 | |
|                 r"cannot subtract PeriodArray from timedelta64\[[hD]\]",
 | |
|             ]
 | |
|         )
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             other - rng
 | |
| 
 | |
|     def test_pi_add_iadd_timedeltalike_daily(self, three_days):
 | |
|         # Tick
 | |
|         other = three_days
 | |
|         rng = period_range("2014-05-01", "2014-05-15", freq="D")
 | |
|         expected = period_range("2014-05-04", "2014-05-18", freq="D")
 | |
| 
 | |
|         result = rng + other
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|         rng += other
 | |
|         tm.assert_index_equal(rng, expected)
 | |
| 
 | |
|     def test_pi_sub_isub_timedeltalike_daily(self, three_days):
 | |
|         # Tick-like 3 Days
 | |
|         other = three_days
 | |
|         rng = period_range("2014-05-01", "2014-05-15", freq="D")
 | |
|         expected = period_range("2014-04-28", "2014-05-12", freq="D")
 | |
| 
 | |
|         result = rng - other
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|         rng -= other
 | |
|         tm.assert_index_equal(rng, expected)
 | |
| 
 | |
|     def test_parr_add_sub_timedeltalike_freq_mismatch_daily(
 | |
|         self, not_daily, box_with_array
 | |
|     ):
 | |
|         other = not_daily
 | |
|         rng = period_range("2014-05-01", "2014-05-15", freq="D")
 | |
|         rng = tm.box_expected(rng, box_with_array)
 | |
| 
 | |
|         msg = "|".join(
 | |
|             [
 | |
|                 # non-timedelta-like DateOffset
 | |
|                 "Input has different freq(=.+)? from Period.*?\\(freq=D\\)",
 | |
|                 # timedelta/td64/Timedelta but not a multiple of 24H
 | |
|                 "Cannot add/subtract timedelta-like from PeriodArray that is "
 | |
|                 "not an integer multiple of the PeriodArray's freq.",
 | |
|             ]
 | |
|         )
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng + other
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng += other
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng - other
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng -= other
 | |
| 
 | |
|     def test_pi_add_iadd_timedeltalike_hourly(self, two_hours):
 | |
|         other = two_hours
 | |
|         rng = period_range("2014-01-01 10:00", "2014-01-05 10:00", freq="h")
 | |
|         expected = period_range("2014-01-01 12:00", "2014-01-05 12:00", freq="h")
 | |
| 
 | |
|         result = rng + other
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|         rng += other
 | |
|         tm.assert_index_equal(rng, expected)
 | |
| 
 | |
|     def test_parr_add_timedeltalike_mismatched_freq_hourly(
 | |
|         self, not_hourly, box_with_array
 | |
|     ):
 | |
|         other = not_hourly
 | |
|         rng = period_range("2014-01-01 10:00", "2014-01-05 10:00", freq="h")
 | |
|         rng = tm.box_expected(rng, box_with_array)
 | |
|         msg = "|".join(
 | |
|             [
 | |
|                 # non-timedelta-like DateOffset
 | |
|                 "Input has different freq(=.+)? from Period.*?\\(freq=h\\)",
 | |
|                 # timedelta/td64/Timedelta but not a multiple of 24H
 | |
|                 "Cannot add/subtract timedelta-like from PeriodArray that is "
 | |
|                 "not an integer multiple of the PeriodArray's freq.",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng + other
 | |
| 
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng += other
 | |
| 
 | |
|     def test_pi_sub_isub_timedeltalike_hourly(self, two_hours):
 | |
|         other = two_hours
 | |
|         rng = period_range("2014-01-01 10:00", "2014-01-05 10:00", freq="h")
 | |
|         expected = period_range("2014-01-01 08:00", "2014-01-05 08:00", freq="h")
 | |
| 
 | |
|         result = rng - other
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|         rng -= other
 | |
|         tm.assert_index_equal(rng, expected)
 | |
| 
 | |
|     def test_add_iadd_timedeltalike_annual(self):
 | |
|         # offset
 | |
|         # DateOffset
 | |
|         rng = period_range("2014", "2024", freq="Y")
 | |
|         result = rng + pd.offsets.YearEnd(5)
 | |
|         expected = period_range("2019", "2029", freq="Y")
 | |
|         tm.assert_index_equal(result, expected)
 | |
|         rng += pd.offsets.YearEnd(5)
 | |
|         tm.assert_index_equal(rng, expected)
 | |
| 
 | |
|     def test_pi_add_sub_timedeltalike_freq_mismatch_annual(self, mismatched_freq):
 | |
|         other = mismatched_freq
 | |
|         rng = period_range("2014", "2024", freq="Y")
 | |
|         msg = "Input has different freq(=.+)? from Period.*?\\(freq=Y-DEC\\)"
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng + other
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng += other
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng - other
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng -= other
 | |
| 
 | |
|     def test_pi_add_iadd_timedeltalike_M(self):
 | |
|         rng = period_range("2014-01", "2016-12", freq="M")
 | |
|         expected = period_range("2014-06", "2017-05", freq="M")
 | |
| 
 | |
|         result = rng + pd.offsets.MonthEnd(5)
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|         rng += pd.offsets.MonthEnd(5)
 | |
|         tm.assert_index_equal(rng, expected)
 | |
| 
 | |
|     def test_pi_add_sub_timedeltalike_freq_mismatch_monthly(self, mismatched_freq):
 | |
|         other = mismatched_freq
 | |
|         rng = period_range("2014-01", "2016-12", freq="M")
 | |
|         msg = "Input has different freq(=.+)? from Period.*?\\(freq=M\\)"
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng + other
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng += other
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng - other
 | |
|         with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|             rng -= other
 | |
| 
 | |
|     @pytest.mark.parametrize("transpose", [True, False])
 | |
|     def test_parr_add_sub_td64_nat(self, box_with_array, transpose):
 | |
|         # GH#23320 special handling for timedelta64("NaT")
 | |
|         pi = period_range("1994-04-01", periods=9, freq="19D")
 | |
|         other = np.timedelta64("NaT")
 | |
|         expected = PeriodIndex(["NaT"] * 9, freq="19D")
 | |
| 
 | |
|         obj = tm.box_expected(pi, box_with_array, transpose=transpose)
 | |
|         expected = tm.box_expected(expected, box_with_array, transpose=transpose)
 | |
| 
 | |
|         result = obj + other
 | |
|         tm.assert_equal(result, expected)
 | |
|         result = other + obj
 | |
|         tm.assert_equal(result, expected)
 | |
|         result = obj - other
 | |
|         tm.assert_equal(result, expected)
 | |
|         msg = r"cannot subtract .* from .*"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             other - obj
 | |
| 
 | |
|     @pytest.mark.parametrize(
 | |
|         "other",
 | |
|         [
 | |
|             np.array(["NaT"] * 9, dtype="m8[ns]"),
 | |
|             TimedeltaArray._from_sequence(["NaT"] * 9, dtype="m8[ns]"),
 | |
|         ],
 | |
|     )
 | |
|     def test_parr_add_sub_tdt64_nat_array(self, box_with_array, other):
 | |
|         pi = period_range("1994-04-01", periods=9, freq="19D")
 | |
|         expected = PeriodIndex(["NaT"] * 9, freq="19D")
 | |
| 
 | |
|         obj = tm.box_expected(pi, box_with_array)
 | |
|         expected = tm.box_expected(expected, box_with_array)
 | |
| 
 | |
|         result = obj + other
 | |
|         tm.assert_equal(result, expected)
 | |
|         result = other + obj
 | |
|         tm.assert_equal(result, expected)
 | |
|         result = obj - other
 | |
|         tm.assert_equal(result, expected)
 | |
|         msg = r"cannot subtract .* from .*"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             other - obj
 | |
| 
 | |
|         # some but not *all* NaT
 | |
|         other = other.copy()
 | |
|         other[0] = np.timedelta64(0, "ns")
 | |
|         expected = PeriodIndex([pi[0]] + ["NaT"] * 8, freq="19D")
 | |
|         expected = tm.box_expected(expected, box_with_array)
 | |
| 
 | |
|         result = obj + other
 | |
|         tm.assert_equal(result, expected)
 | |
|         result = other + obj
 | |
|         tm.assert_equal(result, expected)
 | |
|         result = obj - other
 | |
|         tm.assert_equal(result, expected)
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             other - obj
 | |
| 
 | |
|     # ---------------------------------------------------------------
 | |
|     # Unsorted
 | |
| 
 | |
|     def test_parr_add_sub_index(self):
 | |
|         # Check that PeriodArray defers to Index on arithmetic ops
 | |
|         pi = period_range("2000-12-31", periods=3)
 | |
|         parr = pi.array
 | |
| 
 | |
|         result = parr - pi
 | |
|         expected = pi - pi
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|     def test_parr_add_sub_object_array(self):
 | |
|         pi = period_range("2000-12-31", periods=3, freq="D")
 | |
|         parr = pi.array
 | |
| 
 | |
|         other = np.array([Timedelta(days=1), pd.offsets.Day(2), 3])
 | |
| 
 | |
|         with tm.assert_produces_warning(PerformanceWarning):
 | |
|             result = parr + other
 | |
| 
 | |
|         expected = PeriodIndex(
 | |
|             ["2001-01-01", "2001-01-03", "2001-01-05"], freq="D"
 | |
|         )._data.astype(object)
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         with tm.assert_produces_warning(PerformanceWarning):
 | |
|             result = parr - other
 | |
| 
 | |
|         expected = PeriodIndex(["2000-12-30"] * 3, freq="D")._data.astype(object)
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|     def test_period_add_timestamp_raises(self, box_with_array):
 | |
|         # GH#17983
 | |
|         ts = Timestamp("2017")
 | |
|         per = Period("2017", freq="M")
 | |
| 
 | |
|         arr = pd.Index([per], dtype="Period[M]")
 | |
|         arr = tm.box_expected(arr, box_with_array)
 | |
| 
 | |
|         msg = "cannot add PeriodArray and Timestamp"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             arr + ts
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             ts + arr
 | |
|         msg = "cannot add PeriodArray and DatetimeArray"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             arr + Series([ts])
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             Series([ts]) + arr
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             arr + pd.Index([ts])
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             pd.Index([ts]) + arr
 | |
| 
 | |
|         if box_with_array is pd.DataFrame:
 | |
|             msg = "cannot add PeriodArray and DatetimeArray"
 | |
|         else:
 | |
|             msg = r"unsupported operand type\(s\) for \+: 'Period' and 'DatetimeArray"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             arr + pd.DataFrame([ts])
 | |
|         if box_with_array is pd.DataFrame:
 | |
|             msg = "cannot add PeriodArray and DatetimeArray"
 | |
|         else:
 | |
|             msg = r"unsupported operand type\(s\) for \+: 'DatetimeArray' and 'Period'"
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             pd.DataFrame([ts]) + arr
 | |
| 
 | |
| 
 | |
| class TestPeriodSeriesArithmetic:
 | |
|     def test_parr_add_timedeltalike_scalar(self, three_days, box_with_array):
 | |
|         # GH#13043
 | |
|         ser = Series(
 | |
|             [Period("2015-01-01", freq="D"), Period("2015-01-02", freq="D")],
 | |
|             name="xxx",
 | |
|         )
 | |
|         assert ser.dtype == "Period[D]"
 | |
| 
 | |
|         expected = Series(
 | |
|             [Period("2015-01-04", freq="D"), Period("2015-01-05", freq="D")],
 | |
|             name="xxx",
 | |
|         )
 | |
| 
 | |
|         obj = tm.box_expected(ser, box_with_array)
 | |
|         if box_with_array is pd.DataFrame:
 | |
|             assert (obj.dtypes == "Period[D]").all()
 | |
| 
 | |
|         expected = tm.box_expected(expected, box_with_array)
 | |
| 
 | |
|         result = obj + three_days
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         result = three_days + obj
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|     def test_ops_series_period(self):
 | |
|         # GH#13043
 | |
|         ser = Series(
 | |
|             [Period("2015-01-01", freq="D"), Period("2015-01-02", freq="D")],
 | |
|             name="xxx",
 | |
|         )
 | |
|         assert ser.dtype == "Period[D]"
 | |
| 
 | |
|         per = Period("2015-01-10", freq="D")
 | |
|         off = per.freq
 | |
|         # dtype will be object because of original dtype
 | |
|         expected = Series([9 * off, 8 * off], name="xxx", dtype=object)
 | |
|         tm.assert_series_equal(per - ser, expected)
 | |
|         tm.assert_series_equal(ser - per, -1 * expected)
 | |
| 
 | |
|         s2 = Series(
 | |
|             [Period("2015-01-05", freq="D"), Period("2015-01-04", freq="D")],
 | |
|             name="xxx",
 | |
|         )
 | |
|         assert s2.dtype == "Period[D]"
 | |
| 
 | |
|         expected = Series([4 * off, 2 * off], name="xxx", dtype=object)
 | |
|         tm.assert_series_equal(s2 - ser, expected)
 | |
|         tm.assert_series_equal(ser - s2, -1 * expected)
 | |
| 
 | |
| 
 | |
| class TestPeriodIndexSeriesMethods:
 | |
|     """Test PeriodIndex and Period Series Ops consistency"""
 | |
| 
 | |
|     def _check(self, values, func, expected):
 | |
|         idx = PeriodIndex(values)
 | |
|         result = func(idx)
 | |
|         tm.assert_equal(result, expected)
 | |
| 
 | |
|         ser = Series(values)
 | |
|         result = func(ser)
 | |
| 
 | |
|         exp = Series(expected, name=values.name)
 | |
|         tm.assert_series_equal(result, exp)
 | |
| 
 | |
|     def test_pi_ops(self):
 | |
|         idx = PeriodIndex(
 | |
|             ["2011-01", "2011-02", "2011-03", "2011-04"], freq="M", name="idx"
 | |
|         )
 | |
| 
 | |
|         expected = PeriodIndex(
 | |
|             ["2011-03", "2011-04", "2011-05", "2011-06"], freq="M", name="idx"
 | |
|         )
 | |
| 
 | |
|         self._check(idx, lambda x: x + 2, expected)
 | |
|         self._check(idx, lambda x: 2 + x, expected)
 | |
| 
 | |
|         self._check(idx + 2, lambda x: x - 2, idx)
 | |
| 
 | |
|         result = idx - Period("2011-01", freq="M")
 | |
|         off = idx.freq
 | |
|         exp = pd.Index([0 * off, 1 * off, 2 * off, 3 * off], name="idx")
 | |
|         tm.assert_index_equal(result, exp)
 | |
| 
 | |
|         result = Period("2011-01", freq="M") - idx
 | |
|         exp = pd.Index([0 * off, -1 * off, -2 * off, -3 * off], name="idx")
 | |
|         tm.assert_index_equal(result, exp)
 | |
| 
 | |
|     @pytest.mark.parametrize("ng", ["str", 1.5])
 | |
|     @pytest.mark.parametrize(
 | |
|         "func",
 | |
|         [
 | |
|             lambda obj, ng: obj + ng,
 | |
|             lambda obj, ng: ng + obj,
 | |
|             lambda obj, ng: obj - ng,
 | |
|             lambda obj, ng: ng - obj,
 | |
|             lambda obj, ng: np.add(obj, ng),
 | |
|             lambda obj, ng: np.add(ng, obj),
 | |
|             lambda obj, ng: np.subtract(obj, ng),
 | |
|             lambda obj, ng: np.subtract(ng, obj),
 | |
|         ],
 | |
|     )
 | |
|     def test_parr_ops_errors(self, ng, func, box_with_array):
 | |
|         idx = PeriodIndex(
 | |
|             ["2011-01", "2011-02", "2011-03", "2011-04"], freq="M", name="idx"
 | |
|         )
 | |
|         obj = tm.box_expected(idx, box_with_array)
 | |
|         msg = "|".join(
 | |
|             [
 | |
|                 r"unsupported operand type\(s\)",
 | |
|                 "can only concatenate",
 | |
|                 r"must be str",
 | |
|                 "object to str implicitly",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             func(obj, ng)
 | |
| 
 | |
|     def test_pi_ops_nat(self):
 | |
|         idx = PeriodIndex(
 | |
|             ["2011-01", "2011-02", "NaT", "2011-04"], freq="M", name="idx"
 | |
|         )
 | |
|         expected = PeriodIndex(
 | |
|             ["2011-03", "2011-04", "NaT", "2011-06"], freq="M", name="idx"
 | |
|         )
 | |
| 
 | |
|         self._check(idx, lambda x: x + 2, expected)
 | |
|         self._check(idx, lambda x: 2 + x, expected)
 | |
|         self._check(idx, lambda x: np.add(x, 2), expected)
 | |
| 
 | |
|         self._check(idx + 2, lambda x: x - 2, idx)
 | |
|         self._check(idx + 2, lambda x: np.subtract(x, 2), idx)
 | |
| 
 | |
|         # freq with mult
 | |
|         idx = PeriodIndex(
 | |
|             ["2011-01", "2011-02", "NaT", "2011-04"], freq="2M", name="idx"
 | |
|         )
 | |
|         expected = PeriodIndex(
 | |
|             ["2011-07", "2011-08", "NaT", "2011-10"], freq="2M", name="idx"
 | |
|         )
 | |
| 
 | |
|         self._check(idx, lambda x: x + 3, expected)
 | |
|         self._check(idx, lambda x: 3 + x, expected)
 | |
|         self._check(idx, lambda x: np.add(x, 3), expected)
 | |
| 
 | |
|         self._check(idx + 3, lambda x: x - 3, idx)
 | |
|         self._check(idx + 3, lambda x: np.subtract(x, 3), idx)
 | |
| 
 | |
|     def test_pi_ops_array_int(self):
 | |
|         idx = PeriodIndex(
 | |
|             ["2011-01", "2011-02", "NaT", "2011-04"], freq="M", name="idx"
 | |
|         )
 | |
|         f = lambda x: x + np.array([1, 2, 3, 4])
 | |
|         exp = PeriodIndex(
 | |
|             ["2011-02", "2011-04", "NaT", "2011-08"], freq="M", name="idx"
 | |
|         )
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: np.add(x, np.array([4, -1, 1, 2]))
 | |
|         exp = PeriodIndex(
 | |
|             ["2011-05", "2011-01", "NaT", "2011-06"], freq="M", name="idx"
 | |
|         )
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: x - np.array([1, 2, 3, 4])
 | |
|         exp = PeriodIndex(
 | |
|             ["2010-12", "2010-12", "NaT", "2010-12"], freq="M", name="idx"
 | |
|         )
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: np.subtract(x, np.array([3, 2, 3, -2]))
 | |
|         exp = PeriodIndex(
 | |
|             ["2010-10", "2010-12", "NaT", "2011-06"], freq="M", name="idx"
 | |
|         )
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|     def test_pi_ops_offset(self):
 | |
|         idx = PeriodIndex(
 | |
|             ["2011-01-01", "2011-02-01", "2011-03-01", "2011-04-01"],
 | |
|             freq="D",
 | |
|             name="idx",
 | |
|         )
 | |
|         f = lambda x: x + pd.offsets.Day()
 | |
|         exp = PeriodIndex(
 | |
|             ["2011-01-02", "2011-02-02", "2011-03-02", "2011-04-02"],
 | |
|             freq="D",
 | |
|             name="idx",
 | |
|         )
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: x + pd.offsets.Day(2)
 | |
|         exp = PeriodIndex(
 | |
|             ["2011-01-03", "2011-02-03", "2011-03-03", "2011-04-03"],
 | |
|             freq="D",
 | |
|             name="idx",
 | |
|         )
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|         f = lambda x: x - pd.offsets.Day(2)
 | |
|         exp = PeriodIndex(
 | |
|             ["2010-12-30", "2011-01-30", "2011-02-27", "2011-03-30"],
 | |
|             freq="D",
 | |
|             name="idx",
 | |
|         )
 | |
|         self._check(idx, f, exp)
 | |
| 
 | |
|     def test_pi_offset_errors(self):
 | |
|         idx = PeriodIndex(
 | |
|             ["2011-01-01", "2011-02-01", "2011-03-01", "2011-04-01"],
 | |
|             freq="D",
 | |
|             name="idx",
 | |
|         )
 | |
|         ser = Series(idx)
 | |
| 
 | |
|         msg = (
 | |
|             "Cannot add/subtract timedelta-like from PeriodArray that is not "
 | |
|             "an integer multiple of the PeriodArray's freq"
 | |
|         )
 | |
|         for obj in [idx, ser]:
 | |
|             with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|                 obj + pd.offsets.Hour(2)
 | |
| 
 | |
|             with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|                 pd.offsets.Hour(2) + obj
 | |
| 
 | |
|             with pytest.raises(IncompatibleFrequency, match=msg):
 | |
|                 obj - pd.offsets.Hour(2)
 | |
| 
 | |
|     def test_pi_sub_period(self):
 | |
|         # GH#13071
 | |
|         idx = PeriodIndex(
 | |
|             ["2011-01", "2011-02", "2011-03", "2011-04"], freq="M", name="idx"
 | |
|         )
 | |
| 
 | |
|         result = idx - Period("2012-01", freq="M")
 | |
|         off = idx.freq
 | |
|         exp = pd.Index([-12 * off, -11 * off, -10 * off, -9 * off], name="idx")
 | |
|         tm.assert_index_equal(result, exp)
 | |
| 
 | |
|         result = np.subtract(idx, Period("2012-01", freq="M"))
 | |
|         tm.assert_index_equal(result, exp)
 | |
| 
 | |
|         result = Period("2012-01", freq="M") - idx
 | |
|         exp = pd.Index([12 * off, 11 * off, 10 * off, 9 * off], name="idx")
 | |
|         tm.assert_index_equal(result, exp)
 | |
| 
 | |
|         result = np.subtract(Period("2012-01", freq="M"), idx)
 | |
|         tm.assert_index_equal(result, exp)
 | |
| 
 | |
|         exp = TimedeltaIndex([np.nan, np.nan, np.nan, np.nan], name="idx")
 | |
|         result = idx - Period("NaT", freq="M")
 | |
|         tm.assert_index_equal(result, exp)
 | |
|         assert result.freq == exp.freq
 | |
| 
 | |
|         result = Period("NaT", freq="M") - idx
 | |
|         tm.assert_index_equal(result, exp)
 | |
|         assert result.freq == exp.freq
 | |
| 
 | |
|     def test_pi_sub_pdnat(self):
 | |
|         # GH#13071, GH#19389
 | |
|         idx = PeriodIndex(
 | |
|             ["2011-01", "2011-02", "NaT", "2011-04"], freq="M", name="idx"
 | |
|         )
 | |
|         exp = TimedeltaIndex([pd.NaT] * 4, name="idx")
 | |
|         tm.assert_index_equal(pd.NaT - idx, exp)
 | |
|         tm.assert_index_equal(idx - pd.NaT, exp)
 | |
| 
 | |
|     def test_pi_sub_period_nat(self):
 | |
|         # GH#13071
 | |
|         idx = PeriodIndex(
 | |
|             ["2011-01", "NaT", "2011-03", "2011-04"], freq="M", name="idx"
 | |
|         )
 | |
| 
 | |
|         result = idx - Period("2012-01", freq="M")
 | |
|         off = idx.freq
 | |
|         exp = pd.Index([-12 * off, pd.NaT, -10 * off, -9 * off], name="idx")
 | |
|         tm.assert_index_equal(result, exp)
 | |
| 
 | |
|         result = Period("2012-01", freq="M") - idx
 | |
|         exp = pd.Index([12 * off, pd.NaT, 10 * off, 9 * off], name="idx")
 | |
|         tm.assert_index_equal(result, exp)
 | |
| 
 | |
|         exp = TimedeltaIndex([np.nan, np.nan, np.nan, np.nan], name="idx")
 | |
|         tm.assert_index_equal(idx - Period("NaT", freq="M"), exp)
 | |
|         tm.assert_index_equal(Period("NaT", freq="M") - idx, exp)
 |