Repository: modin-project/modin

Issue:
BUG: Reshuffling mechanism fails if the key column is a type of 'string'
### Modin version checks

- [X] I have checked that this issue has not already been reported.

- [X] I have confirmed this bug exists on the latest released version of Modin.

- [X] I have confirmed this bug exists on the main branch of Modin. (In order to do this you can follow [this guide](https://modin.readthedocs.io/en/stable/getting_started/installation.html#installing-from-the-github-master-branch).)


### Reproducible Example

```python
import modin.pandas as pd
import modin.config as cfg

cfg.ExperimentalGroupbyImpl.put(True)

df = pd.DataFrame({"col1": ["a"] * 50 + ["b"] * 50, "col2": range(100)})

print(df.dtypes["col1"]) # object
print(df.groupby("col1").mean())

df = df.astype({"col1": "string"})
print(df.dtypes["col1"]) # string
print(df.groupby("col1").mean()) # TypeError: unsupported operand type(s) for -: 'str' and 'str'
```


### Issue Description

There's a condition dispatching between reshuffling implementation for numeric and non-numeric dtypes. However, the condition seems to work incorrect as the `if "string" == object` check doesn't work as intended:
https://github.com/modin-project/modin/blob/da385c96079806dcc9d6e6562db8a9220525824d/modin/core/dataframe/pandas/dataframe/dataframe.py#L2372-L2377

### Expected Behavior

to work

### Error Logs

<details>

```python-traceback

Traceback (most recent call last):
  File "t3.py", line 13, in <module>
    print(df.groupby("col1").mean()) # fails
  File "repos/modin/modin/logging/logger_decorator.py", line 128, in run_and_log
    return obj(*args, **kwargs)
  File "repos/modin/modin/pandas/groupby.py", line 291, in mean
    self._wrap_aggregation(
  File "repos/modin/modin/logging/logger_decorator.py", line 128, in run_and_log
    return obj(*args, **kwargs)
  File "repos/modin/modin/pandas/groupby.py", line 1516, in _wrap_aggregation
    query_compiler=qc_method(
  File "repos/modin/modin/logging/logger_decorator.py", line 128, in run_and_log
    return obj(*args, **kwargs)
  File "repos/modin/modin/core/storage_formats/pandas/query_compiler.py", line 3273, in groupby_mean
    return self._groupby_shuffle(
  File "repos/modin/modin/logging/logger_decorator.py", line 128, in run_and_log
    return obj(*args, **kwargs)
  File "repos/modin/modin/core/storage_formats/pandas/query_compiler.py", line 3579, in _groupby_shuffle
    result = obj._modin_frame.groupby(
  File "repos/modin/modin/logging/logger_decorator.py", line 128, in run_and_log
    return obj(*args, **kwargs)
  File "repos/modin/modin/core/dataframe/pandas/dataframe/utils.py", line 376, in run_f_on_minimally_updated_metadata
    result = f(self, *args, **kwargs)
  File "repos/modin/modin/core/dataframe/pandas/dataframe/dataframe.py", line 3645, in groupby
    result = self._apply_func_to_range_partitioning(
  File "repos/modin/modin/logging/logger_decorator.py", line 128, in run_and_log
    return obj(*args, **kwargs)
  File "repos/modin/modin/core/dataframe/pandas/dataframe/dataframe.py", line 2419, in _apply_func_to_range_partitioning
    new_partitions = self._partition_mgr_cls.shuffle_partitions(
  File "repos/modin/modin/logging/logger_decorator.py", line 128, in run_and_log
    return obj(*args, **kwargs)
  File "repos/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 58, in wait
    result = func(cls, *args, **kwargs)
  File "repos/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 1586, in shuffle_partitions
    pivots = np.unique(shuffle_functions.pivot_function(samples))
  File "repos/modin/modin/core/dataframe/pandas/dataframe/utils.py", line 72, in pivot_fn
    return pick_pivots_from_samples_for_sort(
  File "repos/modin/modin/core/dataframe/pandas/dataframe/utils.py", line 207, in pick_pivots_from_samples_for_sort
    return _find_quantiles(all_pivots, quantiles, method)
  File "repos/modin/modin/core/dataframe/pandas/dataframe/utils.py", line 116, in _find_quantiles
    return np.quantile(df, quantiles)
  File "<__array_function__ internals>", line 180, in quantile
  File "miniconda3/envs/modinc/lib/python3.8/site-packages/numpy/lib/function_base.py", line 4412, in quantile
    return _quantile_unchecked(
  File "miniconda3/envs/modinc/lib/python3.8/site-packages/numpy/lib/function_base.py", line 4424, in _quantile_unchecked
    r, k = _ureduce(a,
  File "miniconda3/envs/modinc/lib/python3.8/site-packages/numpy/lib/function_base.py", line 3725, in _ureduce
    r = func(a, **kwargs)
  File "miniconda3/envs/modinc/lib/python3.8/site-packages/numpy/lib/function_base.py", line 4593, in _quantile_ureduce_func
    result = _quantile(arr,
  File "miniconda3/envs/modinc/lib/python3.8/site-packages/numpy/lib/function_base.py", line 4710, in _quantile
    result = _lerp(previous,
  File "miniconda3/envs/modinc/lib/python3.8/site-packages/numpy/lib/function_base.py", line 4527, in _lerp
    diff_b_a = subtract(b, a)
TypeError: unsupported operand type(s) for -: 'str' and 'str'

```

</details>


### Installed Versions

<details>

Replace this line with the output of pd.show_versions()

</details>



Discussion:

Resolving patch:
diff --git a/modin/conftest.py b/modin/conftest.py
--- a/modin/conftest.py
+++ b/modin/conftest.py
@@ -693,3 +693,18 @@ def s3_resource(s3_base):
         if not cli.list_buckets()["Buckets"]:
             break
         time.sleep(0.1)
+
+
+@pytest.fixture
+def modify_config(request):
+    values = request.param
+    old_values = {}
+
+    for key, value in values.items():
+        old_values[key] = key.get()
+        key.put(value)
+
+    yield  # waiting for the test to be completed
+    # restoring old parameters
+    for key, value in old_values.items():
+        key.put(value)
diff --git a/modin/core/dataframe/pandas/dataframe/dataframe.py b/modin/core/dataframe/pandas/dataframe/dataframe.py
--- a/modin/core/dataframe/pandas/dataframe/dataframe.py
+++ b/modin/core/dataframe/pandas/dataframe/dataframe.py
@@ -2369,12 +2369,12 @@ def _apply_func_to_range_partitioning(
                 # simply combine all partitions and apply the sorting to the whole dataframe
                 return self.combine_and_apply(func=func)
 
-        if self.dtypes[key_column] == object:
+        if is_numeric_dtype(self.dtypes[key_column]):
+            method = "linear"
+        else:
             # This means we are not sorting numbers, so we need our quantiles to not try
             # arithmetic on the values.
             method = "inverted_cdf"
-        else:
-            method = "linear"
 
         shuffling_functions = build_sort_functions(
             self,


Test patch:
diff --git a/modin/pandas/test/test_groupby.py b/modin/pandas/test/test_groupby.py
--- a/modin/pandas/test/test_groupby.py
+++ b/modin/pandas/test/test_groupby.py
@@ -2787,3 +2787,20 @@ def perform(lib):
         return getattr(grp, func)()
 
     eval_general(pd, pandas, perform)
+
+
+@pytest.mark.parametrize(
+    "modify_config", [{ExperimentalGroupbyImpl: True}], indirect=True
+)
+def test_reshuffling_groupby_on_strings(modify_config):
+    # reproducer from https://github.com/modin-project/modin/issues/6509
+    modin_df, pandas_df = create_test_dfs(
+        {"col1": ["a"] * 50 + ["b"] * 50, "col2": range(100)}
+    )
+
+    modin_df = modin_df.astype({"col1": "string"})
+    pandas_df = pandas_df.astype({"col1": "string"})
+
+    eval_general(
+        modin_df.groupby("col1"), pandas_df.groupby("col1"), lambda grp: grp.mean()
+    )
diff --git a/modin/test/storage_formats/pandas/test_internals.py b/modin/test/storage_formats/pandas/test_internals.py
--- a/modin/test/storage_formats/pandas/test_internals.py
+++ b/modin/test/storage_formats/pandas/test_internals.py
@@ -117,21 +117,6 @@ def construct_modin_df_by_scheme(pandas_df, partitioning_scheme):
     return md_df
 
 
-@pytest.fixture
-def modify_config(request):
-    values = request.param
-    old_values = {}
-
-    for key, value in values.items():
-        old_values[key] = key.get()
-        key.put(value)
-
-    yield  # waiting for the test to be completed
-    # restoring old parameters
-    for key, value in old_values.items():
-        key.put(value)
-
-
 def validate_partitions_cache(df):
     """Assert that the ``PandasDataframe`` shape caches correspond to the actual partition's shapes."""
     row_lengths = df._row_lengths_cache