def aggregate(X, On=None, AggFuncDict=None, AggFunc=None,<EOL>AggList=None, returnsort=False, KeepOthers=True,<EOL>keyfuncdict=None):
names = X.dtype.names<EOL>if len(X) == <NUM_LIT:0>:<EOL><INDENT>if returnsort:<EOL><INDENT>return [X,None]<EOL><DEDENT>else:<EOL><INDENT>return X<EOL><DEDENT><DEDENT>if On == None:<EOL><INDENT>On = []<EOL><DEDENT>elif isinstance(On,str):<EOL><INDENT>On = On.split('<STR_LIT:U+002C>')<EOL><DEDENT>assert all([o in names for o in On]),("<STR_LIT>" + str([o for o in On if o not in names]) + <EOL>"<STR_LIT>")<EOL>if AggList is None:<EOL><INDENT>AggList = []<EOL><DEDENT>if AggFuncDict:<EOL><INDENT>AggList += list(AggFuncDict.items())<EOL><DEDENT>for (i,x) in enumerate(AggList):<EOL><INDENT>if utils.is_string_like(x):<EOL><INDENT>AggList[i] = (x,)<EOL><DEDENT>elif isinstance(x,tuple):<EOL><INDENT>assert <NUM_LIT:1> <= len(x) <= <NUM_LIT:3><EOL>assert isinstance(x[<NUM_LIT:0>],str)<EOL>if len(x) == <NUM_LIT:2> and isinstance(x[<NUM_LIT:1>],tuple):<EOL><INDENT>assert len(x[<NUM_LIT:1>]) == <NUM_LIT:2><EOL>AggList[i] = (x[<NUM_LIT:0>],) + x[<NUM_LIT:1>]<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT><DEDENT>Names = [x[<NUM_LIT:0>] for x in AggList]<EOL>assert Names == utils.uniqify(Names)<EOL>if KeepOthers:<EOL><INDENT>AggList = [(x,) for x in X.dtype.names if x not in Names + On] + AggList<EOL><DEDENT>DefaultChoices = {'<STR_LIT:string>':[], '<STR_LIT>':[], '<STR_LIT>':[]}<EOL>for (i,v) in enumerate(AggList):<EOL><INDENT>if len(v) == <NUM_LIT:1>:<EOL><INDENT>assert v[<NUM_LIT:0>] in X.dtype.names<EOL>if AggFunc:<EOL><INDENT>AggList[i] = v + (AggFunc,v[<NUM_LIT:0>])<EOL><DEDENT>else:<EOL><INDENT>AggList[i] = v + (DefaultChooser(X,v[<NUM_LIT:0>], DefaultChoices),v[<NUM_LIT:0>])<EOL><DEDENT><DEDENT>elif len(v) == <NUM_LIT:2>:<EOL><INDENT>if isftype(v[<NUM_LIT:1>]):<EOL><INDENT>assert v[<NUM_LIT:0>] in X.dtype.names<EOL>AggList[i] = v + (v[<NUM_LIT:0>],)<EOL><DEDENT>elif utils.is_string_like(v[<NUM_LIT:1>]):<EOL><INDENT>if AggFunc:<EOL><INDENT>_a = v[<NUM_LIT:1>] in X.dtype.names<EOL>_b = isinstance(v[<NUM_LIT:1>],list) and set(v[<NUM_LIT:1>]) <= set(X.dtype.names)<EOL>assert _a or _b<EOL>AggList[i] = (v[<NUM_LIT:0>], AggFunc, v[<NUM_LIT:1>])<EOL><DEDENT>else:<EOL><INDENT>assert v[<NUM_LIT:1>] in X.dtype.names<EOL>AggList[i] = (v[<NUM_LIT:0>],<EOL>DefaultChooser(X,v[<NUM_LIT:1>],<EOL>DefaultChoices),<EOL>v[<NUM_LIT:1>])<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT><DEDENT>elif len(v) == <NUM_LIT:3>:<EOL><INDENT>if utils.is_string_like(v[<NUM_LIT:2>]):<EOL><INDENT>assert isftype(v[<NUM_LIT:1>]) and v[<NUM_LIT:2>] in X.dtype.names<EOL><DEDENT>else:<EOL><INDENT>assert isftype(v[<NUM_LIT:1>]) and(isinstance(v[<NUM_LIT:2>],list) andset(v[<NUM_LIT:2>]) <= set(X.dtype.names))   <EOL><DEDENT><DEDENT><DEDENT>if len(DefaultChoices['<STR_LIT>']) > <NUM_LIT:0>:<EOL><INDENT>print(('<STR_LIT>', DefaultChoices['<STR_LIT>'], <EOL>'<STR_LIT>'))<EOL><DEDENT>if len(DefaultChoices['<STR_LIT:string>']) > <NUM_LIT:0>:<EOL><INDENT>print(('<STR_LIT>', DefaultChoices['<STR_LIT:string>'], <EOL>'<STR_LIT>'))<EOL><DEDENT>if len(DefaultChoices['<STR_LIT>']) > <NUM_LIT:0>:<EOL><INDENT>print(('<STR_LIT>', DefaultChoices['<STR_LIT>'], <EOL>'<STR_LIT>' <EOL>'<STR_LIT>'))<EOL><DEDENT>return strictaggregate(X, On, AggList, returnsort, keyfuncdict)<EOL>
Aggregate a ndarray with structured dtype (or recarray) on columns for 
given functions.

