Skip to content

Commit d3b1159

Browse files
author
Resul Akay
committed
V0.1.1 release
1 parent 8f9d65c commit d3b1159

35 files changed

Lines changed: 597 additions & 659 deletions

CRAN-SUBMISSION

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Version: 0.1.1
2+
Date: 2022-10-23 19:55:28 UTC
3+
SHA: 8f9d65c189202cc31ce9d95940002e2dbfc29d4c

DESCRIPTION

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ Authors@R:
66
family = "Akay",
77
role = c("aut", "cre"),
88
email = "resulakay1@gmail.com")
9-
Description: The caretForecast package makes it easy to create conformal time
10-
series forecasts using the caret infrastructure. The primary function ARml
11-
provides access to state-of-the-art machine learning models for forecasting
12-
applications. The hyperparameter of each model is selected based on time series
13-
cross-validation, and forecasting is done recursively.
9+
Description: Conformal time series forecasting using the caret infrastructure.
10+
It provides access to state-of-the-art machine learning models for forecasting
11+
applications. The hyperparameter of each model is selected based on time
12+
series cross-validation, and forecasting is done recursively.
1413
License: GPL (>= 3)
1514
URL: https://github.com/Akai01/caretForecast
1615
BugReports: https://github.com/Akai01/caretForecast/issues

NAMESPACE

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,41 @@
11
# Generated by roxygen2: do not edit by hand
22

33
S3method(forecast,ARml)
4+
S3method(predict,conformalRegressor)
5+
S3method(residuals,ARml)
46
export("%<>%")
57
export("%>%")
68
export(ARml)
79
export(accuracy)
810
export(autolayer)
911
export(autoplot)
12+
export(conformalRegressor)
1013
export(forecast)
1114
export(get_var_imp)
1215
export(split_ts)
1316
export(suggested_methods)
1417
importFrom(caret,train)
1518
importFrom(caret,trainControl)
1619
importFrom(caret,varImp)
20+
importFrom(dplyr,bind_cols)
1721
importFrom(forecast,BoxCox)
1822
importFrom(forecast,BoxCox.lambda)
1923
importFrom(forecast,InvBoxCox)
20-
importFrom(forecast,accuracy)
2124
importFrom(forecast,autolayer)
2225
importFrom(forecast,autoplot)
23-
importFrom(forecast,bld.mbb.bootstrap)
2426
importFrom(forecast,fourier)
2527
importFrom(forecast,is.constant)
2628
importFrom(forecast,na.interp)
29+
importFrom(generics,accuracy)
30+
importFrom(generics,forecast)
2731
importFrom(magrittr,"%<>%")
2832
importFrom(magrittr,"%>%")
2933
importFrom(methods,is)
3034
importFrom(stats,"tsp<-")
3135
importFrom(stats,frequency)
3236
importFrom(stats,is.ts)
3337
importFrom(stats,predict)
34-
importFrom(stats,quantile)
38+
importFrom(stats,residuals)
3539
importFrom(stats,sd)
3640
importFrom(stats,start)
3741
importFrom(stats,time)

R/arml.R

