def _get_grouper(obj, key=None, axis=<NUM_LIT:0>, level=None, sort=True,<EOL>observed=False, mutated=False, validate=True):
group_axis = obj._get_axis(axis)<EOL>if level is not None:<EOL><INDENT>if isinstance(group_axis, MultiIndex):<EOL><INDENT>if is_list_like(level) and len(level) == <NUM_LIT:1>:<EOL><INDENT>level = level[<NUM_LIT:0>]<EOL><DEDENT>if key is None and is_scalar(level):<EOL><INDENT>key = group_axis.get_level_values(level)<EOL>level = None<EOL><DEDENT><DEDENT>else:<EOL><INDENT>if is_list_like(level):<EOL><INDENT>nlevels = len(level)<EOL>if nlevels == <NUM_LIT:1>:<EOL><INDENT>level = level[<NUM_LIT:0>]<EOL><DEDENT>elif nlevels == <NUM_LIT:0>:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>'<EOL>'<STR_LIT>')<EOL><DEDENT><DEDENT>if isinstance(level, str):<EOL><INDENT>if obj.index.name != level:<EOL><INDENT>raise ValueError('<STR_LIT>'<EOL>'<STR_LIT:index>'.format(level))<EOL><DEDENT><DEDENT>elif level > <NUM_LIT:0> or level < -<NUM_LIT:1>:<EOL><INDENT>raise ValueError(<EOL>'<STR_LIT>')<EOL><DEDENT>level = None<EOL>key = group_axis<EOL><DEDENT><DEDENT>if isinstance(key, Grouper):<EOL><INDENT>binner, grouper, obj = key._get_grouper(obj, validate=False)<EOL>if key.key is None:<EOL><INDENT>return grouper, [], obj<EOL><DEDENT>else:<EOL><INDENT>return grouper, {key.key}, obj<EOL><DEDENT><DEDENT>elif isinstance(key, BaseGrouper):<EOL><INDENT>return key, [], obj<EOL><DEDENT>is_tuple = isinstance(key, tuple)<EOL>all_hashable = is_tuple and is_hashable(key)<EOL>if is_tuple:<EOL><INDENT>if ((all_hashable and key not in obj and set(key).issubset(obj))<EOL>or not all_hashable):<EOL><INDENT>msg = ("<STR_LIT>"<EOL>"<STR_LIT>"<EOL>"<STR_LIT>")<EOL>warnings.warn(msg, FutureWarning, stacklevel=<NUM_LIT:5>)<EOL>key = list(key)<EOL><DEDENT><DEDENT>if not isinstance(key, list):<EOL><INDENT>keys = [key]<EOL>match_axis_length = False<EOL><DEDENT>else:<EOL><INDENT>keys = key<EOL>match_axis_length = len(keys) == len(group_axis)<EOL><DEDENT>any_callable = any(callable(g) or isinstance(g, dict) for g in keys)<EOL>any_groupers = any(isinstance(g, Grouper) for g in keys)<EOL>any_arraylike = any(isinstance(g, (list, tuple, Series, Index, np.ndarray))<EOL>for g in keys)<EOL>if (not any_callable and not any_arraylike and not any_groupers and<EOL>match_axis_length and level is None):<EOL><INDENT>if isinstance(obj, DataFrame):<EOL><INDENT>all_in_columns_index = all(g in obj.columns or g in<EOL>obj.index.names for g in keys)<EOL><DEDENT>elif isinstance(obj, Series):<EOL><INDENT>all_in_columns_index = all(g in obj.index.names for g in keys)<EOL><DEDENT>if not all_in_columns_index:<EOL><INDENT>keys = [com.asarray_tuplesafe(keys)]<EOL><DEDENT><DEDENT>if isinstance(level, (tuple, list)):<EOL><INDENT>if key is None:<EOL><INDENT>keys = [None] * len(level)<EOL><DEDENT>levels = level<EOL><DEDENT>else:<EOL><INDENT>levels = [level] * len(keys)<EOL><DEDENT>groupings = []<EOL>exclusions = []<EOL>def is_in_axis(key):<EOL><INDENT>if not _is_label_like(key):<EOL><INDENT>try:<EOL><INDENT>obj._data.items.get_loc(key)<EOL><DEDENT>except Exception:<EOL><INDENT>return False<EOL><DEDENT><DEDENT>return True<EOL><DEDENT>def is_in_obj(gpr):<EOL><INDENT>try:<EOL><INDENT>return id(gpr) == id(obj[gpr.name])<EOL><DEDENT>except Exception:<EOL><INDENT>return False<EOL><DEDENT><DEDENT>for i, (gpr, level) in enumerate(zip(keys, levels)):<EOL><INDENT>if is_in_obj(gpr):  <EOL><INDENT>in_axis, name = True, gpr.name<EOL>exclusions.append(name)<EOL><DEDENT>elif is_in_axis(gpr):  <EOL><INDENT>if gpr in obj:<EOL><INDENT>if validate:<EOL><INDENT>obj._check_label_or_level_ambiguity(gpr)<EOL><DEDENT>in_axis, name, gpr = True, gpr, obj[gpr]<EOL>exclusions.append(name)<EOL><DEDENT>elif obj._is_level_reference(gpr):<EOL><INDENT>in_axis, name, level, gpr = False, None, gpr, None<EOL><DEDENT>else:<EOL><INDENT>raise KeyError(gpr)<EOL><DEDENT><DEDENT>elif isinstance(gpr, Grouper) and gpr.key is not None:<EOL><INDENT>exclusions.append(gpr.key)<EOL>in_axis, name = False, None<EOL><DEDENT>else:<EOL><INDENT>in_axis, name = False, None<EOL><DEDENT>if is_categorical_dtype(gpr) and len(gpr) != obj.shape[axis]:<EOL><INDENT>raise ValueError(<EOL>("<STR_LIT>"<EOL>"<STR_LIT>"<EOL>.format(len_gpr=len(gpr), len_axis=obj.shape[axis])))<EOL><DEDENT>ping = (Grouping(group_axis,<EOL>gpr,<EOL>obj=obj,<EOL>name=name,<EOL>level=level,<EOL>sort=sort,<EOL>observed=observed,<EOL>in_axis=in_axis)<EOL>if not isinstance(gpr, Grouping) else gpr)<EOL>groupings.append(ping)<EOL><DEDENT>if len(groupings) == <NUM_LIT:0>:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>grouper = BaseGrouper(group_axis, groupings, sort=sort, mutated=mutated)<EOL>return grouper, exclusions, obj<EOL>
create and return a BaseGrouper, which is an internal
mapping of how to create the grouper indexers.
This may be composed of multiple Grouping objects, indicating
multiple groupers

Groupers are ultimately index mappings. They can originate as:
index mappings, keys to columns, functions, or Groupers

Groupers enable local references to axis,level,sort, while
the passed in axis, level, and sort are 'global'.

This routine tries to figure out what the passing in references
are and then creates a Grouping for each one, combined into
a BaseGrouper.

If observed & we have a categorical grouper, only show the observed
values

If validate, then check for key/level overlaps