//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** @author John Miller * @version 1.5 * @date Tue Oct 9 17:40:30 EDT 2018 * @see LICENSE (MIT style license file). */ package scalation.analytics import scala.math.round import scalation.linalgebra.{MatriD, MatrixD, VectoD, VectorD, VectoI, VectorI} import PredictorMat.pullResponse import RegTechnique._ //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** The `RoundRegression` class supports rounded multiple linear regression. * In this case, 'x' is multi-dimensional [1, x_1, ... x_k]. Fit the parameter * vector 'b' in the transformed regression equation *

* y = round (b dot x) + e = round (b_0 + b_1 * x_1 + b_2 * x_2 ... b_k * x_k) + e *

* where 'e' represents the residuals (the part not explained by the model). * Use Least-Squares (minimizing the residuals) to fit the parameter vector 'b' * @param x the design/data matrix * @param y the response vector * @param technique the technique used to solve for b in x.t*x*b = x.t*y */ class RoundRegression (x: MatriD, y: VectoD, technique: RegTechnique = QR) extends Regression (x, y, null, null, technique) { //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Round the predicted response values 'yp' to their nearest integer values. * @param yp the unrounded predicted response vector */ def vround (yp: VectoD): VectoI = { val yq = new VectorI (yp.dim) for (i <- yp.range) yq(i) = round (yp(i)).toInt yq } // vround //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Compute the error and useful diagnostics */ override def eval () { e = y - vround (x * b).toDouble // residual/error vector diagnose (e) } // eval //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Predict the value of y = f(z) by evaluating the formula y = b dot z, * e.g., (b_0, b_1, b_2) dot (1, z_1, z_2). * @param z the new vector to predict */ override def predict (z: VectoD): Double = b dot z def ipredict (z: VectoD): Int = round (b dot z).toInt //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Predict the value of y = f(z) by evaluating the formula y = b dot z for * each row of matrix z. * @param z the new matrix to predict */ override def predict (z: MatriD): VectoD = z * b def ipredict (z: MatriD): VectoI = vround (z * b) //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Perform 'k'-fold cross-validation. * @param k the number of folds * @param rando whether to use randomized cross-validation */ override def crossVal (k: Int = 10, rando: Boolean = true) { crossValidate ((x: MatriD, y: VectoD) => new RoundRegression (x, y), k, rando) } // crossVal } // RoundRegression class //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** The `RoundRegression` companion object provides a factory method. */ object RoundRegression { //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Create a `RoundRegression` object for an integer response vector. * @param x the design/data matrix * @param y the integer response vector * @param technique the technique used to solve for b in x.t*x*b = x.t*y */ def apply (x: MatriD, y: VectoI, technique: RegTechnique): RoundRegression = { new RoundRegression (x, y.toDouble, technique) } // apply //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Create a `RoundRegression` object using a combined matrix. * @param xy the combined data/design matrix and response vector * @param technique the technique used to solve for b in x.t*x*b = x.t*y */ def apply (xy: MatriD, technique: RegTechnique = QR): RoundRegression = { val (x, y) = pullResponse (xy) new RoundRegression (x, y, technique) } // apply } // RoundRegression class //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** The `RoundRegressionTest` object tests `RoundRegression` class using the following * regression equation. *

* y = round (b dot x) = round (b_0 + b_1*x_1 + b_2*x_2). *

* > runMain scalation.analytics.RoundRegressionTest */ object RoundRegressionTest extends App { // 1 x0 x1 y val xy = new MatrixD ((9, 4), 1, 0, 0, 1, // 9-by-4 matrix 1, 0, 1, 0, 1, 0, 2, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 2, 0, 1, 2, 0, 1, 1, 2, 1, 0, 1, 2, 2, 1) println ("xy = " + xy) val (x, y) = pullResponse (xy) val rrg = new RoundRegression (x, y) rrg.train ().eval () println ("parameter = " + rrg.parameter) println ("fitMap = " + rrg.fitMap) val yp = rrg.predict (x) val ypi = rrg.vround (yp) for (j <- y.range) println (s"y_j = ${y(j)} \t ypi_j = ${ypi(j)} \t yp_j = ${yp(j)}") } // RoundRegressionTest object