% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/char_lowess.R
\name{char_lowess}
\alias{char_lowess}
\title{Locally-weighted linear regression matching MATLAB charLowess.m / smooth()}
\usage{
char_lowess(y, x = NULL, span = 0.1, iter = 0L)
}
\arguments{
\item{y}{Numeric vector of values to smooth (NaN-free; bridge NaNs
before calling and restore afterward, matching the MATLAB workflow).}

\item{x}{Numeric vector of x-positions (same length as \code{y}).
If \code{NULL}, \code{seq_along(y)} is used (index coordinates).}

\item{span}{Smoothing span.
\itemize{
  \item \code{span >= 1}: number of data points.
  \item \code{span < 1}: fraction of data length.
}
Converted to integer \code{k = max(3, round(span * n))} or
\code{max(3, round(span))}, matching \code{charLowess.m} lines 63-68.}

\item{iter}{Number of bisquare robustness passes after the initial fit.
\itemize{
  \item \code{iter = 0}: plain lowess (1 total WLS pass, no robustness).
  \item \code{iter = 4}: robust lowess, matching MATLAB's \code{'rlowess'}
    which uses \code{nIter = 5} total iterations (1 initial + 4 updates).
}}
}
\value{
Numeric vector of smoothed values, same length as \code{y}.
}
\description{
Pure-R implementation of the shifted-window lowess algorithm from MATLAB's
\code{charLowess.m} v2.0, which itself replicates MATLAB's
\code{smooth(y, k, 'lowess')} from the Curve Fitting Toolbox.  Replaces
the previous wrapper around \code{\link[stats]{lowess}}, which used a
shrinking window at record boundaries and different tricubic weight
normalisation, causing systematic differences of ~1e-3 in \code{charBkg}.
}
\details{
## Algorithm
  For each point \eqn{i}, a window of exactly \eqn{k} points is selected:
  the window is centred on \eqn{i} at interior points and SHIFTED (not
  shrunk) at record boundaries, maintaining \eqn{k} neighbours throughout.
  Each neighbour \eqn{j} receives a tricubic weight:
  \deqn{w_j = \left(1 - \left(\frac{|i-j|}{d_{\max}}\right)^3\right)^3}
  where \eqn{d_{\max}} is the distance to the \strong{furthest} point in
  the window (not the half-window radius).  A weighted least-squares line
  is fitted and evaluated at \eqn{i}.  This matches
  \code{charLowess.m} lines 109-147 exactly.

  For \code{iter > 0} (\code{'rlowess'}), bisquare robustness weights are
  computed from the residuals after each WLS pass and multiplied into the
  tricubic weights for the next pass (matching \code{charLowess.m} lines
  151-157).

  ## Why not stats::lowess()?
  \code{stats::lowess()} trims (shrinks) the window at boundaries and
  normalises tricubic weights by the half-window radius \eqn{hw}.  The two
  differences compound to ~1e-3 absolute error in \code{charBkg} for the CO
  dataset (500 yr / 15 yr = 33-point window, 17-point boundary zone).
}
\seealso{
[char_smooth()], [char_thresh_local()]
}
