Repository: modin-project/modin

Issue:
Need to test DataFrame.product with small DataFrames
### System information
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**:

Ubuntu 19.10

- **Modin version** (`modin.__version__`):

0.7.3+42.gc78828e.dirty

- **Python version**:

Python 3.7.5

- **Code we can use to reproduce**:

```patch
diff --git a/modin/pandas/test/utils.py b/modin/pandas/test/utils.py
index 1faaf38..d4d257c 100644
--- a/modin/pandas/test/utils.py
+++ b/modin/pandas/test/utils.py
@@ -63,6 +63,11 @@ test_data = {
         ]
         for i in range(NCOLS)
     },
+    "small": {
+        "col0": [1, 2, 3, 4],
+        "col1": [8.0, 9.4, 10.1, 11.3],
+        "col2": [4, 5, 6, 7],
+    },
     # "int_float_object_data": {
     #     "col3": [1, 2, 3, 4],
     #     "col4": [4, 5, 6, 7],
```

<!--
You can obtain the Modin version with

python -c "import modin; print(modin.__version__)"
-->

### Describe the problem
<!-- Describe the problem clearly here. -->

When dataframe size is small and is smaller than `min_count` parameter value for `product` it causes a lot of tests to fail. Pandas behaves quite strangely and Modin works differently.

### 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. -->



Discussion:

Resolving patch:
diff --git a/modin/pandas/dataframe.py b/modin/pandas/dataframe.py
--- a/modin/pandas/dataframe.py
+++ b/modin/pandas/dataframe.py
@@ -1738,8 +1738,13 @@ def prod(
         **kwargs,
     ):
         axis = self._get_axis_number(axis)
-        new_index = self.columns if axis else self.index
-        if min_count > len(new_index):
+        axis_to_apply = self.columns if axis else self.index
+        if (
+            skipna is not False
+            and numeric_only is None
+            and min_count > len(axis_to_apply)
+        ):
+            new_index = self.columns if not axis else self.index
             return Series(
                 [np.nan] * len(new_index), index=new_index, dtype=np.dtype("object")
             )
@@ -2063,8 +2068,13 @@ def sum(
         **kwargs,
     ):
         axis = self._get_axis_number(axis)
-        new_index = self.columns if axis else self.index
-        if min_count > len(new_index):
+        axis_to_apply = self.columns if axis else self.index
+        if (
+            skipna is not False
+            and numeric_only is None
+            and min_count > len(axis_to_apply)
+        ):
+            new_index = self.columns if not axis else self.index
             return Series(
                 [np.nan] * len(new_index), index=new_index, dtype=np.dtype("object")
             )


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
@@ -56,6 +56,8 @@
     int_arg_values,
     eval_general,
     create_test_dfs,
+    test_data_small_values,
+    test_data_small_keys,
 )
 
 pd.DEFAULT_NPARTITIONS = 4
@@ -3390,7 +3392,11 @@ def test_min(self, data, axis, skipna, numeric_only):
         os.name == "nt",
         reason="Windows has a memory issue for large numbers on this test",
     )
