def pivot(X, a, b, Keep=None, NullVals=None, order = None, prefix='<STR_LIT:_>'):
othernames = [o for o in X.dtype.names if o not in [a,b]]<EOL>for c in [a,b]:<EOL><INDENT>assert c in X.dtype.names, '<STR_LIT>' + c + '<STR_LIT>'<EOL><DEDENT>[D,s] = fast.recarrayuniqify(X[[a,b]])<EOL>unique_ab = X[[a,b]][s[D.nonzero()[<NUM_LIT:0>]]]<EOL>assert len(X) == len(unique_ab) ,('<STR_LIT>', a, '<STR_LIT>', b, '<STR_LIT>')<EOL>[D,s] = fast.arrayuniqify(X[a])<EOL>unique_a = X[a][s[D.nonzero()[<NUM_LIT:0>]]]<EOL>[D,s] = fast.arrayuniqify(X[b])<EOL>unique_b = X[b][s[D.nonzero()[<NUM_LIT:0>]]]<EOL>Da = len(unique_a)<EOL>Db = len(unique_b)<EOL>if len(X) != Da * Db:<EOL><INDENT>if list(X.dtype.names).index(a) < list(X.dtype.names).index(b):<EOL><INDENT>n1 = a ; f1 = unique_a; n2 = b ; f2 = unique_b<EOL><DEDENT>else:<EOL><INDENT>n1 = b ; f1 = unique_b; n2 = a ; f2 = unique_a<EOL><DEDENT>dtype = np.dtype([(n1,f1.dtype.descr[<NUM_LIT:0>][<NUM_LIT:1>]),(n2,f2.dtype.descr[<NUM_LIT:0>][<NUM_LIT:1>])])<EOL>allvalues = utils.fromarrays([np.repeat(f1,<EOL>len(f2)),<EOL>np.tile(f2,len(f1))],<EOL>np.ndarray,<EOL>dtype=dtype)<EOL>missingvalues = allvalues[np.invert(fast.recarrayisin(allvalues,<EOL>X[[a,b]]))]<EOL>if NullVals == None:<EOL><INDENT>NullVals = {}<EOL><DEDENT>if not isinstance(NullVals,dict):<EOL><INDENT>if hasattr(NullVals,'<STR_LIT>'):<EOL><INDENT>NullVals = dict([(o,NullVals(o)) for o in othernames])<EOL><DEDENT>else:<EOL><INDENT>NullVals = dict([(o,NullVals) for o in othernames])<EOL><DEDENT><DEDENT>nullvals = utils.fromrecords([[NullVals[o] if o in list(NullVals.keys()) <EOL>else utils.DEFAULT_NULLVALUE(X[o][<NUM_LIT:0>]) for o in <EOL>othernames]], type=np.ndarray, names=othernames)<EOL>nullarray = nullvals.repeat(len(missingvalues))<EOL>Y = colstack([missingvalues, nullarray])<EOL>Y = Y.astype(np.dtype([(o,<EOL>X.dtype[o].descr[<NUM_LIT:0>][<NUM_LIT:1>]) for o in Y.dtype.names]))<EOL>X = rowstack([X, Y])<EOL><DEDENT>X.sort(order = [a,b])<EOL>Bvals = X[b][:Db]<EOL>bnames = [str(bv).replace('<STR_LIT:U+0020>','<STR_LIT>') for bv in Bvals]<EOL>assert (len(set(othernames).intersection(bnames)) == <NUM_LIT:0> and <EOL>a not in bnames), ('<STR_LIT>', b, <EOL>'<STR_LIT>')<EOL>acol = X[a][::Db]<EOL>Cols = [acol]<EOL>names = [a]<EOL>Trivials = []<EOL>NonTrivials = []<EOL>for c in othernames:<EOL><INDENT>Z = X[c].reshape((Da,Db))<EOL>if all([len(set(Z[:,i])) == <NUM_LIT:1> for i in range(Z.shape[<NUM_LIT:1>])]):<EOL><INDENT>Trivials.append(c)<EOL><DEDENT>else:<EOL><INDENT>NonTrivials.append(c)<EOL><DEDENT>Cols += [Z[:,i] for i in range(Z.shape[<NUM_LIT:1>])]<EOL>names += [bn + '<STR_LIT:_>' + c for bn in bnames]<EOL><DEDENT>if order is not None:<EOL><INDENT>ordering = [names.index(ord) for ord in order]<EOL>Cols = [Cols[i] for i in ordering]<EOL>names = [names[i] for i in ordering]<EOL><DEDENT>dtype = np.dtype([(n,c.dtype.descr[<NUM_LIT:0>][<NUM_LIT:1>]) for (n,c) in zip(names,Cols)])<EOL>D = utils.fromarrays(Cols,type=np.ndarray,dtype=dtype)<EOL>coloring = {}<EOL>if Keep != None:<EOL><INDENT>Trivials = set(Trivials).intersection(Keep)<EOL>for c in Trivials:<EOL><INDENT>X.sort(order=[c])<EOL>cvals = np.array(utils.uniqify(X[c]))<EOL>[AA,BB] = fast.equalspairs(cvals,X[c])<EOL>for (i,cc) in enumerate(cvals):<EOL><INDENT>blist = [str(bv).replace('<STR_LIT:U+0020>', '<STR_LIT>') for bv in Bvals if bv in <EOL>X[b][AA[i]:BB[i]]]<EOL>coloring[str(cc)] = [a] + [bn + '<STR_LIT:_>' + d for bn in blist for d <EOL>in NonTrivials]<EOL>for d in NonTrivials:<EOL><INDENT>coloring[str(cc) + '<STR_LIT:_>' + d] = [a] + blist<EOL><DEDENT><DEDENT><DEDENT><DEDENT>for c in NonTrivials:<EOL><INDENT>coloring[c] = [a] + [bn + '<STR_LIT:_>' + c for bn in bnames]<EOL><DEDENT>for bn in bnames:<EOL><INDENT>coloring[prefix + bn] = [a] + [bn + '<STR_LIT:_>' + c for c in NonTrivials]<EOL><DEDENT>return [D, coloring]<EOL>
Implements pivoting on numpy ndarrays (with structured dtype) or recarrays.

