Skip to content

Commit 6ec6a09

Browse files
Merge pull request #440 from tidyverts/439-update-ets-to-allow-missing-values
Updated ETS to allow missing values
2 parents 70e55ac + 3838d55 commit 6ec6a09

File tree

10 files changed

+328
-265
lines changed

10 files changed

+328
-265
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,4 @@ Encoding: UTF-8
6868
Language: en-GB
6969
Roxygen: list(markdown = TRUE, roclets=c('rd', 'collate',
7070
'namespace'))
71-
RoxygenNote: 7.3.2
71+
RoxygenNote: 7.3.3

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ S3method(glance,model_mean)
5454
S3method(hfitted,ARIMA)
5555
S3method(hfitted,ETS)
5656
S3method(interpolate,ARIMA)
57+
S3method(interpolate,ETS)
5758
S3method(interpolate,TSLM)
5859
S3method(interpolate,model_mean)
5960
S3method(model_sum,AR)

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Improvements
44

5+
* `ETS()` now supports missing values.
56
* Documentation improvements.
67

78
## Bug fixes

R/ets.R

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ train_ets <- function(.data, specials, opt_crit,
1717
y <- unclass(.data)[[measured_vars(.data)]]
1818
idx <- unclass(.data)[[index_var(.data)]]
1919

20-
if (any(is.na(y))) {
21-
abort("ETS does not support missing values.")
22-
}
23-
2420
# Build possible models
2521
model_opts <- expand.grid(
2622
errortype = ets_spec$error$method,
@@ -33,7 +29,7 @@ train_ets <- function(.data, specials, opt_crit,
3329

3430
# Remove bad models
3531
if (NROW(model_opts) > 1) {
36-
if (min(y) <= 0) {
32+
if (min(y, na.rm = TRUE) <= 0) {
3733
model_opts <- model_opts[model_opts$errortype != "M", ]
3834
}
3935
if (restrict) {
@@ -720,4 +716,26 @@ initial_ets_states <- function(object) {
720716
)
721717
colnames(states_init) <- unsplit(states_names, states_type)
722718
states_init
723-
}
719+
}
720+
721+
722+
#' @inherit interpolate.model_mean
723+
#'
724+
#' @examples
725+
#' library(tsibbledata)
726+
#'
727+
#' olympic_running |>
728+
#' model(mean = ETS(Time)) %>%
729+
#' interpolate(olympic_running)
730+
#' @export
731+
interpolate.ETS <- function(object, new_data, specials, ...) {
732+
# Get missing values
733+
y <- unclass(new_data)[[measured_vars(new_data)]]
734+
miss_val <- which(is.na(y))
735+
# Forward fitted values
736+
forward_fits <- object$est[[".fitted"]][miss_val]
737+
# Ideally, we would also apply the model to the reversed time series
738+
# and get the backward fitted values, then combine the two.
739+
new_data[[measured_vars(new_data)]][miss_val] <- forward_fits
740+
new_data
741+
}

R/etsmodel.R

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -140,16 +140,11 @@ estimate_ets <- function(y, m, init.state, errortype, trendtype, seasontype,
140140
alpha = unname(alpha), beta = unname(beta),
141141
gamma = unname(gamma), phi = unname(phi), init.state
142142
)
143-
if (errortype == "A") {
144-
fits <- y - e$e
145-
} else {
146-
fits <- y / (1 + e$e)
147-
}
148143

149144
return(list(
150145
loglik = -0.5 * e$lik, aic = aic, bic = bic, aicc = aicc,
151146
mse = mse, amse = amse, mae = mae,
152-
residuals = e$e, fitted = fits,
147+
residuals = e$e, fitted = e$fits,
153148
states = states, par = fit.par
154149
))
155150
}
@@ -429,7 +424,7 @@ pegelsresid.C <- function(y, m, init.state, errortype, trendtype, seasontype, da
429424
p <- length(init.state)
430425
x <- numeric(p * (n + 1))
431426
x[1:p] <- init.state
432-
e <- numeric(n)
427+
e <- fits <- numeric(n)
433428
lik <- 0
434429
if (!damped) {
435430
phi <- 1
@@ -457,18 +452,15 @@ pegelsresid.C <- function(y, m, init.state, errortype, trendtype, seasontype, da
457452
as.double(gamma),
458453
as.double(phi),
459454
as.double(e),
455+
as.double(fits),
460456
as.double(lik),
461457
as.double(amse),
462458
as.integer(nmse),
459+
NAOK = TRUE,
463460
PACKAGE = "fable"
464461
)
465-
if (!is.na(Cout[[13]])) {
466-
if (abs(Cout[[13]] + 99999) < 1e-7) {
467-
Cout[[13]] <- NA
468-
}
469-
}
470462

471-
return(list(lik = Cout[[13]], amse = Cout[[14]], e = Cout[[12]], states = matrix(Cout[[3]], nrow = n + 1, ncol = p, byrow = TRUE)))
463+
return(list(lik = Cout[[14]], amse = Cout[[15]], e = Cout[[12]], fits = Cout[[13]], states = matrix(Cout[[3]], nrow = n + 1, ncol = p, byrow = TRUE)))
472464
}
473465

474466
admissible <- function(alpha, beta, gamma, phi, m) {

man/interpolate.ETS.Rd

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/etsTargetFunction.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ void EtsTargetFunction::init(std::vector<double> & p_y, int p_nstate, int p_erro
6969
// for(int i=0; i < n; i++) this->e.push_back(0);
7070
this->amse.resize(30, 0);
7171
this->e.resize(n, 0);
72+
this->fits.resize(n, 0);
7273

7374
}
7475

@@ -165,8 +166,7 @@ void EtsTargetFunction::eval(const double* p_par, int p_par_length) {
165166
for(int i=0; i <= p*this->y.size(); i++) state.push_back(0);
166167

167168
etscalc(&this->y[0], &this->n, &this->state[0], &this->m, &this->errortype, &this->trendtype, &this->seasontype,
168-
&this->alpha, &this->beta, &this->gamma, &this->phi, &this->e[0], &this->lik, &this->amse[0], &this->nmse);
169-
169+
&this->alpha, &this->beta, &this->gamma, &this->phi, &this->e[0], &this->fits[0], &this->lik, &this->amse[0], &this->nmse);
170170

171171
// Avoid perfect fits
172172
if (this->lik < -1e10) this->lik = -1e10;

src/etsTargetFunction.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@
55
extern "C" {
66

77
void etscalc(double *, int *, double *, int *, int *, int *, int *,
8-
double *, double *, double *, double *, double *, double *, double *, int *);
8+
double *, double *, double *, double *, double *, double *, double *, double *, int *);
99

10-
void cpolyroot(double *opr, double *opi, int *degree,
11-
double *zeror, double *zeroi, Rboolean *fail);
1210
}
13-
1411
class EtsTargetFunction {
1512

1613
public:
@@ -53,6 +50,7 @@ class EtsTargetFunction {
5350
double alpha, beta, gamma, phi;
5451

5552
std::vector<double> e;
53+
std::vector<double> fits;
5654
std::vector<double> amse;
5755

5856
double lik, objval;

0 commit comments

Comments
 (0)