func (rt *Runtime) CloseOOBStatsFile() (e error) {
	if rt.oobWriter == nil {
		return
	}
	e = rt.oobWriter.Close()
	rt.oobWriter = nil
	return
}
CloseOOBStatsFile will close statistics file for writing.
package classifier
import (
	"fmt"
	"math"
	"github.com/shuLhan/share/lib/debug"
	"github.com/shuLhan/share/lib/dsv"
	"github.com/shuLhan/share/lib/floats64"
	"github.com/shuLhan/share/lib/ints"
	"github.com/shuLhan/share/lib/numbers"
	libstrings "github.com/shuLhan/share/lib/strings"
	"github.com/shuLhan/share/lib/tabula"
)
const (
	tag = "[classifier.runtime]"
)
type Runtime struct {
	RunOOB bool `json:"RunOOB"`
	OOBStatsFile string `json:"OOBStatsFile"`
	PerfFile string `json:"PerfFile"`
	StatFile string `json:"StatFile"`
	oobCms []CM
	oobStats Stats
	oobStatTotal Stat
	oobWriter *dsv.Writer
	perfs Stats
}
func (rt *Runtime) Initialize() error {
	rt.oobStatTotal.Start()
	return rt.OpenOOBStatsFile()
}
func (rt *Runtime) Finalize() (e error) {
	st := &rt.oobStatTotal
	st.End()
	st.ID = int64(len(rt.oobStats))
	e = rt.WriteOOBStat(st)
	if e != nil {
		return e
	}
	return rt.CloseOOBStatsFile()
}
func (rt *Runtime) OOBStats() *Stats {
	return &rt.oobStats
}
func (rt *Runtime) StatTotal() *Stat {
	return &rt.oobStatTotal
}
func (rt *Runtime) AddOOBCM(cm *CM) {
	rt.oobCms = append(rt.oobCms, *cm)
}
func (rt *Runtime) AddStat(stat *Stat) {
	rt.oobStats = append(rt.oobStats, stat)
}
func (rt *Runtime) ComputeCM(sampleIds []int,
	vs, actuals, predicts []string,
) (
	cm *CM,
) {
	cm = &CM{}
	cm.ComputeStrings(vs, actuals, predicts)
	cm.GroupIndexPredictionsStrings(sampleIds, actuals, predicts)
	if debug.Value >= 2 {
		fmt.Println(tag, cm)
	}
	return cm
}
func (rt *Runtime) ComputeStatFromCM(stat *Stat, cm *CM) {
	stat.OobError = cm.GetFalseRate()
	stat.OobErrorMean = rt.oobStatTotal.OobError /
		float64(len(rt.oobStats)+1)
	stat.TP = int64(cm.TP())
	stat.FP = int64(cm.FP())
	stat.TN = int64(cm.TN())
	stat.FN = int64(cm.FN())
	t := float64(stat.TP + stat.FN)
	if t == 0 {
		stat.TPRate = 0
	} else {
		stat.TPRate = float64(stat.TP) / t
	}
	t = float64(stat.FP + stat.TN)
	if t == 0 {
		stat.FPRate = 0
	} else {
		stat.FPRate = float64(stat.FP) / t
	}
	t = float64(stat.FP + stat.TN)
	if t == 0 {
		stat.TNRate = 0
	} else {
		stat.TNRate = float64(stat.TN) / t
	}
	t = float64(stat.TP + stat.FP)
	if t == 0 {
		stat.Precision = 0
	} else {
		stat.Precision = float64(stat.TP) / t
	}
	t = (1 / stat.Precision) + (1 / stat.TPRate)
	if t == 0 {
		stat.FMeasure = 0
	} else {
		stat.FMeasure = 2 / t
	}
	t = float64(stat.TP + stat.TN + stat.FP + stat.FN)
	if t == 0 {
		stat.Accuracy = 0
	} else {
		stat.Accuracy = float64(stat.TP+stat.TN) / t
	}
	if debug.Value >= 1 {
		rt.PrintOobStat(stat, cm)
		rt.PrintStat(stat)
	}
}
func (rt *Runtime) ComputeStatTotal(stat *Stat) {
	if stat == nil {
		return
	}
	nstat := len(rt.oobStats)
	if nstat == 0 {
		return
	}
	t := &rt.oobStatTotal
	t.OobError += stat.OobError
	t.OobErrorMean = t.OobError / float64(nstat)
	t.TP += stat.TP
	t.FP += stat.FP
	t.TN += stat.TN
	t.FN += stat.FN
	total := float64(t.TP + t.FN)
	if total == 0 {
		t.TPRate = 0
	} else {
		t.TPRate = float64(t.TP) / total
	}
	total = float64(t.FP + t.TN)
	if total == 0 {
		t.FPRate = 0
	} else {
		t.FPRate = float64(t.FP) / total
	}
	total = float64(t.FP + t.TN)
	if total == 0 {
		t.TNRate = 0
	} else {
		t.TNRate = float64(t.TN) / total
	}
	total = float64(t.TP + t.FP)
	if total == 0 {
		t.Precision = 0
	} else {
		t.Precision = float64(t.TP) / total
	}
	total = (1 / t.Precision) + (1 / t.TPRate)
	if total == 0 {
		t.FMeasure = 0
	} else {
		t.FMeasure = 2 / total
	}
	total = float64(t.TP + t.TN + t.FP + t.FN)
	if total == 0 {
		t.Accuracy = 0
	} else {
		t.Accuracy = float64(t.TP+t.TN) / total
	}
}
func (rt *Runtime) OpenOOBStatsFile() error {
	if rt.oobWriter != nil {
		_ = rt.CloseOOBStatsFile()
	}
	rt.oobWriter = &dsv.Writer{}
	return rt.oobWriter.OpenOutput(rt.OOBStatsFile)
}
func (rt *Runtime) WriteOOBStat(stat *Stat) error {
	if rt.oobWriter == nil {
		return nil
	}
	if stat == nil {
		return nil
	}
	return rt.oobWriter.WriteRawRow(stat.ToRow(), nil, nil)
}