Skip to content

Commit 54bd76c

Browse files
rhshadrachyehoshuadimarsky
authored andcommitted
DEPR: mad (pandas-dev#46707)
1 parent 3d8be2c commit 54bd76c

File tree

11 files changed

+147
-55
lines changed

11 files changed

+147
-55
lines changed

doc/source/whatsnew/v1.5.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ Other Deprecations
430430
- Deprecated :attr:`Timedelta.freq` and :attr:`Timedelta.is_populated` (:issue:`46430`)
431431
- Deprecated :attr:`Timedelta.delta` (:issue:`46476`)
432432
- Deprecated the ``closed`` argument in :meth:`interval_range` in favor of ``inclusive`` argument; In a future version passing ``closed`` will raise (:issue:`40245`)
433-
-
433+
- Deprecated the methods :meth:`DataFrame.mad`, :meth:`Series.mad`, and the corresponding groupby methods (:issue:`11787`)
434434

435435
.. ---------------------------------------------------------------------------
436436
.. _whatsnew_150.performance:

pandas/core/generic.py

+9
Original file line numberDiff line numberDiff line change
@@ -10881,6 +10881,9 @@ def mad(
1088110881
"""
1088210882
{desc}
1088310883
10884+
.. deprecated:: 1.5.0
10885+
mad is deprecated.
10886+
1088410887
Parameters
1088510888
----------
1088610889
axis : {axis_descr}
@@ -10897,6 +10900,12 @@ def mad(
1089710900
{see_also}\
1089810901
{examples}
1089910902
"""
10903+
msg = (
10904+
"The 'mad' method is deprecated and will be removed in a future version. "
10905+
"To compute the same result, you may do `(df - df.mean()).abs().mean()`."
10906+
)
10907+
warnings.warn(msg, FutureWarning, stacklevel=find_stack_level())
10908+
1090010909
if not is_bool(skipna):
1090110910
warnings.warn(
1090210911
"Passing None for skipna is deprecated and will raise in a future"

pandas/tests/frame/test_reductions.py

+53-25
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ def assert_stat_op_calc(
6767
skipna_alternative : function, default None
6868
NaN-safe version of alternative
6969
"""
70+
warn = FutureWarning if opname == "mad" else None
7071
f = getattr(frame, opname)
7172

7273
if check_dates:
@@ -88,8 +89,9 @@ def wrapper(x):
8889
return alternative(x.values)
8990

9091
skipna_wrapper = tm._make_skipna_wrapper(alternative, skipna_alternative)
91-
result0 = f(axis=0, skipna=False)
92-
result1 = f(axis=1, skipna=False)
92+
with tm.assert_produces_warning(warn, match="The 'mad' method is deprecated"):
93+
result0 = f(axis=0, skipna=False)
94+
result1 = f(axis=1, skipna=False)
9395
tm.assert_series_equal(
9496
result0, frame.apply(wrapper), check_dtype=check_dtype, rtol=rtol, atol=atol
9597
)
@@ -102,8 +104,9 @@ def wrapper(x):
102104
else:
103105
skipna_wrapper = alternative
104106

105-
result0 = f(axis=0)
106-
result1 = f(axis=1)
107+
with tm.assert_produces_warning(warn, match="The 'mad' method is deprecated"):
108+
result0 = f(axis=0)
109+
result1 = f(axis=1)
107110
tm.assert_series_equal(
108111
result0,
109112
frame.apply(skipna_wrapper),
@@ -125,14 +128,18 @@ def wrapper(x):
125128
assert lcd_dtype == result1.dtype
126129

127130
# bad axis
128-
with pytest.raises(ValueError, match="No axis named 2"):
129-
f(axis=2)
131+
with tm.assert_produces_warning(warn, match="The 'mad' method is deprecated"):
132+
with pytest.raises(ValueError, match="No axis named 2"):
133+
f(axis=2)
130134

131135
# all NA case
132136
if has_skipna:
133137
all_na = frame * np.NaN
134-
r0 = getattr(all_na, opname)(axis=0)
135-
r1 = getattr(all_na, opname)(axis=1)
138+
with tm.assert_produces_warning(
139+
warn, match="The 'mad' method is deprecated", raise_on_extra_warnings=False
140+
):
141+
r0 = getattr(all_na, opname)(axis=0)
142+
r1 = getattr(all_na, opname)(axis=1)
136143
if opname in ["sum", "prod"]:
137144
unit = 1 if opname == "prod" else 0 # result for empty sum/prod
138145
expected = Series(unit, index=r0.index, dtype=r0.dtype)
@@ -167,9 +174,13 @@ class TestDataFrameAnalytics:
167174
],
168175
)
169176
def test_stat_op_api_float_string_frame(self, float_string_frame, axis, opname):
170-
getattr(float_string_frame, opname)(axis=axis)
171-
if opname not in ("nunique", "mad"):
172-
getattr(float_string_frame, opname)(axis=axis, numeric_only=True)
177+
warn = FutureWarning if opname == "mad" else None
178+
with tm.assert_produces_warning(
179+
warn, match="The 'mad' method is deprecated", raise_on_extra_warnings=False
180+
):
181+
getattr(float_string_frame, opname)(axis=axis)
182+
if opname not in ("nunique", "mad"):
183+
getattr(float_string_frame, opname)(axis=axis, numeric_only=True)
173184

174185
@pytest.mark.filterwarnings("ignore:Dropping of nuisance:FutureWarning")
175186
@pytest.mark.parametrize("axis", [0, 1])
@@ -1424,7 +1435,9 @@ def test_frame_any_with_timedelta(self):
14241435
def test_reductions_deprecation_skipna_none(self, frame_or_series):
14251436
# GH#44580
14261437
obj = frame_or_series([1, 2, 3])
1427-
with tm.assert_produces_warning(FutureWarning, match="skipna"):
1438+
with tm.assert_produces_warning(
1439+
FutureWarning, match="skipna", raise_on_extra_warnings=False
1440+
):
14281441
obj.mad(skipna=None)
14291442

14301443
def test_reductions_deprecation_level_argument(
@@ -1445,7 +1458,7 @@ def test_reductions_skipna_none_raises(
14451458
pytest.mark.xfail(reason="Count does not accept skipna")
14461459
)
14471460
elif reduction_functions == "mad":
1448-
pytest.skip("Mad needs a deprecation cycle: GH 11787")
1461+
pytest.skip("Mad is deprecated: GH#11787")
14491462
obj = frame_or_series([1, 2, 3])
14501463
msg = 'For argument "skipna" expected type bool, received type NoneType.'
14511464
with pytest.raises(ValueError, match=msg):
@@ -1644,25 +1657,37 @@ def test_mad_nullable_integer(any_signed_int_ea_dtype):
16441657
df = DataFrame(np.random.randn(100, 4).astype(np.int64))
16451658
df2 = df.astype(any_signed_int_ea_dtype)
16461659

1647-
result = df2.mad()
1648-
expected = df.mad()
1660+
with tm.assert_produces_warning(
1661+
FutureWarning, match="The 'mad' method is deprecated"
1662+
):
1663+
result = df2.mad()
1664+
expected = df.mad()
16491665
tm.assert_series_equal(result, expected)
16501666

1651-
result = df2.mad(axis=1)
1652-
expected = df.mad(axis=1)
1667+
with tm.assert_produces_warning(
1668+
FutureWarning, match="The 'mad' method is deprecated"
1669+
):
1670+
result = df2.mad(axis=1)
1671+
expected = df.mad(axis=1)
16531672
tm.assert_series_equal(result, expected)
16541673

16551674
# case with NAs present
16561675
df2.iloc[::2, 1] = pd.NA
16571676

1658-
result = df2.mad()
1659-
expected = df.mad()
1660-
expected[1] = df.iloc[1::2, 1].mad()
1677+
with tm.assert_produces_warning(
1678+
FutureWarning, match="The 'mad' method is deprecated"
1679+
):
1680+
result = df2.mad()
1681+
expected = df.mad()
1682+
expected[1] = df.iloc[1::2, 1].mad()
16611683
tm.assert_series_equal(result, expected)
16621684

1663-
result = df2.mad(axis=1)
1664-
expected = df.mad(axis=1)
1665-
expected[::2] = df.T.loc[[0, 2, 3], ::2].mad()
1685+
with tm.assert_produces_warning(
1686+
FutureWarning, match="The 'mad' method is deprecated"
1687+
):
1688+
result = df2.mad(axis=1)
1689+
expected = df.mad(axis=1)
1690+
expected[::2] = df.T.loc[[0, 2, 3], ::2].mad()
16661691
tm.assert_series_equal(result, expected)
16671692

16681693

@@ -1675,8 +1700,11 @@ def test_mad_nullable_integer_all_na(any_signed_int_ea_dtype):
16751700
# case with all-NA row/column
16761701
df2.iloc[:, 1] = pd.NA # FIXME(GH#44199): this doesn't operate in-place
16771702
df2.iloc[:, 1] = pd.array([pd.NA] * len(df2), dtype=any_signed_int_ea_dtype)
1678-
result = df2.mad()
1679-
expected = df.mad()
1703+
with tm.assert_produces_warning(
1704+
FutureWarning, match="The 'mad' method is deprecated"
1705+
):
1706+
result = df2.mad()
1707+
expected = df.mad()
16801708
expected[1] = pd.NA
16811709
expected = expected.astype("Float64")
16821710
tm.assert_series_equal(result, expected)

pandas/tests/groupby/aggregate/test_aggregate.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1325,11 +1325,13 @@ def test_groupby_aggregate_directory(reduction_func):
13251325
# GH#32793
13261326
if reduction_func in ["corrwith", "nth"]:
13271327
return None
1328+
warn = FutureWarning if reduction_func == "mad" else None
13281329

13291330
obj = DataFrame([[0, 1], [0, np.nan]])
13301331

1331-
result_reduced_series = obj.groupby(0).agg(reduction_func)
1332-
result_reduced_frame = obj.groupby(0).agg({1: reduction_func})
1332+
with tm.assert_produces_warning(warn, match="The 'mad' method is deprecated"):
1333+
result_reduced_series = obj.groupby(0).agg(reduction_func)
1334+
result_reduced_frame = obj.groupby(0).agg({1: reduction_func})
13331335

13341336
if reduction_func in ["size", "ngroup"]:
13351337
# names are different: None / 1

pandas/tests/groupby/test_allowlist.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ def test_regression_allowlist_methods(raw_frame, op, level, axis, skipna, sort):
178178
# GH6944
179179
# GH 17537
180180
# explicitly test the allowlist methods
181+
warn = FutureWarning if op == "mad" else None
181182

182183
if axis == 0:
183184
frame = raw_frame
@@ -186,7 +187,8 @@ def test_regression_allowlist_methods(raw_frame, op, level, axis, skipna, sort):
186187

187188
if op in AGG_FUNCTIONS_WITH_SKIPNA:
188189
grouped = frame.groupby(level=level, axis=axis, sort=sort)
189-
result = getattr(grouped, op)(skipna=skipna)
190+
with tm.assert_produces_warning(warn, match="The 'mad' method is deprecated"):
191+
result = getattr(grouped, op)(skipna=skipna)
190192
with tm.assert_produces_warning(FutureWarning):
191193
expected = getattr(frame, op)(level=level, axis=axis, skipna=skipna)
192194
if sort:

pandas/tests/groupby/test_apply.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,8 @@ def test_apply_with_timezones_aware():
10471047
def test_apply_is_unchanged_when_other_methods_are_called_first(reduction_func):
10481048
# GH #34656
10491049
# GH #34271
1050+
warn = FutureWarning if reduction_func == "mad" else None
1051+
10501052
df = DataFrame(
10511053
{
10521054
"a": [99, 99, 99, 88, 88, 88],
@@ -1068,7 +1070,8 @@ def test_apply_is_unchanged_when_other_methods_are_called_first(reduction_func):
10681070
# Check output when another method is called before .apply()
10691071
grp = df.groupby(by="a")
10701072
args = {"nth": [0], "corrwith": [df]}.get(reduction_func, [])
1071-
_ = getattr(grp, reduction_func)(*args)
1073+
with tm.assert_produces_warning(warn, match="The 'mad' method is deprecated"):
1074+
_ = getattr(grp, reduction_func)(*args)
10721075
result = grp.apply(sum)
10731076
tm.assert_frame_equal(result, expected)
10741077

pandas/tests/groupby/test_categorical.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,7 @@ def test_series_groupby_on_2_categoricals_unobserved(reduction_func, observed, r
13571357
reason="TODO: implemented SeriesGroupBy.corrwith. See GH 32293"
13581358
)
13591359
request.node.add_marker(mark)
1360+
warn = FutureWarning if reduction_func == "mad" else None
13601361

13611362
df = DataFrame(
13621363
{
@@ -1371,7 +1372,8 @@ def test_series_groupby_on_2_categoricals_unobserved(reduction_func, observed, r
13711372

13721373
series_groupby = df.groupby(["cat_1", "cat_2"], observed=observed)["value"]
13731374
agg = getattr(series_groupby, reduction_func)
1374-
result = agg(*args)
1375+
with tm.assert_produces_warning(warn, match="The 'mad' method is deprecated"):
1376+
result = agg(*args)
13751377

13761378
assert len(result) == expected_length
13771379

@@ -1390,6 +1392,7 @@ def test_series_groupby_on_2_categoricals_unobserved_zeroes_or_nans(
13901392
reason="TODO: implemented SeriesGroupBy.corrwith. See GH 32293"
13911393
)
13921394
request.node.add_marker(mark)
1395+
warn = FutureWarning if reduction_func == "mad" else None
13931396

13941397
df = DataFrame(
13951398
{
@@ -1403,7 +1406,8 @@ def test_series_groupby_on_2_categoricals_unobserved_zeroes_or_nans(
14031406

14041407
series_groupby = df.groupby(["cat_1", "cat_2"], observed=False)["value"]
14051408
agg = getattr(series_groupby, reduction_func)
1406-
result = agg(*args)
1409+
with tm.assert_produces_warning(warn, match="The 'mad' method is deprecated"):
1410+
result = agg(*args)
14071411

14081412
zero_or_nan = _results_for_groupbys_with_missing_categories[reduction_func]
14091413

@@ -1426,6 +1430,7 @@ def test_dataframe_groupby_on_2_categoricals_when_observed_is_true(reduction_fun
14261430
# does not return the categories that are not in df when observed=True
14271431
if reduction_func == "ngroup":
14281432
pytest.skip("ngroup does not return the Categories on the index")
1433+
warn = FutureWarning if reduction_func == "mad" else None
14291434

14301435
df = DataFrame(
14311436
{
@@ -1439,7 +1444,8 @@ def test_dataframe_groupby_on_2_categoricals_when_observed_is_true(reduction_fun
14391444
df_grp = df.groupby(["cat_1", "cat_2"], observed=True)
14401445

14411446
args = {"nth": [0], "corrwith": [df]}.get(reduction_func, [])
1442-
res = getattr(df_grp, reduction_func)(*args)
1447+
with tm.assert_produces_warning(warn, match="The 'mad' method is deprecated"):
1448+
res = getattr(df_grp, reduction_func)(*args)
14431449

14441450
for cat in unobserved_cats:
14451451
assert cat not in res.index
@@ -1456,6 +1462,7 @@ def test_dataframe_groupby_on_2_categoricals_when_observed_is_false(
14561462

14571463
if reduction_func == "ngroup":
14581464
pytest.skip("ngroup does not return the Categories on the index")
1465+
warn = FutureWarning if reduction_func == "mad" else None
14591466

14601467
df = DataFrame(
14611468
{
@@ -1469,7 +1476,8 @@ def test_dataframe_groupby_on_2_categoricals_when_observed_is_false(
14691476
df_grp = df.groupby(["cat_1", "cat_2"], observed=observed)
14701477

14711478
args = {"nth": [0], "corrwith": [df]}.get(reduction_func, [])
1472-
res = getattr(df_grp, reduction_func)(*args)
1479+
with tm.assert_produces_warning(warn, match="The 'mad' method is deprecated"):
1480+
res = getattr(df_grp, reduction_func)(*args)
14731481

14741482
expected = _results_for_groupbys_with_missing_categories[reduction_func]
14751483

pandas/tests/groupby/test_function.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -321,11 +321,17 @@ def test_mad(self, gb, gni):
321321
# mad
322322
expected = DataFrame([[0], [np.nan]], columns=["B"], index=[1, 3])
323323
expected.index.name = "A"
324-
result = gb.mad()
324+
with tm.assert_produces_warning(
325+
FutureWarning, match="The 'mad' method is deprecated"
326+
):
327+
result = gb.mad()
325328
tm.assert_frame_equal(result, expected)
326329

327330
expected = DataFrame([[1, 0.0], [3, np.nan]], columns=["A", "B"], index=[0, 1])
328-
result = gni.mad()
331+
with tm.assert_produces_warning(
332+
FutureWarning, match="The 'mad' method is deprecated"
333+
):
334+
result = gni.mad()
329335
tm.assert_frame_equal(result, expected)
330336

331337
def test_describe(self, df, gb, gni):

0 commit comments

Comments
 (0)