% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/shelf_life.R
\name{shelf_life}
\alias{shelf_life}
\title{Compute the forecast shelf life}
\usage{
shelf_life(
  predictions,
  response_scale,
  ci_level = 0.9,
  threshold = 1,
  time_col = NULL,
  min_slope_for_projection = 1e-04,
  max_extrapolation_factor = 10,
  ...,
  plausible_range = NULL
)
}
\arguments{
\item{predictions}{An \code{et_prediction} or \code{et_prediction_list}.}

\item{response_scale}{Numeric vector of length 2 (\code{c(min, max)})
giving the response scale used as the denominator in the
CI-width / range ratio.  For unbounded responses use
\code{range(training_data$response)} as a conservative default, or
supply a biologically motivated interval.
The effective range is \code{diff(response_scale)}.}

\item{ci_level}{Numeric.  The credible interval level to use (default
0.90).  Must be present in the \code{et_prediction} object.}

\item{threshold}{Numeric.  CI width / response scale above which the
forecast is uninformative (default 1.0).}

\item{time_col}{Character.  Optional column in
\code{predictions$newdata} to use as the time axis.  If \code{NULL},
observation index is used.}

\item{min_slope_for_projection}{Numeric.  Minimum linear slope (of
ratio vs.\ time) required to attempt extrapolation when all periods
are informative.  Below this value the shelf life is reported as a
lower bound only.  Default \code{1e-4}.}

\item{max_extrapolation_factor}{Numeric.  Cap on how far the linear
projection may reach beyond the observed window.  If the projected
crossing time exceeds
\code{max(time) + max_extrapolation_factor * (max(time) - min(time))},
the result is reported as a lower bound instead of a projection.
Set to \code{Inf} to disable the cap.  Default \code{10}.}

\item{...}{Unused.}

\item{plausible_range}{Deprecated.  Use \code{response_scale} instead.}
}
\value{
An \code{et_shelf_life} object (a \code{data.frame}) with columns:
  \describe{
    \item{obs_id}{Observation index.}
    \item{time}{Time axis value.}
    \item{ci_width}{Width of the credible interval at \code{ci_level}.}
    \item{plausible_range}{Effective response scale (scalar diff).}
    \item{ratio}{CI width / response scale.}
    \item{informative}{Logical; \code{TRUE} when ratio < threshold.}
  }
  For grouped predictions a \code{group} column is prepended.
  The object carries three attributes that drive \code{print()}:
  \describe{
    \item{\code{horizon}}{Named list with elements \code{value},
      \code{type} (\code{"observed"}, \code{"projected"}, or
      \code{"lower_bound"}), \code{last_informative},
      \code{description}, and (for projected) \code{se_t_star}.}
    \item{\code{horizon_by_group}}{For grouped objects: named list of
      per-group horizon lists.}
    \item{\code{threshold}}{The threshold value used.}
  }
}
\description{
Quantifies \emph{when} a forecast becomes uninformative by comparing the
width of credible intervals to a response scale.  A forecast is
uninformative when its CI width exceeds \code{threshold * response_scale}.
}
\details{
The function operates in three modes depending on the available data and
whether the uninformative threshold is crossed within the forecast window:

\describe{
  \item{\strong{Observed}}{The threshold is crossed within the
    forecast/validation window.  The shelf life is the first time point
    at which \code{ratio >= threshold}.}
  \item{\strong{Projected}}{All forecast periods remain informative but
    the CI/range ratio is trending upward.  A linear trend is fitted to
    the ratios and extrapolated to estimate when the threshold would be
    reached.  The projected crossing time \eqn{t^* = (\tau - a) / b}
    (where \eqn{\tau} is the threshold, \eqn{a} the fitted intercept,
    \eqn{b} the fitted slope) is reported together with a Monte Carlo
    standard error \code{se_t_star} derived via the delta method.}
  \item{\strong{Lower bound}}{All forecast periods are informative with
    no upward trend in the ratio.  The shelf life is reported as a lower
    bound: \code{> last observed time}.}
}

The intended workflow is:
\enumerate{
  \item Fit the model (\code{\link{et_fit}}).
  \item Predict on held-out data or a future time window
    (\code{\link{et_predict}}).
  \item Call \code{shelf_life()} on those predictions.
  \item If the threshold is not crossed in the held-out window, the
    \emph{projected} mode extrapolates the horizon automatically.
}
}
\examples{
\donttest{
set.seed(1)
df  <- data.frame(y = rnorm(20), year = 2001:2020, x1 = rnorm(20))
fit <- et_fit(y ~ x1, data = df,
              chains = 1, iter = 500, warmup = 250,
              cores = 1, refresh = 0)
new_df <- data.frame(x1 = rnorm(10), year = 2021:2030)
pred   <- et_predict(fit, newdata = new_df,
                     n_draws = 200, n_perturb = 50)
sl <- shelf_life(pred,
                 response_scale            = range(df$y),
                 ci_level                  = 0.90,
                 threshold                 = 1.0,
                 time_col                  = "year",
                 max_extrapolation_factor  = 10)
print(sl)
}
}
