func GroupByIdx(ix *etable.IdxView, colIdxs []int) *etable.Splits {
	nc := len(colIdxs)
	if nc == 0 || ix.Table == nil {
		return nil
	}
	if ix.Table.ColNames == nil {
		log.Println("split.GroupBy: Table does not have any column names -- will not work")
		return nil
	}
	spl := &etable.Splits{}
	spl.Levels = make([]string, nc)
	for i, ci := range colIdxs {
		spl.Levels[i] = ix.Table.ColNames[ci]
	}
	srt := ix.Clone()
	srt.SortStableCols(colIdxs, true)  
	lstVals := make([]string, nc)
	curVals := make([]string, nc)
	var curIx *etable.IdxView
	for _, rw := range srt.Idxs {
		diff := false
		for i, ci := range colIdxs {
			cl := ix.Table.Cols[ci]
			cv := cl.StringVal1D(rw)
			curVals[i] = cv
			if cv != lstVals[i] {
				diff = true
			}
		}
		if diff || curIx == nil {
			curIx = spl.New(ix.Table, curVals, rw)
			copy(lstVals, curVals)
		} else {
			curIx.AddIndex(rw)
		}
	}
	return spl
}
GroupByIdx returns a new Splits set based on the groups of values
across the given set of column indexes.
Uses a stable sort on columns, so ordering of other dimensions is preserved.
package split
import (
	"log"
	"github.com/emer/etable/etable"
	"github.com/goki/ki/sliceclone"
)
func All(ix *etable.IdxView) *etable.Splits {
	spl := &etable.Splits{}
	spl.Levels = []string{"All"}
	spl.New(ix.Table, []string{"All"}, ix.Idxs...)
	return spl
}