diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index fc09f3d5853a2..23e06b8f0af6e 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -28,6 +28,7 @@ enhancement2 Other enhancements ^^^^^^^^^^^^^^^^^^ +- :meth:`MultiIndex.sort_values` now supports ``na_position`` (:issue:`51612`) - :meth:`MultiIndex.sortlevel` and :meth:`Index.sortlevel` gained a new keyword ``na_position`` (:issue:`51612`) - Improve error message when setting :class:`DataFrame` with wrong number of columns through :meth:`DataFrame.isetitem` (:issue:`51701`) - Let :meth:`DataFrame.to_feather` accept a non-default :class:`Index` and non-string column names (:issue:`51787`) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 4f89686e5a820..38028eafb0925 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -5597,7 +5597,7 @@ def sort_values( items=idx, ascending=ascending, na_position=na_position, key=key ) else: - _as = idx.argsort() + _as = idx.argsort(na_position=na_position) if not ascending: _as = _as[::-1] diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 857d1fb3f6554..58a5dcb627266 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2132,11 +2132,15 @@ def append(self, other): except (TypeError, IndexError): return Index(new_tuples) - def argsort(self, *args, **kwargs) -> npt.NDArray[np.intp]: + def argsort( + self, *args, na_position: str = "last", **kwargs + ) -> npt.NDArray[np.intp]: if len(args) == 0 and len(kwargs) == 0: # lexsort is significantly faster than self._values.argsort() target = self._sort_levels_monotonic(raise_if_incomparable=True) - return lexsort_indexer(target._get_codes_for_sorting()) + return lexsort_indexer( + target._get_codes_for_sorting(), na_position=na_position + ) return self._values.argsort(*args, **kwargs) @Appender(_index_shared_docs["repeat"] % _index_doc_kwargs) diff --git a/pandas/tests/indexes/multi/test_sorting.py b/pandas/tests/indexes/multi/test_sorting.py index 9392ff9368c6a..3f1aaebc0f47c 100644 --- a/pandas/tests/indexes/multi/test_sorting.py +++ b/pandas/tests/indexes/multi/test_sorting.py @@ -14,6 +14,7 @@ Index, MultiIndex, RangeIndex, + Series, Timestamp, ) import pandas._testing as tm @@ -311,3 +312,27 @@ def test_sort_values_incomparable(): match = "'<' not supported between instances of 'Timestamp' and 'int'" with pytest.raises(TypeError, match=match): mi.sort_values() + + +@pytest.mark.parametrize("na_position", ["first", "last"]) +@pytest.mark.parametrize("dtype", ["float64", "Int64", "Float64"]) +def test_sort_values_with_na_na_position(dtype, na_position): + # 51612 + arrays = [ + Series([1, 1, 2], dtype=dtype), + Series([1, None, 3], dtype=dtype), + ] + index = MultiIndex.from_arrays(arrays) + result = index.sort_values(na_position=na_position) + if na_position == "first": + arrays = [ + Series([1, 1, 2], dtype=dtype), + Series([None, 1, 3], dtype=dtype), + ] + else: + arrays = [ + Series([1, 1, 2], dtype=dtype), + Series([1, None, 3], dtype=dtype), + ] + expected = MultiIndex.from_arrays(arrays) + tm.assert_index_equal(result, expected)