/// <summary>
        /// Aggregates over all rows.
        /// </summary>
        public DataFrame Aggregate(AggregatedFunction func)
        {
            var dfs    = new List <DataFrame>();
            int nbkeys = 0;

            foreach (var view in EnumerateGroups())
            {
                var df  = view.Drop(view.ColumnsKey);
                var agg = df.Aggregate(func);
                nbkeys = view.Keys.Length;
                foreach (var pair in view.Keys)
                {
                    agg.AddColumn(pair.Key, pair.Kind, 1);
                    agg.loc[0, pair.Key] = pair.Value;
                }
                dfs.Add(agg);
            }
            var res       = DataFrame.Concat(dfs);
            var columns   = res.Columns;
            int nbnotkeys = columns.Length - nbkeys;

            columns = columns.Skip(nbnotkeys).Concat(columns.Take(nbnotkeys)).ToArray();
            res.OrderColumns(columns);
            return(res);
        }
        public static DataFrame TJoin <TMutKey, TImutKey>(
            IDataFrameView left, IDataFrameView right,
            int[] rowsLeft, int[] rowsRight,
            int[] columnsLeft, int[] columnsRight,
            IEnumerable <int> colsLeft, IEnumerable <int> colsRight,
            bool sort,
            string leftSuffix, string rightSuffix,
            JoinStrategy joinType,
            MultiGetterAt <TMutKey> getterLeft,
            MultiGetterAt <TMutKey> getterRight,
            Func <TMutKey, TImutKey> conv,
            Func <TImutKey, DataFrameGroupKey[]> convLeft,
            Func <TImutKey, DataFrameGroupKey[]> convRight)
            where TMutKey : ITUple, new()
            where TImutKey : IComparable <TImutKey>, IEquatable <TImutKey>
        {
            var icolsLeft  = colsLeft.ToArray();
            var icolsRight = colsRight.ToArray();

            int[] orderLeft = rowsLeft == null?rowsLeft.Select(c => c).ToArray() : Enumerable.Range(0, left.Length).ToArray();

            int[] orderRight = rowsLeft == null?rowsRight.Select(c => c).ToArray() : Enumerable.Range(0, right.Length).ToArray();

            var keysLeft  = left.EnumerateItems(icolsLeft, true, rowsLeft, getterLeft).Select(c => conv(c)).ToArray();
            var keysRight = right.EnumerateItems(icolsRight, true, rowsRight, getterRight).Select(c => conv(c)).ToArray();

            if (sort)
            {
                DataFrameSorting.TSort(left, ref orderLeft, keysLeft, true);
                DataFrameSorting.TSort(right, ref orderRight, keysRight, true);
            }
            var iter = TJoin <TImutKey>(left, right, orderLeft, orderRight, keysLeft, keysRight,
                                        icolsLeft, icolsRight, leftSuffix, rightSuffix, joinType, convLeft, convRight);

            return(DataFrame.Concat(iter));
        }