Lines changed: 58 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,13 @@ ARml <- function(y,
100100
pre_process = NULL,
101101
cv = TRUE,
102102
cv_horizon = 4,
103-
initial_window = length(y) - max_lag - cv_horizon * 2,
103+
initial_window = NULL,
104104
fixed_window = FALSE,
105105
verbose = TRUE,
106106
seasonal = TRUE,
107107
K = frequency(y) / 2,
108108
tune_grid = NULL,
109-
lambda = "auto",
109+
lambda = NULL,
110110
BoxCox_method = c("guerrero", "loglik"),
111111
BoxCox_lower = -1,
112112
BoxCox_upper = 2,
@@ -118,27 +118,52 @@ ARml <- function(y,
118118
if ("ts" %notin% class(y)) {
119119
stop("y must be a univariate time series")
120120
}
121-
121+
freq <- stats::frequency(y)
122122
length_y <- length(y)
123123

124-
freq <- stats::frequency(y)
124+
if (c(length_y - freq - round(freq / 4)) < max_lag) {
125+
if(length_y > 3){
126+
max_lag <- length_y + 3 - length_y
127+
} else {
128+
max_lag <- 1
129+
}
130+
if(max_lag >= length_y - max_lag - 2){
131+
max_lag <- 1
132+
}
133+
134+
warning(paste("Input data is too short. setting max_lag = ", max_lag))
135+
}
125136

126-
if (length_y < freq) {
137+
if (length_y < 3) {
127138
stop("Not enough data to fit a model")
128139
}
140+
constant_data <- is.constant(na.interp(y))
141+
if (constant_data) {
142+
warning("Constant data, setting max_lag = 1, seasonal = FALSE, lambda = NULL,
143+
pre_process = NULL")
144+
pre_process <- NULL
145+
lambda <- NULL
146+
max_lag <- 1
147+
}
148+
149+
if (!is.null(xreg)) {
150+
constant_xreg <- any(apply(as.matrix(xreg), 2,
151+
function(x) {
152+
is.constant(na.interp(x))
153+
}
154+
)
155+
)
156+
if (constant_xreg) {
157+
warning("Constant xreg column, setting pre_process=NULL")
158+
pre_process <- NULL
159+
}
160+
}
129161

130162
if (max_lag <= 0) {
131163
warning("max_lag increased to 1. max_lag must be max_lag >= 1")
132164
max_lag <- 1
133165
}
134166

135-
if (c(length_y - freq - round(freq / 4)) < max_lag) {
136-
warning(paste(
137-
"Input data is too short. Reducing max_lags to ",
138-
round(length_y - freq - round(freq / 4))
139-
))
140-
max_lag <- round(length_y - freq - round(freq / 4))
141-
}
142167

143168
if (max_lag != round(max_lag)) {
144169
max_lag <- round(max_lag)
@@ -151,14 +176,6 @@ ARml <- function(y,
151176
}
152177
}
153178

154-
constant_y <- forecast::is.constant(forecast::na.interp(y))
155-
156-
if (constant_y) {
157-
warning("Constant data, setting max_lag = 1, lambda = NULL")
158-
lambda <- NULL
159-
max_lag = 1
160-
}
161-
162179
if (!is.null(xreg))
163180
{
164181
ncolxreg <- ncol(xreg)
@@ -185,7 +202,11 @@ ARml <- function(y,
185202
start = time(modified_y)[max_lag + 1],
186203
frequency = freq)
187204

188-
if (seasonal == TRUE | freq > 1)
205+
if(length_y - max_lag < freq + 1) {
206+
seasonal <- FALSE
207+
}
208+
209+
if (seasonal == TRUE & freq > 1)
189210
{
190211
if (K == freq / 2) {
191212
ncolx <- max_lag + K * 2 - 1
@@ -235,6 +256,22 @@ ARml <- function(y,
235256
}
236257
}
237258

259+
initial_window_setted <- FALSE
260+
if(is.null(initial_window)){
261+
initial_window <- length_y - max_lag - cv_horizon * 2
262+
message("initial_window = NULL. Setting initial_window = ", initial_window)
263+
initial_window_setted <- TRUE
264+
}
265+
if(initial_window<1 | initial_window >= nrow(x)){
266+
initial_window <- length_y - max_lag - 1
267+
cv_horizon <- 1
268+
if(initial_window_setted){
269+
warning("Resetting initial_window = ", initial_window, " cv_horizon = 1")
270+
} else {
271+
warning("Setting initial_window = ", initial_window, " cv_horizon = 1")
272+
}
273+
}
274+
238275
model <- caret::train(
239276
x = x,
240277
y = as.numeric(modified_y_2),
@@ -303,134 +340,3 @@ ARml <- function(y,
303340
class(output) <- "ARml"
304341
return(output)
305342
}
306-
307-
308-
#' @title Forecasting an ARml object
309-
#'
310-
#' @param object A list class of ARml
311-
#' @param h forecast horizon
312-
#' @param xreg Optionally, a numerical vector or matrix of future external
313-
#' regressors
314-
#' @param level Confidence level for prediction intervals.
315-
#' @param PI If TRUE, prediction intervals are produced, otherwise only point
316-
#' forecasts are calculated. If PI is FALSE, then level, fan, bootstrap and
317-
#' npaths are all ignored.
318-
#' @param num_bs Number of bootstrapped versions to generate.
319-
#' @param ... Other arguments pased to forecast::forecast()
320-
#' @return A list class of forecast containing the following elemets
321-
#' * x : The input time series
322-
#' * method : The name of the forecasting method as a character string
323-
#' * mean : Point forecasts as a time series
324-
#' * lower : Lower limits for prediction intervals
325-
#' * upper : Upper limits for prediction intervals
326-
#' * level : The confidence values associated with the prediction intervals
327-
#' * model : A list containing information about the fitted model
328-
#' * newxreg : A matrix containing regressors
329-
#' @author Resul Akay
330-
#'
331-
#' @examples
332-
#'
333-
#'library(caretForecast)
334-
#'
335-
#'train_data <- window(AirPassengers, end = c(1959, 12))
336-
#'
337-
#'test <- window(AirPassengers, start = c(1960, 1))
338-
#'
339-
#'ARml(train_data, caret_method = "lm", max_lag = 12) -> fit
340-
#'
341-
#'forecast(fit, h = length(test), level = c(80,95), PI = TRUE) -> fc
342-
#'
343-
#'autoplot(fc)+ autolayer(test)
344-
#'
345-
#'accuracy(fc, test)
346-
#'
347-
#' @export
348-
forecast.ARml <- function(object,
349-
h = frequency(object$y),
350-
xreg = NULL,
351-
level = c(80, 95),
352-
PI = FALSE,
353-
num_bs = 1000,
354-
...) {
355-
if (!is.null(object$xreg_fit)) {
356-
ncolxreg <- ncol(object$xreg_fit)
357-
}
358-
359-
if (is.null(xreg)) {
360-
if (!is.null(object$xreg_fit)) {
361-
stop("No regressors provided")
362-
}
363-
}
364-
365-
if (!is.null(xreg)) {
366-
if (is.null(object$xreg_fit)) {
367-
stop("No regressors provided to fitted model")
368-
}
369-
370-
if (ncol(xreg) != ncolxreg) {
371-
stop("Number of regressors does not match to fitted model")
372-
}
373-
374-
h <- nrow(xreg)
375-
newxreg1 <- xreg
376-
}
377-
378-
if (is.null(h)) {
379-
h <- ifelse(frequency(object$y) > 1, 2 * frequency(object$y), 10)
380-
}
381-
382-
if (is.null(xreg)) {
383-
newxreg1 <- NULL
384-
}
385-
386-
if (PI) {
387-
if (is.null(level)) {
388-
warning("level was not provided. Prediction intervals will not be returned")
389-
PI <- FALSE
390-
}
391-
}
392-
393-
lambda <- object$lambda
394-
BoxCox_biasadj <- object$BoxCox_biasadj
395-
BoxCox_fvar <- object$BoxCox_fvar
396-
397-
fc_x <- forecast_loop(object = object, xreg = newxreg1, h = h)
398-
x <- fc_x$x
399-
y <- fc_x$y
400-
401-
if (!is.null(lambda)) {
402-
y <- forecast::InvBoxCox(y,
403-
lambda = lambda,
404-
biasadj = BoxCox_biasadj,
405-
fvar = BoxCox_fvar)
406-
}
407-
408-
if (PI) {
409-
bs_pi <- pi(
410-
y = object$y,
411-
fc = y,
412-
num = num_bs,
413-
block_size = NULL,
414-
level = level
415-
)
416-
lower <- bs_pi[["lower"]]
417-
upper <- bs_pi[["upper"]]
418-
} else {
419-
lower <- NULL
420-
upper <- NULL
421-
}
422-
423-
output <- list(
424-
x = object$y,
425-
mean = y,
426-
lower = lower,
427-
upper = upper,
428-
fitted = object$fitted,
429-
level = level,
430-
newxreg = x,
431-
method = object$method,
432-
model = object$model
433-
)
434-
class(output) <- c("forecast", "forecastARml")
435-
return(output)
436-
}

R/fit_conformal_reg.R

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#' Fit a conformal regressor.
2+
#'
3+
#' @param residuals Model residuals.
4+
#' @param sigmas A vector of difficulty estimates
5+
#' @author Resul Akay
6+
#' @return A conformalRegressor object
7+
#'
8+
#' @references
9+
#' Boström, H., 2022. crepes: a Python Package for Generating Conformal
10+
#' Regressors and Predictive Systems. In Conformal and Probabilistic Prediction
11+
#' and Applications. PMLR, 179.
12+
#' \url{https://copa-conference.com/papers/COPA2022_paper_11.pdf}
13+
#'
14+
#' @export
15+
conformalRegressor <- function(residuals, sigmas=NULL) {
16+
abs_residuals <- abs(residuals)
17+
if(is.null(sigmas)){
18+
normalized <- FALSE
19+
alphas <- rev(sort(abs_residuals))
20+
} else {
21+
normalized <- TRUE
22+
alphas = rev(sort(abs_residuals/sigmas))
23+
}
24+
out <- list(
25+
alphas = alphas,
26+
normalized = normalized
27+
)
28+
out <- structure(out, class = "conformalRegressor")
29+
return(out)
30+
}

0 commit comments

Comments
 (0)