Skip to content

Commit ebc4ac1

Browse files
agrabosojreback
authored andcommitted
ENH: Accept list as level for groupby in non-MultiIndexed objects
closes #13901 Author: agraboso <[email protected]> Closes #13907 from agraboso/fix-13901 and squashes the following commits: fcbb724 [agraboso] ENH: Accept list as level for groupby in non-MultiIndexed objects
1 parent 14a1c80 commit ebc4ac1

File tree

3 files changed

+58
-9
lines changed

3 files changed

+58
-9
lines changed

doc/source/whatsnew/v0.19.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ Other enhancements
515515
- ``astype()`` will now accept a dict of column name to data types mapping as the ``dtype`` argument. (:issue:`12086`)
516516
- The ``pd.read_json`` and ``DataFrame.to_json`` has gained support for reading and writing json lines with ``lines`` option see :ref:`Line delimited json <io.jsonl>` (:issue:`9180`)
517517
- :func:``read_excel`` now supports the true_values and false_values keyword arguments (:issue:`13347`)
518+
- ``groupby()`` will now accept a scalar and a single-element list for specifying ``level`` on a non-``MultiIndex`` grouper. (:issue:`13907`)
518519

519520
.. _whatsnew_0190.api:
520521

pandas/core/groupby.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
is_complex_dtype,
2424
is_bool_dtype,
2525
is_scalar,
26+
is_list_like,
2627
_ensure_float64,
2728
_ensure_platform_int,
2829
_ensure_int64,
@@ -2370,12 +2371,26 @@ def _get_grouper(obj, key=None, axis=0, level=None, sort=True,
23702371
# axis of the object
23712372
if level is not None:
23722373
if not isinstance(group_axis, MultiIndex):
2374+
# allow level to be a length-one list-like object
2375+
# (e.g., level=[0])
2376+
# GH 13901
2377+
if is_list_like(level):
2378+
nlevels = len(level)
2379+
if nlevels == 1:
2380+
level = level[0]
2381+
elif nlevels == 0:
2382+
raise ValueError('No group keys passed!')
2383+
else:
2384+
raise ValueError('multiple levels only valid with '
2385+
'MultiIndex')
2386+
23732387
if isinstance(level, compat.string_types):
23742388
if obj.index.name != level:
23752389
raise ValueError('level name %s is not the name of the '
23762390
'index' % level)
2377-
elif level > 0:
2378-
raise ValueError('level > 0 only valid with MultiIndex')
2391+
elif level > 0 or level < -1:
2392+
raise ValueError('level > 0 or level < -1 only valid with '
2393+
' MultiIndex')
23792394

23802395
level = None
23812396
key = group_axis

pandas/tests/test_groupby.py

+40-7
Original file line numberDiff line numberDiff line change
@@ -2029,7 +2029,7 @@ def loop(df):
20292029

20302030
loop(frame)
20312031

2032-
def test_mulitindex_passthru(self):
2032+
def test_multiindex_passthru(self):
20332033

20342034
# GH 7997
20352035
# regression from 0.14.1
@@ -2039,6 +2039,24 @@ def test_mulitindex_passthru(self):
20392039
result = df.groupby(axis=1, level=[0, 1]).first()
20402040
assert_frame_equal(result, df)
20412041

2042+
def test_multiindex_negative_level(self):
2043+
# GH 13901
2044+
result = self.mframe.groupby(level=-1).sum()
2045+
expected = self.mframe.groupby(level='second').sum()
2046+
assert_frame_equal(result, expected)
2047+
2048+
result = self.mframe.groupby(level=-2).sum()
2049+
expected = self.mframe.groupby(level='first').sum()
2050+
assert_frame_equal(result, expected)
2051+
2052+
result = self.mframe.groupby(level=[-2, -1]).sum()
2053+
expected = self.mframe
2054+
assert_frame_equal(result, expected)
2055+
2056+
result = self.mframe.groupby(level=[-1, 'first']).sum()
2057+
expected = self.mframe.groupby(level=['second', 'first']).sum()
2058+
assert_frame_equal(result, expected)
2059+
20422060
def test_multifunc_select_col_integer_cols(self):
20432061
df = self.df
20442062
df.columns = np.arange(len(df.columns))
@@ -2566,13 +2584,28 @@ def test_groupby_level_mapper(self):
25662584
assert_frame_equal(result0, expected0)
25672585
assert_frame_equal(result1, expected1)
25682586

2569-
def test_groupby_level_0_nonmulti(self):
2570-
# #1313
2571-
a = Series([1, 2, 3, 10, 4, 5, 20, 6], Index([1, 2, 3, 1,
2572-
4, 5, 2, 6], name='foo'))
2587+
def test_groupby_level_nonmulti(self):
2588+
# GH 1313, GH 13901
2589+
s = Series([1, 2, 3, 10, 4, 5, 20, 6],
2590+
Index([1, 2, 3, 1, 4, 5, 2, 6], name='foo'))
2591+
expected = Series([11, 22, 3, 4, 5, 6],
2592+
Index(range(1, 7), name='foo'))
25732593

2574-
result = a.groupby(level=0).sum()
2575-
self.assertEqual(result.index.name, a.index.name)
2594+
result = s.groupby(level=0).sum()
2595+
self.assert_series_equal(result, expected)
2596+
result = s.groupby(level=[0]).sum()
2597+
self.assert_series_equal(result, expected)
2598+
result = s.groupby(level=-1).sum()
2599+
self.assert_series_equal(result, expected)
2600+
result = s.groupby(level=[-1]).sum()
2601+
self.assert_series_equal(result, expected)
2602+
2603+
tm.assertRaises(ValueError, s.groupby, level=1)
2604+
tm.assertRaises(ValueError, s.groupby, level=-2)
2605+
tm.assertRaises(ValueError, s.groupby, level=[])
2606+
tm.assertRaises(ValueError, s.groupby, level=[0, 0])
2607+
tm.assertRaises(ValueError, s.groupby, level=[0, 1])
2608+
tm.assertRaises(ValueError, s.groupby, level=[1])
25762609

25772610
def test_groupby_complex(self):
25782611
# GH 12902

0 commit comments

Comments
 (0)