func (qf QFrame) Len() int {
	if qf.Err != nil {
		return -1
	}
	return qf.index.Len()
}
Len returns the number of rows in the QFrame.

Time complexity O(1).
package qframe
import (
	"database/sql"
	stdcsv "encoding/csv"
	"fmt"
	"io"
	"reflect"
	"sort"
	"strings"
	"github.com/tobgu/qframe/config/rolling"
	"github.com/tobgu/qframe/config/csv"
	"github.com/tobgu/qframe/config/eval"
	"github.com/tobgu/qframe/config/groupby"
	"github.com/tobgu/qframe/config/newqf"
	qsql "github.com/tobgu/qframe/config/sql"
	"github.com/tobgu/qframe/filter"
	"github.com/tobgu/qframe/internal/bcolumn"
	"github.com/tobgu/qframe/internal/column"
	"github.com/tobgu/qframe/internal/ecolumn"
	"github.com/tobgu/qframe/internal/fcolumn"
	"github.com/tobgu/qframe/internal/grouper"
	"github.com/tobgu/qframe/internal/icolumn"
	"github.com/tobgu/qframe/internal/index"
	qfio "github.com/tobgu/qframe/internal/io"
	qfsqlio "github.com/tobgu/qframe/internal/io/sql"
	"github.com/tobgu/qframe/internal/math/integer"
	"github.com/tobgu/qframe/internal/scolumn"
	qfsort "github.com/tobgu/qframe/internal/sort"
	qfstrings "github.com/tobgu/qframe/internal/strings"
	"github.com/tobgu/qframe/qerrors"
	"github.com/tobgu/qframe/types"
	_ "github.com/mauricelam/genny/generic"
)
type namedColumn struct {
	column.Column
	name string
	pos  int
}
func (ns namedColumn) ByteSize() int {
	return ns.Column.ByteSize() + 2*8 + 8 + len(ns.name)
}
type QFrame struct {
	columns       []namedColumn
	columnsByName map[string]namedColumn
	index         index.Int
	Err error
}
func (qf QFrame) withErr(err error) QFrame {
	return QFrame{Err: err, columns: qf.columns, columnsByName: qf.columnsByName, index: qf.index}
}
func (qf QFrame) withIndex(ix index.Int) QFrame {
	return QFrame{Err: qf.Err, columns: qf.columns, columnsByName: qf.columnsByName, index: ix}
}
type ConstString struct {
	Val   *string
	Count int
}
type ConstInt struct {
	Val   int
	Count int
}
type ConstFloat struct {
	Val   float64
	Count int
}
type ConstBool struct {
	Val   bool
	Count int
}
func createColumn(name string, data interface{}, config *newqf.Config) (column.Column, error) {
	var localS column.Column
	if sc, ok := data.([]string); ok {
		sp := make([]*string, len(sc))
		for i := range sc {
			sp[i] = &sc[i]
		}
		data = sp
	}
	var err error
	switch t := data.(type) {
	case []int:
		localS = icolumn.New(t)
	case ConstInt:
		localS = icolumn.NewConst(t.Val, t.Count)
	case []float64:
		localS = fcolumn.New(t)
	case ConstFloat:
		localS = fcolumn.NewConst(t.Val, t.Count)
	case []*string:
		if values, ok := config.EnumColumns[name]; ok {
			localS, err = ecolumn.New(t, values)
			if err != nil {
				return nil, qerrors.Propagate(fmt.Sprintf("New columns %s", name), err)
			}
			delete(config.EnumColumns, name)
		} else {
			localS = scolumn.New(t)
		}
	case ConstString:
		if values, ok := config.EnumColumns[name]; ok {
			localS, err = ecolumn.NewConst(t.Val, t.Count, values)
			if err != nil {
				return nil, qerrors.Propagate(fmt.Sprintf("New columns %s", name), err)
			}
			delete(config.EnumColumns, name)
		} else {
			localS = scolumn.NewConst(t.Val, t.Count)
		}
	case []bool:
		localS = bcolumn.New(t)
	case ConstBool:
		localS = bcolumn.NewConst(t.Val, t.Count)
	case ecolumn.Column:
		localS = t
	case qfstrings.StringBlob:
		localS = scolumn.NewBytes(t.Pointers, t.Data)
	case column.Column:
		localS = t
	default:
		return nil, qerrors.New("createColumn", `unknown column data type "%s" for column "%s"`, reflect.TypeOf(t), name)
	}
	return localS, nil
}
func New(data map[string]types.DataSlice, fns ...newqf.ConfigFunc) QFrame {
	config := newqf.NewConfig(fns)
	for colName := range data {
		if err := qfstrings.CheckName(colName); err != nil {
			return QFrame{Err: qerrors.Propagate("New", err)}
		}
	}
	if len(config.ColumnOrder) == 0 {
		config.ColumnOrder = make([]string, 0, len(data))
		for name := range data {
			config.ColumnOrder = append(config.ColumnOrder, name)
			sort.Strings(config.ColumnOrder)
		}
	}
	if len(config.ColumnOrder) != len(data) {
		return QFrame{Err: qerrors.New("New", "number of columns and columns order length do not match, %d, %d", len(config.ColumnOrder), len(data))}
	}
	for _, name := range config.ColumnOrder {
		if _, ok := data[name]; !ok {
			return QFrame{Err: qerrors.New("New", `column "%s" in column order does not exist`, name)}
		}
	}
	columns := make([]namedColumn, len(data))
	colByName := make(map[string]namedColumn, len(data))
	firstLen, currentLen := 0, 0
	for i, name := range config.ColumnOrder {
		col := data[name]
		localCol2, err := createColumn(name, col, config)
		if err != nil {
			return QFrame{Err: err}
		}
		columns[i] = namedColumn{name: name, Column: localCol2, pos: i}
		colByName[name] = columns[i]
		currentLen = localCol2.Len()
		if firstLen == 0 {
			firstLen = currentLen
		}
		if firstLen != currentLen {
			return QFrame{Err: qerrors.New("New", "different lengths on columns not allowed")}
		}
	}
	if len(config.EnumColumns) > 0 {
		colNames := make([]string, 0)
		for k := range config.EnumColumns {
			colNames = append(colNames, k)
		}
		return QFrame{Err: qerrors.New("New", "unknown enum columns: %v", colNames)}
	}
	return QFrame{columns: columns, columnsByName: colByName, index: index.NewAscending(uint32(currentLen)), Err: nil}
}
func (qf QFrame) Contains(colName string) bool {
	_, ok := qf.columnsByName[colName]
	return ok
}
func (qf QFrame) Filter(clause FilterClause) QFrame {
	if qf.Err != nil {
		return qf
	}
	return clause.filter(qf)
}
func unknownCol(c string) string {
	return fmt.Sprintf(`unknown column: "%s"`, c)
}
func (qf QFrame) filter(filters ...filter.Filter) QFrame {
	if qf.Err != nil {
		return qf
	}
	bIndex := index.NewBool(qf.index.Len())
	for _, f := range filters {
		s, ok := qf.columnsByName[f.Column]
		if !ok {
			return qf.withErr(qerrors.New("Filter", unknownCol(f.Column)))
		}
		if name, ok := f.Arg.(types.ColumnName); ok {
			argC, ok := qf.columnsByName[string(name)]
			if !ok {
				return qf.withErr(qerrors.New("Filter", `unknown argument column: "%s"`, name))
			}
			if ic, ok := s.Column.(icolumn.Column); ok {
				if _, ok := argC.Column.(fcolumn.Column); ok {
					s.Column = fcolumn.New(ic.FloatSlice())
				}
			} else if _, ok := s.Column.(fcolumn.Column); ok {
				if ic, ok := argC.Column.(icolumn.Column); ok {
					argC.Column = fcolumn.New(ic.FloatSlice())
				}
			}  
			f.Arg = argC.Column
		}
		var err error
		if f.Inverse {
			done := false
			if sComp, ok := f.Comparator.(string); ok {
				if inverse, ok := filter.Inverse[sComp]; ok {
					err = s.Filter(qf.index, inverse, f.Arg, bIndex)
					if err == nil {
						done = true
					}
				}
			}
			if !done {
				invBIndex := index.NewBool(bIndex.Len())
				err = s.Filter(qf.index, f.Comparator, f.Arg, invBIndex)
				if err == nil {
					for i, x := range bIndex {
						if !x {
							bIndex[i] = !invBIndex[i]
						}
					}
				}
			}
		} else {
			err = s.Filter(qf.index, f.Comparator, f.Arg, bIndex)
		}
		if err != nil {
			return qf.withErr(qerrors.Propagate(fmt.Sprintf("Filter column '%s'", f.Column), err))
		}
	}
	return qf.withIndex(qf.index.Filter(bIndex))
}
func (qf QFrame) Equals(other QFrame) (equal bool, reason string) {
	if len(qf.index) != len(other.index) {
		return false, "Different length"
	}
	if len(qf.columns) != len(other.columns) {
		return false, "Different number of columns"
	}
	for i, s := range qf.columns {
		otherCol := other.columns[i]
		if s.name != otherCol.name {
			return false, fmt.Sprintf("Column name difference at %d, %s != %s", i, s.name, otherCol.name)
		}
		if !s.Equals(qf.index, otherCol.Column, other.index) {
			return false, fmt.Sprintf("Content of columns %s differ", s.name)
		}
	}
	return true, ""
}