-    @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
+    @pytest.mark.parametrize(
+        "data",
+        test_data_values + test_data_small_values,
+        ids=test_data_keys + test_data_small_keys,
+    )
     @pytest.mark.parametrize("axis", axis_values, ids=axis_keys)
     @pytest.mark.parametrize(
         "skipna", bool_arg_values, ids=arg_keys("skipna", bool_arg_keys)
@@ -3401,84 +3407,48 @@ def test_min(self, data, axis, skipna, numeric_only):
     @pytest.mark.parametrize(
         "min_count", int_arg_values, ids=arg_keys("min_count", int_arg_keys)
     )
-    def test_prod(self, request, data, axis, skipna, numeric_only, min_count):
-        modin_df = pd.DataFrame(data)
-        pandas_df = pandas.DataFrame(data)
-
-        try:
-            pandas_result = pandas_df.prod(
-                axis=axis, skipna=skipna, numeric_only=numeric_only, min_count=min_count
-            )
-        except Exception:
-            with pytest.raises(TypeError):
-                modin_df.prod(
-                    axis=axis,
-                    skipna=skipna,
-                    numeric_only=numeric_only,
-                    min_count=min_count,
-                )
-        else:
-            modin_result = modin_df.prod(
-                axis=axis, skipna=skipna, numeric_only=numeric_only, min_count=min_count
-            )
-            df_equals(modin_result, pandas_result)
-
-        try:
-            pandas_result = pandas_df.T.prod(
-                axis=axis, skipna=skipna, numeric_only=numeric_only, min_count=min_count
-            )
-        except Exception:
-            with pytest.raises(TypeError):
-                modin_df.T.prod(
-                    axis=axis,
-                    skipna=skipna,
-                    numeric_only=numeric_only,
-                    min_count=min_count,
-                )
-        else:
-            modin_result = modin_df.T.prod(
-                axis=axis, skipna=skipna, numeric_only=numeric_only, min_count=min_count
-            )
-            df_equals(modin_result, pandas_result)
-
-    @pytest.mark.skipif(
-        os.name == "nt",
-        reason="Windows has a memory issue for large numbers on this test",
-    )
-    @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
-    @pytest.mark.parametrize("axis", axis_values, ids=axis_keys)
+    @pytest.mark.parametrize("is_transposed", [False, True])
     @pytest.mark.parametrize(
-        "skipna", bool_arg_values, ids=arg_keys("skipna", bool_arg_keys)
-    )
-    @pytest.mark.parametrize(
-        "numeric_only", bool_arg_values, ids=arg_keys("numeric_only", bool_arg_keys)
+        "operation",
+        [
+            "prod",
+            pytest.param(
+                "product",
+                marks=pytest.mark.skipif(
+                    pandas.DataFrame.product == pandas.DataFrame.prod
+                    and pd.DataFrame.product == pd.DataFrame.prod,
+                    reason="That operation was already tested.",
+                ),
+            ),
+        ],
     )
+    def test_prod(
+        self,
+        request,
+        data,
+        axis,
+        skipna,
+        numeric_only,
+        min_count,
+        is_transposed,
+        operation,
+    ):
+        eval_general(
+            *create_test_dfs(data),
+            lambda df, *args, **kwargs: getattr(
+                df.T if is_transposed else df, operation
+            )(*args, **kwargs),
+            axis=axis,
+            skipna=skipna,
+            numeric_only=numeric_only,
+            min_count=min_count,
+        )
+
     @pytest.mark.parametrize(
-        "min_count", int_arg_values, ids=arg_keys("min_count", int_arg_keys)
+        "data",
+        test_data_values + test_data_small_values,
+        ids=test_data_keys + test_data_small_keys,
     )
-    def test_product(self, request, data, axis, skipna, numeric_only, min_count):
-        modin_df = pd.DataFrame(data)
-        pandas_df = pandas.DataFrame(data)
-
-        try:
-            pandas_result = pandas_df.product(
-                axis=axis, skipna=skipna, numeric_only=numeric_only, min_count=min_count
-            )
-        except Exception:
-            with pytest.raises(TypeError):
-                modin_df.product(
-                    axis=axis,
-                    skipna=skipna,
-                    numeric_only=numeric_only,
-                    min_count=min_count,
-                )
-        else:
-            modin_result = modin_df.product(
-                axis=axis, skipna=skipna, numeric_only=numeric_only, min_count=min_count
-            )
-            df_equals(modin_result, pandas_result)
-
-    @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
     @pytest.mark.parametrize("axis", axis_values, ids=axis_keys)
     @pytest.mark.parametrize(
         "skipna", bool_arg_values, ids=arg_keys("skipna", bool_arg_keys)
@@ -3489,44 +3459,20 @@ def test_product(self, request, data, axis, skipna, numeric_only, min_count):
     @pytest.mark.parametrize(
         "min_count", int_arg_values, ids=arg_keys("min_count", int_arg_keys)
     )
-    def test_sum(self, request, data, axis, skipna, numeric_only, min_count):
-        modin_df = pd.DataFrame(data)
-        pandas_df = pandas.DataFrame(data)
-
-        try:
-            pandas_result = pandas_df.sum(
-                axis=axis, skipna=skipna, numeric_only=numeric_only, min_count=min_count
-            )
-        except Exception:
-            with pytest.raises(TypeError):
-                modin_df.sum(
-                    axis=axis,
-                    skipna=skipna,
-                    numeric_only=numeric_only,
-                    min_count=min_count,
-                )
-        else:
-            modin_result = modin_df.sum(
-                axis=axis, skipna=skipna, numeric_only=numeric_only, min_count=min_count
-            )
-            df_equals(modin_result, pandas_result)
-        try:
-            pandas_result = pandas_df.T.sum(
-                axis=axis, skipna=skipna, numeric_only=numeric_only, min_count=min_count
-            )
-        except Exception:
-            with pytest.raises(TypeError):
-                modin_df.T.sum(
-                    axis=axis,
-                    skipna=skipna,
-                    numeric_only=numeric_only,
-                    min_count=min_count,
-                )
-        else:
-            modin_result = modin_df.T.sum(
-                axis=axis, skipna=skipna, numeric_only=numeric_only, min_count=min_count
-            )
-            df_equals(modin_result, pandas_result)
+    @pytest.mark.parametrize("is_transposed", [False, True])
+    def test_sum(
+        self, request, data, axis, skipna, numeric_only, min_count, is_transposed
+    ):
+        eval_general(
+            *create_test_dfs(data),
+            lambda df, *args, **kwargs: (df.T if is_transposed else df).sum(
+                *args, **kwargs
+            ),
+            axis=axis,
+            skipna=skipna,
+            numeric_only=numeric_only,
+            min_count=min_count,
+        )
 
     @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
     def test_sum_single_column(self, data):
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
@@ -54,6 +54,8 @@
     encoding_types,
     categories_equals,
     eval_general,
+    test_data_small_values,
+    test_data_small_keys,
 )
 
 pd.DEFAULT_NPARTITIONS = 4
@@ -2086,19 +2088,53 @@ def test_pow(data):
     inter_df_math_helper(modin_series, pandas_series, "pow")
 
 
-@pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
-def test_prod(data):
-    modin_series, pandas_series = create_test_series(data)
-    # Wrap in Series to test almost_equal because of overflow
-    df_equals(pd.Series([modin_series.prod()]), pandas.Series([pandas_series.prod()]))
-
-
-@pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
-def test_product(data):
-    modin_series, pandas_series = create_test_series(data)
-    # Wrap in Series to test almost_equal because of overflow
-    df_equals(
-        pd.Series([modin_series.product()]), pandas.Series([pandas_series.product()])
+@pytest.mark.parametrize(
+    "data",
+    test_data_values + test_data_small_values,
+    ids=test_data_keys + test_data_small_keys,
+)
+@pytest.mark.parametrize("axis", axis_values, ids=axis_keys)
+@pytest.mark.parametrize(
+    "skipna", bool_arg_values, ids=arg_keys("skipna", bool_arg_keys)
+)
+@pytest.mark.parametrize(
+    "numeric_only",
+    [
+        None,
+        False,
+        pytest.param(
+            True,
+            marks=pytest.mark.xfail(
+                reason="numeric_only not implemented for pandas.Series"
+            ),
+        ),
+    ],
+)
+@pytest.mark.parametrize(
+    "min_count", int_arg_values, ids=arg_keys("min_count", int_arg_keys)
+)
+@pytest.mark.parametrize(
+    "operation",
+    [
+        "prod",
+        pytest.param(
+            "product",
+            marks=pytest.mark.skipif(
+                pandas.Series.product == pandas.Series.prod
+                and pd.Series.product == pd.Series.prod,
+                reason="That operation was already tested.",
+            ),
+        ),
+    ],
+)
+def test_prod(data, axis, skipna, numeric_only, min_count, operation):
+    eval_general(
+        *create_test_series(data),
+        lambda df, *args, **kwargs: type(df)([getattr(df, operation)(*args, **kwargs)]),
+        axis=axis,
+        skipna=skipna,
+        numeric_only=numeric_only,
+        min_count=min_count,
     )
 
 
@@ -2685,23 +2721,40 @@ def test_subtract(data):
     inter_df_math_helper(modin_series, pandas_series, "subtract")
 
 
-@pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
+@pytest.mark.parametrize(
+    "data",
+    test_data_values + test_data_small_values,
+    ids=test_data_keys + test_data_small_keys,
+)
+@pytest.mark.parametrize("axis", axis_values, ids=axis_keys)
 @pytest.mark.parametrize(
     "skipna", bool_arg_values, ids=arg_keys("skipna", bool_arg_keys)
 )
+@pytest.mark.parametrize(
+    "numeric_only",
+    [
+        None,
+        False,
+        pytest.param(
+            True,
+            marks=pytest.mark.xfail(
+                reason="numeric_only not implemented for pandas.Series"
+            ),
+        ),
+    ],
+)
 @pytest.mark.parametrize(
     "min_count", int_arg_values, ids=arg_keys("min_count", int_arg_keys)
 )
-def test_sum(data, skipna, min_count):
-    modin_series, pandas_series = create_test_series(data)
-    try:
-        pandas_result = pandas_series.sum(skipna=skipna, min_count=min_count)
-    except Exception:
-        with pytest.raises(TypeError):
-            modin_series.sum(skipna=skipna, min_count=min_count)
-    else:
-        modin_result = modin_series.sum(skipna=skipna, min_count=min_count)
-        df_equals(modin_result, pandas_result)
+def test_sum(data, axis, skipna, numeric_only, min_count):
+    eval_general(
+        *create_test_series(data),
+        lambda df, *args, **kwargs: df.sum(*args, **kwargs),
+        axis=axis,
+        skipna=skipna,
+        numeric_only=numeric_only,
+        min_count=min_count,
+    )
 
 
 @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
diff --git a/modin/pandas/test/utils.py b/modin/pandas/test/utils.py
--- a/modin/pandas/test/utils.py
+++ b/modin/pandas/test/utils.py
@@ -159,6 +159,17 @@
     },
 }
 
+test_data_small = {
+    "small": {
+        "col0": [1, 2, 3, 4],
+        "col1": [8.0, 9.4, 10.1, 11.3],
+        "col2": [4, 5, 6, 7],
+    }
+}
+
+test_data_small_values = list(test_data_small.values())
+test_data_small_keys = list(test_data_small.keys())
+
 test_data_with_duplicates_values = list(test_data_with_duplicates.values())
 test_data_with_duplicates_keys = list(test_data_with_duplicates.keys())