func (m *MaxAbsScaler) Transform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	Xmat := base.ToDense(X).RawMatrix()
	Xout = mat.NewDense(Xmat.Rows, Xmat.Cols, nil)
	Xoutmat := Xout.RawMatrix()
	for jX, jXout := 0, 0; jX < Xmat.Rows*Xmat.Stride; jX, jXout = jX+Xmat.Stride, jXout+Xoutmat.Stride {
		for i, v := range Xmat.Data[jX : jX+Xmat.Cols] {
			Xoutmat.Data[jXout+i] = v / m.Scale[i]
		}
	}
	Yout = base.ToDense(Y)
	return
}
Transform for MaxAbsScaler ...
package preprocessing
import (
	"fmt"
	"math"
	"sort"
	"golang.org/x/exp/rand"
	"github.com/pa-m/sklearn/base"
	"github.com/pa-m/optimize"
	"gonum.org/v1/gonum/floats"
	"gonum.org/v1/gonum/mat"
	"gonum.org/v1/gonum/stat"
)
type float = float64
type Transformer = base.Transformer
type InverseTransformer interface {
	Transformer
	InverseTransform(X, Y *mat.Dense) (Xout, Yout *mat.Dense)
}
type MinMaxScaler struct {
	FeatureRange                            []float
	Scale, Min, DataMin, DataMax, DataRange *mat.Dense
	NSamplesSeen                            int
}
func NewMinMaxScaler(featureRange []float) *MinMaxScaler {
	return &MinMaxScaler{FeatureRange: featureRange}
}
func (scaler *MinMaxScaler) TransformerClone() base.Transformer {
	var clone = *scaler
	return &clone
}
func (scaler *MinMaxScaler) Reset() *MinMaxScaler {
	scaler.NSamplesSeen = 0
	return scaler
}
func (scaler *MinMaxScaler) Fit(X, Y mat.Matrix) base.Fiter {
	scaler.Reset()
	return scaler.PartialFit(X, Y)
}
func (scaler *MinMaxScaler) PartialFit(Xmatrix, Ymatrix mat.Matrix) Transformer {
	X := base.ToDense(Xmatrix)
	nSamples, nFeatures := X.Dims()
	if nSamples == 0 {
		return scaler
	}
	if scaler.NSamplesSeen == 0 {
		scaler.DataRange = mat.NewDense(1, nFeatures, nil)
		scaler.DataMin = mat.NewDense(1, nFeatures, nil)
		scaler.DataMax = mat.NewDense(1, nFeatures, nil)
		scaler.Min = mat.NewDense(1, nFeatures, nil)
		scaler.Scale = mat.NewDense(1, nFeatures, nil)
		scaler.DataMin.CloneFrom(X.RowView(0).T())
		scaler.DataMax.CloneFrom(X.RowView(0).T())
	}
	X.Apply(func(i int, j int, x float64) float64 {
		scaler.DataMin.Set(0, j, math.Min(scaler.DataMin.At(0, j), x))
		scaler.DataMax.Set(0, j, math.Max(scaler.DataMax.At(0, j), x))
		return x
	}, X)
	scaler.NSamplesSeen += nSamples
	scaler.DataRange.Sub(scaler.DataMax, scaler.DataMin)
	scaler.DataRange.Apply(func(i int, j int, x float64) float64 {
		if x == 0. {
			return 1.
		}
		return x
	}, scaler.DataRange)
	scaler.Scale.Apply(func(i int, j int, x float64) float64 {
		return (scaler.FeatureRange[1] - scaler.FeatureRange[0]) / x
	}, scaler.DataRange)
	scaler.Min.Apply(func(i int, j int, x float64) float64 {
		return scaler.FeatureRange[0] - scaler.DataMin.At(i, j)*scaler.Scale.At(i, j)
	}, scaler.DataRange)
	return scaler
}
func (scaler *MinMaxScaler) Transform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	nSamples, nFeatures := X.Dims()
	Xout = mat.NewDense(nSamples, nFeatures, nil)
	Xout.Apply(func(i int, j int, x float64) float64 {
		return scaler.Min.At(0, j) + x*scaler.Scale.At(0, j)
	}, X)
	return Xout, base.ToDense(Y)
}
func (scaler *MinMaxScaler) FitTransform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	scaler.Fit(X, Y)
	return scaler.Transform(X, Y)
}
func (scaler *MinMaxScaler) InverseTransform(X, Y *mat.Dense) (Xout, Yout *mat.Dense) {
	if X == nil {
		return X, Y
	}
	nSamples, nFeatures := X.Dims()
	Xout = mat.NewDense(nSamples, nFeatures, nil)
	Xout.Apply(func(i int, j int, x float64) float64 {
		return (x - scaler.Min.At(0, j)) / scaler.Scale.At(0, j)
	}, X)
	return Xout, Y
}
type StandardScaler struct {
	WithMean, WithStd bool
	Scale, Mean, Var  *mat.Dense
	NSamplesSeen      int
}
func NewStandardScaler() *StandardScaler {
	return &StandardScaler{WithMean: true, WithStd: true}
}
func (scaler *StandardScaler) TransformerClone() base.Transformer {
	var clone = *scaler
	return &clone
}
func (scaler *StandardScaler) Reset() *StandardScaler {
	scaler.NSamplesSeen = 0
	return scaler
}
func (scaler *StandardScaler) Fit(Xmatrix, Ymatrix mat.Matrix) base.Fiter {
	X, Y := base.ToDense(Xmatrix), base.ToDense(Ymatrix)
	scaler.Reset()
	return scaler.PartialFit(X, Y)
}
func (scaler *StandardScaler) PartialFit(X, Y *mat.Dense) Transformer {
	nSamples, nFeatures := X.Dims()
	if nSamples == 0 {
		return scaler
	}
	if scaler.NSamplesSeen == 0 {
		scaler.Var = mat.NewDense(1, nFeatures, nil)
		scaler.Mean = mat.NewDense(1, nFeatures, nil)
		scaler.Scale = mat.NewDense(1, nFeatures, nil)
	}
	scaler.Mean, scaler.Var, scaler.NSamplesSeen = IncrementalMeanAndVar(X, scaler.Mean, scaler.Var, scaler.NSamplesSeen)
	scaler.Scale.Apply(func(i int, j int, vj float64) float64 {
		if vj == 0. {
			vj = 1.
		}
		return math.Sqrt(vj)
	}, scaler.Var)
	return scaler
}
func (scaler *StandardScaler) Transform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	Xmat := base.ToDense(X).RawMatrix()
	Xout = mat.NewDense(Xmat.Rows, Xmat.Cols, nil)
	Xoutmat := Xout.RawMatrix()
	for i := 0; i < Xmat.Cols; i++ {
		mean, scale := 0., 1.
		if scaler.WithMean {
			mean = scaler.Mean.At(0, i)
		}
		if scaler.WithStd {
			scale = scaler.Scale.At(0, i)
		}
		for jX, jXout := 0, 0; jX < Xmat.Rows*Xmat.Stride; jX, jXout = jX+Xmat.Stride, jXout+Xoutmat.Stride {
			Xoutmat.Data[jXout+i] = (Xmat.Data[jX+i] - mean) / scale
		}
	}
	return Xout, base.ToDense(Y)
}
func (scaler *StandardScaler) FitTransform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	scaler.Fit(X, Y)
	return scaler.Transform(X, Y)
}
func (scaler *StandardScaler) InverseTransform(X, Y *mat.Dense) (Xout, Yout *mat.Dense) {
	if X == nil {
		return X, Y
	}
	Xmat := X.RawMatrix()
	Xout = mat.NewDense(Xmat.Rows, Xmat.Cols, nil)
	Xoutmat := Xout.RawMatrix()
	for i := 0; i < Xmat.Cols; i++ {
		mean, scale := 0., 1.
		if scaler.WithMean {
			mean = scaler.Mean.At(0, i)
		}
		if scaler.WithStd {
			scale = scaler.Scale.At(0, i)
		}
		for jX, jXout := 0, 0; jX < Xmat.Rows*Xmat.Stride; jX, jXout = jX+Xmat.Stride, jXout+Xoutmat.Stride {
			Xoutmat.Data[jXout+i] = mean + Xmat.Data[jX+i]*scale
		}
	}
	return Xout, Y
}
func Scale(X *mat.Dense) *mat.Dense {
	Xout, _ := NewStandardScaler().FitTransform(X, nil)
	return Xout
}
func MeanStdDev(X mat.Matrix) (mean, std *mat.Dense) {
	m, n := X.Dims()
	mean, std = mat.NewDense(1, n, nil), mat.NewDense(1, n, nil)
	base.Parallelize(-1, n, func(th, start, end int) {
		tmp := make([]float64, m)
		for col := start; col < end; col++ {
			mat.Col(tmp, col, X)
			meancol, stdcol := stat.MeanStdDev(tmp, nil)
			mean.Set(0, col, meancol)
			std.Set(0, col, stdcol*float64(n-1)/float64(n))
		}
	})
	return
}
func Mean(X mat.Matrix) (mean *mat.Dense) {
	m, n := X.Dims()
	mean = mat.NewDense(1, n, nil)
	base.Parallelize(-1, n, func(th, start, end int) {
		tmp := make([]float64, m)
		for col := start; col < end; col++ {
			mat.Col(tmp, col, X)
			meancol := stat.Mean(tmp, nil)
			mean.Set(0, col, meancol)
		}
	})
	return
}
type NumpyLike struct{}
func (NumpyLike) Var(X mat.Matrix) *mat.Dense {
	m, n := X.Dims()
	mean := Mean(X)
	variance := mat.NewDense(1, n, nil)
	base.Parallelize(-1, n, func(th, start, end int) {
		tmp := make([]float64, m)
		for col := start; col < end; col++ {
			mat.Col(tmp, col, X)
			floats.AddConst(-mean.At(0, col), tmp)
			floats.Mul(tmp, tmp)
			variance.Set(0, col, floats.Sum(tmp)/float64(n))
		}
	})
	return variance
}
func (m NumpyLike) Std(X mat.Matrix) *mat.Dense {
	tmp := m.Var(X)
	rm := tmp.RawMatrix()
	for i := 0; i < rm.Cols; i++ {
		rm.Data[i] = math.Sqrt(rm.Data[i])
	}
	return tmp
}
type RobustScaler struct {
	Center          bool
	Scale           bool
	Quantiles       *QuantilePair
	Median          *mat.Dense
	Tmp             *mat.Dense
	QuantileDivider *mat.Dense
}
type QuantilePair struct {
	Left  float64
	Right float64
}
func NewDefaultRobustScaler() *RobustScaler {
	return NewRobustScaler(true, true, &QuantilePair{0.25, 0.75})
}
func (scaler *RobustScaler) TransformerClone() base.Transformer {
	var clone = *scaler
	return &clone
}
func NewRobustScaler(center bool, scale bool, quantiles *QuantilePair) *RobustScaler {
	if scale && (quantiles == nil) {
		quantiles = &QuantilePair{0.25, 0.75}
	}
	return &RobustScaler{
		Center:    center,
		Scale:     scale,
		Quantiles: quantiles,
	}
}
func (scaler *RobustScaler) Reset() *RobustScaler {
	return scaler
}
func (scaler *RobustScaler) Fit(Xmatrix, Ymatrix mat.Matrix) base.Fiter {
	scaler.Reset()
	return scaler.PartialFit(Xmatrix, Ymatrix)
}
func (scaler *RobustScaler) PartialFit(Xmatrix, Ymatrix mat.Matrix) Transformer {
	X := base.ToDense(Xmatrix)
	nSamples, nFeatures := X.Dims()
	if nSamples == 0 {
		return scaler
	}
	if scaler.Center && (scaler.Median == nil) {
		scaler.Median = mat.NewDense(1, nFeatures, nil)
	}
	if scaler.Scale && (scaler.QuantileDivider == nil) {
		scaler.QuantileDivider = mat.NewDense(1, nFeatures, nil)
	}
	if scaler.Tmp == nil {
		scaler.Tmp = mat.NewDense(1, nSamples, nil)
	}
	for c := 0; c < nFeatures; c++ {
		for r := 0; r < nSamples; r++ {
			scaler.Tmp.Set(0, r, X.At(r, c))
		}
		sort.Float64s(scaler.Tmp.RawRowView(0))
		if scaler.Center {
			scaler.Median.Set(0, c, stat.Quantile(0.5, stat.Empirical, scaler.Tmp.RawRowView(0), nil))
		}
		if scaler.Scale {
			q1 := stat.Quantile(scaler.Quantiles.Left, stat.Empirical, scaler.Tmp.RawRowView(0), nil)
			q2 := stat.Quantile(scaler.Quantiles.Right, stat.Empirical, scaler.Tmp.RawRowView(0), nil)
			scaler.QuantileDivider.Set(0, c, q2-q1)
			scaler.QuantileDivider.Apply(
				func(r int, c int, v float64) float64 {
					if v == 0.0 {
						return 1.0
					}
					return v
				},
				scaler.QuantileDivider)
		}
	}
	return scaler
}
func (scaler *RobustScaler) Transform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	Xout = mat.DenseCopyOf(X)
	Xout.Apply(func(i int, j int, x float64) float64 {
		res := x
		if scaler.Center {
			res = res - scaler.Median.At(0, j)
		}
		if scaler.Scale {
			res = res / scaler.QuantileDivider.At(0, j)
		}
		return res
	}, X)
	return Xout, base.ToDense(Y)
}
func (scaler *RobustScaler) FitTransform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	scaler.Fit(X, Y)
	return scaler.Transform(X, Y)
}
func (scaler *RobustScaler) InverseTransform(X, Y *mat.Dense) (Xout, Yout *mat.Dense) {
	if X == nil {
		return X, Y
	}
	Xout = mat.DenseCopyOf(X)
	Xout.Apply(func(i int, j int, x float64) float64 {
		res := x
		if scaler.Scale {
			res = res * scaler.QuantileDivider.At(0, j)
		}
		if scaler.Center {
			res = res + scaler.Median.At(0, j)
		}
		return res
	}, X)
	return Xout, Y
}
func IncrementalMeanAndVar(X, lastMean, lastVariance *mat.Dense,
	lastSampleCount int) (updatedMean, updatedVariance *mat.Dense, updatedSampleCount int) {
	newSampleCount, nFeatures := X.Dims()
	lastSum := mat.DenseCopyOf(lastMean)
	lastSum.Scale(float(lastSampleCount), lastSum)
	newSum := mat.NewDense(1, nFeatures, nil)
	for i := 0; i < newSampleCount; i++ {
		newSum.Add(newSum, X.RowView(i).T())
	}
	updatedSampleCount = lastSampleCount + newSampleCount
	updatedMean = mat.NewDense(1, nFeatures, nil)
	updatedMean.Add(lastSum, newSum)
	updatedMean.Scale(1./float(updatedSampleCount), updatedMean)
	newUnnormalizedVariance := mat.NewDense(1, nFeatures, nil)
	updatedUnnormalizedVariance := mat.NewDense(1, nFeatures, nil)
	newMean := mat.NewDense(1, nFeatures, nil)
	newMean.Scale(1./float(newSampleCount), newSum)
	tmp := mat.NewDense(1, nFeatures, nil)
	for i := 0; i < newSampleCount; i++ {
		tmp.Sub(X.RowView(i).T(), newMean)
		tmp.MulElem(tmp, tmp)
		newUnnormalizedVariance.Add(newUnnormalizedVariance, tmp)
	}
	if lastSampleCount == 0 {  
		updatedUnnormalizedVariance.CloneFrom(newUnnormalizedVariance)
	} else {
		lastOverNewCount := float(lastSampleCount) / float(newSampleCount)
		lastUnnormalizedVariance := mat.NewDense(1, nFeatures, nil)
		lastUnnormalizedVariance.CloneFrom(lastVariance)
		lastUnnormalizedVariance.Scale(float(lastSampleCount), lastUnnormalizedVariance)
		tmp.CloneFrom(lastSum)
		tmp.Scale(1./lastOverNewCount, tmp)
		tmp.Add(tmp, newSum)
		tmp.Mul(tmp, tmp)
		tmp.Scale(lastOverNewCount/float(updatedSampleCount), tmp)
		updatedUnnormalizedVariance.CloneFrom(lastUnnormalizedVariance)
		updatedUnnormalizedVariance.Add(updatedUnnormalizedVariance, newUnnormalizedVariance)
		updatedUnnormalizedVariance.Add(updatedUnnormalizedVariance, tmp)
	}
	updatedVariance = mat.NewDense(1, nFeatures, nil)
	updatedVariance.CloneFrom(updatedUnnormalizedVariance)
	updatedVariance.Scale(1./float(updatedSampleCount), updatedVariance)
	return updatedMean, updatedVariance, updatedSampleCount
}
type PolynomialFeatures struct {
	Degree                       int
	InteractionOnly, IncludeBias bool
	Powers                       [][]int
}
func NewPolynomialFeatures(degree int) *PolynomialFeatures {
	return &PolynomialFeatures{Degree: degree, InteractionOnly: false, IncludeBias: true}
}
func (poly *PolynomialFeatures) TransformerClone() base.Transformer {
	var clone = *poly
	return &clone
}
func (poly *PolynomialFeatures) Fit(Xmatrix, Ymatrix mat.Matrix) base.Fiter {
	X := base.ToDense(Xmatrix)
	_, nFeatures := X.Dims()
	poly.Powers = make([][]int, 0)
	comb := combinationsWithReplacement
	if poly.InteractionOnly {
		comb = combinations
	}
	start := 0
	if !poly.IncludeBias {
		start = 1
	}
	for i := start; i <= poly.Degree; i++ {
		for c := range comb(intrange(nFeatures), i) {
			poly.Powers = append(poly.Powers, bincount(c, nFeatures))
		}
	}
	return poly
}
func (poly *PolynomialFeatures) Transform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	nSamples, _ := X.Dims()
	Xout = mat.NewDense(nSamples, len(poly.Powers), nil)
	xi, xo := base.ToDense(X).RawMatrix(), Xout.RawMatrix()
	for i, ipos, opos := 0, 0, 0; i < nSamples; i, ipos, opos = i+1, ipos+xi.Stride, opos+xo.Stride {
		for ioutput, p := range poly.Powers {
			v := 1.
			for j, pj := range p {
				v *= math.Pow(xi.Data[ipos+j], float(pj))
			}
			xo.Data[opos+ioutput] = v
		}
	}
	return Xout, base.ToDense(Y)
}
func (poly *PolynomialFeatures) FitTransform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	poly.Fit(X, Y)
	return poly.Transform(X, Y)
}
func (poly *PolynomialFeatures) InverseTransform(X, Y *mat.Dense) (Xout, Yout *mat.Dense) {
	if X == nil {
		return X, Y
	}
	type jt struct{ jorig, jpoly int }
	var jts []jt
	nSamples, _ := X.Dims()
	XoutCols := 0
	intsum := func(a []int) int {
		var s int
		for _, v := range a {
			s += v
		}
		return s
	}
	intmaxidx := func(a []int) int {
		i := 0
		for j, v := range a {
			if v > a[i] {
				i = j
			}
		}
		return i
	}
	for ioutput, p := range poly.Powers {
		var jMax = intmaxidx(p)
		var sumpj = intsum(p)
		if sumpj == 1 {
			jts = append(jts, jt{jMax, ioutput})
			if jMax >= XoutCols {
				XoutCols = jMax + 1
			}
		}
	}
	Xout = mat.NewDense(nSamples, XoutCols, nil)
	for _, pair := range jts {
		for i := 0; i < nSamples; i++ {
			Xout.Set(i, pair.jorig, X.At(i, pair.jpoly))
		}
	}
	return
}
func DenseMean(Xmean *mat.Dense, X mat.Matrix) *mat.Dense {
	nSamples, nFeatures := X.Dims()
	if Xmean == nil {
		Xmean = mat.NewDense(1, nFeatures, nil)
	}
	Xmean.Apply(func(i int, j int, v float64) float64 {
		for i := 0; i < nSamples; i++ {
			v += X.At(i, j)
		}
		return v / float64(nSamples)
	}, Xmean)
	return Xmean
}
func AddDummyFeature(X *mat.Dense) {
	nSamples, nFeatures := X.Dims()
	X1 := mat.NewDense(nSamples, nFeatures+1, nil)
	X1.Apply(func(i, j int, _ float64) float64 {
		if j == 0 {
			return 1
		}
		return X.At(i, j-1)
	}, X1)
	X.CloneFrom(X1)
}
type OneHotEncoder struct {
	NValues, FeatureIndices []int
	Values                  [][]float64
}
func NewOneHotEncoder() *OneHotEncoder {
	return &OneHotEncoder{}
}
func (m *OneHotEncoder) TransformerClone() base.Transformer {
	var clone = *m
	return &clone
}
func (m *OneHotEncoder) Fit(Xmatrix, Ymatrix mat.Matrix) base.Fiter {
	X := base.ToDense(Xmatrix)
	NSamples, NFeatures := X.Dims()
	m.NValues = make([]int, NFeatures)
	m.FeatureIndices = make([]int, NFeatures+1)
	m.Values = make([][]float64, NFeatures)
	for feature := 0; feature < NFeatures; feature++ {
		cmap := make(map[float64]bool)
		for sample := 0; sample < NSamples; sample++ {
			val := X.At(sample, feature)
			cmap[val] = true
		}
		m.NValues[feature] = len(cmap)
		m.FeatureIndices[feature+1] = m.FeatureIndices[feature] + len(cmap)
		vals := make([]float64, len(cmap))
		ival := 0
		for v := range cmap {
			vals[ival] = v
			ival++
		}
		sort.Float64s(vals)
		m.Values[feature] = vals
	}
	return m
}
func (m *OneHotEncoder) Transform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	NSamples, nfeatures := X.Dims()
	Yout = base.ToDense(Y)
	columns := 0
	for feature := 0; feature < nfeatures; feature++ {
		ovo := false
		if ovo {
			columns++
		} else {
			columns += m.NValues[feature]
		}
	}
	Xout = mat.NewDense(NSamples, columns, nil)
	baseColumn := 0
	for feature := 0; feature < nfeatures; feature++ {
		ovo := false
		if ovo {
			for sample := 0; sample < NSamples; sample++ {
				v := Y.At(sample, feature)
				Yout.Set(sample, baseColumn, v)
			}
			baseColumn++
			break
		}
		cmap := make(map[float64]int)
		for i, v := range m.Values[feature] {
			cmap[v] = i
		}
		for sample := 0; sample < NSamples; sample++ {
			v := X.At(sample, feature)
			Xout.Set(sample, baseColumn+cmap[v], 1.)
		}
		baseColumn += m.NValues[feature]
	}
	return
}
func (m *OneHotEncoder) FitTransform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	m.Fit(X, Y)
	return m.Transform(X, Y)
}
func (m *OneHotEncoder) InverseTransform(X, Y *mat.Dense) (Xout, Yout *mat.Dense) {
	nSamples, _ := X.Dims()
	nFeatures := len(m.NValues)
	Yout = Y
	Xout = mat.NewDense(nSamples, nFeatures, nil)
	for feature := 0; feature < nFeatures; feature++ {
		cstart, cend := m.FeatureIndices[feature], m.FeatureIndices[feature+1]
		for sample := 0; sample < nSamples; sample++ {
			classNo := floats.MaxIdx(X.RawRowView(sample)[cstart:cend])
			Xout.Set(sample, feature, float64(classNo))
		}
	}
	return
}
type Shuffler struct {
	Perm        []int
	RandomState base.Source
}
func NewShuffler() *Shuffler { return &Shuffler{} }
func (m *Shuffler) TransformerClone() base.Transformer {
	var clone = *m
	return &clone
}
func (m *Shuffler) Fit(Xmatrix, Ymatrix mat.Matrix) base.Fiter {
	X := base.ToDense(Xmatrix)
	Perm := rand.Perm
	if m.RandomState != base.Source(nil) {
		Perm = rand.New(m.RandomState).Perm
	}
	m.Perm = Perm(X.RawMatrix().Rows)
	return m
}
func (m *Shuffler) Transform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	Xout, Yout = mat.DenseCopyOf(X), mat.DenseCopyOf(Y)
	xmat, ymat := Xout.RawMatrix(), Yout.RawMatrix()
	var dstxpos, dstypos, j int
	var v float64
	for i, srcxpos, srcypos := 0, 0, 0; i < len(m.Perm); i, srcxpos, srcypos = i+1, srcxpos+xmat.Stride, srcypos+ymat.Stride {
		if m.Perm[i] > i {
			dstxpos, dstypos = m.Perm[i]*xmat.Stride, m.Perm[i]*ymat.Stride
			for j = 0; j < xmat.Cols; j++ {
				v = xmat.Data[srcxpos+j]
				xmat.Data[srcxpos+j] = xmat.Data[dstxpos+j]
				xmat.Data[dstxpos+j] = v
			}
			for j = 0; j < ymat.Cols; j++ {
				v = ymat.Data[srcypos+j]
				ymat.Data[srcypos+j] = ymat.Data[dstypos+j]
				ymat.Data[dstypos+j] = v
			}
		}
	}
	return
}
func (m *Shuffler) FitTransform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	m.Fit(X, Y)
	return m.Transform(X, Y)
}
func (m *Shuffler) InverseTransform(X, Y *mat.Dense) (Xout, Yout *mat.Dense) {
	xmat, ymat := X.RawMatrix(), Y.RawMatrix()
	var srcxpos, srcypos, dstxpos, dstypos, j int
	var v float64
	for i := len(m.Perm) - 1; i >= 0; i-- {
		if m.Perm[i] > i {
			srcxpos, dstxpos = m.Perm[i]*xmat.Stride, i*xmat.Stride
			srcypos, dstypos = m.Perm[i]*ymat.Stride, i*ymat.Stride
			for j = 0; j < xmat.Stride; j++ {
				v = xmat.Data[srcxpos+j]
				xmat.Data[srcxpos+j] = xmat.Data[dstxpos+j]
				xmat.Data[dstxpos+j] = v
			}
			for j = 0; j < ymat.Stride; j++ {
				v := ymat.Data[srcypos+j]
				ymat.Data[srcypos+j] = ymat.Data[dstypos+j]
				ymat.Data[dstypos+j] = v
			}
		}
	}
	return X, Y
}
type Binarizer struct{ Threshold float64 }
func NewBinarizer() *Binarizer { return &Binarizer{} }
func (m *Binarizer) TransformerClone() base.Transformer {
	var clone = *m
	return &clone
}
func (m *Binarizer) Fit(Xmatrix, Ymatrix mat.Matrix) base.Fiter {
	return m
}
func (m *Binarizer) Transform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	rmx := base.ToDense(X).RawMatrix()
	Xout = mat.NewDense(rmx.Rows, rmx.Cols, nil)
	rmXout := Xout.RawMatrix()
	Yout = base.ToDense(Y)
	for r, xpos := 0, 0; r < rmx.Rows; r, xpos = r+1, xpos+rmx.Stride {
		for c := 0; c < rmx.Cols; c++ {
			if rmx.Data[xpos+c] > m.Threshold {
				rmXout.Data[xpos+c] = 1
			} else {
				rmXout.Data[xpos+c] = 0
			}
		}
	}
	return
}
func (m *Binarizer) FitTransform(X, Y mat.Matrix) (Xout, Yout *mat.Dense) {
	m.Fit(X, Y)
	return m.Transform(X, Y)
}
type MaxAbsScaler struct {
	Scale, MaxAbs []float64
	NSamplesSeen  int
}
func NewMaxAbsScaler() *MaxAbsScaler { return &MaxAbsScaler{} }
func (m *MaxAbsScaler) TransformerClone() base.Transformer {
	var clone = *m
	return &clone
}
func (m *MaxAbsScaler) Fit(Xmatrix, Ymatrix mat.Matrix) base.Fiter {
	X, Y := base.ToDense(Xmatrix), base.ToDense(Ymatrix)
	Xmat := X.RawMatrix()
	m.MaxAbs = make([]float64, Xmat.Cols)
	m.Scale = make([]float64, Xmat.Cols)
	return m.PartialFit(X, Y)
}
func (m *MaxAbsScaler) PartialFit(X, Y *mat.Dense) base.Transformer {
	Xmat := X.RawMatrix()
	for jX := 0; jX < Xmat.Rows*Xmat.Stride; jX = jX + Xmat.Stride {
		for i, v := range Xmat.Data[jX : jX+Xmat.Cols] {
			if v < 0. {
				v = -v
			}
			if v > m.MaxAbs[i] {
				m.MaxAbs[i] = v
			}
		}
	}
	for i, v := range m.MaxAbs {
		if v > 0. {
			m.Scale[i] = v
		} else {
			m.Scale[i] = 1.
		}
	}
	m.NSamplesSeen += Xmat.Rows
	return m
}