| |
| |
| |
| 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 |
|
|
|
|
| |
| |
|
|
|
|
| class TestPeriodArrayLikeComparisons: |
| |
| |
| |
|
|
| @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): |
| |
|
|
| 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, |
| False, |
| None, |
| ], |
| ) |
| def test_compare_invalid_scalar(self, box_with_array, scalar): |
| |
| |
| 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)), |
| |
| [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: |
| |
|
|
| 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) |
|
|
| |
| result = idx.values.reshape(10, 2) < per |
| tm.assert_numpy_array_equal(result, exp.reshape(10, 2)) |
|
|
| |
| result = idx < np.array(per) |
| tm.assert_numpy_array_equal(result, exp) |
|
|
| |
| 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): |
| |
| 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): |
| |
| base = PeriodIndex(["2011-01", "2011-02", "2011-03", "2011-04"], freq=freq) |
| base = tm.box_expected(base, box_with_array) |
|
|
| |
| 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): |
| |
| |
| 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 |
|
|
| |
| 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 |
|
|
| |
| 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) |
|
|
| |
| @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): |
| |
| 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""" |
|
|
| |
|
|
| def _check(self, values, func, expected): |
| |
|
|
| idx = PeriodIndex(values) |
| result = func(idx) |
|
|
| |
| 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) |
|
|
|
|
| |
| |
|
|
|
|
| class TestPeriodFrameArithmetic: |
| def test_ops_frame_period(self): |
| |
| 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 |
| |
| 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: |
| |
| |
| |
| |
| |
|
|
| 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) |
| |
|
|
| rng = tm.box_expected(rng, box_with_array) |
| |
| |
| |
| |
| 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): |
| |
| |
| |
| |
| 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): |
| |
| 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): |
| |
| 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) |
|
|
| |
| |
|
|
| @pytest.mark.parametrize( |
| "other", |
| [ |
| |
| Timestamp("2016-01-01"), |
| Timestamp("2016-01-01").to_pydatetime(), |
| Timestamp("2016-01-01").to_datetime64(), |
| |
| 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, |
| |
| 3.14, |
| np.array([2.0, 3.0, 4.0]), |
| ], |
| ) |
| def test_parr_add_sub_invalid(self, other, box_with_array): |
| |
| 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 |
|
|
| |
| |
|
|
| 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): |
| |
| |
| 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) |
|
|
| |
| 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) |
|
|
| |
| 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": |
| |
| 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: |
| |
| |
| msg = "Cannot add or subtract timedelta64" |
| with pytest.raises(TypeError, match=msg): |
| pi - td64obj |
| with pytest.raises(TypeError, match=msg): |
| pi[0] - td64obj |
|
|
| |
| |
|
|
| @pytest.mark.parametrize("box", [np.array, pd.Index]) |
| def test_pi_add_offset_array(self, box): |
| |
| 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)]) |
| |
| |
| 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): |
| |
| 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)]) |
|
|
| |
| |
| 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): |
| |
| 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): |
| |
| |
| 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): |
| |
| |
| 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): |
| |
| |
|
|
| 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): |
| |
| |
| 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) |
|
|
| |
| |
|
|
| @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): |
| |
| 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): |
| |
| 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 |
|
|
| |
| |
| |
|
|
| def test_parr_add_timedeltalike_minute_gt1(self, three_days, box_with_array): |
| |
| |
| |
| |
| 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) |
|
|
| |
| 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): |
| |
| |
| 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) |
|
|
| |
| 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): |
| |
| 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): |
| |
| 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( |
| [ |
| |
| "Input has different freq(=.+)? from Period.*?\\(freq=D\\)", |
| |
| "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( |
| [ |
| |
| "Input has different freq(=.+)? from Period.*?\\(freq=h\\)", |
| |
| "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): |
| |
| |
| 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): |
| |
| 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 |
|
|
| |
| 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 |
|
|
| |
| |
|
|
| def test_parr_add_sub_index(self): |
| |
| 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): |
| |
| 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): |
| |
| 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): |
| |
| 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 |
| |
| 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) |
|
|
| |
| 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): |
| |
| 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): |
| |
| 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): |
| |
| 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) |
|
|