Aggregate a numpy recarray (or tabular tabarray) on a set of specified 
factors, using specified aggregation functions.

Intuitively, this function will aggregate the dataset `X` on a set of 
columns, whose names are listed in `On`, so that the resulting aggregate 
data set has one record for each unique tuples of values in those columns.

The more factors listed in `On` argument, the "finer" is the aggregation, 
the fewer factors, the "coarser" the aggregation.  For example, if::

        On = 'A'

the resulting tabarray will have one record for each unique value of a in 
X['A'], while if On = ['A', 'B'] then the resulting tabarray will have 
one record for each unique (a, b) pair in X[['A', 'B']]

The `AggFunc` argument is a function that specifies how to aggregate the 
factors _not_ listed in `On`, e.g. the so-called `Off` columns.  For 
example.  For instance, if On = ['A', 'B'] and `C` is a third column, then ::

        AggFunc = numpy.mean

will result in a tabarray containing a `C` column whose values are the 
average of the values from the original `C` columns corresponding to each 
unique (a, b) pair. 

If you want to specify a different aggreagtion method for each `Off` column,
use `AggFuncDict` instead of AggFunc.  `AggFuncDict` is a dictionary of
functions whose keys are column names.  AggFuncDict[C] will be applied to 
the C column, AggFuncDict[D] to the D column, etc.   AggFunc and AggFuncDict
can be used simultaneously, with the elements of AggFuncDict overriding 
AggFunc for the specified columns. 

Using either AggFunc or AggFuncDict, the resulting tabarray has the same 
columns as the original tabarray.  Sometimes you want to specify the ability
to create new aggregate columns not corresponding to one specific column in the 
original tabarray, and taking data from several.  To achieve this, use the
AggList argument.   AggList is a list of three-element lists of the form:
     (name, func, col_names)
where `name` specifies the resulting column name in the aggregated tabarray,
`func` specifies the aggregation function, and `col_names` specifies the 
list of columns names from the original tabarray that will be needed to 
compute the aggregate values.  (That is, for each unique tuple `t` in the `On` 
columns, the subarray of X[col_names] for which X[On] == t is passed to 
`func`.)

If an `Off` column is _not_ provided as a key in `AggFuncDict`, a default 
aggregator function will be used:  the sum function for numerical columns, 
concatenation for string columns.

Implemented by the tabarray method 
:func:`tabular.tab.tabarray.aggregate`.

**Parameters**

        **X** :  numpy ndarray with structured dtype or recarray

                The data set to aggregate.

        **On** :  string or list of  strings, optional

                List of column names in `X`.

        **AggFuncDict** :  dictionary, optional

                Dictionary where

                *   keys are some (all) column names of `X` that are NOT
                    in `On`

                *   values are functions that can be applied to lists or
                    numpy arrays.

                This specifies how to aggregate the factors _not_ listed in
                `On`, e.g. the so-called `Off` columns.

        **AggFunc** :  function, optional

                Function that can be applied to lists or numpy arrays,
                specifying how to aggregate factors not listed in either
                `On` or the keys of `AggFuncDict`, e.g. a "default"
                aggregation function for the `Off` columns not explicitly
                listed in `AggFuncDict`.

        **AggList** :  list, optional

                List of tuples 

        **returnsort** :        Boolean, optional

                If `returnsort == True`, then return a list of indices
                describing how `X` was sorted as a result of aggregation.
                Default value is `False`.

**Returns**

        **agg** :  numpy ndarray with structured dtype

                Aggregated data set.

        **index_array** :  numpy ndarray (int, 1D)

                Returned only if `returnsort == True`.  List of indices
                describing how `X` was sorted as a result of aggregation.

**See also:**

        :func:`tabular.spreadsheet.aggregate_in`