func (txy *TableXY) Len() int {
	if txy.Table == nil || txy.Table.Table == nil {
		return 0
	}
	return txy.Table.Len()
}
Len returns the number of rows in the view of table
package eplot
import (
	"errors"
	"log"
	"math"
	"github.com/emer/etable/etable"
	"github.com/emer/etable/etensor"
	"github.com/emer/etable/minmax"
)
type TableXY struct {
	Table          *etable.IdxView `desc:"the index view of data table to plot from"`
	XCol, YCol     int             `desc:"the indexes of the tensor columns to use for the X and Y data, respectively"`
	XRowSz, YRowSz int             `desc:"numer of elements in each row of data -- 1 for scalar, > 1 for multi-dimensional"`
	XIdx, YIdx     int             `desc:"the indexes of the element within each tensor cell if cells are n-dimensional, respectively"`
	LblCol         int             `desc:"the column to use for returning a label using Label interface -- for string cols"`
	ErrCol         int             `desc:"the column to use for returning errorbars (+/- given value) -- if YCol is tensor then this must also be a tensor and given YIdx used"`
	YRange         minmax.Range64  `desc:"range constraints on Y values"`
}
func NewTableXY(dt *etable.IdxView, xcol, xtsrIdx, ycol, ytsrIdx int, yrng minmax.Range64) (*TableXY, error) {
	txy := &TableXY{Table: dt.Clone(), XCol: xcol, YCol: ycol, XIdx: xtsrIdx, YIdx: ytsrIdx, YRange: yrng}
	return txy, txy.Validate()
}
func NewTableXYName(dt *etable.IdxView, xi, xtsrIdx int, ycol string, ytsrIdx int, yrng minmax.Range64) (*TableXY, error) {
	yi, err := dt.Table.ColIdxTry(ycol)
	if err != nil {
		log.Println(err)
		return nil, err
	}
	txy := &TableXY{Table: dt.Clone(), XCol: xi, YCol: yi, XIdx: xtsrIdx, YIdx: ytsrIdx, YRange: yrng}
	return txy, txy.Validate()
}
func (txy *TableXY) Validate() error {
	if txy.Table == nil {
		return errors.New("eplot.TableXY table is nil")
	}
	nc := txy.Table.Table.NumCols()
	if txy.XCol >= nc || txy.XCol < 0 {
		txy.XCol = 0
		return errors.New("eplot.TableXY XCol index invalid -- reset to 0")
	}
	if txy.YCol >= nc || txy.YCol < 0 {
		txy.YCol = 0
		return errors.New("eplot.TableXY YCol index invalid -- reset to 0")
	}
	xc := txy.Table.Table.Cols[txy.XCol]
	yc := txy.Table.Table.Cols[txy.YCol]
	if xc.NumDims() > 1 {
		_, txy.XRowSz = xc.RowCellSize()
	}
	if yc.NumDims() > 1 {
		_, txy.YRowSz = yc.RowCellSize()
		if txy.YIdx >= txy.YRowSz || txy.YIdx < 0 {
			txy.YIdx = 0
			return errors.New("eplot.TableXY Y TensorIdx invalid -- reset to 0")
		}
	}
	txy.FilterVals()
	return nil
}
func (txy *TableXY) FilterVals() {
	txy.Table.Filter(func(et *etable.Table, row int) bool {
		xv := txy.TRowXValue(row)
		yv := txy.TRowValue(row)
		if math.IsNaN(yv) || math.IsNaN(xv) {
			return false
		}
		if txy.YRange.FixMin && yv < txy.YRange.Min {
			return false
		}
		if txy.YRange.FixMax && yv > txy.YRange.Max {
			return false
		}
		return true
	})
}