Repository: modin-project/modin

Issue:
Unique function output doesn't match Pandas with single unique value
### System information
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**: MacOS 10.15.7
- **Modin version** (`modin.__version__`): 0.8.2
- **Python version**: 3.8.5
- **Code we can use to reproduce**:
```
import pandas as pd
print("===PANDAS===")
s = pd.Series(['green'])
print(s)
print(type(s))
su = s.unique()
# Leads to same error as modin's unique()
# su = s.unique().squeeze()
print(su)
print(type(su))
print(len(su))

import modin.pandas as md
print("\n===MODIN===")
s = md.Series(['green'])
print(s)
print(type(s))
su = s.unique()
print(su)
print(type(su))
print(len(su))
```

<!--
You can obtain the Modin version with

python -c "import modin; print(modin.__version__)"
-->

### Describe the problem
<!-- Describe the problem clearly here. -->
Whenever `unique` is called on a Series and there is only one unique value, Modin will output a scalar numpy value whereas Pandas will output an numpy array of length 1. As a result, trying to call `len` on Modin's `unique` result throws an error because scalar values do not have an `len` attribute, but Pandas does not. This is likely because Modin's implementation calls `squeeze` as squeezing an array of length 1 transforms it into a scalar. 

This error does not occur when there are two or more unique values. The solution could be to remove `squeeze` from Modin's `unique` implementation. I will do more testing and try to follow up with a PR.

### Source code / logs
<!-- 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. -->
Log from above code to reproduce:
```
===PANDAS===
0    green
dtype: object
<class 'pandas.core.series.Series'>
['green']
<class 'numpy.ndarray'>
1

===MODIN===
0    green
dtype: object
<class 'modin.pandas.series.Series'>
green
<class 'numpy.ndarray'>
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-c4f0aa247643> in <module>
     19 print(su)
     20 print(type(su))
---> 21 print(len(su))

TypeError: len() of unsized object
```

Source code for Modin's `unique` (calls `squeeze` after `to_numpy`):
https://github.com/modin-project/modin/blob/c86422afcfe311743d7f326a40063e1dc436c5d9/modin/pandas/series.py#L1347-L1348


Discussion:

Resolving patch:
diff --git a/modin/pandas/series.py b/modin/pandas/series.py
--- a/modin/pandas/series.py
+++ b/modin/pandas/series.py
@@ -1345,7 +1345,9 @@ def truncate(self, before=None, after=None, axis=None, copy=True):
         )
 
     def unique(self):
-        return self._query_compiler.unique().to_numpy().squeeze()
+        return self.__constructor__(
+            query_compiler=self._query_compiler.unique()
+        ).to_numpy()
 
     def update(self, other):
         if not isinstance(other, Series):


Test patch:
diff --git a/modin/pandas/test/test_general.py b/modin/pandas/test/test_general.py
--- a/modin/pandas/test/test_general.py
+++ b/modin/pandas/test/test_general.py
@@ -528,10 +528,12 @@ def test_unique():
     modin_result = pd.unique([2, 1, 3, 3])
     pandas_result = pandas.unique([2, 1, 3, 3])
     assert_array_equal(modin_result, pandas_result)
+    assert modin_result.shape == pandas_result.shape
 
     modin_result = pd.unique(pd.Series([2] + [1] * 5))
     pandas_result = pandas.unique(pandas.Series([2] + [1] * 5))
     assert_array_equal(modin_result, pandas_result)
+    assert modin_result.shape == pandas_result.shape
 
     modin_result = pd.unique(
         pd.Series([pd.Timestamp("20160101"), pd.Timestamp("20160101")])
@@ -540,6 +542,7 @@ def test_unique():
         pandas.Series([pandas.Timestamp("20160101"), pandas.Timestamp("20160101")])
     )
     assert_array_equal(modin_result, pandas_result)
+    assert modin_result.shape == pandas_result.shape
 
     modin_result = pd.unique(
         pd.Series(
@@ -558,6 +561,7 @@ def test_unique():
         )
     )
     assert_array_equal(modin_result, pandas_result)
+    assert modin_result.shape == pandas_result.shape
 
     modin_result = pd.unique(
         pd.Index(
@@ -576,10 +580,12 @@ def test_unique():
         )
     )
     assert_array_equal(modin_result, pandas_result)
+    assert modin_result.shape == pandas_result.shape
 
     modin_result = pd.unique(pd.Series(pd.Categorical(list("baabc"))))
     pandas_result = pandas.unique(pandas.Series(pandas.Categorical(list("baabc"))))
     assert_array_equal(modin_result, pandas_result)
+    assert modin_result.shape == pandas_result.shape
 
 
 @pytest.mark.parametrize("normalize, bins, dropna", [(True, 3, False)])
diff --git a/modin/pandas/test/test_series.py b/modin/pandas/test/test_series.py
--- a/modin/pandas/test/test_series.py
+++ b/modin/pandas/test/test_series.py
@@ -3287,16 +3287,19 @@ def test_unique(data):
     modin_result = modin_series.unique()
     pandas_result = pandas_series.unique()
     assert_array_equal(modin_result, pandas_result)
+    assert modin_result.shape == pandas_result.shape
 
     modin_result = pd.Series([2, 1, 3, 3], name="A").unique()
     pandas_result = pandas.Series([2, 1, 3, 3], name="A").unique()
     assert_array_equal(modin_result, pandas_result)
+    assert modin_result.shape == pandas_result.shape
 
     modin_result = pd.Series([pd.Timestamp("2016-01-01") for _ in range(3)]).unique()
     pandas_result = pandas.Series(
         [pd.Timestamp("2016-01-01") for _ in range(3)]
     ).unique()
     assert_array_equal(modin_result, pandas_result)
+    assert modin_result.shape == pandas_result.shape
 
     modin_result = pd.Series(
         [pd.Timestamp("2016-01-01", tz="US/Eastern") for _ in range(3)]
@@ -3305,10 +3308,12 @@ def test_unique(data):
         [pd.Timestamp("2016-01-01", tz="US/Eastern") for _ in range(3)]
     ).unique()
     assert_array_equal(modin_result, pandas_result)
+    assert modin_result.shape == pandas_result.shape
 
     modin_result = pandas.Series(pd.Categorical(list("baabc"))).unique()
     pandas_result = pd.Series(pd.Categorical(list("baabc"))).unique()
     assert_array_equal(modin_result, pandas_result)
+    assert modin_result.shape == pandas_result.shape
 
     modin_result = pd.Series(
         pd.Categorical(list("baabc"), categories=list("abc"), ordered=True)
@@ -3317,6 +3322,7 @@ def test_unique(data):
         pd.Categorical(list("baabc"), categories=list("abc"), ordered=True)
     ).unique()
     assert_array_equal(modin_result, pandas_result)
+    assert modin_result.shape == pandas_result.shape
 
 
 @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)