Repository: modin-project/modin

Issue:
`DataFrame.mean` is broken in case of axis=1 and numeric_only=None
### Describe the problem
`mean` produces an empty frame in case of applying on `axis=1`, However, other reduction operations works fine with `axis=1`

Reproducer:
```python
import modin.pandas as pd
import pandas
from modin.pandas.test.utils import (
    test_data_diff_dtype,
    df_equals,
)

pd_df = pandas.DataFrame(test_data_diff_dtype)
md_df = pd.DataFrame(test_data_diff_dtype)

reduction_operation = ["max", "min", "median", "skew", "kurt", "sem", "std", "var"]

for op in reduction_operation:
    df_equals(getattr(md_df, op)(axis=1), getattr(pd_df, op)(axis=1)) # works fine

df_equals(md_df.mean(axis=1), pd_df.mean(axis=1)) # AssertionError
```


<details><summary> Traceback </summary>


```

Traceback (most recent call last):
  File "../TESTS/t2.py", line 108, in <module>
    df_equals(md_df.mean(axis=1), pd_df.mean(axis=1))
  File "/localdisk/dchigare/repos/modin_bp/modin/pandas/test/utils.py", line 520, in df_equals
    assert_series_equal(df1, df2, check_dtype=False, check_series_type=False)
  File "/localdisk/dchigare/miniconda3/envs/modin_tests/lib/python3.7/site-packages/pandas/_testing.py", line 1401, in assert_series_equal
    index_values=np.asarray(left.index),
  File "pandas/_libs/testing.pyx", line 67, in pandas._libs.testing.assert_almost_equal
  File "pandas/_libs/testing.pyx", line 182, in pandas._libs.testing.assert_almost_equal
  File "/localdisk/dchigare/miniconda3/envs/modin_tests/lib/python3.7/site-packages/pandas/_testing.py", line 1036, in raise_assert_detail
    raise AssertionError(msg)
AssertionError: Series are different

Series values are different (100.0 %)
[index]: [0, 1, 2, 3]
[left]:  [nan, nan, nan, nan]
[right]: [-2.5, -2.1333333333333333, 6.033333333333334, 8.0]
```


</details>



Discussion:
Before [this commit](https://github.com/modin-project/modin/commit/55e345951818761ad26711cde91bb0d2b296aa5f) there was the exception.
```
TypeError: unsupported operand type(s) for +: 'int' and 'str'
```
After the commit the result is the following:
```
0   NaN
1   NaN
2   NaN
3   NaN
dtype: float64
```
May be, it will help somehow.

Resolving patch:
diff --git a/asv_bench/benchmarks/benchmarks.py b/asv_bench/benchmarks/benchmarks.py
--- a/asv_bench/benchmarks/benchmarks.py
+++ b/asv_bench/benchmarks/benchmarks.py
@@ -260,3 +260,6 @@ def time_nunique(self, impl, data_type, data_size, axis):
 
     def time_apply(self, impl, data_type, data_size, axis):
         self.df.apply(lambda df: df.sum(), axis=axis)
+    
+    def time_mean(self, impl, data_type, data_size, axis):
+        self.df.mean(axis=axis)
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
@@ -677,33 +677,31 @@ def mean(self, axis, **kwargs):
 
         skipna = kwargs.get("skipna", True)
 
-        def map_apply_fn(ser, **kwargs):
-            try:
-                sum_result = ser.sum(skipna=skipna)
-                count_result = ser.count()
-            except TypeError:
-                return None
-            else:
-                return (sum_result, count_result)
-
-        def reduce_apply_fn(ser, **kwargs):
-            sum_result = ser.apply(lambda x: x[0]).sum(skipna=skipna)
-            count_result = ser.apply(lambda x: x[1]).sum(skipna=skipna)
-            return sum_result / count_result
+        # TODO-FIX: this function may work incorrectly with user-defined "numeric" values.
+        # Since `count(numeric_only=True)` discards all unknown "numeric" types, we can get incorrect
+        # divisor inside the reduce function.
+        def map_fn(df, **kwargs):
+            result = pandas.DataFrame(
+                {
+                    "sum": df.sum(axis=axis, skipna=skipna),
+                    "count": df.count(axis=axis, numeric_only=True),
+                }
+            )
+            return result if axis else result.T
 
         def reduce_fn(df, **kwargs):
-            df.dropna(axis=1, inplace=True, how="any")
-            return build_applyier(reduce_apply_fn, axis=axis)(df)
-
-        def build_applyier(func, **applyier_kwargs):
-            def applyier(df, **kwargs):
-                result = df.apply(func, **applyier_kwargs)
-                return result.set_axis(df.axes[axis ^ 1], axis=0)
+            sum_cols = df["sum"] if axis else df.loc["sum"]
+            count_cols = df["count"] if axis else df.loc["count"]
 
-            return applyier
+            if not isinstance(sum_cols, pandas.Series):
+                # If we got `NaN` as the result of the sum in any axis partition,
+                # then we must consider the whole sum as `NaN`, so setting `skipna=False`
+                sum_cols = sum_cols.sum(axis=axis, skipna=False)
+                count_cols = count_cols.sum(axis=axis, skipna=False)
+            return sum_cols / count_cols
 
         return MapReduceFunction.register(
-            build_applyier(map_apply_fn, axis=axis, result_type="reduce"),
+            map_fn,
             reduce_fn,
             preserve_index=(kwargs.get("numeric_only") is not None),
         )(self, axis=axis, **kwargs)


Test patch:
diff --git a/modin/pandas/test/dataframe/test_reduction.py b/modin/pandas/test/dataframe/test_reduction.py
--- a/modin/pandas/test/dataframe/test_reduction.py
+++ b/modin/pandas/test/dataframe/test_reduction.py
@@ -363,8 +363,6 @@ def test_sum_single_column(data):
     "numeric_only", bool_arg_values, ids=arg_keys("numeric_only", bool_arg_keys)
 )
 def test_reduction_specific(fn, numeric_only, axis):
-    if fn == "mean" and axis == 1:
-        pytest.skip("See issue #2313 for details")
     eval_general(
         *create_test_dfs(test_data_diff_dtype),
         lambda df: getattr(df, fn)(numeric_only=numeric_only, axis=axis),