Repository: modin-project/modin

Issue:
`reindex` after transpose throws a ValueError
<!--
General questions should be asked on the mailing list modin-dev@googlegroups.com.

Before submitting an issue, please fill out the following form.
-->

### System information
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**: OSX
- **Modin installed from (source or binary)**: source
- **Modin version**: source
- **Python version**: 3.6
- **Exact command to reproduce**: df.T.reindex(index=...)

<!--
You can obtain the Modin version with

python -c "import modin; print(modin.__version__)"
-->

### Describe the problem
<!-- Describe the problem clearly here. -->
Because of the way that `transpose` is written, when we go to perform operations along an axis, the internal partitions end up getting incorrectly formatted.

### Source code / logs
```python
df.T.reindex(index=...)
```
<!-- Include any logs or source code that would be helpful to diagnose the problem. If including tracebacks, please include the full traceback. Large logs and files should be attached. Try to provide a reproducible test case that is the bare minimum necessary to generate the problem. -->



Discussion:

Resolving patch:
diff --git a/modin/backends/pandas/query_compiler.py b/modin/backends/pandas/query_compiler.py
--- a/modin/backends/pandas/query_compiler.py
+++ b/modin/backends/pandas/query_compiler.py
@@ -30,6 +30,7 @@ def __init__(
         index: pandas.Index,
         columns: pandas.Index,
         dtypes=None,
+        is_transposed=False,
     ):
         assert isinstance(block_partitions_object, BaseFrameManager)
         self.data = block_partitions_object
