diff --git a/pandas/conftest.py b/pandas/conftest.py index 84e29492c94e5..a08f278bbc780 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -716,6 +716,7 @@ def _create_mi_with_dt64tz_level(): ), "mi-with-dt64tz-level": _create_mi_with_dt64tz_level(), "multi": _create_multiindex(), + "mixed-int-string": Index([0, "a", 1, "b", 2, "c"]), "repeats": Index([0, 0, 1, 1, 2, 2]), "nullable_int": Index(np.arange(10), dtype="Int64"), "nullable_uint": Index(np.arange(10), dtype="UInt16"), @@ -745,6 +746,14 @@ def index(request): return indices_dict[request.param].copy(deep=False) +@pytest.fixture(params=[key for key in indices_dict if key != "mixed-int-string"]) +def index_sortable(request): + """ + index fixture, but excluding types that are not orderable. + """ + return indices_dict[request.param].copy(deep=False) + + @pytest.fixture( params=[ key for key, value in indices_dict.items() if not isinstance(value, MultiIndex) @@ -758,6 +767,20 @@ def index_flat(request): return indices_dict[key].copy(deep=False) +@pytest.fixture( + params=[ + key + for key, value in indices_dict.items() + if not isinstance(value, MultiIndex) and key != "mixed-int-string" + ] +) +def index_flat_sortable(request): + """ + index_flat fixture, but excluding types that are not orderable. + """ + return indices_dict[request.param].copy(deep=False) + + @pytest.fixture( params=[ key @@ -792,6 +815,28 @@ def index_with_missing(request): return type(ind)(vals, copy=False) +@pytest.fixture( + params=[ + key + for key, value in indices_dict.items() + if not ( + key.startswith(("int", "uint", "float")) + or key in ["range", "empty", "repeats", "bool-dtype", "mixed-int-string"] + ) + and not isinstance(value, MultiIndex) + ] +) +def index_with_missing_sortable(request): + """ + index_with_missing fixture, but excluding types that are not orderable. + """ + ind = indices_dict[request.param] + vals = ind.values.copy() + vals[0] = None + vals[-1] = None + return type(ind)(vals, copy=False) + + # ---------------------------------------------------------------- # Series' # ---------------------------------------------------------------- @@ -870,6 +915,21 @@ def index_or_series_obj(request): return _index_or_series_objs[request.param].copy(deep=False) +_index_or_series_objs_orderable = { + key: value + for key, value in _index_or_series_objs.items() + if "mixed-int-string" not in key +} + + +@pytest.fixture(params=_index_or_series_objs_orderable.keys()) +def index_or_series_obj_orderable(request): + """ + index_or_series_obj fixture, but excluding types that are not orderable. + """ + return _index_or_series_objs_orderable[request.param].copy(deep=False) + + _typ_objects_series = { f"{dtype.__name__}-series": Series(dtype) for dtype in tm.PYTHON_DATA_TYPES } diff --git a/pandas/tests/base/test_misc.py b/pandas/tests/base/test_misc.py index f2f1f3b7fc435..3315cce0d1270 100644 --- a/pandas/tests/base/test_misc.py +++ b/pandas/tests/base/test_misc.py @@ -139,10 +139,10 @@ def test_memory_usage_components_narrow_series(any_real_numpy_dtype): assert total_usage == non_index_usage + index_usage -def test_searchsorted(request, index_or_series_obj): +def test_searchsorted(request, index_or_series_obj_orderable): # numpy.searchsorted calls obj.searchsorted under the hood. # See gh-12238 - obj = index_or_series_obj + obj = index_or_series_obj_orderable if isinstance(obj, pd.MultiIndex): # See gh-14833 diff --git a/pandas/tests/base/test_value_counts.py b/pandas/tests/base/test_value_counts.py index a9479acfff44a..404f0ba955da7 100644 --- a/pandas/tests/base/test_value_counts.py +++ b/pandas/tests/base/test_value_counts.py @@ -53,8 +53,8 @@ def test_value_counts(index_or_series_obj): @pytest.mark.parametrize("null_obj", [np.nan, None]) @pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning") -def test_value_counts_null(null_obj, index_or_series_obj): - orig = index_or_series_obj +def test_value_counts_null(null_obj, index_or_series_obj_orderable): + orig = index_or_series_obj_orderable if not allow_na_ops(orig): pytest.skip("type doesn't allow for NA operations") diff --git a/pandas/tests/indexes/multi/test_setops.py b/pandas/tests/indexes/multi/test_setops.py index 7149daa3ac573..83f2f83477422 100644 --- a/pandas/tests/indexes/multi/test_setops.py +++ b/pandas/tests/indexes/multi/test_setops.py @@ -625,8 +625,9 @@ def test_union_with_duplicates_keep_ea_dtype(dupe_val, any_numeric_ea_dtype): @pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning") -def test_union_duplicates(index, request): +def test_union_duplicates(index_sortable, request): # GH#38977 + index = index_sortable if index.empty or isinstance(index, (IntervalIndex, CategoricalIndex)): pytest.skip(f"No duplicates in an empty {type(index).__name__}") diff --git a/pandas/tests/indexes/test_common.py b/pandas/tests/indexes/test_common.py index 0f2c8ddacd111..3c123a5349fbd 100644 --- a/pandas/tests/indexes/test_common.py +++ b/pandas/tests/indexes/test_common.py @@ -436,17 +436,19 @@ def test_hasnans_isnans(self, index_flat): @pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning") @pytest.mark.parametrize("na_position", [None, "middle"]) -def test_sort_values_invalid_na_position(index_with_missing, na_position): +def test_sort_values_invalid_na_position(index_with_missing_sortable, na_position): with pytest.raises(ValueError, match=f"invalid na_position: {na_position}"): - index_with_missing.sort_values(na_position=na_position) + index_with_missing_sortable.sort_values(na_position=na_position) @pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning") @pytest.mark.parametrize("na_position", ["first", "last"]) -def test_sort_values_with_missing(index_with_missing, na_position): +def test_sort_values_with_missing(index_with_missing_sortable, na_position): # GH 35584. Test that sort_values works with missing values, # sort non-missing and place missing according to na_position + index_with_missing = index_with_missing_sortable + missing_count = np.sum(index_with_missing.isna()) not_na_vals = index_with_missing[index_with_missing.notna()].values sorted_values = np.sort(not_na_vals) diff --git a/pandas/tests/indexes/test_numpy_compat.py b/pandas/tests/indexes/test_numpy_compat.py index 8004e97698b67..5725e5843ed2f 100644 --- a/pandas/tests/indexes/test_numpy_compat.py +++ b/pandas/tests/indexes/test_numpy_compat.py @@ -151,8 +151,9 @@ def test_numpy_ufuncs_other(index, func): @pytest.mark.parametrize("func", [np.maximum, np.minimum]) -def test_numpy_ufuncs_reductions(index, func): +def test_numpy_ufuncs_reductions(index_sortable, func): # TODO: overlap with tests.series.test_ufunc.test_reductions + index = index_sortable if len(index) == 0: pytest.skip("Test doesn't make sense for empty index.") @@ -160,8 +161,8 @@ def test_numpy_ufuncs_reductions(index, func): with pytest.raises(TypeError, match="is not ordered for"): func.reduce(index) return - else: - result = func.reduce(index) + + result = func.reduce(index) if func is np.maximum: expected = index.max(skipna=False) diff --git a/pandas/tests/indexes/test_old_base.py b/pandas/tests/indexes/test_old_base.py index f3434aae24905..c5a4b7296644f 100644 --- a/pandas/tests/indexes/test_old_base.py +++ b/pandas/tests/indexes/test_old_base.py @@ -355,7 +355,8 @@ def test_memory_usage_doesnt_trigger_engine(self, index): assert res_without_engine > 0 assert res_with_engine > 0 - def test_argsort(self, index): + def test_argsort(self, index_sortable): + index = index_sortable if isinstance(index, CategoricalIndex): pytest.skip(f"{type(self).__name__} separately tested") @@ -363,7 +364,8 @@ def test_argsort(self, index): expected = np.array(index).argsort() tm.assert_numpy_array_equal(result, expected) - def test_numpy_argsort(self, index): + def test_numpy_argsort(self, index_sortable): + index = index_sortable result = np.argsort(index) expected = index.argsort() tm.assert_numpy_array_equal(result, expected) diff --git a/pandas/tests/indexes/test_setops.py b/pandas/tests/indexes/test_setops.py index eabaacc7d7ff2..82061f04959e9 100644 --- a/pandas/tests/indexes/test_setops.py +++ b/pandas/tests/indexes/test_setops.py @@ -63,19 +63,24 @@ def index_flat2(index_flat): return index_flat -def test_union_same_types(index): +@pytest.fixture +def index_flat2_sortable(index_flat_sortable): + return index_flat_sortable + + +def test_union_same_types(index_sortable): # Union with a non-unique, non-monotonic index raises error # Only needed for bool index factory - idx1 = index.sort_values() - idx2 = index.sort_values() + idx1 = index_sortable.sort_values() + idx2 = index_sortable.sort_values() assert idx1.union(idx2).dtype == idx1.dtype -def test_union_different_types(index_flat, index_flat2): +def test_union_different_types(index_flat_sortable, index_flat2_sortable): # This test only considers combinations of indices # GH 23525 - idx1 = index_flat - idx2 = index_flat2 + idx1 = index_flat_sortable + idx2 = index_flat2_sortable common_dtype = find_common_type([idx1.dtype, idx2.dtype]) @@ -220,8 +225,8 @@ def test_intersection_base(self, index): first.intersection([1, 2, 3]) @pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning") - def test_union_base(self, index): - index = index.unique() + def test_union_base(self, index_sortable): + index = index_sortable.unique() first = index[3:] second = index[:5] everything = index @@ -272,7 +277,8 @@ def test_difference_base(self, sort, index): first.difference([1, 2, 3], sort) @pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning") - def test_symmetric_difference(self, index, using_infer_string, request): + def test_symmetric_difference(self, index_sortable, using_infer_string, request): + index = index_sortable if ( using_infer_string and index.dtype == "object" @@ -362,11 +368,11 @@ def test_corner_union(self, index_flat, fname, sname, expected_name): (None, None, None), ], ) - def test_union_unequal(self, index_flat, fname, sname, expected_name): - if not index_flat.is_unique: - index = index_flat.unique() + def test_union_unequal(self, index_flat_sortable, fname, sname, expected_name): + if not index_flat_sortable.is_unique: + index = index_flat_sortable.unique() else: - index = index_flat + index = index_flat_sortable # test copy.union(subset) - need sort for unicode and string first = index.copy().set_names(fname) @@ -431,11 +437,11 @@ def test_corner_intersect(self, index_flat, fname, sname, expected_name): (None, None, None), ], ) - def test_intersect_unequal(self, index_flat, fname, sname, expected_name): - if not index_flat.is_unique: - index = index_flat.unique() + def test_intersect_unequal(self, index_flat_sortable, fname, sname, expected_name): + if not index_flat_sortable.is_unique: + index = index_flat_sortable.unique() else: - index = index_flat + index = index_flat_sortable # test copy.intersection(subset) - need sort for unicode and string first = index.copy().set_names(fname) diff --git a/pandas/tests/test_algos.py b/pandas/tests/test_algos.py index 23fdb85c7943b..9f7a9cffb4b64 100644 --- a/pandas/tests/test_algos.py +++ b/pandas/tests/test_algos.py @@ -63,8 +63,8 @@ def test_factorize_complex(self): expected_uniques = np.array([(1 + 0j), (2 + 0j), (2 + 1j)], dtype=complex) tm.assert_numpy_array_equal(uniques, expected_uniques) - def test_factorize(self, index_or_series_obj, sort): - obj = index_or_series_obj + def test_factorize(self, index_or_series_obj_orderable, sort): + obj = index_or_series_obj_orderable result_codes, result_uniques = obj.factorize(sort=sort) constructor = Index