Skip to content

Commit d59aad6

Browse files
No-Streamjorisvandenbossche
authored andcommitted
ENH: groupby().is_monotonic_increasing #17015 (#17453)
1 parent db55f47 commit d59aad6

File tree

5 files changed

+71
-4
lines changed

5 files changed

+71
-4
lines changed

doc/source/api.rst

+2
Original file line numberDiff line numberDiff line change
@@ -2240,6 +2240,8 @@ The following methods are available only for ``SeriesGroupBy`` objects.
22402240
SeriesGroupBy.nunique
22412241
SeriesGroupBy.unique
22422242
SeriesGroupBy.value_counts
2243+
SeriesGroupBy.is_monotonic_increasing
2244+
SeriesGroupBy.is_monotonic_decreasing
22432245

22442246
The following methods are available only for ``DataFrameGroupBy`` objects.
22452247

doc/source/whatsnew/v0.23.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ Other Enhancements
323323

324324
- ``IntervalIndex.astype`` now supports conversions between subtypes when passed an ``IntervalDtype`` (:issue:`19197`)
325325
- :class:`IntervalIndex` and its associated constructor methods (``from_arrays``, ``from_breaks``, ``from_tuples``) have gained a ``dtype`` parameter (:issue:`19262`)
326+
- Added :func:`SeriesGroupBy.is_monotonic_increasing` and :func:`SeriesGroupBy.is_monotonic_decreasing` (:issue:`17015`)
326327

327328
.. _whatsnew_0230.api_breaking:
328329

pandas/core/groupby.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,9 @@
336336
]) | _plotting_methods
337337

338338
_series_apply_whitelist = ((_common_apply_whitelist |
339-
{'nlargest', 'nsmallest'}) -
339+
{'nlargest', 'nsmallest',
340+
'is_monotonic_increasing',
341+
'is_monotonic_decreasing'}) -
340342
{'boxplot'}) | frozenset(['dtype', 'unique'])
341343

342344
_dataframe_apply_whitelist = ((_common_apply_whitelist |

pandas/tests/groupby/test_groupby.py

+60-1
Original file line numberDiff line numberDiff line change
@@ -2639,7 +2639,7 @@ def test_group_shift_with_null_key(self):
26392639
# Generate a moderately large dataframe with occasional missing
26402640
# values in column `B`, and then group by [`A`, `B`]. This should
26412641
# force `-1` in `labels` array of `g.grouper.group_info` exactly
2642-
# at those places, where the group-by key is partilly missing.
2642+
# at those places, where the group-by key is partially missing.
26432643
df = DataFrame([(i % 12, i % 3 if i % 3 else np.nan, i)
26442644
for i in range(n_rows)], dtype=float,
26452645
columns=["A", "B", "Z"], index=None)
@@ -2764,6 +2764,65 @@ def test_cummin_cummax(self):
27642764
expected = pd.Series([1, 2, 1], name='b')
27652765
tm.assert_series_equal(result, expected)
27662766

2767+
@pytest.mark.parametrize('in_vals, out_vals', [
2768+
2769+
# Basics: strictly increasing (T), strictly decreasing (F),
2770+
# abs val increasing (F), non-strictly increasing (T)
2771+
([1, 2, 5, 3, 2, 0, 4, 5, -6, 1, 1],
2772+
[True, False, False, True]),
2773+
2774+
# Test with inf vals
2775+
([1, 2.1, np.inf, 3, 2, np.inf, -np.inf, 5, 11, 1, -np.inf],
2776+
[True, False, True, False]),
2777+
2778+
# Test with nan vals; should always be False
2779+
([1, 2, np.nan, 3, 2, np.nan, np.nan, 5, -np.inf, 1, np.nan],
2780+
[False, False, False, False]),
2781+
])
2782+
def test_is_monotonic_increasing(self, in_vals, out_vals):
2783+
# GH 17015
2784+
source_dict = {
2785+
'A': ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'],
2786+
'B': ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd'],
2787+
'C': in_vals}
2788+
df = pd.DataFrame(source_dict)
2789+
result = df.groupby('B').C.is_monotonic_increasing
2790+
index = Index(list('abcd'), name='B')
2791+
expected = pd.Series(index=index, data=out_vals, name='C')
2792+
tm.assert_series_equal(result, expected)
2793+
2794+
# Also check result equal to manually taking x.is_monotonic_increasing.
2795+
expected = (
2796+
df.groupby(['B']).C.apply(lambda x: x.is_monotonic_increasing))
2797+
tm.assert_series_equal(result, expected)
2798+
2799+
@pytest.mark.parametrize('in_vals, out_vals', [
2800+
# Basics: strictly decreasing (T), strictly increasing (F),
2801+
# abs val decreasing (F), non-strictly increasing (T)
2802+
([10, 9, 7, 3, 4, 5, -3, 2, 0, 1, 1],
2803+
[True, False, False, True]),
2804+
2805+
# Test with inf vals
2806+
([np.inf, 1, -np.inf, np.inf, 2, -3, -np.inf, 5, -3, -np.inf, -np.inf],
2807+
[True, True, False, True]),
2808+
2809+
# Test with nan vals; should always be False
2810+
([1, 2, np.nan, 3, 2, np.nan, np.nan, 5, -np.inf, 1, np.nan],
2811+
[False, False, False, False]),
2812+
])
2813+
def test_is_monotonic_decreasing(self, in_vals, out_vals):
2814+
# GH 17015
2815+
source_dict = {
2816+
'A': ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'],
2817+
'B': ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd'],
2818+
'C': in_vals}
2819+
2820+
df = pd.DataFrame(source_dict)
2821+
result = df.groupby('B').C.is_monotonic_decreasing
2822+
index = Index(list('abcd'), name='B')
2823+
expected = pd.Series(index=index, data=out_vals, name='C')
2824+
tm.assert_series_equal(result, expected)
2825+
27672826
def test_apply_numeric_coercion_when_datetime(self):
27682827
# In the past, group-by/apply operations have been over-eager
27692828
# in converting dtypes to numeric, in the presence of datetime

pandas/tests/groupby/test_whitelist.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@
8888
'unique',
8989
'nlargest',
9090
'nsmallest',
91+
'is_monotonic_increasing',
92+
'is_monotonic_decreasing',
9193
])
9294

9395

@@ -184,7 +186,7 @@ def test_regression_whitelist_methods(
184186
axis, skipna, sort):
185187
# GH6944
186188
# GH 17537
187-
# explicitly test the whitelest methods
189+
# explicitly test the whitelist methods
188190

189191
if axis == 0:
190192
frame = raw_frame
@@ -249,7 +251,8 @@ def test_tab_completion(mframe):
249251
'cumsum', 'cumcount', 'ngroup', 'all', 'shift', 'skew',
250252
'take', 'tshift', 'pct_change', 'any', 'mad', 'corr', 'corrwith',
251253
'cov', 'dtypes', 'ndim', 'diff', 'idxmax', 'idxmin',
252-
'ffill', 'bfill', 'pad', 'backfill', 'rolling', 'expanding', 'pipe'}
254+
'ffill', 'bfill', 'pad', 'backfill', 'rolling', 'expanding', 'pipe',
255+
}
253256
assert results == expected
254257

255258

0 commit comments

Comments
 (0)