func (pca *PCA) ProjectCol(vals *[]float64, ix *etable.IdxView, colNm string, idx int) error {
	col, err := ix.Table.ColByNameTry(colNm)
	if err != nil {
		return err
	}
	if pca.Vectors == nil {
		return fmt.Errorf("PCA.ProjectCol Vectors are nil -- must call PCA first")
	}
	nr := pca.Vectors.Dim(0)
	if idx >= nr {
		return fmt.Errorf("PCA.ProjectCol eigenvector index > rank of matrix")
	}
	cvec := make([]float64, nr)
	eidx := nr - 1 - idx  
	for ri := 0; ri < nr; ri++ {
		cvec[ri] = pca.Vectors.Value([]int{ri, eidx})  
	}
	rows := ix.Len()
	if len(*vals) != rows {
		*vals = make([]float64, rows)
	}
	ln := col.Len()
	sz := ln / col.Dim(0)  
	if sz != nr {
		return fmt.Errorf("PCA.ProjectCol column cell size != pca eigenvectors")
	}
	rdim := []int{0}
	for row := 0; row < rows; row++ {
		sum := 0.0
		rdim[0] = ix.Idxs[row]
		rt := col.SubSpace(rdim)
		for ci := 0; ci < sz; ci++ {
			sum += cvec[ci] * rt.FloatVal1D(ci)
		}
		(*vals)[row] = sum
	}
	return nil
}
ProjectCol projects values from the given colNm of given table (via IdxView)
onto the idx'th eigenvector (0 = largest eigenvalue, 1 = next, etc).
Must have already called PCA() method.
package pca
import (
	"fmt"
	"github.com/emer/etable/etable"
	"github.com/emer/etable/etensor"
	"github.com/emer/etable/metric"
	"gonum.org/v1/gonum/mat"
)
type PCA struct {
	Covar   *etensor.Float64 `view:"no-inline" desc:"the covariance matrix computed on original data, which is then eigen-factored"`
	Vectors *etensor.Float64 `view:"no-inline" desc:"the eigenvectors, in same size as Covar - each eigenvector is a column in this 2D square matrix, ordered *lowest* to *highest* across the columns -- i.e., maximum eigenvector is the last column"`
	Values  []float64        `view:"no-inline" desc:"the eigenvalues, ordered *lowest* to *highest*"`
}
func (pca *PCA) Init() {
	pca.Covar = &etensor.Float64{}
	pca.Vectors = &etensor.Float64{}
	pca.Values = nil
}
func (pca *PCA) TableCol(ix *etable.IdxView, colNm string, mfun metric.Func64) error {
	if pca.Covar == nil {
		pca.Init()
	}
	err := CovarTableCol(pca.Covar, ix, colNm, mfun)
	if err != nil {
		return err
	}
	return pca.PCA()
}
func (pca *PCA) Tensor(tsr etensor.Tensor, mfun metric.Func64) error {
	if pca.Covar == nil {
		pca.Init()
	}
	err := CovarTensor(pca.Covar, tsr, mfun)
	if err != nil {
		return err
	}
	return pca.PCA()
}
func (pca *PCA) TableColStd(ix *etable.IdxView, colNm string, met metric.StdMetrics) error {
	return pca.TableCol(ix, colNm, metric.StdFunc64(met))
}
func (pca *PCA) TensorStd(tsr etensor.Tensor, met metric.StdMetrics) error {
	return pca.Tensor(tsr, metric.StdFunc64(met))
}
func (pca *PCA) PCA() error {
	if pca.Covar == nil || pca.Covar.NumDims() != 2 {
		return fmt.Errorf("pca.PCA: Covar matrix is nil or not 2D")
	}
	var eig mat.EigenSym
	ok := eig.Factorize(pca.Covar, true)
	if !ok {
		return fmt.Errorf("gonum EigenSym Factorize failed")
	}
	if pca.Vectors == nil {
		pca.Vectors = &etensor.Float64{}
	}
	var ev mat.Dense
	eig.VectorsTo(&ev)
	etensor.CopyDense(pca.Vectors, &ev)
	nr := pca.Vectors.Dim(0)
	if len(pca.Values) != nr {
		pca.Values = make([]float64, nr)
	}
	eig.Values(pca.Values)
	return nil
}