func (tsr *String) Agg(ini float64, fun AggFunc) float64 {
	ag := ini
	for j, vl := range tsr.Values {
		val := StringToFloat64(vl)
		if !tsr.IsNull1D(j) && !math.IsNaN(val) {
			ag = fun(j, val, ag)
		}
	}
	return ag
}
Agg applies given aggregation function to each element in the tensor
(automatically skips IsNull and NaN elements), using float64 conversions of the values.
init is the initial value for the agg variable. returns final aggregate value
package etensor
import (
	"errors"
	"fmt"
	"log"
	"math"
	"strconv"
	"strings"
	"github.com/emer/etable/bitslice"
	"github.com/goki/ki/ints"
	"gonum.org/v1/gonum/mat"
)
type String struct {
	Shape
	Values []string
	Nulls  bitslice.Slice
	Meta   map[string]string
}
func NewString(shape, strides []int, names []string) *String {
	bt := &String{}
	bt.SetShape(shape, strides, names)
	ln := bt.Len()
	bt.Values = make([]string, ln)
	return bt
}
func NewStringShape(shape *Shape) *String {
	bt := &String{}
	bt.CopyShape(shape)
	ln := bt.Len()
	bt.Values = make([]string, ln)
	return bt
}
func (tsr *String) ShapeObj() *Shape { return &tsr.Shape }
func (tsr *String) DataType() Type   { return STRING }
func (tsr *String) Value(i []int) string {
	j := int(tsr.Offset(i))
	return tsr.Values[j]
}
func (tsr *String) Value1D(i int) string {
	return tsr.Values[i]
}
func (tsr *String) Set(i []int, val string) {
	j := int(tsr.Offset(i))
	tsr.Values[j] = val
}
func (tsr *String) Set1D(i int, val string) {
	tsr.Values[i] = val
}
func (tsr *String) IsNull(i []int) bool {
	if tsr.Nulls == nil {
		return false
	}
	j := tsr.Offset(i)
	return tsr.Nulls.Index(j)
}
func (tsr *String) IsNull1D(i int) bool {
	if tsr.Nulls == nil {
		return false
	}
	return tsr.Nulls.Index(i)
}
func (tsr *String) SetNull(i []int, nul bool) {
	if tsr.Nulls == nil {
		tsr.Nulls = bitslice.Make(tsr.Len(), 0)
	}
	j := tsr.Offset(i)
	tsr.Nulls.Set(j, nul)
}
func (tsr *String) SetNull1D(i int, nul bool) {
	if tsr.Nulls == nil {
		tsr.Nulls = bitslice.Make(tsr.Len(), 0)
	}
	tsr.Nulls.Set(i, nul)
}
func StringToFloat64(str string) float64 {
	if fv, err := strconv.ParseFloat(str, 64); err == nil {
		return fv
	}
	return 0
}
func Float64ToString(val float64) string {
	return strconv.FormatFloat(val, 'g', -1, 64)
}
func (tsr *String) FloatVal(i []int) float64 {
	j := tsr.Offset(i)
	return StringToFloat64(tsr.Values[j])
}
func (tsr *String) SetFloat(i []int, val float64) {
	j := tsr.Offset(i)
	tsr.Values[j] = Float64ToString(val)
}
func (tsr *String) StringVal(i []int) string      { j := tsr.Offset(i); return tsr.Values[j] }
func (tsr *String) SetString(i []int, val string) { j := tsr.Offset(i); tsr.Values[j] = val }
func (tsr *String) FloatVal1D(off int) float64 {
	return StringToFloat64(tsr.Values[off])
}
func (tsr *String) SetFloat1D(off int, val float64) {
	tsr.Values[off] = Float64ToString(val)
}
func (tsr *String) FloatValRowCell(row, cell int) float64 {
	_, sz := tsr.RowCellSize()
	return StringToFloat64(tsr.Values[row*sz+cell])
}
func (tsr *String) SetFloatRowCell(row, cell int, val float64) {
	_, sz := tsr.RowCellSize()
	tsr.Values[row*sz+cell] = Float64ToString(val)
}
func (tsr *String) Floats(flt *[]float64) {
	SetFloat64SliceLen(flt, len(tsr.Values))
	for j, vl := range tsr.Values {
		(*flt)[j] = StringToFloat64(vl)
	}
}
func (tsr *String) SetFloats(vals []float64) {
	sz := ints.MinInt(len(tsr.Values), len(vals))
	for j := 0; j < sz; j++ {
		tsr.Values[j] = Float64ToString(vals[j])
	}
}
func (tsr *String) StringVal1D(off int) string      { return tsr.Values[off] }
func (tsr *String) SetString1D(off int, val string) { tsr.Values[off] = val }
func (tsr *String) StringValRowCell(row, cell int) string {
	_, sz := tsr.RowCellSize()
	return tsr.Values[row*sz+cell]
}
func (tsr *String) SetStringRowCell(row, cell int, val string) {
	_, sz := tsr.RowCellSize()
	tsr.Values[row*sz+cell] = val
}
func (tsr *String) Range() (min, max float64, minIdx, maxIdx int) {
	minIdx = -1
	maxIdx = -1
	return
}