Skip to content

Commit cac5f8b

Browse files
committed
BUG: addtl fix for compat summary of groupby/resample with dicts
closes #9052 closes #12332 Author: Jeff Reback <[email protected]> Closes #12329 from jreback/groupby_compat and squashes the following commits: 0d8561e [Jeff Reback] BUG: resampling with a how could trigger asfreq instead 66c23aa [Jeff Reback] BUG: addtl fix for compat summary of groupby/resample with dicts
1 parent b358876 commit cac5f8b

File tree

5 files changed

+55
-23
lines changed

5 files changed

+55
-23
lines changed

doc/source/whatsnew/v0.18.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ other anchored offsets like ``MonthBegin`` and ``YearBegin``.
581581
Resample API
582582
^^^^^^^^^^^^
583583

584-
Like the change in the window functions API :ref:`above <whatsnew_0180.enhancements.moments>`, ``.resample(...)`` is changing to have a more groupby-like API. (:issue:`11732`, :issue:`12702`, :issue:`12202`).
584+
Like the change in the window functions API :ref:`above <whatsnew_0180.enhancements.moments>`, ``.resample(...)`` is changing to have a more groupby-like API. (:issue:`11732`, :issue:`12702`, :issue:`12202`, :issue:`12332`).
585585

586586
.. ipython:: python
587587

pandas/core/groupby.py

+14-9
Original file line numberDiff line numberDiff line change
@@ -2526,7 +2526,8 @@ def aggregate(self, func_or_funcs, *args, **kwargs):
25262526
return getattr(self, func_or_funcs)(*args, **kwargs)
25272527

25282528
if hasattr(func_or_funcs, '__iter__'):
2529-
ret = self._aggregate_multiple_funcs(func_or_funcs, _level)
2529+
ret = self._aggregate_multiple_funcs(func_or_funcs,
2530+
(_level or 0) + 1)
25302531
else:
25312532
cyfunc = self._is_cython_func(func_or_funcs)
25322533
if cyfunc and not args and not kwargs:
@@ -2546,6 +2547,18 @@ def aggregate(self, func_or_funcs, *args, **kwargs):
25462547
if not self.as_index: # pragma: no cover
25472548
print('Warning, ignoring as_index=True')
25482549

2550+
# _level handled at higher
2551+
if not _level and isinstance(ret, dict):
2552+
from pandas import concat
2553+
2554+
# our result is a Series-like
2555+
if len(ret) == 1:
2556+
ret = concat([r for r in ret.values()],
2557+
axis=1)
2558+
2559+
# our result is a DataFrame like
2560+
else:
2561+
ret = concat(ret, axis=1)
25492562
return ret
25502563

25512564
agg = aggregate
@@ -2571,14 +2584,6 @@ def _aggregate_multiple_funcs(self, arg, _level):
25712584
columns.append(com._get_callable_name(f))
25722585
arg = lzip(columns, arg)
25732586

2574-
# for a ndim=1, disallow a nested dict for an aggregator as
2575-
# this is a mis-specification of the aggregations, via a
2576-
# specificiation error
2577-
# e.g. g['A'].agg({'A': ..., 'B': ...})
2578-
if self.name in columns and len(columns) > 1:
2579-
raise SpecificationError('invalid aggregation names specified '
2580-
'for selected objects')
2581-
25822587
results = {}
25832588
for name, func in arg:
25842589
obj = self

pandas/tests/test_groupby.py

+7
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,13 @@ def f():
15581558
'ra', 'std'), ('rb', 'mean'), ('rb', 'std')])
15591559
assert_frame_equal(result, expected, check_like=True)
15601560

1561+
# same name as the original column
1562+
# GH9052
1563+
expected = g['D'].agg({'result1': np.sum, 'result2': np.mean})
1564+
expected = expected.rename(columns={'result1': 'D'})
1565+
result = g['D'].agg({'D': np.sum, 'result2': np.mean})
1566+
assert_frame_equal(result, expected, check_like=True)
1567+
15611568
def test_multi_iter(self):
15621569
s = Series(np.arange(6))
15631570
k1 = np.array(['a', 'a', 'a', 'b', 'b', 'b'])

pandas/tseries/resample.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ def _downsample(self, how, **kwargs):
500500
# do we have a regular frequency
501501
if ax.freq is not None or ax.inferred_freq is not None:
502502

503-
if len(self.grouper.binlabels) > len(ax):
503+
if len(self.grouper.binlabels) > len(ax) and how is None:
504504

505505
# let's do an asfreq
506506
return self.asfreq()

pandas/tseries/tests/test_resample.py

+32-12
Original file line numberDiff line numberDiff line change
@@ -419,25 +419,32 @@ def test_agg_misc(self):
419419
assert_frame_equal(result, expected, check_like=True)
420420

421421
# series like aggs
422-
expected = pd.concat([t['A'].sum(),
423-
t['A'].std()],
424-
axis=1)
425-
expected.columns = ['sum', 'std']
426-
427422
for t in [r, g]:
428-
result = r['A'].agg({'A': ['sum', 'std']})
423+
result = t['A'].agg({'A': ['sum', 'std']})
424+
expected = pd.concat([t['A'].sum(),
425+
t['A'].std()],
426+
axis=1)
427+
expected.columns = ['sum', 'std']
428+
429+
assert_frame_equal(result, expected, check_like=True)
430+
431+
expected = pd.concat([t['A'].agg(['sum', 'std']),
432+
t['A'].agg(['mean', 'std'])],
433+
axis=1)
434+
expected.columns = pd.MultiIndex.from_tuples([('A', 'sum'),
435+
('A', 'std'),
436+
('B', 'mean'),
437+
('B', 'std')])
438+
result = t['A'].agg({'A': ['sum', 'std'], 'B': ['mean', 'std']})
429439
assert_frame_equal(result, expected, check_like=True)
430440

431441
# errors
442+
# invalid names in the agg specification
432443
for t in [r, g]:
433444

434-
# invalid names in the agg specification
435445
def f():
436-
r['A'].agg({'A': ['sum', 'std'], 'B': ['mean', 'std']})
437-
self.assertRaises(SpecificationError, f)
438-
439-
def f():
440-
r[['A']].agg({'A': ['sum', 'std'], 'B': ['mean', 'std']})
446+
r[['A']].agg({'A': ['sum', 'std'],
447+
'B': ['mean', 'std']})
441448
self.assertRaises(SpecificationError, f)
442449

443450
def test_agg_nested_dicts(self):
@@ -918,6 +925,19 @@ def test_resample_ohlc(self):
918925
self.assertEqual(xs['low'], s[:5].min())
919926
self.assertEqual(xs['close'], s[4])
920927

928+
def test_resample_ohlc_result(self):
929+
930+
# GH 12332
931+
index = pd.date_range('1-1-2000', '2-15-2000', freq='h')
932+
index = index.union(pd.date_range('4-15-2000', '5-15-2000', freq='h'))
933+
s = Series(range(len(index)), index=index)
934+
935+
a = s.loc[:'4-15-2000'].resample('30T').ohlc()
936+
self.assertIsInstance(a, DataFrame)
937+
938+
b = s.loc[:'4-14-2000'].resample('30T').ohlc()
939+
self.assertIsInstance(b, DataFrame)
940+
921941
def test_resample_ohlc_dataframe(self):
922942
df = (
923943
pd.DataFrame({

0 commit comments

Comments
 (0)