diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index 20496c9fb3f31c..fca9ff80fd5d99 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -1335,6 +1335,7 @@ Groupby/Resample/Rolling - Bug in :meth:`DataFrame.resample` and :meth:`Series.resample` when resampling by a weekly offset (``'W'``) across a DST transition (:issue:`9119`, :issue:`21459`) - Bug in :meth:`DataFrame.expanding` in which the ``axis`` argument was not being respected during aggregations (:issue:`23372`) - Bug in :meth:`pandas.core.groupby.DataFrameGroupBy.transform` which caused missing values when the input function can accept a :class:`DataFrame` but renames it (:issue:`23455`). +- Bug in :meth:`pandas.core.groupby.DataFrameGroupBy.nunique` in which the names of column levels were lost (:issue:`23222`). Reshaping ^^^^^^^^^ diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index b0477c7d3a8ad9..10a02d8c960a10 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -1568,6 +1568,7 @@ def groupby_series(obj, col=None): from pandas.core.reshape.concat import concat results = [groupby_series(obj[col], col) for col in obj.columns] results = concat(results, axis=1) + results.columns.names = obj.columns.names if not self.as_index: results.index = ibase.default_index(len(results)) diff --git a/pandas/tests/groupby/test_function.py b/pandas/tests/groupby/test_function.py index 646445623778b7..a2a2c0d7302d28 100644 --- a/pandas/tests/groupby/test_function.py +++ b/pandas/tests/groupby/test_function.py @@ -892,6 +892,14 @@ def test_nunique_with_timegrouper(): tm.assert_series_equal(result, expected) +def test_nunique_preserves_column_level_names(): + # GH 23222 + test = pd.DataFrame([1, 2, 3], + columns=pd.Index(['A'], name="level_0")) + result = test.groupby([0, 0, 0]).nunique() + tm.assert_index_equal(test.columns, result.columns) + + # count # -------------------------------- diff --git a/pandas/tests/test_resample.py b/pandas/tests/test_resample.py index 7e0342e8b987a5..c295cfec91218b 100644 --- a/pandas/tests/test_resample.py +++ b/pandas/tests/test_resample.py @@ -1947,6 +1947,14 @@ def test_resample_nunique(self): result = df.ID.groupby(pd.Grouper(freq='D')).nunique() assert_series_equal(result, expected) + def test_resample_nunique_preserves_column_level_names(self): + # GH 23222 + df = tm.makeTimeDataFrame(freq='1D').abs() + df.columns = pd.MultiIndex.from_arrays([df.columns.tolist()] * 2, + names=["lev0", "lev1"]) + result = df.resample("1h").nunique() + tm.assert_index_equal(df.columns, result.columns) + def test_resample_nunique_with_date_gap(self): # GH 13453 index = pd.date_range('1-1-2000', '2-15-2000', freq='h')