//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** @author John Miller * @version 1.6 * @date Sat Jun 13 01:27:00 EST 2017 * @see LICENSE (MIT style license file). * * @title Model: Random Walk */ package scalation.analytics package forecaster import scala.collection.mutable.Set import scala.math.{max, min} import scalation.linalgebra.{MatriD, MatrixD, VectoD, VectorD} import scalation.plot.Plot import scalation.random.{Normal, Uniform} import scalation.stat.{Statistic, vectorD2StatVector} import scalation.util.banner import Fit._ import ForecasterVec.testInSamp import RollingValidation.trSize //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** The `RandomWalk` class provides basic time series analysis capabilities. * For a 'RandomWalk' model with the time series data stored in vector 'y', the * next value 'y_t = y(t)' may be predicted based on the prior value of 'y' and its noise: *

* y_t = y_t-1 + e_t *

* where 'e' is the noise vector. * Random Walk is a special case of AR(1) with the parameter set to one. *------------------------------------------------------------------------------ * @param y the response vector (time series data) * @param hparam the hyper-parameters */ class RandomWalk (y: VectoD, hparam: HyperParameter = null) extends ForecasterVec (y, 1) with NoFeatureSelectionF { private val DEBUG = true // debug flag //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Return the model name including its current hyper-parameter. */ override def modelName: String = "RandomWalk" //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Train/fit an `RandomWalk` model to the times-series data in vector 'y_'. * Note: for `RandomWalk` there are no parameters to train. * @param x_null the data/input matrix (ignored) * @param y_ the response/output vector (currently only works for y) */ override def train (x_null: MatriD, y_ : VectoD): RandomWalk = { super.train (null, y_) this } // train //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Return the parameter vector (its null). */ def parameter: VectoD = null //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Return a vector that is the predictions (zero-centered) of a random walk model. */ def predictAllz (): VectoD = { val zp = new VectorD (m) // forecasts for all time points t zp(0) = z(0) // copy first actual value into zp for (t <- 1 until m) zp(t) = z(t-1) // estimate the rest values for zp zp // return vector of predicted values } // predictAllz //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Forecast values for all 'm' time points and all horizons (1 through 'h'-steps ahead). * Record these in the 'yf' matrix, where *

* yf(t, k) = k-steps ahead forecast for y_t *

* Note, 'yf.col(0)' is set to 'y' (the actual time-series values). * @param h the maximum forecasting horizon, number of steps ahead to produce forecasts */ def forecastAll (h: Int): MatriD = { yf = new MatrixD (m, h+1) // forecasts for all time points t & horizons to h yf.setCol (0, y) // first column is actual values, horizon 0 for (k <- 1 to h) { val c = 1 // cut point from actual to forecasted values for (t <- 0 until c) yf(t, k) = y(t) // copy first c actual values for (t <- c until m) { // forecast the rest yf(t, k) = yf(max (0, t-1), max (0, k-1)) } // for if (DEBUG) println (s"forecastAll: yf.col ($k) = ${yf.col (k)}") } // for yf // return matrix of forecasted values } // forecastAll //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Produce h-steps ahead forecast on the testing data during cross validation. * @param y the current response vector * @param t the time point/index to be forecast * @param h the forecasting horizon, number of steps ahead to produce forecast */ override def forecastX (y: VectoD, t: Int, h: Int = 1): Double = y(max (0, t-1)) } // RandomWalk class //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** The `RandomWalk` companion object provides factory methods for the `RandomWalk` class. */ object RandomWalk { //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** Create a `RandomWalk` object. * @param y the response vector (time series data) * @param hparam the hyper-parameters */ def apply (y: VectoD, hparam: HyperParameter = null): RandomWalk = { new RandomWalk (y, hparam) } // apply } // RandomWalk object //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** The `RandomWalkTest` object is used to test the `RandomWalk` class. * > runMain scalation.analytics.forecaster.RandomWalkTest */ object RandomWalkTest extends App { val m = 100 val noise = Uniform (-5, 5) val y = VectorD (for (i <- 0 until m) yield i + noise.gen) banner ("Build AR(1) Model") val ar = new AR (y) // time series model ar.train (null, y).eval () // train for AR(1) model println (ar.report) new Plot (null, y, ar.predictAll (), "Plot of y, AR(1) vs. t", true) banner ("Build RandomWalk Model") val rw = new RandomWalk (y) // time series model rw.train (null, y).eval () // train for RandomWalk model println (rw.report) new Plot (null, y, rw.predictAll (), "Plot of y, RandomWalk vs. t", true) } // RandomWalkTest object //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** The `RandomWalkTest2` object is used to test the `RandomWalk` class. The order * hyper-parameter 'p' is re-assigned. * > runMain scalation.analytics.forecaster.RandomWalkTest2 */ object RandomWalkTest2 extends App { val m = 30 val noise = Normal (0, 1) val y = VectorD (for (i <- 0 until m) yield i + noise.gen) banner ("Build AR(1) Model") val ar = new AR (y) // time series model ar.train (null, y).eval () // train for AR(1) model println (ar.report) new Plot (null, y, ar.predictAll (), "Plot of y, AR(1) vs. t", true) banner ("Build RandomWalk Model") val rw = new RandomWalk (y) // time series model rw.train (null, y).eval () // train for RandomWalk model println (rw.report) new Plot (null, y, rw.predictAll (), s"Plot of y, RandomWalk vs. t", true) banner ("Make Forecasts") val steps = 10 // number of steps for the forecasts val rw_f = rw.forecast (steps) println (s"$steps-step ahead forecasts using RandomWalk model = $rw_f") new Plot (null, rw_f, null, s"Plot RandomWalk forecasts vs. t", true) } // RandomWalkTest2 object //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** The `RandomWalkTest3` object is used to test the `RandomWalk` class. * Forecasting lake levels. * @see cran.r-project.org/web/packages/fpp/fpp.pdf * > runMain scalation.analytics.forecaster.RandomWalkTest3 */ object RandomWalkTest3 extends App { import ForecasterVec.y var rw: RandomWalk = null for (h <- 1 to 2) { // forecasting horizon banner (s"Build RandomWalk Model for h = $h") rw = new RandomWalk (y) // create model for time series data rw.train (null, y).eval () // train model and evaluate val yf = rw.forecastAll (h) val yf2 = testInSamp (y, 0, rw, h) // in-sample test } // for banner ("Make Forecasts") val steps = 2 // number of steps for the forecasts val rw_f = rw.forecast (steps) println (s"$steps-step ahead forecasts using RandomWalk model = $rw_f") banner ("Rolling Validation") val stats = SimpleRollingValidation.crossValidate2 (rw, kt_ = 2) // val stats = RollingValidation.crossValidate2 (rw, kt_ = 2) Fit.showQofStatTable (stats) } // RandomWalkTest3 object //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** The `RandomWalkTest4` object is used to test the `RandomWalk` class. Forecasting travel times. * > runMain scalation.analytics.forecaster.RandomWalkTest4 */ object RandomWalkTest4 extends App { val path = BASE_DIR + "travelTime.csv" val data = MatrixD (path) val (t, y) = (data.col(0), data.col(1)) banner ("Build AR(1) Model") val ar = new AR (y) // time series model ar.train (null, y).eval () // train for AR(1) model println (ar.report) new Plot (t, y, ar.predictAll (), "Plot of y, AR(1) vs. t", true) banner (s"Build RandomWalk model") val rw = new RandomWalk (y) // time series model rw.train (null, y).eval () // train for RandomWalk model println (rw.report) new Plot (t, y, rw.predictAll (), s"Plot of y, RandomWalk vs. t", true) banner ("Make Forecasts") val steps = 1 // number of steps for the forecasts val rw_f = rw.forecast (steps) println (s"$steps-step ahead forecasts using RandomWalk model = $rw_f") } // RandomWalkTest4 object //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** The `RandomWalkTest5` object is used to test the `RandomWalk` class. * Having a quadratic trend. * > runMain scalation.analytics.forecaster.RandomWalkTest5 */ object RandomWalkTest5 extends App { val sig2 = 10000.0 val noise = Normal (0.0, sig2) val n = 50 val t = VectorD.range (0, n) val y = VectorD (for (i <- 0 until n) yield 40 * (i-1) - (i-2) * (i-2) + noise.gen) banner ("Build AR(1) Model") val ar = new AR (y) // time series model ar.train (null, y).eval () // train for AR(1) model println (ar.report) new Plot (t, y, ar.predictAll (), "Plot of y, AR(1) vs. t", true) banner ("Build RandomWalk model") val rw = new RandomWalk (y) // time series model rw.train (null, y).eval () // train for RandomWalk model println (rw.report) new Plot (t, y, rw.predictAll (), s"Plot of y, RandomWalk vs. t", true) banner ("Make Forecasts") val steps = 2 // number of steps for the forecasts val rw_f = rw.forecast (steps) println (s"$steps-step ahead forecasts using RandomWalk model = $rw_f") } // RandomWalkTest5 object //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /** The `RandomWalkTest6` object is used to test the `RandomWalk` class. * Using a Random Walk model on white noise. * > runMain scalation.analytics.forecaster.RandomWalkTest6 */ object RandomWalkTest6 extends App { val sig2 = 1.0 val noise = Normal (0.0, sig2) val m = 100 val y = VectorD (for (i <- 0 until m) yield noise.gen) banner ("Build RandomWalk model") val rw = new RandomWalk (y) // time series model rw.train (null, y).eval () // train for RandomWalk model println (rw.report) new Plot (null, y, rw.predictAll (), s"Plot of y, RandomWalk vs. t", true) banner ("Plot the ACF and PACF") rw.plotFunc2 (rw.acF, "ACF") rw.plotFunc2 (rw.pacF, "PACF") } // RandomWalkTest6 object