From 8f5671db610e1e455b33b54986ac2e57de0da0d7 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Tue, 20 Jun 2017 16:47:50 +0200
Subject: [PATCH] update following 23/05 TODOs

---
 pkg/DESCRIPTION         |  1 +
 pkg/R/F_Neighbors.R     | 43 +++++++++++++++++++----------------------
 pkg/R/Forecaster.R      | 23 +++++++++-------------
 pkg/R/J_LastValue.R     | 13 +++++++++++++
 pkg/R/J_Zero.R          |  5 +++--
 pkg/R/computeForecast.R | 14 ++++++--------
 reports/year2015.gj     |  6 +++---
 7 files changed, 55 insertions(+), 50 deletions(-)
 create mode 100644 pkg/R/J_LastValue.R

diff --git a/pkg/DESCRIPTION b/pkg/DESCRIPTION
index e3a14af..14d0551 100644
--- a/pkg/DESCRIPTION
+++ b/pkg/DESCRIPTION
@@ -41,3 +41,4 @@ Collate:
     'Forecast.R'
     'Data.R'
     'A_NAMESPACE.R'
+    'J_LastValue.R'
diff --git a/pkg/R/F_Neighbors.R b/pkg/R/F_Neighbors.R
index 8eb1ddc..0295cd5 100644
--- a/pkg/R/F_Neighbors.R
+++ b/pkg/R/F_Neighbors.R
@@ -57,7 +57,7 @@ NeighborsForecaster = R6::R6Class("NeighborsForecaster",
 			if (!opera)
 				tdays = setdiff(tdays, today) #always exclude current day
 
-			# Shortcut if window is known #TODO: cross-validation for number of days, on similar (yerste)days
+			# Shortcut if window is known
 			if (hasArg("window"))
 			{
 				return ( private$.predictShapeAux(data, tdays, today, predict_from, horizon,
@@ -99,6 +99,11 @@ NeighborsForecaster = R6::R6Class("NeighborsForecaster",
 				best_window_exo = optimize(
 					errorOnLastNdays, c(0,7), simtype="exo")$minimum
 			}
+			if (local)
+			{
+				best_window_local = optimize(
+					errorOnLastNdays, c(3,30), simtype="none")$minimum
+			}
 
 			best_window =
 				if (simtype == "endo")
@@ -107,8 +112,10 @@ NeighborsForecaster = R6::R6Class("NeighborsForecaster",
 					best_window_exo
 				else if (simtype == "mix")
 					c(best_window_endo,best_window_exo)
-				else #none: value doesn't matter
-					1
+				else #none: no value
+					NULL
+			if (local)
+				best_window = c(best_window, best_window_local)
 
 			return( private$.predictShapeAux(data, tdays, today, predict_from, horizon, local,
 				best_window, simtype, opera, TRUE) )
@@ -126,21 +133,22 @@ NeighborsForecaster = R6::R6Class("NeighborsForecaster",
 			if (local)
 			{
 				# limit=Inf to not censor any day (TODO: finite limit? 60?)
-				tdays = getSimilarDaysIndices(today, data, limit=Inf, same_season=TRUE,
+				tdays <- getSimilarDaysIndices(today, data, limit=Inf, same_season=TRUE,
 					days_in=tdays_cut, operational=opera)
+				nb_neighbs <- round( window[length(window)] )
 				# TODO: 10 == magic number
-				tdays = .getConstrainedNeighbs(today, data, tdays, min_neighbs=10)
+				tdays <- .getConstrainedNeighbs(today, data, tdays, min_neighbs=nb_neighbs)
 				if (length(tdays) == 1)
 				{
 					if (final_call)
 					{
 						private$.params$weights <- 1
 						private$.params$indices <- tdays
-						private$.params$window <- 1
+						private$.params$window <- window
 					}
 					return ( data$getSerie(tdays[1])[predict_from:horizon] )
 				}
-				max_neighbs = 12 #TODO: 10 or 12 or... ?
+				max_neighbs = nb_neighbs #TODO: something else?
 				if (length(tdays) > max_neighbs)
 				{
 					distances2 <- .computeDistsEndo(data, today, tdays, predict_from)
@@ -153,23 +161,20 @@ NeighborsForecaster = R6::R6Class("NeighborsForecaster",
 
 			if (simtype == "endo" || simtype == "mix")
 			{
-				# Compute endogen similarities using given window
-				window_endo = ifelse(simtype=="mix", window[1], window)
-
 				# Distances from last observed day to selected days in the past
 				# TODO: redundant computation if local==TRUE
 				distances2 <- .computeDistsEndo(data, today, tdays, predict_from)
 
-				simils_endo <- .computeSimils(distances2, window_endo)
+				# Compute endogen similarities using the given window
+				simils_endo <- .computeSimils(distances2, window[1])
 			}
 
 			if (simtype == "exo" || simtype == "mix")
 			{
-				# Compute exogen similarities using given window
-				window_exo = ifelse(simtype=="mix", window[2], window)
-
 				distances2 <- .computeDistsExo(data, today, tdays)
 
+				# Compute exogen similarities using the given window
+				window_exo = ifelse(simtype=="mix", window[2], window[1])
 				simils_exo <- .computeSimils(distances2, window_exo)
 			}
 
@@ -195,15 +200,7 @@ NeighborsForecaster = R6::R6Class("NeighborsForecaster",
 			{
 				private$.params$weights <- similarities
 				private$.params$indices <- tdays
-				private$.params$window <-
-					if (simtype=="endo")
-						window_endo
-					else if (simtype=="exo")
-						window_exo
-					else if (simtype=="mix")
-						c(window_endo,window_exo)
-					else #none
-						1
+				private$.params$window <- window
 			}
 
 			return (prediction)
diff --git a/pkg/R/Forecaster.R b/pkg/R/Forecaster.R
index 784f86e..a10f0bd 100644
--- a/pkg/R/Forecaster.R
+++ b/pkg/R/Forecaster.R
@@ -28,9 +28,8 @@
 #'
 #' @section Methods:
 #' \describe{
-#' \item{\code{initialize(data, pjump)}}{
-#'   Initialize a Forecaster object with a Data object and a jump prediction function,
-#'   or NULL if \code{predictShape()} returns an adjusted curve.}
+#' \item{\code{initialize(pjump)}}{
+#'   Initialize a Forecaster object with a jump prediction function.}
 #' \item{\code{predictSerie(data,today,memory,predict_from,horizon,...)}}{
 #'   Predict the next curve (at index today) from predict_from to horizon (hours), using
 #'   \code{memory} days in the past.}
@@ -63,19 +62,15 @@ Forecaster = R6::R6Class("Forecaster",
 			if (is.na(predicted_shape))
 				return (NA)
 
-			predicted_delta <-
-				if (is.null(private$.pjump))
-					NULL
-				else
-					private$.pjump(data,today,memory,predict_from,horizon,private$.params,...)
+			predicted_delta <- private$.pjump(data, today, memory, predict_from,
+					horizon, private$.params, first_pred=predicted_shape[1], ...)
 
-			# Predicted shape is aligned on the end of current day + jump (if jump!=NULL)
+			# Predicted shape is aligned on the end of current day + jump
 			c( data$getSerie(today)[if (predict_from>=2) 1:(predict_from-1) else c()],
-				predicted_shape + ifelse( is.null(private$.pjump),
-					0,
-					predicted_delta - predicted_shape[1] +
-						ifelse(predict_from>=2,
-							data$getSerie(today)[predict_from-1], tail(data$getSerie(today-1),1)) ) )
+				(predicted_shape - predicted_shape[1]) + #shape with first_pred = 0
+				ifelse(predict_from>=2, #last observed value
+					data$getSerie(today)[predict_from-1], tail(data$getSerie(today-1),1)) +
+				predicted_delta ) #jump
 		},
 		predictShape = function(data, today, memory, predict_from, horizon, ...)
 			NULL #empty default implementation: to implement in inherited classes
diff --git a/pkg/R/J_LastValue.R b/pkg/R/J_LastValue.R
new file mode 100644
index 0000000..4190b9c
--- /dev/null
+++ b/pkg/R/J_LastValue.R
@@ -0,0 +1,13 @@
+#' getLastValueJumpPredict
+#'
+#' Just predict zero "jump" (for reference, benchmarking at least).
+#'
+#' @inheritParams computeForecast
+#' @inheritParams getZeroJumpPredict
+#'
+#' @aliases J_LastValue
+#'
+getLastValueJumpPredict = function(data, today, memory, horizon, params, ...)
+{
+	0
+}
diff --git a/pkg/R/J_Zero.R b/pkg/R/J_Zero.R
index fb15e3d..16b6ed5 100644
--- a/pkg/R/J_Zero.R
+++ b/pkg/R/J_Zero.R
@@ -1,6 +1,6 @@
 #' getZeroJumpPredict
 #'
-#' Just predict zero "jump" (for reference, benchmarking at least).
+#' "Reset level to 0": jump by -A where A is the last observed value.
 #'
 #' @inheritParams computeForecast
 #' @param today Index of the current day (predict tomorrow)
@@ -10,5 +10,6 @@
 #'
 getZeroJumpPredict = function(data, today, memory, horizon, params, ...)
 {
-	0
+	list(...)$first_pred - ifelse( predict_from >= 2,
+		data$getSerie(today)[predict_from-1], tail(data$getSerie(today-1),1) )
 }
diff --git a/pkg/R/computeForecast.R b/pkg/R/computeForecast.R
index e1b29b6..082ba80 100644
--- a/pkg/R/computeForecast.R
+++ b/pkg/R/computeForecast.R
@@ -19,10 +19,9 @@
 #'   \itemize{
 #'     \item Persistence : use last (similar) day
 #'     \item Neighbors: re-use the weights from F_Neighbors
-#'     \item Zero: just output 0 (no adjustment)
+#'     \item LastValue: start serie with last observed value
+#'     \item Zero: no adjustment => use shape prediction only
 #'   }
-#'   If pjump=NULL, then no adjustment is performed (output of \code{predictShape()} is
-#'   used directly).
 #' @param predict_from First time step to predict.
 #' @param memory Data depth (in days) to be used for prediction.
 #' @param horizon Last time step to predict.
@@ -36,7 +35,7 @@
 #' ts_data <- system.file("extdata","pm10_mesures_H_loc.csv",package="talweg")
 #' exo_data <- system.file("extdata","meteo_extra_noNAs.csv",package="talweg")
 #' data <- getData(ts_data, exo_data, limit=200)
-#' pred <- computeForecast(data, 100:130, "Persistence", "Zero",
+#' pred <- computeForecast(data, 100:130, "Persistence", "LastValue",
 #'   predict_from=8, memory=50, horizon=12, ncores=1)
 #' \dontrun{
 #' #Sketch for real-time mode:
@@ -77,15 +76,14 @@ computeForecast = function(data, indices, forecaster, pjump, predict_from,
 		stop("Indices out of range")
 	if (!is.character(forecaster))
 		stop("forecaster (name): character")
-	if (!is.null(pjump) && !is.character(pjump))
-		stop("pjump (function): character or NULL")
+	if (!is.character(pjump))
+		stop("pjump (function): character")
 
 	pred = Forecast$new( sapply(indices, function(i) integerIndexToDate(i,data)) )
 	forecaster_class_name = getFromNamespace(
 		paste(forecaster,"Forecaster",sep=""), "talweg")
 
-	if (!is.null(pjump))
-		pjump <- getFromNamespace(paste("get",pjump,"JumpPredict",sep=""), "talweg")
+	pjump <- getFromNamespace(paste("get",pjump,"JumpPredict",sep=""), "talweg")
 	forecaster = forecaster_class_name$new(pjump)
 
 	computeOneForecast <- function(i)
diff --git a/reports/year2015.gj b/reports/year2015.gj
index 58340e7..678b962 100644
--- a/reports/year2015.gj
+++ b/reports/year2015.gj
@@ -14,11 +14,11 @@ indices = seq(as.Date("2015-01-01"),as.Date("2015-12-31"),"days")
 -----r
 p1 = computeForecast(data, indices, "Neighbors", "Neighbors",
 	predict_from=P, horizon=H, simtype="mix", local=FALSE)
-p2 = computeForecast(data, indices, "Neighbors", NULL,
+p2 = computeForecast(data, indices, "Neighbors", "Zero",
 	predict_from=P, horizon=H, simtype="none", local=TRUE)
-p3 = computeForecast(data, indices, "Average", "Zero",
+p3 = computeForecast(data, indices, "Average", "LastValue",
 	predict_from=P, horizon=H)
-p4 = computeForecast(data, indices, "Persistence", "Zero",
+p4 = computeForecast(data, indices, "Persistence", "LastValue",
 	predict_from=P, horizon=H, same_day=TRUE)
 -----r
 e1 = computeError(data, p1, P, H)
-- 
2.44.0