Skip to content
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

Define boxplot inside DataFrameGroupBy definition #17179

Closed
wants to merge 7 commits into from
68 changes: 44 additions & 24 deletions pandas/core/groupby.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import types
from functools import wraps
from functools import wraps, partial
import numpy as np
import datetime
import collections
Expand Down Expand Up @@ -63,6 +63,8 @@
import pandas.core.common as com
from pandas.core.config import option_context

from pandas.plotting._core import boxplot_frame_groupby

from pandas._libs import lib, groupby as libgroupby, Timestamp, NaT, iNaT
from pandas._libs.lib import count_level_2d

Expand Down Expand Up @@ -950,7 +952,6 @@ def _apply_filter(self, indices, dropna):


class GroupBy(_GroupBy):

"""
Class for grouping and aggregating relational data. See aggregate,
transform, and apply functions on this object.
Expand Down Expand Up @@ -2742,6 +2743,19 @@ def _convert_grouper(axis, grouper):
return grouper


def pin_method(cls, target_cls, name):
if not hasattr(cls, name):
# Avoid over-writing `DataFrameGroupBy.boxplot`
items = list(_whitelist_method_generator(target_cls, [name]))
assert len(items) <= 1, items
if items:
func = items[0]
if not isinstance(func, str):
# Note: checking `if callable(func)` fails here on `dtype`.
setattr(cls, name, func)
return


def _whitelist_method_generator(klass, whitelist):
"""
Yields all GroupBy member defs for DataFrame/Series names in _whitelist.
Expand All @@ -2762,20 +2776,12 @@ def _whitelist_method_generator(klass, whitelist):
base class, any such name is skipped.
"""

method_wrapper_template = \
"""def %(name)s(%(sig)s) :
method_wrapper_template = """def %(name)s(%(sig)s):
\"""
%(doc)s
\"""
f = %(self)s.__getattr__('%(name)s')
return f(%(args)s)"""
property_wrapper_template = \
"""@property
def %(name)s(self) :
\"""
%(doc)s
\"""
return self.__getattr__('%(name)s')"""
for name in whitelist:
# don't override anything that was explicitly defined
# in the base class
Expand All @@ -2794,22 +2800,31 @@ def %(name)s(self) :
args_by_name = ['{0}={0}'.format(arg) for arg in args[1:]]
params = {'name': name,
'doc': doc,
'sig': ','.join(decl),
'sig': ', '.join(decl),
'self': args[0],
'args': ','.join(args_by_name)}
'args': ', '.join(args_by_name)}

yield wrapper_template % params

else:
wrapper_template = property_wrapper_template
params = {'name': name, 'doc': doc}
yield wrapper_template % params

def getter(self):
return self.__getattr__(name)

getter.__name__ = name
getter.__doc__ = doc

prop = property(getter, doc=doc)
yield prop


class SeriesGroupBy(GroupBy):
#
# Make class defs of attributes on SeriesGroupBy whitelist
_apply_whitelist = _series_apply_whitelist
for _def_str in _whitelist_method_generator(Series,
_series_apply_whitelist):
exec(_def_str)
for _def_str in _whitelist_method_generator(Series, _apply_whitelist):
if isinstance(_def_str, str):
exec(_def_str)

@property
def _selection_name(self):
Expand Down Expand Up @@ -3189,7 +3204,6 @@ def describe(self, **kwargs):
def value_counts(self, normalize=False, sort=True, ascending=False,
bins=None, dropna=True):

from functools import partial
from pandas.core.reshape.tile import cut
from pandas.core.reshape.merge import _get_join_indexers

Expand Down Expand Up @@ -3330,6 +3344,10 @@ def _apply_to_column_groupbys(self, func):
return func(self)


for name in SeriesGroupBy._apply_whitelist:
pin_method(SeriesGroupBy, Series, name)


class NDFrameGroupBy(GroupBy):

def _iterate_slices(self):
Expand Down Expand Up @@ -3967,7 +3985,8 @@ class DataFrameGroupBy(NDFrameGroupBy):
#
# Make class defs of attributes on DataFrameGroupBy whitelist.
for _def_str in _whitelist_method_generator(DataFrame, _apply_whitelist):
exec(_def_str)
if isinstance(_def_str, str):
exec(_def_str)

_block_agg_axis = 1

Expand Down Expand Up @@ -4203,7 +4222,6 @@ def _apply_to_column_groupbys(self, func):

def count(self):
""" Compute count of group, excluding missing values """
from functools import partial
from pandas.core.dtypes.missing import _isna_ndarraylike as isna

data, _ = self._get_data_to_aggregate()
Expand Down Expand Up @@ -4283,9 +4301,11 @@ def groupby_series(obj, col=None):
results.index = _default_index(len(results))
return results

boxplot = boxplot_frame_groupby


from pandas.plotting._core import boxplot_frame_groupby # noqa
DataFrameGroupBy.boxplot = boxplot_frame_groupby
for name in DataFrameGroupBy._apply_whitelist:
pin_method(DataFrameGroupBy, DataFrame, name)


class PanelGroupBy(NDFrameGroupBy):
Expand Down