Repository: modin-project/modin

Issue:
sort_index not working with NaNs
### Describe the problem
`sort_index` throws a `RayGetError` if there are multiple `NaN`s in the index/column

### Source code / logs
```
import modin.pandas as pd

data= {
    "col3": [0.0, 1.0, 2.0, 3.0],
    "col4": [4.0, 5.0, 6.0, 7.0],
    "col5": [8.0, 9.0, 10.0, 11.0],
    "col1": [12.0, 13.0, 14.0, 15.0],
    "col2": [0.0, 0.0, 0.0, 0.0],
}
    
modin_df = pd.DataFrame(data)
length = len(modin_df.index)
modin_df.index = [np.nan if i % 2 == 0 else modin_df.index[i] for i in range(length)]
modin_result = modin_df.sort_index(axis = 0, ascending = True, na_position = 'first', sort_remaining = True)
modin_result
```
The above code throws a `RayGetError` due to a `ValueError: cannot reindex from a duplicate axis`


Discussion:

Resolving patch:
diff --git a/modin/data_management/query_compiler/pandas_query_compiler.py b/modin/data_management/query_compiler/pandas_query_compiler.py
--- a/modin/data_management/query_compiler/pandas_query_compiler.py
+++ b/modin/data_management/query_compiler/pandas_query_compiler.py
@@ -1865,6 +1865,42 @@ def rank(self, **kwargs):
         new_dtypes = pandas.Series([np.float64 for _ in new_columns], index=new_columns)
         return self.__constructor__(new_data, self.index, new_columns, new_dtypes)
 
+    def sort_index(self, **kwargs):
+        """Sorts the data with respect to either the columns or the indices.
+
+        Returns:
+            DataManager containing the data sorted by columns or indices.
+        """
+        axis = kwargs.pop("axis", 0)
+        index = self.columns if axis else self.index
+
+        # sort_index can have ascending be None and behaves as if it is False.
+        # sort_values cannot have ascending be None. Thus, the following logic is to
+        # convert the ascending argument to one that works with sort_values
+        ascending = kwargs.pop("ascending", True)
+        if ascending is None:
+            ascending = False
+        kwargs["ascending"] = ascending
+
+        def sort_index_builder(df, **kwargs):
+            if axis:
+                df.columns = index
+            else:
+                df.index = index
+            return df.sort_index(axis=axis, **kwargs)
+
+        func = self._prepare_method(sort_index_builder, **kwargs)
+        new_data = self.map_across_full_axis(axis, func)
+        if axis:
+            new_columns = pandas.Series(self.columns).sort_values(**kwargs)
+            new_index = self.index
+        else:
+            new_index = pandas.Series(self.index).sort_values(**kwargs)
+            new_columns = self.columns
+        return self.__constructor__(
+            new_data, new_index, new_columns, self.dtypes.copy()
+        )
+
     # END Map across rows/columns
 
     # Map across rows/columns
@@ -2261,6 +2297,7 @@ def insert(df, internal_indices=[]):
             old_index = df.index
             df.index = pandas.RangeIndex(len(df.index))
             df.insert(internal_idx, internal_idx, value, allow_duplicates=True)
+            df.columns = pandas.RangeIndex(len(df.columns))
             df.index = old_index
             return df
 
diff --git a/modin/pandas/dataframe.py b/modin/pandas/dataframe.py
--- a/modin/pandas/dataframe.py
+++ b/modin/pandas/dataframe.py
@@ -3686,6 +3686,7 @@ def sort_index(
         Returns:
             A sorted DataFrame
         """
+        axis = pandas.DataFrame()._get_axis_number(axis)
         if level is not None:
             new_query_compiler = self._default_to_pandas(
                 pandas.DataFrame.sort_index,
@@ -3708,14 +3709,13 @@ def sort_index(
             if level is not None:
                 raise ValueError("unable to simultaneously sort by and level")
             return self.sort_values(by, axis=axis, ascending=ascending, inplace=inplace)
-        axis = pandas.DataFrame()._get_axis_number(axis)
-        if not axis:
-            new_index = self.index.sort_values(ascending=ascending)
-            new_columns = None
+        new_query_compiler = self._query_compiler.sort_index(
+            axis=axis, ascending=ascending, kind=kind, na_position=na_position
+        )
+        if inplace:
+            self._update_inplace(new_query_compiler=new_query_compiler)
         else:
-            new_index = None
-            new_columns = self.columns.sort_values(ascending=ascending)
-        return self.reindex(index=new_index, columns=new_columns)
+            return DataFrame(query_compiler=new_query_compiler)
 
     def sort_values(
         self,


Test patch: