diff --git a/doc/source/api.rst b/doc/source/api.rst index 551b3eff10fa0..77d37ec2a7b2e 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -2352,6 +2352,7 @@ Computations / Descriptive Stats Resampler.std Resampler.sum Resampler.var + Resampler.quantile Style ----- diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index 9e1d2487b00fe..a3d4a95a7d01a 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -183,6 +183,7 @@ Other Enhancements - :class:`Series` and :class:`DataFrame` now support :class:`Iterable` in constructor (:issue:`2193`) - :class:`DatetimeIndex` gained :attr:`DatetimeIndex.timetz` attribute. Returns local time with timezone information. (:issue:`21358`) - :class:`Resampler` now is iterable like :class:`GroupBy` (:issue:`15314`). +- :ref:`Series.resample` and :ref:`DataFrame.resample` have gained the :meth:`Resampler.quantile` (:issue:`15023`). .. _whatsnew_0240.api_breaking: diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index 3f84fa0f0670e..61dadd833be35 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -1168,7 +1168,12 @@ def var(self, ddof=1, *args, **kwargs): """ nv.validate_groupby_func('var', args, kwargs) if ddof == 1: - return self._cython_agg_general('var', **kwargs) + try: + return self._cython_agg_general('var', **kwargs) + except Exception: + f = lambda x: x.var(ddof=ddof, **kwargs) + with _group_selection_context(self): + return self._python_agg_general(f) else: f = lambda x: x.var(ddof=ddof, **kwargs) with _group_selection_context(self): diff --git a/pandas/core/resample.py b/pandas/core/resample.py index b9194443c3033..3d749848afdad 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -766,6 +766,24 @@ def size(self): result = pd.Series([], index=result.index, dtype='int64') return result + def quantile(self, q=0.5, **kwargs): + """ + Return value at the given quantile. + + .. versionadded:: 0.24.0 + + Parameters + ---------- + q : float or array-like, default 0.5 (50% quantile) + + See Also + -------- + Series.quantile + DataFrame.quantile + DataFrameGroupBy.quantile + """ + return self._downsample('quantile', q=q, **kwargs) + # downsample methods for method in ['sum', 'prod']: @@ -1060,7 +1078,8 @@ def _downsample(self, how, **kwargs): if is_subperiod(ax.freq, self.freq): # Downsampling - return self._groupby_and_aggregate(how, grouper=self.grouper) + return self._groupby_and_aggregate(how, grouper=self.grouper, + **kwargs) elif is_superperiod(ax.freq, self.freq): if how == 'ohlc': # GH #13083 diff --git a/pandas/tests/test_resample.py b/pandas/tests/test_resample.py index 48b37e5da62ae..b80c041a7e69f 100644 --- a/pandas/tests/test_resample.py +++ b/pandas/tests/test_resample.py @@ -41,7 +41,7 @@ # The various methods we support downsample_methods = ['min', 'max', 'first', 'last', 'sum', 'mean', 'sem', - 'median', 'prod', 'var', 'ohlc'] + 'median', 'prod', 'var', 'ohlc', 'quantile'] upsample_methods = ['count', 'size'] series_methods = ['nunique'] resample_methods = downsample_methods + upsample_methods + series_methods @@ -782,6 +782,15 @@ def test_resampler_is_iterable(self): assert rk == gk assert_series_equal(rv, gv) + def test_resample_quantile(self): + # GH 15023 + s = self.create_series() + q = 0.75 + freq = 'H' + result = s.resample(freq).quantile(q) + expected = s.resample(freq).agg(lambda x: x.quantile(q)) + tm.assert_series_equal(result, expected) + class TestDatetimeIndex(Base): _index_factory = lambda x: date_range