See http://en.wikipedia.org/wiki/Pivot_table for information about pivot 
tables.

Returns `X` pivoted on (a,b) with `a` as the row axis and `b` values as the 
column axis.

So-called "nontrivial columns relative to `b`" in `X` are added as 
color-grouped sets of columns, and "trivial columns relative to `b`" are 
also retained as cross-grouped sets of columns if they are listed in `Keep` 
argument.

Note that a column `c` in `X` is "trivial relative to `b`" if for all rows 
i, X[c][i] can be determined from X[b][i], e.g the elements in X[c] are in 
many-to-any correspondence with the values in X[b].

The function will raise an exception if the list of pairs of value in 
X[[a,b]] is not the product of the individual columns values, e.g.::

        X[[a,b]] == set(X[a]) x set(X[b])

in some ordering.

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

**Parameters**

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

                The  data set to pivot.

        **a** : string

                Column name in `X`.

        **b** : string

                Another column name in `X`.

        **Keep** :  list of strings, optional

                List of other columns names in `X`.

        **NullVals** :  optional

                Dictionary mapping column names in `X` other than `a` or 
                `b` to appropriate null values for their types.

                If `None`, then the null values defined by the `nullvalue`
                function are used, see
                :func:`tabular.spreadsheet.nullvalue`.

        **prefix** :  string, optional

                Prefix to add to `coloring` keys corresponding to 
                cross-grouped "trivial columns relative to `b`".  Default 
                value is an underscore, '_'.

**Returns**

        **ptable** :  numpy ndarray with structured dtype

                The resulting pivot table.

        **coloring** :  dictionary

                Dictionary whose keys are strings and corresponding values 
                are lists of column names (e.g. strings).

                There are two groups of keys:

                *   So-called "nontrivial columns relative to `b`" in `X`.  
                    These correspond to columns in::

                                set(`X.dtype.names`) - set([a, b])

                *   Cross-grouped "trivial columns relative to `b`".  The 
                    `prefix` is used to distinguish these.

                The `coloring` parameter is used by the the tabarray pivot 
                method, :func:`tabular.tab.tabarray.pivot`.

                See :func:`tabular.tab.tabarray.__new__` for more
                information about coloring.