From 1797ae0bf7093e6be96f6d36529399dd4372fcba Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 21 Jan 2020 20:01:28 -0800 Subject: [PATCH 1/2] TST/ENH: require freq match in assert_index_equal --- pandas/_testing.py | 9 ++++++--- pandas/tests/series/methods/test_to_dict.py | 7 ++++--- pandas/tests/tseries/offsets/test_offsets.py | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pandas/_testing.py b/pandas/_testing.py index 631d550c60534..f303037397457 100644 --- a/pandas/_testing.py +++ b/pandas/_testing.py @@ -703,7 +703,9 @@ def _get_ilevel_values(index, level): # metadata comparison if check_names: assert_attr_equal("names", left, right, obj=obj) - if isinstance(left, pd.PeriodIndex) or isinstance(right, pd.PeriodIndex): + + freq_classes = (pd.PeriodIndex, pd.DatetimeIndex, pd.TimedeltaIndex) + if isinstance(left, freq_classes) or isinstance(right, freq_classes): assert_attr_equal("freq", left, right, obj=obj) if isinstance(left, pd.IntervalIndex) or isinstance(right, pd.IntervalIndex): assert_interval_array_equal(left.values, right.values) @@ -742,8 +744,9 @@ def repr_class(x): raise_assert_detail(obj, msg, repr_class(left), repr_class(right)) -def assert_attr_equal(attr, left, right, obj="Attributes"): - """checks attributes are equal. Both objects must have attribute. +def assert_attr_equal(attr: str, left, right, obj="Attributes"): + """ + Check attributes are equal. Both objects must have attribute. Parameters ---------- diff --git a/pandas/tests/series/methods/test_to_dict.py b/pandas/tests/series/methods/test_to_dict.py index 2fbf3e8d39cf3..9e620b90f5816 100644 --- a/pandas/tests/series/methods/test_to_dict.py +++ b/pandas/tests/series/methods/test_to_dict.py @@ -12,9 +12,10 @@ class TestSeriesToDict: ) def test_to_dict(self, mapping, datetime_series): # GH#16122 - tm.assert_series_equal( - Series(datetime_series.to_dict(mapping), name="ts"), datetime_series - ) + result = Series(datetime_series.to_dict(mapping), name="ts") + expected = datetime_series.copy() + expected.index._set_freq(None) + tm.assert_series_equal(result, expected) from_method = Series(datetime_series.to_dict(collections.Counter)) from_constructor = Series(collections.Counter(datetime_series.items())) tm.assert_series_equal(from_method, from_constructor) diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index 2f00a58fe80be..044dfa703c081 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -3501,7 +3501,7 @@ def test_offset_whole_year(self): # ensure generating a range with DatetimeIndex gives same result result = date_range(start=dates[0], end=dates[-1], freq="SM") - exp = DatetimeIndex(dates) + exp = DatetimeIndex(dates, freq="SM") tm.assert_index_equal(result, exp) offset_cases = [] @@ -3760,7 +3760,7 @@ def test_offset_whole_year(self): # ensure generating a range with DatetimeIndex gives same result result = date_range(start=dates[0], end=dates[-1], freq="SMS") - exp = DatetimeIndex(dates) + exp = DatetimeIndex(dates, freq="SMS") tm.assert_index_equal(result, exp) offset_cases = [] From e60478af425e2338261ff7e6f69b4ade2017748a Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 22 Jan 2020 17:39:11 -0800 Subject: [PATCH 2/2] down to 501 test failures --- pandas/_testing.py | 20 ++++++++++++++++--- pandas/core/arrays/timedeltas.py | 3 +++ pandas/core/indexes/datetimelike.py | 2 +- pandas/core/tools/timedeltas.py | 9 ++++++--- pandas/tests/indexes/common.py | 2 +- .../tests/indexes/timedeltas/test_setops.py | 5 +++-- .../timedeltas/test_timedelta_range.py | 10 ++++++++++ pandas/tests/indexing/test_datetime.py | 9 +++++++-- 8 files changed, 48 insertions(+), 12 deletions(-) diff --git a/pandas/_testing.py b/pandas/_testing.py index f303037397457..bea1de5e70472 100644 --- a/pandas/_testing.py +++ b/pandas/_testing.py @@ -589,6 +589,7 @@ def assert_index_equal( check_less_precise: Union[bool, int] = False, check_exact: bool = True, check_categorical: bool = True, + check_freq: bool = True, obj: str = "Index", ) -> None: """ @@ -612,6 +613,8 @@ def assert_index_equal( Whether to compare number exactly. check_categorical : bool, default True Whether to compare internal Categorical exactly. + check_freq : bool, default True + Whether to check the freq attribute on DatetimeIndex/TimedeltaIndex. obj : str, default 'Index' Specify object name being compared, internally used to show appropriate assertion message. @@ -705,7 +708,9 @@ def _get_ilevel_values(index, level): assert_attr_equal("names", left, right, obj=obj) freq_classes = (pd.PeriodIndex, pd.DatetimeIndex, pd.TimedeltaIndex) - if isinstance(left, freq_classes) or isinstance(right, freq_classes): + if check_freq and ( + isinstance(left, freq_classes) or isinstance(right, freq_classes) + ): assert_attr_equal("freq", left, right, obj=obj) if isinstance(left, pd.IntervalIndex) or isinstance(right, pd.IntervalIndex): assert_interval_array_equal(left.values, right.values) @@ -879,8 +884,13 @@ def assert_interval_array_equal(left, right, exact="equiv", obj="IntervalArray") """ _check_isinstance(left, right, IntervalArray) - assert_index_equal(left.left, right.left, exact=exact, obj=f"{obj}.left") - assert_index_equal(left.right, right.right, exact=exact, obj=f"{obj}.left") + # TODO: re-enable check_freq? + assert_index_equal( + left.left, right.left, exact=exact, obj=f"{obj}.left", check_freq=False + ) + assert_index_equal( + left.right, right.right, exact=exact, obj=f"{obj}.left", check_freq=False + ) assert_attr_equal("closed", left, right, obj=obj) @@ -1073,6 +1083,7 @@ def assert_series_equal( check_exact=False, check_datetimelike_compat=False, check_categorical=True, + check_freq: bool = True, obj="Series", ): """ @@ -1107,6 +1118,8 @@ def assert_series_equal( Compare datetime-like which is comparable ignoring dtype. check_categorical : bool, default True Whether to compare internal Categorical exactly. + check_freq : bool, default True + Whether to check the freq attribute on a DatetimeIndex/TimedeltaIndex obj : str, default 'Series' Specify object name being compared, internally used to show appropriate assertion message. @@ -1137,6 +1150,7 @@ def assert_series_equal( check_less_precise=check_less_precise, check_exact=check_exact, check_categorical=check_categorical, + check_freq=check_freq, obj=f"{obj}.index", ) diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index 516a271042c9b..4cadcaa33cd91 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -259,6 +259,9 @@ def _generate_range(cls, start, end, periods, freq, closed=None): index = _generate_regular_range(start, end, periods, freq) else: index = np.linspace(start.value, end.value, periods).astype("i8") + if len(index) >= 2: + td = Timedelta(index[1] - index[0]) + freq = to_offset(td) if not left_closed: index = index[1:] diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 1bfec9fbad0ed..6ba72095718fa 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -781,7 +781,7 @@ def _fast_union(self, other, sort=None): loc = right.searchsorted(left_start, side="left") right_chunk = right.values[:loc] dates = concat_compat((left.values, right_chunk)) - return self._shallow_copy(dates) + return self._shallow_copy(dates, freq=None) else: left, right = other, self diff --git a/pandas/core/tools/timedeltas.py b/pandas/core/tools/timedeltas.py index 3e185feaea38e..df3360df4ca6c 100644 --- a/pandas/core/tools/timedeltas.py +++ b/pandas/core/tools/timedeltas.py @@ -10,7 +10,7 @@ from pandas.core.dtypes.common import is_list_like from pandas.core.dtypes.generic import ABCIndexClass, ABCSeries -from pandas.core.arrays.timedeltas import sequence_to_td64ns +from pandas.core.arrays.timedeltas import TimedeltaArray, sequence_to_td64ns def to_timedelta(arg, unit="ns", errors="raise"): @@ -136,7 +136,9 @@ def _convert_listlike(arg, unit="ns", errors="raise", name=None): arg = np.array(list(arg), dtype=object) try: - value = sequence_to_td64ns(arg, unit=unit, errors=errors, copy=False)[0] + value, inferred_freq = sequence_to_td64ns( + arg, unit=unit, errors=errors, copy=False + ) except ValueError: if errors == "ignore": return arg @@ -152,5 +154,6 @@ def _convert_listlike(arg, unit="ns", errors="raise", name=None): from pandas import TimedeltaIndex - value = TimedeltaIndex(value, unit="ns", name=name) + tda = TimedeltaArray._simple_new(value, freq=inferred_freq) + value = TimedeltaIndex(tda, name=name) return value diff --git a/pandas/tests/indexes/common.py b/pandas/tests/indexes/common.py index f3ebe8313d0c6..b2e3ee24a6bd1 100644 --- a/pandas/tests/indexes/common.py +++ b/pandas/tests/indexes/common.py @@ -443,7 +443,7 @@ def test_where(self, klass): cond = [False] + [True] * len(i[1:]) expected = pd.Index([i._na_value] + i[1:].tolist(), dtype=i.dtype) result = i.where(klass(cond)) - tm.assert_index_equal(result, expected) + tm.assert_index_equal(result, expected, check_freq=False) @pytest.mark.parametrize("case", [0.5, "xxx"]) @pytest.mark.parametrize( diff --git a/pandas/tests/indexes/timedeltas/test_setops.py b/pandas/tests/indexes/timedeltas/test_setops.py index 0aa784cbb7710..f73a13af46b23 100644 --- a/pandas/tests/indexes/timedeltas/test_setops.py +++ b/pandas/tests/indexes/timedeltas/test_setops.py @@ -186,7 +186,7 @@ def test_intersection(self, rng, expected, sort): TimedeltaIndex(["2 hour", "5 hour", "5 hour", "1 hour"], name="other"), TimedeltaIndex(["1 hour", "2 hour"], name=None), ), - # reveresed index + # reversed index ( TimedeltaIndex(["1 hour", "2 hour", "4 hour", "3 hour"], name="idx")[ ::-1 @@ -202,10 +202,11 @@ def test_intersection_non_monotonic(self, rng, expected, sort): result = base.intersection(rng, sort=sort) if sort is None: expected = expected.sort_values() + expected._set_freq("infer") tm.assert_index_equal(result, expected) assert result.name == expected.name - # if reveresed order, frequency is still the same + # if reversed order, frequency is still the same if all(base == rng[::-1]) and sort is None: assert isinstance(result.freq, Hour) else: diff --git a/pandas/tests/indexes/timedeltas/test_timedelta_range.py b/pandas/tests/indexes/timedeltas/test_timedelta_range.py index 1cef9de6a3a77..0e2058fb98edc 100644 --- a/pandas/tests/indexes/timedeltas/test_timedelta_range.py +++ b/pandas/tests/indexes/timedeltas/test_timedelta_range.py @@ -12,22 +12,32 @@ class TestTimedeltas: def test_timedelta_range(self): expected = to_timedelta(np.arange(5), unit="D") + expected._set_freq("infer") + result = timedelta_range("0 days", periods=5, freq="D") tm.assert_index_equal(result, expected) expected = to_timedelta(np.arange(11), unit="D") + expected._set_freq("infer") + result = timedelta_range("0 days", "10 days", freq="D") tm.assert_index_equal(result, expected) expected = to_timedelta(np.arange(5), unit="D") + Second(2) + Day() + expected._set_freq("infer") + result = timedelta_range("1 days, 00:00:02", "5 days, 00:00:02", freq="D") tm.assert_index_equal(result, expected) expected = to_timedelta([1, 3, 5, 7, 9], unit="D") + Second(2) + expected._set_freq("infer") + result = timedelta_range("1 days, 00:00:02", periods=5, freq="2D") tm.assert_index_equal(result, expected) expected = to_timedelta(np.arange(50), unit="T") * 30 + expected._set_freq("infer") + result = timedelta_range("0 days", freq="30T", periods=50) tm.assert_index_equal(result, expected) diff --git a/pandas/tests/indexing/test_datetime.py b/pandas/tests/indexing/test_datetime.py index 42f992339f036..7d6c07a50b2b6 100644 --- a/pandas/tests/indexing/test_datetime.py +++ b/pandas/tests/indexing/test_datetime.py @@ -145,7 +145,11 @@ def test_indexing_with_datetimeindex_tz(self): for sel in (index, list(index)): # getitem - tm.assert_series_equal(ser[sel], ser) + + # list(index) loses the freq + check_freq = True if sel is index else False + + tm.assert_series_equal(ser[sel], ser, check_freq=check_freq) # setitem result = ser.copy() @@ -154,7 +158,7 @@ def test_indexing_with_datetimeindex_tz(self): tm.assert_series_equal(result, expected) # .loc getitem - tm.assert_series_equal(ser.loc[sel], ser) + tm.assert_series_equal(ser.loc[sel], ser, check_freq=check_freq) # .loc setitem result = ser.copy() @@ -222,6 +226,7 @@ def test_series_partial_set_datetime(self): # GH 11497 idx = date_range("2011-01-01", "2011-01-02", freq="D", name="idx") + idx._set_freq(None) ser = Series([0.1, 0.2], index=idx, name="s") result = ser.loc[[Timestamp("2011-01-01"), Timestamp("2011-01-02")]]