def group_by(keys, values=None, reduction=None, axis=0):
    'construct a grouping object on the given keys, optionally performing the given reduction on the given values\n\n    Parameters\n    ----------\n    keys : indexable object\n        keys to group by\n    values : array_like, optional\n        sequence of values, of the same length as keys\n        if a reduction function is provided, the given values are reduced by key\n        if no reduction is provided, the given values are grouped and split by key\n    reduction : lambda, optional\n        reduction function to apply to the values in each group\n    axis : int, optional\n        axis to regard as the key-sequence, in case keys is multi-dimensional\n\n    Returns\n    -------\n    iterable\n        if values is None, a GroupBy object of the given keys object\n        if reduction is None, an tuple of a sequence of unique keys and a sequence of grouped values\n        else, a sequence of tuples of unique keys and reductions of values over that key-group\n\n    See Also\n    --------\n    numpy_indexed.as_index : for information regarding the casting rules to a valid Index object\n    '
    g = GroupBy(keys, axis)
    if (values is None):
        return g
    groups = g.split(values)
    if (reduction is None):
        return (g.unique, groups)
    return [(key, reduction(group)) for (key, group) in zip(g.unique, groups)]
construct a grouping object on the given keys, optionally performing the given reduction on the given values

Parameters
----------
keys : indexable object
    keys to group by
values : array_like, optional
    sequence of values, of the same length as keys
    if a reduction function is provided, the given values are reduced by key
    if no reduction is provided, the given values are grouped and split by key
reduction : lambda, optional
    reduction function to apply to the values in each group
axis : int, optional
    axis to regard as the key-sequence, in case keys is multi-dimensional

Returns
-------
iterable
    if values is None, a GroupBy object of the given keys object
    if reduction is None, an tuple of a sequence of unique keys and a sequence of grouped values
    else, a sequence of tuples of unique keys and reductions of values over that key-group

See Also
--------
numpy_indexed.as_index : for information regarding the casting rules to a valid Index object
def group_by(keys, values=None, reduction=None, axis=0):
    'construct a grouping object on the given keys, optionally performing the given reduction on the given values\n\n    Parameters\n    ----------\n    keys : indexable object\n        keys to group by\n    values : array_like, optional\n        sequence of values, of the same length as keys\n        if a reduction function is provided, the given values are reduced by key\n        if no reduction is provided, the given values are grouped and split by key\n    reduction : lambda, optional\n        reduction function to apply to the values in each group\n    axis : int, optional\n        axis to regard as the key-sequence, in case keys is multi-dimensional\n\n    Returns\n    -------\n    iterable\n        if values is None, a GroupBy object of the given keys object\n        if reduction is None, an tuple of a sequence of unique keys and a sequence of grouped values\n        else, a sequence of tuples of unique keys and reductions of values over that key-group\n\n    See Also\n    --------\n    numpy_indexed.as_index : for information regarding the casting rules to a valid Index object\n    '
    g = GroupBy(keys, axis)
    if (values is None):
        return g
    groups = g.split(values)
    if (reduction is None):
        return (g.unique, groups)
    return [(key, reduction(group)) for (key, group) in zip(g.unique, groups)]