Repository: modin-project/modin

Issue:
mad function implementation



Discussion:

Resolving patch:
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
@@ -361,6 +361,7 @@ def transpose(self, *args, **kwargs):
     prod_min_count = ReductionFunction.register(pandas.DataFrame.prod)
     mean = ReductionFunction.register(pandas.DataFrame.mean)
     quantile_for_single_value = ReductionFunction.register(pandas.DataFrame.quantile)
+    mad = ReductionFunction.register(pandas.DataFrame.mad)
 
     # END Reduction operations
 
diff --git a/modin/pandas/base.py b/modin/pandas/base.py
--- a/modin/pandas/base.py
+++ b/modin/pandas/base.py
@@ -1548,7 +1548,31 @@ def loc(self):
         return _LocIndexer(self)
 
     def mad(self, axis=None, skipna=None, level=None):
-        return self._default_to_pandas("mad", axis=axis, skipna=skipna, level=level)
+        """
+        Return the mean absolute deviation of the values for the requested axis.
+
+        Parameters
+        ----------
+            axis : {index (0), columns (1)}
+            skipna : bool, default True
+                Exclude NA/null values when computing the result.
+            level : int or level name, default None
+
+        Returns
+        -------
+            Series or DataFrame (if level specified)
+
+        """
+        axis = self._get_axis_number(axis)
+
+        if level is not None:
+            return self._handle_level_agg(
+                axis=axis, level=level, skipna=skipna, op="mad"
+            )
+
+        return self._reduce_dimension(
+            self._query_compiler.mad(axis=axis, skipna=skipna, level=level)
+        )
 
     def mask(
         self,
diff --git a/modin/pandas/groupby.py b/modin/pandas/groupby.py
--- a/modin/pandas/groupby.py
+++ b/modin/pandas/groupby.py
@@ -327,8 +327,8 @@ def aggregate(self, arg, *args, **kwargs):
     def last(self, **kwargs):
         return self._default_to_pandas(lambda df: df.last(**kwargs))
 
-    def mad(self):
-        return self._default_to_pandas(lambda df: df.mad())
+    def mad(self, **kwargs):
+        return self._default_to_pandas(lambda df: df.mad(**kwargs))
 
     def rank(self, **kwargs):
         result = self._apply_agg_function(lambda df: df.rank(**kwargs))


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
@@ -2383,10 +2383,16 @@ def test_lookup(self):
         with pytest.warns(UserWarning):
             pd.DataFrame(data).lookup([0, 1], ["col1", "col2"])
 
-    def test_mad(self):
-        data = test_data_values[0]
-        with pytest.warns(UserWarning):
-            pd.DataFrame(data).mad()
+    @pytest.mark.parametrize("data", test_data_values)
+    @pytest.mark.parametrize("axis", [None, 0, 1])
+    @pytest.mark.parametrize("skipna", [None, True, False])
+    @pytest.mark.parametrize("level", [0, -1, None])
+    def test_mad(self, level, data, axis, skipna):
+        modin_df, pandas_df = pd.DataFrame(data), pandas.DataFrame(data)
+        df_equals(
+            modin_df.mad(axis=axis, skipna=skipna, level=level),
+            pandas_df.mad(axis=axis, skipna=skipna, level=level),
+        )
 
     def test_mask(self):
         df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=["A", "B"])
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
@@ -1664,10 +1664,15 @@ def test_lt(data):
 
 
 @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
-def test_mad(data):
-    modin_series, _ = create_test_series(data)  # noqa: F841
-    with pytest.warns(UserWarning):
-        modin_series.mad()
+@pytest.mark.parametrize("axis", [None, 0])
+@pytest.mark.parametrize("skipna", [None, True, False])
+@pytest.mark.parametrize("level", [0, -1, None])
+def test_mad(level, data, axis, skipna):
+    modin_series, pandas_series = create_test_series(data)
+    df_equals(
+        modin_series.mad(axis=axis, skipna=skipna, level=level),
+        pandas_series.mad(axis=axis, skipna=skipna, level=level),
+    )
 
 
 @pytest.mark.parametrize("na_values", ["ignore", None], ids=["na_ignore", "na_none"])