| 1 | #' @include z_util.R |
| 2 | |
| 3 | #' @title Algorithm |
| 4 | #' |
| 5 | #' @description Generic class to represent an algorithm |
| 6 | #' |
| 7 | #' @field H The window [t-H+1, t] considered for prediction at time step t+1 |
| 8 | #' @field data Data frame of the last H experts forecasts + observations. |
| 9 | #' |
| 10 | Algorithm = setRefClass( |
| 11 | Class = "Algorithm", |
| 12 | |
| 13 | fields = list( |
| 14 | H = "numeric", |
| 15 | data = "data.frame" |
| 16 | ), |
| 17 | |
| 18 | methods = list( |
| 19 | initialize = function(...) |
| 20 | { |
| 21 | "Initialize (generic) Algorithm object" |
| 22 | |
| 23 | callSuper(...) |
| 24 | if (length(H) == 0 || H < 1) |
| 25 | H <<- Inf |
| 26 | }, |
| 27 | inputNextForecasts = function(x) |
| 28 | { |
| 29 | "Obtain a new series of vectors of experts forecasts (1 to K)" |
| 30 | |
| 31 | nd = nrow(data) |
| 32 | nx = nrow(x) |
| 33 | indices = (nd+1):(nd+nx) |
| 34 | |
| 35 | appendedData = as.data.frame(matrix(nrow=nx, ncol=ncol(data), NA)) |
| 36 | names(appendedData) = names(data) |
| 37 | data <<- rbind(data, appendedData) |
| 38 | data[indices,names(x)] <<- x |
| 39 | }, |
| 40 | inputNextObservations = function(y) |
| 41 | { |
| 42 | "Obtain the observations corresponding to last input forecasts" |
| 43 | |
| 44 | #if all experts made a large unilateral error and prediction is very bad, remove data |
| 45 | n = nrow(data) |
| 46 | lastTime = data[n,"Date"] |
| 47 | xy = subset(data, subset=(Date == lastTime)) |
| 48 | xy[,"Measure"] = y |
| 49 | x = xy[,names(xy) != "Measure"] |
| 50 | y = xy[,"Measure"] |
| 51 | ranges = apply(x-y, 1, range) |
| 52 | predictableIndices = (ranges[2,] > -MAX_ERROR & ranges[1,] < MAX_ERROR) |
| 53 | # predictableIndices = 1:length(y) |
| 54 | data <<- data[1:(n-nrow(xy)),] |
| 55 | data <<- rbind(data, xy[predictableIndices,]) |
| 56 | |
| 57 | #oldest rows are removed to prevent infinitely growing memory usage, |
| 58 | #or to allow a window effect (parameter H) |
| 59 | delta = nrow(data) - min(H, MAX_HISTORY) |
| 60 | if (delta > 0) |
| 61 | data <<- data[-(1:delta),] |
| 62 | }, |
| 63 | predict_withNA = function() |
| 64 | { |
| 65 | "Predict observations corresponding to the last input forecasts. Potential NAs" |
| 66 | |
| 67 | n = nrow(data) |
| 68 | if (data[n,"Date"] == 1) |
| 69 | { |
| 70 | #no measures added so far |
| 71 | return (rep(NA, n)) |
| 72 | } |
| 73 | |
| 74 | nx = n - nrow(subset(data, subset = (Date == data[n,"Date"]))) |
| 75 | x = data[(nx+1):n, !names(data) %in% c("Date","Measure","Station")] |
| 76 | experts = names(x) |
| 77 | prediction = c() |
| 78 | |
| 79 | #extract a maximal submatrix of data without NAs |
| 80 | |
| 81 | iy = getNoNAindices(x, 2) |
| 82 | if (!any(iy)) |
| 83 | { |
| 84 | #all columns of x have at least one NA |
| 85 | return (rep(NA, n-nx)) |
| 86 | } |
| 87 | |
| 88 | data_noNA = data[1:nx,c(experts[iy], "Measure")] |
| 89 | ix = getNoNAindices(data_noNA) |
| 90 | if (!any(ix)) |
| 91 | { |
| 92 | #no full line with NA-pattern similar to x[,iy] |
| 93 | return (rep(NA, n-nx)) |
| 94 | } |
| 95 | |
| 96 | data_noNA = data_noNA[ix,] |
| 97 | xiy = as.data.frame(x[,iy]) |
| 98 | names(xiy) = names(x)[iy] |
| 99 | res = predict_noNA(data_noNA, xiy) |
| 100 | #basic sanitization: force all values >=0 |
| 101 | res[res < 0.] = 0. |
| 102 | return (res) |
| 103 | }, |
| 104 | predict_noNA = function(XY, x) |
| 105 | { |
| 106 | "Predict observations corresponding to x. No NAs" |
| 107 | |
| 108 | #empty default implementation: to implement in inherited classes |
| 109 | } |
| 110 | ) |
| 111 | ) |