Skip to content

REF: Define methods on resample non-dynamically #50913

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions pandas/core/groupby/groupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -2004,8 +2004,6 @@ def mean(
return result.__finalize__(self.obj, method="groupby")

@final
@Substitution(name="groupby")
@Appender(_common_see_also)
def median(self, numeric_only: bool = False):
"""
Compute median of groups, excluding missing values.
Expand Down Expand Up @@ -2315,8 +2313,6 @@ def _value_counts(
return result.__finalize__(self.obj, method="value_counts")

@final
@Substitution(name="groupby")
@Appender(_common_see_also)
def sem(self, ddof: int = 1, numeric_only: bool = False):
"""
Compute standard error of the mean of groups, excluding missing values.
Expand Down Expand Up @@ -2471,7 +2467,6 @@ def max(
)

@final
@Substitution(name="groupby")
def first(self, numeric_only: bool = False, min_count: int = -1):
"""
Compute the first non-null entry of each column.
Expand Down Expand Up @@ -2542,7 +2537,6 @@ def first(x: Series):
)

@final
@Substitution(name="groupby")
def last(self, numeric_only: bool = False, min_count: int = -1):
"""
Compute the last non-null entry of each column.
Expand Down Expand Up @@ -2602,8 +2596,6 @@ def last(x: Series):
)

@final
@Substitution(name="groupby")
@Appender(_common_see_also)
def ohlc(self) -> DataFrame:
"""
Compute open, high, low and close values of a group, excluding missing values.
Expand Down
178 changes: 97 additions & 81 deletions pandas/core/resample.py
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,74 @@ def asfreq(self, fill_value=None):
"""
return self._upsample("asfreq", fill_value=fill_value)

def sum(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no docstring to inherit?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For these one that were left out, I couldn't inherit since the associated groupby doc contains numba arguments that are not supported by resample.

self,
numeric_only: bool = False,
min_count: int = 0,
*args,
**kwargs,
):
nv.validate_resampler_func("sum", args, kwargs)
return self._downsample("sum", numeric_only=numeric_only, min_count=min_count)

@doc(GroupBy.prod)
def prod(
self,
numeric_only: bool = False,
min_count: int = 0,
*args,
**kwargs,
):
nv.validate_resampler_func("prod", args, kwargs)
return self._downsample("prod", numeric_only=numeric_only, min_count=min_count)

def min(
self,
numeric_only: bool = False,
min_count: int = 0,
*args,
**kwargs,
):
nv.validate_resampler_func("min", args, kwargs)
return self._downsample("min", numeric_only=numeric_only, min_count=min_count)

def max(
self,
numeric_only: bool = False,
min_count: int = 0,
*args,
**kwargs,
):
nv.validate_resampler_func("max", args, kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like args/kwargs are getting ignored for many of these. can we just remove them?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would require a deprecation cycle like what was done in #50338, but I would be +1 for deprecating *args, **kwargs here

return self._downsample("max", numeric_only=numeric_only, min_count=min_count)

@doc(GroupBy.first)
def first(
self,
numeric_only: bool = False,
min_count: int = 0,
*args,
**kwargs,
):
nv.validate_resampler_func("first", args, kwargs)
return self._downsample("first", numeric_only=numeric_only, min_count=min_count)

@doc(GroupBy.last)
def last(
self,
numeric_only: bool = False,
min_count: int = 0,
*args,
**kwargs,
):
nv.validate_resampler_func("last", args, kwargs)
return self._downsample("last", numeric_only=numeric_only, min_count=min_count)

@doc(GroupBy.median)
def median(self, numeric_only: bool = False, *args, **kwargs):
nv.validate_resampler_func("median", args, kwargs)
return self._downsample("median", numeric_only=numeric_only)

def mean(
self,
numeric_only: bool = False,
Expand Down Expand Up @@ -984,6 +1052,35 @@ def var(
nv.validate_resampler_func("var", args, kwargs)
return self._downsample("var", ddof=ddof, numeric_only=numeric_only)

@doc(GroupBy.sem)
def sem(
self,
ddof: int = 1,
numeric_only: bool = False,
*args,
**kwargs,
):
nv.validate_resampler_func("sem", args, kwargs)
return self._downsample("sem", ddof=ddof, numeric_only=numeric_only)

@doc(GroupBy.ohlc)
def ohlc(
self,
*args,
**kwargs,
):
nv.validate_resampler_func("ohlc", args, kwargs)
return self._downsample("ohlc")

@doc(SeriesGroupBy.nunique)
def nunique(
self,
*args,
**kwargs,
):
nv.validate_resampler_func("nunique", args, kwargs)
return self._downsample("nunique")

@doc(GroupBy.size)
def size(self):
result = self._downsample("size")
Expand Down Expand Up @@ -1047,87 +1144,6 @@ def quantile(self, q: float | AnyArrayLike = 0.5, **kwargs):
return self._downsample("quantile", q=q, **kwargs)


def _add_downsample_kernel(
name: str, args: tuple[str, ...], docs_class: type = GroupBy
) -> None:
"""
Add a kernel to Resampler.

Arguments
---------
name : str
Name of the kernel.
args : tuple
Arguments of the method.
docs_class : type
Class to get kernel docstring from.
"""
assert args in (
("numeric_only", "min_count"),
("numeric_only",),
("ddof", "numeric_only"),
(),
)

# Explicitly provide args rather than args/kwargs for API docs
if args == ("numeric_only", "min_count"):

def f(
self,
numeric_only: bool = False,
min_count: int = 0,
*args,
**kwargs,
):
nv.validate_resampler_func(name, args, kwargs)
return self._downsample(
name, numeric_only=numeric_only, min_count=min_count
)

elif args == ("numeric_only",):
# error: All conditional function variants must have identical signatures
def f(self, numeric_only: bool = False, *args, **kwargs): # type: ignore[misc]
nv.validate_resampler_func(name, args, kwargs)
return self._downsample(name, numeric_only=numeric_only)

elif args == ("ddof", "numeric_only"):
# error: All conditional function variants must have identical signatures
def f( # type: ignore[misc]
self,
ddof: int = 1,
numeric_only: bool = False,
*args,
**kwargs,
):
nv.validate_resampler_func(name, args, kwargs)
return self._downsample(name, ddof=ddof, numeric_only=numeric_only)

else:
# error: All conditional function variants must have identical signatures
def f( # type: ignore[misc]
self,
*args,
**kwargs,
):
nv.validate_resampler_func(name, args, kwargs)
return self._downsample(name)

f.__doc__ = getattr(docs_class, name).__doc__
setattr(Resampler, name, f)


for _method in ["sum", "prod", "min", "max", "first", "last"]:
_add_downsample_kernel(_method, ("numeric_only", "min_count"))
for _method in ["median"]:
_add_downsample_kernel(_method, ("numeric_only",))
for _method in ["sem"]:
_add_downsample_kernel(_method, ("ddof", "numeric_only"))
for _method in ["ohlc"]:
_add_downsample_kernel(_method, ())
for _method in ["nunique"]:
_add_downsample_kernel(_method, (), SeriesGroupBy)


class _GroupByMixin(PandasObject):
"""
Provide the groupby facilities.
Expand Down