def aggregate_in(Data, On=None, AggFuncDict=None, AggFunc=None, AggList=None,<EOL>interspersed=True):
<EOL>Data = deletecols(Data,'<STR_LIT>')<EOL>if '<STR_LIT>' in Data.dtype.names:<EOL><INDENT>X = Data[Data['<STR_LIT>'] == '<STR_LIT>'][:]<EOL>OldAggregates = Data[Data['<STR_LIT>'] != '<STR_LIT>'][:]<EOL>AggVars = utils.uniqify(utils.listunion([x.split('<STR_LIT:U+002C>') for x in <EOL>OldAggregates['<STR_LIT>']]))<EOL><DEDENT>else:<EOL><INDENT>X = Data<EOL>OldAggregates = Data[<NUM_LIT:0>:<NUM_LIT:0>]<EOL>AggVars = []<EOL><DEDENT>if On == None:<EOL><INDENT>On = []<EOL><DEDENT>NewAggregates = aggregate(X, On, AggFuncDict=AggFuncDict, <EOL>AggFunc=AggFunc, AggList=AggList, KeepOthers=True)<EOL>on = '<STR_LIT:U+002C>'.join(On)<EOL>NewAggregates = addcols(NewAggregates,   <EOL>utils.fromarrays([[on]*len(NewAggregates)], <EOL>type=np.ndarray, names=['<STR_LIT>']))<EOL>AggVars = utils.uniqify(AggVars + On)<EOL>Aggregates = rowstack([OldAggregates,NewAggregates],mode='<STR_LIT>')<EOL>ANLen = np.array([len(x.split('<STR_LIT:U+002C>')) for x in Aggregates['<STR_LIT>']])<EOL>U = np.array(utils.uniqify(ANLen)); U.sort()<EOL>[A,B] = fast.equalspairs(ANLen,U)<EOL>Grays = np.array(grayspec(len(U)))<EOL>AggColor = utils.fromarrays([Grays[A]], type=np.ndarray, <EOL>names = ['<STR_LIT>'])<EOL>Aggregates = addcols(Aggregates,AggColor)<EOL>if not interspersed or len(AggVars) == <NUM_LIT:0>:<EOL><INDENT>return rowstack([X,Aggregates],mode='<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>s = ANLen.argsort()<EOL>Aggregates = Aggregates[s[list(range(len(Aggregates) - <NUM_LIT:1>, -<NUM_LIT:1>, -<NUM_LIT:1>))]]<EOL>X.sort(order = AggVars)<EOL>Diffs = np.append(np.append([<NUM_LIT:0>], <NUM_LIT:1> + (X[AggVars][<NUM_LIT:1>:] != <EOL>X[AggVars][:-<NUM_LIT:1>]).nonzero()[<NUM_LIT:0>]), [len(X)])<EOL>DiffAtts = ([[t for t in AggVars if X[t][Diffs[i]] != X[t][Diffs[i+<NUM_LIT:1>]]] <EOL>for i in range(len(Diffs) - <NUM_LIT:2>)] <EOL>if len(Diffs) > <NUM_LIT:2> else []) + [AggVars]<EOL>HH = {}<EOL>for l in utils.uniqify(Aggregates['<STR_LIT>']):<EOL><INDENT>Avars = l.split('<STR_LIT:U+002C>')<EOL>HH[l] = fast.recarrayequalspairs(X[Avars][Diffs[:-<NUM_LIT:1>]], <EOL>Aggregates[Avars])<EOL><DEDENT>Order = []<EOL>for i in range(len(Diffs)-<NUM_LIT:1>):<EOL><INDENT>Order.extend(list(range(Diffs[i], Diffs[i+<NUM_LIT:1>])))<EOL>Get = []<EOL>for l in list(HH.keys()):<EOL><INDENT>Get += [len(X) + j for j in <EOL>HH[l][<NUM_LIT:2>][list(range(HH[l][<NUM_LIT:0>][i], HH[l][<NUM_LIT:1>][i]))] if <EOL>len(set(DiffAtts[i]).intersection(<EOL>Aggregates['<STR_LIT>'][j].split('<STR_LIT:U+002C>'))) > <NUM_LIT:0> and <EOL>set(Aggregates['<STR_LIT>'][j].split('<STR_LIT:U+002C>')) == <EOL>set(l.split('<STR_LIT:U+002C>'))]<EOL><DEDENT>Order.extend(Get)<EOL><DEDENT>return rowstack([X, Aggregates], mode='<STR_LIT>')[Order]<EOL><DEDENT>
Aggregate a ndarray with structured dtype or recarray
and include original data in the result.

Take aggregate of data set on specified columns, then add the resulting 
rows back into data set to make a composite object containing both original 
non-aggregate data rows as well as the aggregate rows.

First read comments for :func:`tabular.spreadsheet.aggregate`.

This function returns a numpy ndarray, with the number of rows equaling::

        len(Data) + len(A)

where `A` is the the result of::

        Data.aggregate(On,AggFuncDict)

`A` represents the aggregate rows; the other rows were the original data 
rows.

This function supports _multiple_ aggregation, meaning that one can first 
aggregate on one set of factors, then repeat aggregation on the result for 
another set of factors, without the results of the first aggregation 
interfering the second.  To achieve this, the method adds two new columns:

*   a column called "__aggregates__" specifying on which factors the rows 
    that are aggregate rows were aggregated.  Rows added by aggregating on 
    factor `A` (a column in the original data set) will have `A` in the 
    "__aggregates__" column.  When multiple factors `A1`, `A2` , ... are 
    aggregated on, the notation is a comma-separated list:  `A1,A2,...`.  
    This way, when you call `aggregate_in` again, the function only 
    aggregates on the columns that have the empty char '' in their 
    "__aggregates__" column.

*   a column called '__color__', specifying Gray-Scale colors for 
    aggregated rows that will be used by the Data Environment system 
    browser for colorizing the  data.   When there are multiple levels of 
    aggregation, the coarser aggregate groups (e.g. on fewer factors) get 
    darker gray color then those on finer aggregate groups (e.g. more 
    factors).

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

**Parameters**

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

                The data set to aggregate in.

        **On** :  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`.

        **interspersed** :  boolean, optional

                *   If `True`, aggregate rows are interleaved with the data 
                    of which they are aggregates.

                *   If `False`, all aggregate rows placed at the end of the 
                    array.

**Returns**

        **agg** :  numpy ndarray with structured dtype

                Composite aggregated data set plus original data set.

**See also:**

        :func:`tabular.spreadsheet.aggregate`