@@ -37,6 +38,7 @@ def __init__(
         self.columns = columns
         if dtypes is not None:
             self._dtype_cache = dtypes
+        self._is_transposed = int(is_transposed)
 
     # Index, columns and dtypes objects
     _dtype_cache = None
@@ -214,28 +216,36 @@ def numeric_function_clean_dataframe(self, axis):
     def add_prefix(self, prefix, axis=1):
         if axis == 1:
             new_columns = self.columns.map(lambda x: str(prefix) + str(x))
-            new_dtype_cache = self._dtype_cache.copy()
-            if new_dtype_cache is not None:
+            if self._dtype_cache is not None:
+                new_dtype_cache = self._dtype_cache.copy()
                 new_dtype_cache.index = new_columns
+            else:
+                new_dtype_cache = None
             new_index = self.index
         else:
             new_index = self.index.map(lambda x: str(prefix) + str(x))
             new_columns = self.columns
-            new_dtype_cache = self._dtype_cache.copy()
-        return self.__constructor__(self.data, new_index, new_columns, new_dtype_cache)
+            new_dtype_cache = self._dtype_cache
+        return self.__constructor__(
+            self.data, new_index, new_columns, new_dtype_cache, self._is_transposed
+        )
 
     def add_suffix(self, suffix, axis=1):
         if axis == 1:
             new_columns = self.columns.map(lambda x: str(x) + str(suffix))
-            new_dtype_cache = self._dtype_cache.copy()
-            if new_dtype_cache is not None:
+            if self._dtype_cache is not None:
+                new_dtype_cache = self._dtype_cache.copy()
                 new_dtype_cache.index = new_columns
+            else:
+                new_dtype_cache = None
             new_index = self.index
         else:
             new_index = self.index.map(lambda x: str(x) + str(suffix))
             new_columns = self.columns
-            new_dtype_cache = self._dtype_cache.copy()
-        return self.__constructor__(self.data, new_index, new_columns, new_dtype_cache)
+            new_dtype_cache = self._dtype_cache
+        return self.__constructor__(
+            self.data, new_index, new_columns, new_dtype_cache, self._is_transposed
+        )
 
     # END Metadata modification methods
 
@@ -244,13 +254,13 @@ def add_suffix(self, suffix, axis=1):
     # copies if we end up modifying something here. We copy all of the metadata
     # to prevent that.
     def copy(self):
-        copied = self.__constructor__(
-            self.data.copy(), self.index.copy(), self.columns.copy(), self._dtype_cache
+        return self.__constructor__(
+            self.data.copy(),
+            self.index.copy(),
+            self.columns.copy(),
+            self._dtype_cache,
+            self._is_transposed,
         )
-        # Copy metadata
-        if self._is_transposed:
-            copied._is_transposed = True
-        return copied
 
     # END Copy
 
@@ -740,6 +750,12 @@ def reindex(self, axis, labels, **kwargs):
         Returns:
             A new QueryCompiler with updated data and new index.
         """
+        if self._is_transposed:
+            return (
+                self.transpose()
+                .reindex(axis=axis ^ 1, labels=labels, **kwargs)
+                .transpose()
+            )
 
         # To reindex, we need a function that will be shipped to each of the
         # partitions.
@@ -821,9 +837,6 @@ def reset_index(self, **kwargs):
     # Our invariants assume that the blocks are transposed, but not the
     # data inside. Sometimes we have to reverse this transposition of blocks
     # for simplicity of implementation.
-    #
-    # _is_transposed, 0 for False or non-transposed, 1 for True or transposed.
-    _is_transposed = 0
 
     def transpose(self, *args, **kwargs):
         """Transposes this DataManager.
@@ -833,9 +846,9 @@ def transpose(self, *args, **kwargs):
         """
         new_data = self.data.transpose(*args, **kwargs)
         # Switch the index and columns and transpose the
-        new_manager = self.__constructor__(new_data, self.columns, self.index)
-        # It is possible that this is already transposed
-        new_manager._is_transposed = self._is_transposed ^ 1
+        new_manager = self.__constructor__(
+            new_data, self.columns, self.index, is_transposed=self._is_transposed ^ 1
+        )
         return new_manager
 
     # END Transpose
@@ -1714,7 +1727,7 @@ def sort_index_builder(df, **kwargs):
             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()
+            new_data, new_index, new_columns, self.dtypes.copy(), self._is_transposed
         )
 
     # END Map across rows/columns
@@ -1826,8 +1839,8 @@ def head(self, n):
                 self.index[:n],
                 self.columns,
                 self._dtype_cache,
+                self._is_transposed,
             )
-            result._is_transposed = True
         else:
             result = self.__constructor__(
                 self.data.take(0, n), self.index[:n], self.columns, self._dtype_cache
@@ -1852,8 +1865,8 @@ def tail(self, n):
                 self.index[-n:],
                 self.columns,
                 self._dtype_cache,
+                self._is_transposed,
             )
-            result._is_transposed = True
         else:
             result = self.__constructor__(
                 self.data.take(0, -n), self.index[-n:], self.columns, self._dtype_cache
@@ -1879,8 +1892,8 @@ def front(self, n):
                 self.index,
                 self.columns[:n],
                 new_dtypes,
+                self._is_transposed,
             )
-            result._is_transposed = True
         else:
             result = self.__constructor__(
                 self.data.take(1, n), self.index, self.columns[:n], new_dtypes
@@ -1906,8 +1919,8 @@ def back(self, n):
                 self.index,
                 self.columns[-n:],
                 new_dtypes,
+                self._is_transposed,
             )
-            result._is_transposed = True
         else:
             result = self.__constructor__(
                 self.data.take(1, -n), self.index, self.columns[-n:], new_dtypes
@@ -1934,8 +1947,9 @@ def getitem_column_array(self, key):
         def getitem(df, internal_indices=[]):
             return df.iloc[:, internal_indices]
 
+        prepared_func = self._prepare_method(getitem)
         result = self.data.apply_func_to_select_indices(
-            0, getitem, numeric_indices, keep_remaining=False
+            0, prepared_func, numeric_indices, keep_remaining=False
         )
         # We can't just set the columns to key here because there may be
         # multiple instances of a key.
@@ -1958,8 +1972,9 @@ def getitem_row_array(self, key):
         def getitem(df, internal_indices=[]):
             return df.iloc[internal_indices]
 
+        prepared_func = self._prepare_method(getitem)
         result = self.data.apply_func_to_select_indices(
-            1, getitem, key, keep_remaining=False
+            1, prepared_func, key, keep_remaining=False
         )
         # We can't just set the index to key here because there may be multiple
         # instances of a key.
diff --git a/modin/pandas/base.py b/modin/pandas/base.py
--- a/modin/pandas/base.py
+++ b/modin/pandas/base.py
@@ -2586,7 +2586,7 @@ def sort_values(
         # Currently, sort_values will just reindex based on the sorted values.
         # TODO create a more efficient way to sort
         if axis == 0:
-            broadcast_value_dict = {col: self[col] for col in by}
+            broadcast_value_dict = {col: self[col]._to_pandas() for col in by}
             broadcast_values = pandas.DataFrame(broadcast_value_dict, index=self.index)
             new_index = broadcast_values.sort_values(
                 by=by,


Test patch:
diff --git a/modin/pandas/test/test_dataframe.py b/modin/pandas/test/test_dataframe.py
--- a/modin/pandas/test/test_dataframe.py
+++ b/modin/pandas/test/test_dataframe.py
@@ -3565,25 +3565,25 @@ def test_reindex(self):
         modin_df = pd.DataFrame(frame_data)
 
         df_equals(modin_df.reindex([0, 3, 2, 1]), pandas_df.reindex([0, 3, 2, 1]))
-
         df_equals(modin_df.reindex([0, 6, 2]), pandas_df.reindex([0, 6, 2]))
-
         df_equals(
             modin_df.reindex(["col1", "col3", "col4", "col2"], axis=1),
             pandas_df.reindex(["col1", "col3", "col4", "col2"], axis=1),
         )
-
         df_equals(
             modin_df.reindex(["col1", "col7", "col4", "col8"], axis=1),
             pandas_df.reindex(["col1", "col7", "col4", "col8"], axis=1),
         )
-
         df_equals(
             modin_df.reindex(index=[0, 1, 5], columns=["col1", "col7", "col4", "col8"]),
             pandas_df.reindex(
                 index=[0, 1, 5], columns=["col1", "col7", "col4", "col8"]
             ),
         )
+        df_equals(
+            modin_df.T.reindex(["col1", "col7", "col4", "col8"], axis=0),
+            pandas_df.T.reindex(["col1", "col7", "col4", "col8"], axis=0),
+        )
 
     def test_reindex_axis(self):
         df = pd.DataFrame(