1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600 |
- # 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,
- )
- # ------------------------------------------------------------------
- # 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="A")
- with pytest.raises(TypeError, match=msg):
- Period("2011", freq="A") >= base
- # TODO: Could parametrize over boxes for idx?
- idx = PeriodIndex(["2011", "2012", "2013", "2014"], freq="A")
- rev_msg = r"Invalid comparison between dtype=period\[A-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="A"),
- Period("2011-02", freq="M"),
- Period("2013", freq="A"),
- Period("2011-04", freq="M"),
- ]
- )
- ser = Series(
- [
- Period("2012", freq="A"),
- Period("2011-01", freq="M"),
- Period("2013", freq="A"),
- 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="A")
- result = rng - pd.offsets.YearEnd(5)
- expected = period_range("2009", "2019", freq="A")
- 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("3M")
- tm.assert_equal(result, expected)
- result = to_offset("3M") + 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", "5T", "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="A")
- result = rng + pd.offsets.YearEnd(5)
- expected = period_range("2019", "2029", freq="A")
- 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="A")
- msg = "Input has different freq(=.+)? from Period.*?\\(freq=A-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),
- ],
- )
- 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)
- 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)
|