% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/pep_flag_outliers.R
\name{pep_flag_outliers}
\alias{pep_flag_outliers}
\title{Flag Phenological Outliers}
\usage{
pep_flag_outliers(
  pep,
  by = c("s_id", "genus", "species", "phase_id"),
  method = c("30day", "mad", "iqr", "zscore", "gam_residual", "mahalanobis"),
  threshold = NULL,
  center = c("median", "mean"),
  min_obs = 5,
  formula = day ~ s(year) + s(alt) + s(lat) + s(s_id, bs = "re"),
  min_n_per_group = 50,
  flag_only = TRUE
)
}
\arguments{
\item{pep}{A \code{pep} object or data.table with phenological observations.
Must contain \code{year}, \code{day}, and grouping columns.}

\item{by}{Character vector of columns defining groups for outlier detection.
Default \code{c("s_id", "genus", "species", "phase_id")} detects outliers
within each station-species-phase combination.}

\item{method}{Character. Outlier detection method:
\describe{
\item{"30day"}{(Default) Flag observations >30 days from group mean/median.
The classic Schaber-Badeck approach for phenological data.}
\item{"mad"}{Use Median Absolute Deviation. Flag if |deviation| > k * MAD,
where k is set by \code{threshold}.}
\item{"iqr"}{Use Interquartile Range. Flag if outside (Q1 - k\emph{IQR, Q3 + k}IQR).}
\item{"zscore"}{Flag if |z-score| > threshold (default 3).}
\item{"gam_residual"}{Fit a GAM (via \code{mgcv::gam()}) per group
using the supplied \code{formula} — by default accounting for year
trend, elevation, latitude, and a station random intercept — and
flag observations whose robust-z-scored residual exceeds
\code{threshold}. Detects covariate-inconsistent anomalies that
the within-group \code{"30day"} rule misses. Falls back to
\code{"30day"} on groups with fewer than \code{min_n_per_group}
observations or when the model fails to converge.}
\item{"mahalanobis"}{Treat each station-year as a vector of DOYs
across phases and flag station-years whose \strong{robust} Mahalanobis
distance exceeds the \code{threshold}. The centre and covariance
are estimated by the Minimum Covariance Determinant (MCD)
estimator of Rousseeuw (1984), via
\code{robustbase::covMcd()} — using the classical, non-robust
covariance here would let the outliers contaminate the
estimate and mask themselves. Under MCD the squared distance is
still approximately \eqn{\chi^2_p}, so the default threshold
is \eqn{\sqrt{\chi^2_{0.975, p}}}, where \eqn{p} is the number
of phases present in the group. Detects station-years whose
pattern across phases is jointly inconsistent (e.g.\ BBCH 60
and 65 impossibly close) even when each marginal DOY looks
fine. All rows within a flagged station-year are marked; the
\code{deviation} column stores the robust Mahalanobis distance.
Groups with too few complete station-years (fewer than
\eqn{\max(p + 5, 15)}) or a singular MCD fit fall back to the
30-day rule.}
}}

\item{threshold}{Numeric. Threshold for outlier detection.
\describe{
\item{For "30day":}{Days deviation to flag as outlier. Default 30.}
\item{For "mad":}{Number of MADs. Default 3 (~2.5 SD equivalent).}
\item{For "iqr":}{IQR multiplier. Default 1.5 (standard Tukey rule).}
\item{For "zscore":}{Z-score threshold. Default 3.}
\item{For "gam_residual":}{Robust-z threshold on residuals. Default 3.5.}
}}

\item{center}{Character. Central tendency measure: \code{"median"} (default,
more robust) or \code{"mean"}.}

\item{min_obs}{Integer. Minimum observations per group required for outlier
detection. Groups with fewer observations are skipped. Default 5.}

\item{formula}{Optional \code{formula} for \code{method = "gam_residual"}.
Default is
\code{day ~ s(year) + s(alt) + s(lat) + s(s_id, bs = "re")}; any
smooth term referencing a column missing from \code{pep} is silently
dropped.}

\item{min_n_per_group}{Integer. Minimum group size required before fitting
the GAM in \code{method = "gam_residual"}. Groups below this threshold
fall back to the \code{"30day"} rule. Default 50.}

\item{flag_only}{Logical. If \code{TRUE} (default), adds outlier flag columns
to data. If \code{FALSE}, removes flagged outliers from data.}
}
\value{
If \code{flag_only = TRUE}: The input data with additional columns:
\describe{
\item{is_outlier}{Logical. TRUE for flagged outliers.}
\item{deviation}{Numeric. Days deviation from expected value.}
\item{expected_doy}{Numeric. Expected DOY for the group.}
}
If \code{flag_only = FALSE}: Data with outliers removed.
}
\description{
Identifies outlier observations in phenological data using the 30-day rule
or statistical methods. Based on the approach described by Schaber & Badeck
(2002) for quality control of phenological observations.
}
\details{
The 30-day rule (Schaber & Badeck 2002) is widely used for phenological data
quality control. It flags observations that deviate more than 30 days from
the expected value for that station-species-phase combination. This threshold
is based on the observation that legitimate phenological variation rarely
exceeds one month.
}
\section{When to Use Each Method}{

\describe{
\item{30day}{Standard for phenological QC. Use when the 30-day biological
threshold is meaningful.}
\item{mad}{Robust to existing outliers. Good for initial screening of
potentially contaminated data.}
\item{iqr}{Standard statistical approach. Useful when comparing with
other quality metrics.}
\item{zscore}{Parametric approach. Use when data is approximately normal.}
}
}

\examples{
\donttest{
data(pep_seed)

# Flag outliers using 30-day rule
pep_flagged <- pep_flag_outliers(pep_seed)
table(pep_flagged$is_outlier)

# View flagged observations
pep_flagged[is_outlier == TRUE]

# Remove outliers instead of flagging
pep_clean <- pep_flag_outliers(pep_seed, flag_only = FALSE)

# Use MAD method with stricter threshold
pep_flagged <- pep_flag_outliers(pep_seed, method = "mad", threshold = 2.5)

# Group by country instead of station
pep_flagged <- pep_flag_outliers(pep_seed,
                              by = c("country", "genus", "phase_id"))
}

}
\references{
Schaber J, Badeck F-W (2002). Evaluation of methods for the combination of
phenological time series and outlier detection. Tree Physiology 22:973-982.

Rousseeuw P J (1984). Least median of squares regression.
\emph{Journal of the American Statistical Association} 79(388):871-880.
(Minimum Covariance Determinant estimator used by
\code{method = "mahalanobis"}.)
}
\seealso{
\code{\link{pep_quality}} for comprehensive quality assessment,
\code{\link{pheno_combine}} which uses residuals for outlier detection
}
\author{
Matthias Templ
}
