Repository: modin-project/modin

Issue:
OmniSci backend incorrectly interprets default `skipna` parameter value at aggregation operations
### System information
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**: Any
- **Modin version** (`modin.__version__`): 67bab94751be8f391d6e37b04a08e0d34099e97d
- **Python version**: 3.7.5
<!--
You can obtain the Modin version with

python -c "import modin; print(modin.__version__)"
-->

### Describe the problem
We're supposed to default to pandas if `skipna=False`, however, the following code does that whenever `skipna` can be cast to bool-false (including `None` value). `skipna` is a bool-only parameter, so `None` is interpreted as a default function parameter (which is `True`, see [pandas doc](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sum.html)). From the same pandas' doc, we see that the actual default `skipna` parameter in a function signature is `None` (going by the docs it's interpreted as `True`), so by default `None` value is propagated to the backend and so has to be processed with care.
https://github.com/modin-project/modin/blob/67bab94751be8f391d6e37b04a08e0d34099e97d/modin/experimental/backends/omnisci/query_compiler.py#L305-L307



Discussion:

Resolving patch:
diff --git a/modin/experimental/backends/omnisci/query_compiler.py b/modin/experimental/backends/omnisci/query_compiler.py
--- a/modin/experimental/backends/omnisci/query_compiler.py
+++ b/modin/experimental/backends/omnisci/query_compiler.py
@@ -303,7 +303,7 @@ def _agg(self, agg, axis=0, level=None, **kwargs):
             return getattr(super(), agg)(axis=axis, level=level, **kwargs)
 
         skipna = kwargs.get("skipna", True)
-        if not skipna:
+        if skipna is False:
             return getattr(super(), agg)(axis=axis, level=level, **kwargs)
 
         new_frame = self._modin_frame.agg(agg)


Test patch:
diff --git a/modin/experimental/engines/omnisci_on_ray/test/test_dataframe.py b/modin/experimental/engines/omnisci_on_ray/test/test_dataframe.py
--- a/modin/experimental/engines/omnisci_on_ray/test/test_dataframe.py
+++ b/modin/experimental/engines/omnisci_on_ray/test/test_dataframe.py
@@ -16,6 +16,7 @@
 import numpy as np
 import pyarrow
 import pytest
+import re
 
 from modin.config import IsExperimental, Engine, Backend
 
@@ -56,7 +57,7 @@ def run_and_compare(
     force_lazy=True,
     force_arrow_execute=False,
     allow_subqueries=False,
-    **kwargs
+    **kwargs,
 ):
     def run_modin(
         fn,
@@ -66,7 +67,7 @@ def run_modin(
         force_arrow_execute,
         allow_subqueries,
         constructor_kwargs,
-        **kwargs
+        **kwargs,
     ):
         kwargs["df1"] = pd.DataFrame(data, **constructor_kwargs)
         kwargs["df2"] = pd.DataFrame(data2, **constructor_kwargs)
@@ -104,7 +105,7 @@ def run_modin(
                 force_arrow_execute=force_arrow_execute,
                 allow_subqueries=allow_subqueries,
                 constructor_kwargs=constructor_kwargs,
-                **kwargs
+                **kwargs,
             )
             _ = exp_res.index
     else:
@@ -116,7 +117,7 @@ def run_modin(
             force_arrow_execute=force_arrow_execute,
             allow_subqueries=allow_subqueries,
             constructor_kwargs=constructor_kwargs,
-            **kwargs
+            **kwargs,
         )
         df_equals(ref_res, exp_res)
 
@@ -1027,6 +1028,28 @@ def value_counts(df, cols, dropna, sort, ascending, **kwargs):
             ascending=ascending,
         )
 
+    @pytest.mark.parametrize("method", ["sum", "mean", "max", "min", "count"])
+    def test_simple_agg_no_default(self, method):
+        def applier(df, **kwargs):
+            if isinstance(df, pd.DataFrame):
+                # At the end of reduction function it does inevitable `transpose`, which
+                # is defaulting to pandas. The following logic check that `transpose` is the only
+                # function that falling back to pandas in the reduction operation flow.
+                with pytest.warns(UserWarning) as warns:
+                    res = getattr(df, method)()
+                assert (
+                    len(warns) == 1
+                ), f"More than one warning were arisen: len(warns) != 1 ({len(warns)} != 1)"
+                message = warns[0].message.args[0]
+                assert (
+                    re.match(r".*transpose.*defaulting to pandas", message) is not None
+                ), f"Expected DataFrame.transpose defaulting to pandas warning, got: {message}"
+            else:
+                res = getattr(df, method)()
+            return res
+
+        run_and_compare(applier, data=self.data, force_lazy=False)
+
 
 class TestMerge:
     data = {