Skip to content

Commit 7017599

Browse files
authored
BUG: groupby nunique changing values (#32175)
1 parent 20a84a5 commit 7017599

File tree

3 files changed

+9
-18
lines changed

3 files changed

+9
-18
lines changed

doc/source/whatsnew/v1.0.2.rst

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Fixed regressions
1919
- Fixed regression in :meth:`Series.align` when ``other`` is a DataFrame and ``method`` is not None (:issue:`31785`)
2020
- Fixed regression in :meth:`pandas.core.groupby.RollingGroupby.apply` where the ``raw`` parameter was ignored (:issue:`31754`)
2121
- Fixed regression in :meth:`rolling(..).corr() <pandas.core.window.Rolling.corr>` when using a time offset (:issue:`31789`)
22+
- Fixed regression in :meth:`DataFrameGroupBy.nunique` which was modifying the original values if ``NaN`` values were present (:issue:`31950`)
2223
- Fixed regression where :func:`read_pickle` raised a ``UnicodeDecodeError`` when reading a py27 pickle with :class:`MultiIndex` column (:issue:`31988`).
2324
- Fixed regression in :class:`DataFrame` arithmetic operations with mis-matched columns (:issue:`31623`)
2425
- Fixed regression in :meth:`GroupBy.agg` calling a user-provided function an extra time on an empty input (:issue:`31760`)

pandas/core/groupby/generic.py

+6-18
Original file line numberDiff line numberDiff line change
@@ -591,30 +591,18 @@ def nunique(self, dropna: bool = True) -> Series:
591591

592592
val = self.obj._internal_get_values()
593593

594-
# GH 27951
595-
# temporary fix while we wait for NumPy bug 12629 to be fixed
596-
val[isna(val)] = np.datetime64("NaT")
597-
598-
try:
599-
sorter = np.lexsort((val, ids))
600-
except TypeError: # catches object dtypes
601-
msg = f"val.dtype must be object, got {val.dtype}"
602-
assert val.dtype == object, msg
603-
val, _ = algorithms.factorize(val, sort=False)
604-
sorter = np.lexsort((val, ids))
605-
_isna = lambda a: a == -1
606-
else:
607-
_isna = isna
608-
609-
ids, val = ids[sorter], val[sorter]
594+
codes, _ = algorithms.factorize(val, sort=False)
595+
sorter = np.lexsort((codes, ids))
596+
codes = codes[sorter]
597+
ids = ids[sorter]
610598

611599
# group boundaries are where group ids change
612600
# unique observations are where sorted values change
613601
idx = np.r_[0, 1 + np.nonzero(ids[1:] != ids[:-1])[0]]
614-
inc = np.r_[1, val[1:] != val[:-1]]
602+
inc = np.r_[1, codes[1:] != codes[:-1]]
615603

616604
# 1st item of each group is a new unique observation
617-
mask = _isna(val)
605+
mask = codes == -1
618606
if dropna:
619607
inc[idx] = 1
620608
inc[mask] = 0

pandas/tests/groupby/test_function.py

+2
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,7 @@ def test_frame_describe_unstacked_format():
10171017
@pytest.mark.parametrize("dropna", [False, True])
10181018
def test_series_groupby_nunique(n, m, sort, dropna):
10191019
def check_nunique(df, keys, as_index=True):
1020+
original_df = df.copy()
10201021
gr = df.groupby(keys, as_index=as_index, sort=sort)
10211022
left = gr["julie"].nunique(dropna=dropna)
10221023

@@ -1026,6 +1027,7 @@ def check_nunique(df, keys, as_index=True):
10261027
right = right.reset_index(drop=True)
10271028

10281029
tm.assert_series_equal(left, right, check_names=False)
1030+
tm.assert_frame_equal(df, original_df)
10291031

10301032
days = date_range("2015-08-23", periods=10)
10311033

0 commit comments

Comments
 (0)