diff --git a/pandas/conftest.py b/pandas/conftest.py index 1e1d14f46cc6e..65d4b936efe44 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -995,17 +995,19 @@ def all_reductions(request): return request.param -@pytest.fixture(params=["__eq__", "__ne__", "__le__", "__lt__", "__ge__", "__gt__"]) -def all_compare_operators(request): +@pytest.fixture( + params=[ + operator.eq, + operator.ne, + operator.gt, + operator.ge, + operator.lt, + operator.le, + ] +) +def comparison_op(request): """ - Fixture for dunder names for common compare operations - - * >= - * > - * == - * != - * < - * <= + Fixture for operator module comparison functions. """ return request.param diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index e511c1bdaca9c..82f1e60f0aea5 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -365,18 +365,14 @@ def test_dt64arr_timestamp_equality(self, box_with_array): class TestDatetimeIndexComparisons: # TODO: moved from tests.indexes.test_base; parametrize and de-duplicate - @pytest.mark.parametrize( - "op", - [operator.eq, operator.ne, operator.gt, operator.lt, operator.ge, operator.le], - ) - def test_comparators(self, op): + def test_comparators(self, comparison_op): index = tm.makeDateIndex(100) element = index[len(index) // 2] element = Timestamp(element).to_datetime64() arr = np.array(index) - arr_result = op(arr, element) - index_result = op(index, element) + arr_result = comparison_op(arr, element) + index_result = comparison_op(index, element) assert isinstance(index_result, np.ndarray) tm.assert_numpy_array_equal(arr_result, index_result) @@ -554,12 +550,9 @@ def test_dti_cmp_nat_behaves_like_float_cmp_nan(self): expected = np.array([True, True, False, True, True, True]) tm.assert_numpy_array_equal(result, expected) - @pytest.mark.parametrize( - "op", - [operator.eq, operator.ne, operator.gt, operator.ge, operator.lt, operator.le], - ) - def test_comparison_tzawareness_compat(self, op, box_with_array): + def test_comparison_tzawareness_compat(self, comparison_op, box_with_array): # GH#18162 + op = comparison_op box = box_with_array dr = date_range("2016-01-01", periods=6) @@ -606,12 +599,10 @@ def test_comparison_tzawareness_compat(self, op, box_with_array): assert np.all(np.array(tolist(dz), dtype=object) == dz) assert np.all(dz == np.array(tolist(dz), dtype=object)) - @pytest.mark.parametrize( - "op", - [operator.eq, operator.ne, operator.gt, operator.ge, operator.lt, operator.le], - ) - def test_comparison_tzawareness_compat_scalars(self, op, box_with_array): + def test_comparison_tzawareness_compat_scalars(self, comparison_op, box_with_array): # GH#18162 + op = comparison_op + dr = date_range("2016-01-01", periods=6) dz = dr.tz_localize("US/Pacific") @@ -638,10 +629,6 @@ def test_comparison_tzawareness_compat_scalars(self, op, box_with_array): with pytest.raises(TypeError, match=msg): op(ts, dz) - @pytest.mark.parametrize( - "op", - [operator.eq, operator.ne, operator.gt, operator.ge, operator.lt, operator.le], - ) @pytest.mark.parametrize( "other", [datetime(2016, 1, 1), Timestamp("2016-01-01"), np.datetime64("2016-01-01")], @@ -652,8 +639,9 @@ def test_comparison_tzawareness_compat_scalars(self, op, box_with_array): @pytest.mark.filterwarnings("ignore:elementwise comp:DeprecationWarning") @pytest.mark.filterwarnings("ignore:Converting timezone-aware:FutureWarning") def test_scalar_comparison_tzawareness( - self, op, other, tz_aware_fixture, box_with_array + self, comparison_op, other, tz_aware_fixture, box_with_array ): + op = comparison_op box = box_with_array tz = tz_aware_fixture dti = date_range("2016-01-01", periods=2, tz=tz) @@ -680,13 +668,11 @@ def test_scalar_comparison_tzawareness( with pytest.raises(TypeError, match=msg): op(other, dtarr) - @pytest.mark.parametrize( - "op", - [operator.eq, operator.ne, operator.gt, operator.ge, operator.lt, operator.le], - ) - def test_nat_comparison_tzawareness(self, op): + def test_nat_comparison_tzawareness(self, comparison_op): # GH#19276 # tzaware DatetimeIndex should not raise when compared to NaT + op = comparison_op + dti = DatetimeIndex( ["2014-01-01", NaT, "2014-03-01", NaT, "2014-05-01", "2014-07-01"] ) diff --git a/pandas/tests/arrays/boolean/test_comparison.py b/pandas/tests/arrays/boolean/test_comparison.py index 8730837b518e5..2741d13ee599b 100644 --- a/pandas/tests/arrays/boolean/test_comparison.py +++ b/pandas/tests/arrays/boolean/test_comparison.py @@ -21,25 +21,23 @@ def dtype(): class TestComparisonOps(ComparisonOps): - def test_compare_scalar(self, data, all_compare_operators): - op_name = all_compare_operators - self._compare_other(data, op_name, True) + def test_compare_scalar(self, data, comparison_op): + self._compare_other(data, comparison_op, True) - def test_compare_array(self, data, all_compare_operators): - op_name = all_compare_operators + def test_compare_array(self, data, comparison_op): other = pd.array([True] * len(data), dtype="boolean") - self._compare_other(data, op_name, other) + self._compare_other(data, comparison_op, other) other = np.array([True] * len(data)) - self._compare_other(data, op_name, other) + self._compare_other(data, comparison_op, other) other = pd.Series([True] * len(data)) - self._compare_other(data, op_name, other) + self._compare_other(data, comparison_op, other) @pytest.mark.parametrize("other", [True, False, pd.NA]) - def test_scalar(self, other, all_compare_operators, dtype): - ComparisonOps.test_scalar(self, other, all_compare_operators, dtype) + def test_scalar(self, other, comparison_op, dtype): + ComparisonOps.test_scalar(self, other, comparison_op, dtype) - def test_array(self, all_compare_operators): - op = self.get_op_from_name(all_compare_operators) + def test_array(self, comparison_op): + op = comparison_op a = pd.array([True] * 3 + [False] * 3 + [None] * 3, dtype="boolean") b = pd.array([True, False, None] * 3, dtype="boolean") diff --git a/pandas/tests/arrays/categorical/test_operators.py b/pandas/tests/arrays/categorical/test_operators.py index 4a00df2d783cf..06296a954c059 100644 --- a/pandas/tests/arrays/categorical/test_operators.py +++ b/pandas/tests/arrays/categorical/test_operators.py @@ -1,4 +1,3 @@ -import operator import warnings import numpy as np @@ -145,9 +144,9 @@ def test_compare_frame(self): expected = DataFrame([[False, True, True, False]]) tm.assert_frame_equal(result, expected) - def test_compare_frame_raises(self, all_compare_operators): + def test_compare_frame_raises(self, comparison_op): # alignment raises unless we transpose - op = getattr(operator, all_compare_operators) + op = comparison_op cat = Categorical(["a", "b", 2, "a"]) df = DataFrame(cat) msg = "Unable to coerce to Series, length must be 1: given 4" diff --git a/pandas/tests/arrays/floating/test_comparison.py b/pandas/tests/arrays/floating/test_comparison.py index bfd54e125159c..c4163c25ae74d 100644 --- a/pandas/tests/arrays/floating/test_comparison.py +++ b/pandas/tests/arrays/floating/test_comparison.py @@ -10,11 +10,11 @@ class TestComparisonOps(NumericOps, ComparisonOps): @pytest.mark.parametrize("other", [True, False, pd.NA, -1.0, 0.0, 1]) - def test_scalar(self, other, all_compare_operators, dtype): - ComparisonOps.test_scalar(self, other, all_compare_operators, dtype) + def test_scalar(self, other, comparison_op, dtype): + ComparisonOps.test_scalar(self, other, comparison_op, dtype) - def test_compare_with_integerarray(self, all_compare_operators): - op = self.get_op_from_name(all_compare_operators) + def test_compare_with_integerarray(self, comparison_op): + op = comparison_op a = pd.array([0, 1, None] * 3, dtype="Int64") b = pd.array([0] * 3 + [1] * 3 + [None] * 3, dtype="Float64") other = b.astype("Int64") diff --git a/pandas/tests/arrays/integer/test_comparison.py b/pandas/tests/arrays/integer/test_comparison.py index 043f5d64d159b..3bbf6866076e8 100644 --- a/pandas/tests/arrays/integer/test_comparison.py +++ b/pandas/tests/arrays/integer/test_comparison.py @@ -9,18 +9,19 @@ class TestComparisonOps(NumericOps, ComparisonOps): @pytest.mark.parametrize("other", [True, False, pd.NA, -1, 0, 1]) - def test_scalar(self, other, all_compare_operators, dtype): - ComparisonOps.test_scalar(self, other, all_compare_operators, dtype) + def test_scalar(self, other, comparison_op, dtype): + ComparisonOps.test_scalar(self, other, comparison_op, dtype) - def test_compare_to_int(self, dtype, all_compare_operators): + def test_compare_to_int(self, dtype, comparison_op): # GH 28930 + op_name = f"__{comparison_op.__name__}__" s1 = pd.Series([1, None, 3], dtype=dtype) s2 = pd.Series([1, None, 3], dtype="float") - method = getattr(s1, all_compare_operators) + method = getattr(s1, op_name) result = method(2) - method = getattr(s2, all_compare_operators) + method = getattr(s2, op_name) expected = method(2).astype("boolean") expected[s2.isna()] = pd.NA diff --git a/pandas/tests/arrays/masked_shared.py b/pandas/tests/arrays/masked_shared.py index 1a461777e08e3..06c05e458958b 100644 --- a/pandas/tests/arrays/masked_shared.py +++ b/pandas/tests/arrays/masked_shared.py @@ -9,8 +9,7 @@ class ComparisonOps(BaseOpsUtil): - def _compare_other(self, data, op_name, other): - op = self.get_op_from_name(op_name) + def _compare_other(self, data, op, other): # array result = pd.Series(op(data, other)) @@ -34,8 +33,8 @@ def _compare_other(self, data, op_name, other): tm.assert_series_equal(result, expected) # subclass will override to parametrize 'other' - def test_scalar(self, other, all_compare_operators, dtype): - op = self.get_op_from_name(all_compare_operators) + def test_scalar(self, other, comparison_op, dtype): + op = comparison_op left = pd.array([1, 0, None], dtype=dtype) result = op(left, other) @@ -59,8 +58,8 @@ def test_no_shared_mask(self, data): result = data + 1 assert np.shares_memory(result._mask, data._mask) is False - def test_array(self, all_compare_operators, dtype): - op = self.get_op_from_name(all_compare_operators) + def test_array(self, comparison_op, dtype): + op = comparison_op left = pd.array([0, 1, 2, None, None, None], dtype=dtype) right = pd.array([0, 1, None, 0, 1, None], dtype=dtype) @@ -81,8 +80,8 @@ def test_array(self, all_compare_operators, dtype): right, pd.array([0, 1, None, 0, 1, None], dtype=dtype) ) - def test_compare_with_booleanarray(self, all_compare_operators, dtype): - op = self.get_op_from_name(all_compare_operators) + def test_compare_with_booleanarray(self, comparison_op, dtype): + op = comparison_op left = pd.array([True, False, None] * 3, dtype="boolean") right = pd.array([0] * 3 + [1] * 3 + [None] * 3, dtype=dtype) diff --git a/pandas/tests/arrays/string_/test_string.py b/pandas/tests/arrays/string_/test_string.py index fa564ac76f8bb..501a79a8bc5ed 100644 --- a/pandas/tests/arrays/string_/test_string.py +++ b/pandas/tests/arrays/string_/test_string.py @@ -199,8 +199,8 @@ def test_add_frame(dtype): tm.assert_frame_equal(result, expected) -def test_comparison_methods_scalar(all_compare_operators, dtype): - op_name = all_compare_operators +def test_comparison_methods_scalar(comparison_op, dtype): + op_name = f"__{comparison_op.__name__}__" a = pd.array(["a", None, "c"], dtype=dtype) other = "a" result = getattr(a, op_name)(other) @@ -209,21 +209,21 @@ def test_comparison_methods_scalar(all_compare_operators, dtype): tm.assert_extension_array_equal(result, expected) -def test_comparison_methods_scalar_pd_na(all_compare_operators, dtype): - op_name = all_compare_operators +def test_comparison_methods_scalar_pd_na(comparison_op, dtype): + op_name = f"__{comparison_op.__name__}__" a = pd.array(["a", None, "c"], dtype=dtype) result = getattr(a, op_name)(pd.NA) expected = pd.array([None, None, None], dtype="boolean") tm.assert_extension_array_equal(result, expected) -def test_comparison_methods_scalar_not_string(all_compare_operators, dtype, request): - if all_compare_operators not in ["__eq__", "__ne__"]: +def test_comparison_methods_scalar_not_string(comparison_op, dtype, request): + op_name = f"__{comparison_op.__name__}__" + if op_name not in ["__eq__", "__ne__"]: reason = "comparison op not supported between instances of 'str' and 'int'" mark = pytest.mark.xfail(raises=TypeError, reason=reason) request.node.add_marker(mark) - op_name = all_compare_operators a = pd.array(["a", None, "c"], dtype=dtype) other = 42 result = getattr(a, op_name)(other) @@ -234,14 +234,14 @@ def test_comparison_methods_scalar_not_string(all_compare_operators, dtype, requ tm.assert_extension_array_equal(result, expected) -def test_comparison_methods_array(all_compare_operators, dtype, request): +def test_comparison_methods_array(comparison_op, dtype, request): if dtype.storage == "pyarrow": mark = pytest.mark.xfail( raises=AssertionError, reason="left is not an ExtensionArray" ) request.node.add_marker(mark) - op_name = all_compare_operators + op_name = f"__{comparison_op.__name__}__" a = pd.array(["a", None, "c"], dtype=dtype) other = [None, None, "c"] diff --git a/pandas/tests/arrays/test_datetimes.py b/pandas/tests/arrays/test_datetimes.py index 180fb9d29224e..5b9df44f5b565 100644 --- a/pandas/tests/arrays/test_datetimes.py +++ b/pandas/tests/arrays/test_datetimes.py @@ -1,8 +1,6 @@ """ Tests for DatetimeArray """ -import operator - import numpy as np import pytest @@ -17,10 +15,9 @@ class TestDatetimeArrayComparisons: # TODO: merge this into tests/arithmetic/test_datetime64 once it is # sufficiently robust - def test_cmp_dt64_arraylike_tznaive(self, all_compare_operators): + def test_cmp_dt64_arraylike_tznaive(self, comparison_op): # arbitrary tz-naive DatetimeIndex - opname = all_compare_operators.strip("_") - op = getattr(operator, opname) + op = comparison_op dti = pd.date_range("2016-01-1", freq="MS", periods=9, tz=None) arr = DatetimeArray(dti) @@ -30,7 +27,7 @@ def test_cmp_dt64_arraylike_tznaive(self, all_compare_operators): right = dti expected = np.ones(len(arr), dtype=bool) - if opname in ["ne", "gt", "lt"]: + if comparison_op.__name__ in ["ne", "gt", "lt"]: # for these the comparisons should be all-False expected = ~expected diff --git a/pandas/tests/extension/base/getitem.py b/pandas/tests/extension/base/getitem.py index 73bff29305f20..bf3985ad198dd 100644 --- a/pandas/tests/extension/base/getitem.py +++ b/pandas/tests/extension/base/getitem.py @@ -138,6 +138,7 @@ def test_getitem_invalid(self, data): "list index out of range", # json "index out of bounds", # pyarrow "Out of bounds access", # Sparse + f"loc must be an integer between -{ub} and {ub}", # Sparse f"index {ub+1} is out of bounds for axis 0 with size {ub}", f"index -{ub+1} is out of bounds for axis 0 with size {ub}", ] diff --git a/pandas/tests/extension/base/ops.py b/pandas/tests/extension/base/ops.py index 88437321b1028..c52f20255eb81 100644 --- a/pandas/tests/extension/base/ops.py +++ b/pandas/tests/extension/base/ops.py @@ -130,10 +130,9 @@ def test_direct_arith_with_ndframe_returns_not_implemented(self, data, box): class BaseComparisonOpsTests(BaseOpsUtil): """Various Series and DataFrame comparison ops methods.""" - def _compare_other(self, ser: pd.Series, data, op_name: str, other): + def _compare_other(self, ser: pd.Series, data, op, other): - op = self.get_op_from_name(op_name) - if op_name in ["__eq__", "__ne__"]: + if op.__name__ in ["eq", "ne"]: # comparison should match point-wise comparisons result = op(ser, other) expected = ser.combine(other, op) @@ -154,23 +153,22 @@ def _compare_other(self, ser: pd.Series, data, op_name: str, other): with pytest.raises(type(exc)): ser.combine(other, op) - def test_compare_scalar(self, data, all_compare_operators): - op_name = all_compare_operators + def test_compare_scalar(self, data, comparison_op): ser = pd.Series(data) - self._compare_other(ser, data, op_name, 0) + self._compare_other(ser, data, comparison_op, 0) - def test_compare_array(self, data, all_compare_operators): - op_name = all_compare_operators + def test_compare_array(self, data, comparison_op): ser = pd.Series(data) other = pd.Series([data[0]] * len(data)) - self._compare_other(ser, data, op_name, other) + self._compare_other(ser, data, comparison_op, other) - @pytest.mark.parametrize("box", [pd.Series, pd.DataFrame]) - def test_direct_arith_with_ndframe_returns_not_implemented(self, data, box): + def test_direct_arith_with_ndframe_returns_not_implemented( + self, data, frame_or_series + ): # EAs should return NotImplemented for ops with Series/DataFrame # Pandas takes care of unboxing the series and calling the EA's op. other = pd.Series(data) - if box is pd.DataFrame: + if frame_or_series is pd.DataFrame: other = other.to_frame() if hasattr(data, "__eq__"): diff --git a/pandas/tests/extension/decimal/test_decimal.py b/pandas/tests/extension/decimal/test_decimal.py index 461dbe5575022..bca87bb8ec2aa 100644 --- a/pandas/tests/extension/decimal/test_decimal.py +++ b/pandas/tests/extension/decimal/test_decimal.py @@ -266,19 +266,17 @@ def _check_divmod_op(self, s, op, other, exc=NotImplementedError): class TestComparisonOps(base.BaseComparisonOpsTests): - def test_compare_scalar(self, data, all_compare_operators): - op_name = all_compare_operators + def test_compare_scalar(self, data, comparison_op): s = pd.Series(data) - self._compare_other(s, data, op_name, 0.5) + self._compare_other(s, data, comparison_op, 0.5) - def test_compare_array(self, data, all_compare_operators): - op_name = all_compare_operators + def test_compare_array(self, data, comparison_op): s = pd.Series(data) alter = np.random.choice([-1, 0, 1], len(data)) # Randomly double, halve or keep same value other = pd.Series(data) * [decimal.Decimal(pow(2.0, i)) for i in alter] - self._compare_other(s, data, op_name, other) + self._compare_other(s, data, comparison_op, other) class DecimalArrayWithoutFromSequence(DecimalArray): diff --git a/pandas/tests/extension/test_boolean.py b/pandas/tests/extension/test_boolean.py index 9c4bf76b27c14..05455905860d2 100644 --- a/pandas/tests/extension/test_boolean.py +++ b/pandas/tests/extension/test_boolean.py @@ -151,11 +151,11 @@ def check_opname(self, s, op_name, other, exc=None): super().check_opname(s, op_name, other, exc=None) @pytest.mark.skip(reason="Tested in tests/arrays/test_boolean.py") - def test_compare_scalar(self, data, all_compare_operators): + def test_compare_scalar(self, data, comparison_op): pass @pytest.mark.skip(reason="Tested in tests/arrays/test_boolean.py") - def test_compare_array(self, data, all_compare_operators): + def test_compare_array(self, data, comparison_op): pass diff --git a/pandas/tests/extension/test_categorical.py b/pandas/tests/extension/test_categorical.py index ea8b1cfb738f5..e9dc63e9bd903 100644 --- a/pandas/tests/extension/test_categorical.py +++ b/pandas/tests/extension/test_categorical.py @@ -270,8 +270,8 @@ def _check_divmod_op(self, s, op, other, exc=NotImplementedError): class TestComparisonOps(base.BaseComparisonOpsTests): - def _compare_other(self, s, data, op_name, other): - op = self.get_op_from_name(op_name) + def _compare_other(self, s, data, op, other): + op_name = f"__{op.__name__}__" if op_name == "__eq__": result = op(s, other) expected = s.combine(other, lambda x, y: x == y) diff --git a/pandas/tests/extension/test_floating.py b/pandas/tests/extension/test_floating.py index 500c2fbb74d17..2b08c5b7be450 100644 --- a/pandas/tests/extension/test_floating.py +++ b/pandas/tests/extension/test_floating.py @@ -142,7 +142,8 @@ def _check_op(self, s, op, other, op_name, exc=NotImplementedError): def check_opname(self, s, op_name, other, exc=None): super().check_opname(s, op_name, other, exc=None) - def _compare_other(self, s, data, op_name, other): + def _compare_other(self, s, data, op, other): + op_name = f"__{op.__name__}__" self.check_opname(s, op_name, other) diff --git a/pandas/tests/extension/test_integer.py b/pandas/tests/extension/test_integer.py index c9c0a4de60a46..7d343aab3c7a0 100644 --- a/pandas/tests/extension/test_integer.py +++ b/pandas/tests/extension/test_integer.py @@ -161,7 +161,8 @@ def _check_op(self, s, op, other, op_name, exc=NotImplementedError): def check_opname(self, s, op_name, other, exc=None): super().check_opname(s, op_name, other, exc=None) - def _compare_other(self, s, data, op_name, other): + def _compare_other(self, s, data, op, other): + op_name = f"__{op.__name__}__" self.check_opname(s, op_name, other) diff --git a/pandas/tests/extension/test_sparse.py b/pandas/tests/extension/test_sparse.py index 6358b2fe27ef3..012a3fbb12cac 100644 --- a/pandas/tests/extension/test_sparse.py +++ b/pandas/tests/extension/test_sparse.py @@ -432,8 +432,8 @@ def test_arith_frame_with_scalar(self, data, all_arithmetic_operators, request): class TestComparisonOps(BaseSparseTests, base.BaseComparisonOpsTests): - def _compare_other(self, s, data, op_name, other): - op = self.get_op_from_name(op_name) + def _compare_other(self, s, data, comparison_op, other): + op = comparison_op # array result = pd.Series(op(data, other)) diff --git a/pandas/tests/extension/test_string.py b/pandas/tests/extension/test_string.py index af86c359c4c00..5049116a9320e 100644 --- a/pandas/tests/extension/test_string.py +++ b/pandas/tests/extension/test_string.py @@ -166,15 +166,15 @@ class TestCasting(base.BaseCastingTests): class TestComparisonOps(base.BaseComparisonOpsTests): - def _compare_other(self, s, data, op_name, other): + def _compare_other(self, s, data, op, other): + op_name = f"__{op.__name__}__" result = getattr(s, op_name)(other) expected = getattr(s.astype(object), op_name)(other).astype("boolean") self.assert_series_equal(result, expected) - def test_compare_scalar(self, data, all_compare_operators): - op_name = all_compare_operators + def test_compare_scalar(self, data, comparison_op): s = pd.Series(data) - self._compare_other(s, data, op_name, "abc") + self._compare_other(s, data, comparison_op, "abc") class TestParsing(base.BaseParsingTests): diff --git a/pandas/tests/indexes/base_class/test_reshape.py b/pandas/tests/indexes/base_class/test_reshape.py index 5ebab965e6f04..acb6936f70d0f 100644 --- a/pandas/tests/indexes/base_class/test_reshape.py +++ b/pandas/tests/indexes/base_class/test_reshape.py @@ -35,6 +35,13 @@ def test_insert(self): null_index = Index([]) tm.assert_index_equal(Index(["a"]), null_index.insert(0, "a")) + def test_insert_missing(self, nulls_fixture): + # GH#22295 + # test there is no mangling of NA values + expected = Index(["a", nulls_fixture, "b", "c"]) + result = Index(list("abc")).insert(1, nulls_fixture) + tm.assert_index_equal(result, expected) + @pytest.mark.parametrize( "pos,expected", [ @@ -48,6 +55,12 @@ def test_delete(self, pos, expected): tm.assert_index_equal(result, expected) assert result.name == expected.name + def test_delete_raises(self): + index = Index(["a", "b", "c", "d"], name="index") + msg = "index 5 is out of bounds for axis 0 with size 4" + with pytest.raises(IndexError, match=msg): + index.delete(5) + def test_append_multiple(self): index = Index(["a", "b", "c", "d", "e", "f"]) diff --git a/pandas/tests/indexes/categorical/test_reindex.py b/pandas/tests/indexes/categorical/test_reindex.py index 72130ef9e4627..5a0b2672e397c 100644 --- a/pandas/tests/indexes/categorical/test_reindex.py +++ b/pandas/tests/indexes/categorical/test_reindex.py @@ -10,7 +10,7 @@ class TestReindex: - def test_reindex_dtype(self): + def test_reindex_list_non_unique(self): # GH#11586 ci = CategoricalIndex(["a", "b", "c", "a"]) with tm.assert_produces_warning(FutureWarning, match="non-unique"): @@ -19,6 +19,7 @@ def test_reindex_dtype(self): tm.assert_index_equal(res, Index(["a", "a", "c"]), exact=True) tm.assert_numpy_array_equal(indexer, np.array([0, 3, 2], dtype=np.intp)) + def test_reindex_categorcal_non_unique(self): ci = CategoricalIndex(["a", "b", "c", "a"]) with tm.assert_produces_warning(FutureWarning, match="non-unique"): res, indexer = ci.reindex(Categorical(["a", "c"])) @@ -27,6 +28,7 @@ def test_reindex_dtype(self): tm.assert_index_equal(res, exp, exact=True) tm.assert_numpy_array_equal(indexer, np.array([0, 3, 2], dtype=np.intp)) + def test_reindex_list_non_unique_unused_category(self): ci = CategoricalIndex(["a", "b", "c", "a"], categories=["a", "b", "c", "d"]) with tm.assert_produces_warning(FutureWarning, match="non-unique"): res, indexer = ci.reindex(["a", "c"]) @@ -34,6 +36,7 @@ def test_reindex_dtype(self): tm.assert_index_equal(res, exp, exact=True) tm.assert_numpy_array_equal(indexer, np.array([0, 3, 2], dtype=np.intp)) + def test_reindex_categorical_non_unique_unused_category(self): ci = CategoricalIndex(["a", "b", "c", "a"], categories=["a", "b", "c", "d"]) with tm.assert_produces_warning(FutureWarning, match="non-unique"): res, indexer = ci.reindex(Categorical(["a", "c"])) diff --git a/pandas/tests/indexes/common.py b/pandas/tests/indexes/common.py index ea76a4b4b1cfc..8f37413dd53c8 100644 --- a/pandas/tests/indexes/common.py +++ b/pandas/tests/indexes/common.py @@ -333,6 +333,12 @@ def test_numpy_argsort(self, index): expected = index.argsort() tm.assert_numpy_array_equal(result, expected) + if not isinstance(index, RangeIndex): + # TODO: add compatibility to RangeIndex? + result = np.argsort(index, kind="mergesort") + expected = index.argsort(kind="mergesort") + tm.assert_numpy_array_equal(result, expected) + # these are the only two types that perform # pandas compatibility input validation - the # rest already perform separate (or no) such @@ -340,16 +346,11 @@ def test_numpy_argsort(self, index): # defined in pandas.core.indexes/base.py - they # cannot be changed at the moment due to # backwards compatibility concerns - if isinstance(type(index), (CategoricalIndex, RangeIndex)): - # TODO: why type(index)? + if isinstance(index, (CategoricalIndex, RangeIndex)): msg = "the 'axis' parameter is not supported" with pytest.raises(ValueError, match=msg): np.argsort(index, axis=1) - msg = "the 'kind' parameter is not supported" - with pytest.raises(ValueError, match=msg): - np.argsort(index, kind="mergesort") - msg = "the 'order' parameter is not supported" with pytest.raises(ValueError, match=msg): np.argsort(index, order=("a", "b")) diff --git a/pandas/tests/indexes/datetimes/test_pickle.py b/pandas/tests/indexes/datetimes/test_pickle.py index 3905daa9688ac..922b4a18119f4 100644 --- a/pandas/tests/indexes/datetimes/test_pickle.py +++ b/pandas/tests/indexes/datetimes/test_pickle.py @@ -18,7 +18,7 @@ def test_pickle(self): assert idx_p[2] == idx[2] def test_pickle_dont_infer_freq(self): - # GH##11002 + # GH#11002 # don't infer freq idx = date_range("1750-1-1", "2050-1-1", freq="7D") idx_p = tm.round_trip_pickle(idx) diff --git a/pandas/tests/indexes/datetimes/test_unique.py b/pandas/tests/indexes/datetimes/test_unique.py index a6df9cb748294..68ac770f612e6 100644 --- a/pandas/tests/indexes/datetimes/test_unique.py +++ b/pandas/tests/indexes/datetimes/test_unique.py @@ -3,8 +3,6 @@ timedelta, ) -import pytest - from pandas import ( DatetimeIndex, NaT, @@ -13,18 +11,12 @@ import pandas._testing as tm -@pytest.mark.parametrize( - "arr, expected", - [ - (DatetimeIndex(["2017", "2017"]), DatetimeIndex(["2017"])), - ( - DatetimeIndex(["2017", "2017"], tz="US/Eastern"), - DatetimeIndex(["2017"], tz="US/Eastern"), - ), - ], -) -def test_unique(arr, expected): - result = arr.unique() +def test_unique(tz_naive_fixture): + + idx = DatetimeIndex(["2017"] * 2, tz=tz_naive_fixture) + expected = idx[:1] + + result = idx.unique() tm.assert_index_equal(result, expected) # GH#21737 # Ensure the underlying data is consistent @@ -60,6 +52,8 @@ def test_index_unique(rand_series_with_duplicate_datetimeindex): assert result.name == "foo" tm.assert_index_equal(result, expected) + +def test_index_unique2(): # NaT, note this is excluded arr = [1370745748 + t for t in range(20)] + [NaT.value] idx = DatetimeIndex(arr * 3) @@ -67,6 +61,8 @@ def test_index_unique(rand_series_with_duplicate_datetimeindex): assert idx.nunique() == 20 assert idx.nunique(dropna=False) == 21 + +def test_index_unique3(): arr = [ Timestamp("2013-06-09 02:42:28") + timedelta(seconds=t) for t in range(20) ] + [NaT] diff --git a/pandas/tests/indexes/period/test_join.py b/pandas/tests/indexes/period/test_join.py index b8b15708466cb..27cba8676d22b 100644 --- a/pandas/tests/indexes/period/test_join.py +++ b/pandas/tests/indexes/period/test_join.py @@ -42,10 +42,12 @@ def test_join_does_not_recur(self): c_idx_type="p", r_idx_type="dt", ) - s = df.iloc[:2, 0] + ser = df.iloc[:2, 0] - res = s.index.join(df.columns, how="outer") - expected = Index([s.index[0], s.index[1], df.columns[0], df.columns[1]], object) + res = ser.index.join(df.columns, how="outer") + expected = Index( + [ser.index[0], ser.index[1], df.columns[0], df.columns[1]], object + ) tm.assert_index_equal(res, expected) def test_join_mismatched_freq_raises(self): diff --git a/pandas/tests/indexes/test_any_index.py b/pandas/tests/indexes/test_any_index.py index 2313afcae607a..39a1ddcbc8a6a 100644 --- a/pandas/tests/indexes/test_any_index.py +++ b/pandas/tests/indexes/test_any_index.py @@ -117,7 +117,7 @@ def test_tolist_matches_list(self, index): class TestRoundTrips: def test_pickle_roundtrip(self, index): result = tm.round_trip_pickle(index) - tm.assert_index_equal(result, index) + tm.assert_index_equal(result, index, exact=True) if result.nlevels > 1: # GH#8367 round-trip with timezone assert index.equal_levels(result) @@ -133,7 +133,7 @@ class TestIndexing: def test_slice_keeps_name(self, index): assert index.name == index[1:].name - @pytest.mark.parametrize("item", [101, "no_int"]) + @pytest.mark.parametrize("item", [101, "no_int", 2.5]) # FutureWarning from non-tuple sequence of nd indexing @pytest.mark.filterwarnings("ignore::FutureWarning") def test_getitem_error(self, index, item): diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index cbcb00a4230cc..f1ece3e363bb6 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -181,29 +181,6 @@ def test_constructor_from_frame_series_freq(self): freq = pd.infer_freq(df["date"]) assert freq == "MS" - @pytest.mark.parametrize( - "array", - [ - np.arange(5), - np.array(["a", "b", "c"]), - date_range("2000-01-01", periods=3).values, - ], - ) - def test_constructor_ndarray_like(self, array): - # GH 5460#issuecomment-44474502 - # it should be possible to convert any object that satisfies the numpy - # ndarray interface directly into an Index - class ArrayLike: - def __init__(self, array): - self.array = array - - def __array__(self, dtype=None) -> np.ndarray: - return self.array - - expected = Index(array) - result = Index(ArrayLike(array)) - tm.assert_index_equal(result, expected) - def test_constructor_int_dtype_nan(self): # see gh-15187 data = [np.nan] @@ -211,20 +188,6 @@ def test_constructor_int_dtype_nan(self): result = Index(data, dtype="float") tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("dtype", ["int64", "uint64"]) - def test_constructor_int_dtype_nan_raises(self, dtype): - # see gh-15187 - data = [np.nan] - msg = "cannot convert" - with pytest.raises(ValueError, match=msg): - Index(data, dtype=dtype) - - def test_constructor_no_pandas_array(self): - ser = Series([1, 2, 3]) - result = Index(ser.array) - expected = Index([1, 2, 3]) - tm.assert_index_equal(result, expected) - @pytest.mark.parametrize( "klass,dtype,na_val", [ @@ -497,19 +460,6 @@ def test_equals_object(self): def test_not_equals_object(self, comp): assert not Index(["a", "b", "c"]).equals(comp) - def test_insert_missing(self, nulls_fixture): - # GH 22295 - # test there is no mangling of NA values - expected = Index(["a", nulls_fixture, "b", "c"]) - result = Index(list("abc")).insert(1, nulls_fixture) - tm.assert_index_equal(result, expected) - - def test_delete_raises(self): - index = Index(["a", "b", "c", "d"], name="index") - msg = "index 5 is out of bounds for axis 0 with size 4" - with pytest.raises(IndexError, match=msg): - index.delete(5) - def test_identical(self): # index @@ -1574,10 +1524,9 @@ def test_is_monotonic_na(self, index): assert index._is_strictly_monotonic_increasing is False assert index._is_strictly_monotonic_decreasing is False - @pytest.mark.parametrize("klass", [Series, DataFrame]) - def test_int_name_format(self, klass): + def test_int_name_format(self, frame_or_series): index = Index(["a", "b", "c"], name=0) - result = klass(list(range(3)), index=index) + result = frame_or_series(list(range(3)), index=index) assert "0" in repr(result) def test_str_to_bytes_raises(self): diff --git a/pandas/tests/indexes/test_index_new.py b/pandas/tests/indexes/test_index_new.py index e9bbe60f2d5ea..293aa6dd57124 100644 --- a/pandas/tests/indexes/test_index_new.py +++ b/pandas/tests/indexes/test_index_new.py @@ -224,6 +224,14 @@ def test_constructor_datetime64_values_mismatched_period_dtype(self): expected = dti.to_period("D") tm.assert_index_equal(result, expected) + @pytest.mark.parametrize("dtype", ["int64", "uint64"]) + def test_constructor_int_dtype_nan_raises(self, dtype): + # see GH#15187 + data = [np.nan] + msg = "cannot convert" + with pytest.raises(ValueError, match=msg): + Index(data, dtype=dtype) + class TestIndexConstructorUnwrapping: # Test passing different arraylike values to pd.Index @@ -235,3 +243,32 @@ def test_constructor_from_series_dt64(self, klass): ser = Series(stamps) result = klass(ser) tm.assert_index_equal(result, expected) + + def test_constructor_no_pandas_array(self): + ser = Series([1, 2, 3]) + result = Index(ser.array) + expected = Index([1, 2, 3]) + tm.assert_index_equal(result, expected) + + @pytest.mark.parametrize( + "array", + [ + np.arange(5), + np.array(["a", "b", "c"]), + date_range("2000-01-01", periods=3).values, + ], + ) + def test_constructor_ndarray_like(self, array): + # GH#5460#issuecomment-44474502 + # it should be possible to convert any object that satisfies the numpy + # ndarray interface directly into an Index + class ArrayLike: + def __init__(self, array): + self.array = array + + def __array__(self, dtype=None) -> np.ndarray: + return self.array + + expected = Index(array) + result = Index(ArrayLike(array)) + tm.assert_index_equal(result, expected) diff --git a/pandas/tests/scalar/timestamp/test_comparisons.py b/pandas/tests/scalar/timestamp/test_comparisons.py index ee36223eb2496..b7cb7ca8d7069 100644 --- a/pandas/tests/scalar/timestamp/test_comparisons.py +++ b/pandas/tests/scalar/timestamp/test_comparisons.py @@ -49,8 +49,7 @@ def test_comparison_dt64_ndarray(self): tm.assert_numpy_array_equal(result, np.array([[True, False]], dtype=bool)) @pytest.mark.parametrize("reverse", [True, False]) - def test_comparison_dt64_ndarray_tzaware(self, reverse, all_compare_operators): - op = getattr(operator, all_compare_operators.strip("__")) + def test_comparison_dt64_ndarray_tzaware(self, reverse, comparison_op): ts = Timestamp.now("UTC") arr = np.array([ts.asm8, ts.asm8], dtype="M8[ns]") @@ -59,18 +58,18 @@ def test_comparison_dt64_ndarray_tzaware(self, reverse, all_compare_operators): if reverse: left, right = arr, ts - if op is operator.eq: + if comparison_op is operator.eq: expected = np.array([False, False], dtype=bool) - result = op(left, right) + result = comparison_op(left, right) tm.assert_numpy_array_equal(result, expected) - elif op is operator.ne: + elif comparison_op is operator.ne: expected = np.array([True, True], dtype=bool) - result = op(left, right) + result = comparison_op(left, right) tm.assert_numpy_array_equal(result, expected) else: msg = "Cannot compare tz-naive and tz-aware timestamps" with pytest.raises(TypeError, match=msg): - op(left, right) + comparison_op(left, right) def test_comparison_object_array(self): # GH#15183 diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index e4f9366be8dd7..ed83377f31317 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -322,22 +322,20 @@ def test_arithmetic_with_duplicate_index(self): class TestSeriesFlexComparison: @pytest.mark.parametrize("axis", [0, None, "index"]) - def test_comparison_flex_basic(self, axis, all_compare_operators): - op = all_compare_operators.strip("__") + def test_comparison_flex_basic(self, axis, comparison_op): left = Series(np.random.randn(10)) right = Series(np.random.randn(10)) - result = getattr(left, op)(right, axis=axis) - expected = getattr(operator, op)(left, right) + result = getattr(left, comparison_op.__name__)(right, axis=axis) + expected = comparison_op(left, right) tm.assert_series_equal(result, expected) - def test_comparison_bad_axis(self, all_compare_operators): - op = all_compare_operators.strip("__") + def test_comparison_bad_axis(self, comparison_op): left = Series(np.random.randn(10)) right = Series(np.random.randn(10)) msg = "No axis named 1 for object type" with pytest.raises(ValueError, match=msg): - getattr(left, op)(right, axis=1) + getattr(left, comparison_op.__name__)(right, axis=1) @pytest.mark.parametrize( "values, op", @@ -598,20 +596,17 @@ def test_comparison_tuples(self): expected = Series([True, False]) tm.assert_series_equal(result, expected) - def test_comparison_operators_with_nas(self, all_compare_operators): - op = all_compare_operators + def test_comparison_operators_with_nas(self, comparison_op): ser = Series(bdate_range("1/1/2000", periods=10), dtype=object) ser[::2] = np.nan - f = getattr(operator, op) - # test that comparisons work val = ser[5] - result = f(ser, val) - expected = f(ser.dropna(), val).reindex(ser.index) + result = comparison_op(ser, val) + expected = comparison_op(ser.dropna(), val).reindex(ser.index) - if op == "__ne__": + if comparison_op is operator.ne: expected = expected.fillna(True).astype(bool) else: expected = expected.fillna(False).astype(bool) @@ -619,8 +614,8 @@ def test_comparison_operators_with_nas(self, all_compare_operators): tm.assert_series_equal(result, expected) # FIXME: dont leave commented-out - # result = f(val, ser) - # expected = f(val, ser.dropna()).reindex(ser.index) + # result = comparison_op(val, ser) + # expected = comparison_op(val, ser.dropna()).reindex(ser.index) # tm.assert_series_equal(result, expected) def test_ne(self):