% File src/library/stats/man/splinefun.Rd
% Part of the R package, https://www.R-project.org
% Copyright 1995-2025 R Core Team
% Distributed under GPL 2 or later

\name{splinefun}
\alias{spline}
\alias{splinefun}
\alias{splinefunH}
\title{Interpolating Splines}
\description{
  Perform cubic (or Hermite) spline interpolation of given data points,
  returning either a list of points obtained by the interpolation or a
  \emph{function} performing the interpolation.
}
\usage{
splinefun(x, y = NULL,
          method = c("fmm", "periodic", "natural", "monoH.FC", "hyman"),
          ties = mean)

spline(x, y = NULL, n = 3*length(x), method = "fmm",
       xmin = min(x), xmax = max(x), xout, ties = mean)

splinefunH(x, y, m)
}
\arguments{
  \item{x, y}{vectors giving the coordinates of the points to be
    interpolated.  Alternatively a single plotting structure can be
    specified: see \code{\link{xy.coords}}.

    \code{y} must be increasing or decreasing for \code{method = "hyman"}.
  }
  \item{m}{(for \code{splinefunH()}): vector of \emph{slopes}
    \eqn{m_i}{m[i]} at the points \eqn{(x_i,y_i)}{(x[i],y[i])}; these
    together determine the \bold{H}ermite \dQuote{spline} which is
    piecewise cubic, (only) \emph{once} differentiable continuously.}
  \item{method}{specifies the type of spline to be used.  Possible
    values are \code{"fmm"}, \code{"natural"}, \code{"periodic"},
    \code{"monoH.FC"} and \code{"hyman"}.  Can be abbreviated.}
  \item{n}{if \code{xout} is left unspecified, interpolation takes place
       at \code{n} equally spaced points spanning the interval
       [\code{xmin}, \code{xmax}].}
  \item{xmin, xmax}{left-hand and right-hand endpoint of the
       interpolation interval (when \code{xout} is unspecified).}
  \item{xout}{an optional set of values specifying where interpolation
    is to take place.}
  \item{ties}{handling of tied \code{x} values.  The string
    \code{"ordered"} or a function (or the name of a function) taking a
    single vector argument and returning a single number or a length-2
    \code{\link{list}} of both, see \code{\link{approx}} and its
    \sQuote{Details} section, and the example below.}
}
\details{
  The inputs can contain missing values which are deleted, so at least
  one complete \code{(x, y)} pair is required.
  If \code{method = "fmm"}, the spline used is that of
  \bibcitet{R:Forsythe+Malcolm+Moler:1977}
  (an exact cubic is fitted through the four points at each
  end of the data, and this is used to determine the end conditions).
  Natural splines are used when \code{method = "natural"}, and periodic
  splines when \code{method = "periodic"}.

  The method \code{"monoH.FC"} computes a \emph{monotone} Hermite spline
  according to the method of \I{Fritsch} and \I{Carlson}.  It does so by
  determining slopes such that the Hermite spline, determined by
  \eqn{(x_i,y_i,m_i)}{(x[i],y[i],m[i])}, is monotone (increasing or
  decreasing) \bold{iff} the data are.

  Method \code{"hyman"} computes a \emph{monotone} cubic spline using
  \I{Hyman} filtering of an \code{method = "fmm"} fit for strictly monotonic
  inputs.

  These interpolation splines can also be used for extrapolation, that is
  prediction at points outside the range of \code{x}.  Extrapolation
  makes little sense for \code{method = "fmm"}; for natural splines it
  is linear using the slope of the interpolating curve at the nearest
  data point.
}
\value{
  \code{spline} returns a list containing components \code{x} and
  \code{y} which give the ordinates where interpolation took place and
  the interpolated values.

  \code{splinefun} returns a function with formal arguments \code{x} and
  \code{deriv}, the latter defaulting to zero.  This function
  can be used to evaluate the interpolating cubic spline
  (\code{deriv} = 0), or its derivatives (\code{deriv} = 1, 2, 3) at the
  points \code{x}, where the spline function interpolates the data
  points originally specified.  It uses data stored in its environment
  when it was created, the details of which are subject to change.
}
\section{Warning}{
  The value returned by \code{splinefun} contains references to the code
  in the current version of \R: it is not intended to be saved and
  loaded into a different \R session.  This is safer in \R >= 3.0.0.
}
\references{
  \bibshow{*,
    R:Becker+Chambers+Wilks:1988,
    R:Dougherty+Edelman+Hyman:1989,
    R:Fritsch+Carlson:1980,
    R:Hyman:1983
  }
}
\seealso{
  \code{\link{approx}} and \code{\link{approxfun}} for constant and
  linear interpolation.

  Package \pkg{splines}, especially \code{\link[splines]{interpSpline}}
  and \code{\link[splines]{periodicSpline}} for interpolation splines.
  That package also generates spline bases that can be used for
  regression splines.

  \code{\link{smooth.spline}} for smoothing splines.
}
\author{
  R Core Team.

  Simon Wood for the original code for Hyman filtering.
}

\examples{
require(graphics)

op <- par(mfrow = c(2,1), mgp = c(2,.8,0), mar = 0.1+c(3,3,3,1))
n <- 9
x <- 1:n
y <- rnorm(n)
plot(x, y, main = paste("spline[fun](.) through", n, "points"))
lines(spline(x, y))
lines(spline(x, y, n = 201), col = 2)

y <- (x-6)^2
plot(x, y, main = "spline(.) -- 3 methods")
lines(spline(x, y, n = 201), col = 2)
lines(spline(x, y, n = 201, method = "natural"), col = 3)
lines(spline(x, y, n = 201, method = "periodic"), col = 4)
legend(6, 25, c("fmm","natural","periodic"), col = 2:4, lty = 1)

y <- sin((x-0.5)*pi)
f <- splinefun(x, y)
ls(envir = environment(f))
splinecoef <- get("z", envir = environment(f))
curve(f(x), 1, 10, col = "green", lwd = 1.5)
points(splinecoef, col = "purple", cex = 2)
curve(f(x, deriv = 1), 1, 10, col = 2, lwd = 1.5)
curve(f(x, deriv = 2), 1, 10, col = 2, lwd = 1.5, n = 401)
curve(f(x, deriv = 3), 1, 10, col = 2, lwd = 1.5, n = 401)
par(op)

## Manual spline evaluation --- demo the coefficients :
.x <- splinecoef$x
u <- seq(3, 6, by = 0.25)
(ii <- findInterval(u, .x))
dx <- u - .x[ii]
f.u <- with(splinecoef,
            y[ii] + dx*(b[ii] + dx*(c[ii] + dx* d[ii])))
stopifnot(all.equal(f(u), f.u))

## An example with ties (non-unique  x values):
set.seed(1); x <- round(rnorm(30), 1); y <- sin(pi * x) + rnorm(30)/10
plot(x, y, main = "spline(x,y)  when x has ties")
lines(spline(x, y, n = 201), col = 2)
## visualizes the non-unique ones:
tx <- table(x); mx <- as.numeric(names(tx[tx > 1]))
ry <- matrix(unlist(tapply(y, match(x, mx), range, simplify = FALSE)),
             ncol = 2, byrow = TRUE)
segments(mx, ry[, 1], mx, ry[, 2], col = "blue", lwd = 2)

## Another example with sorted x, but ties:
set.seed(8); x <- sort(round(rnorm(30), 1)); y <- round(sin(pi * x) + rnorm(30)/10, 3)
summary(diff(x) == 0) # -> 7 duplicated x-values
str(spline(x, y, n = 201, ties="ordered")) # all '$y' entries are NaN
## The default (ties=mean) is ok, but most efficient to use instead is
sxyo <- spline(x, y, n = 201, ties= list("ordered", mean))
sapply(sxyo, summary)# all fine now
plot(x, y, main = "spline(x,y, ties=list(\"ordered\", mean))  for when x has ties")
lines(sxyo, col="blue")

## An example of monotone interpolation
n <- 20
set.seed(11)
x. <- sort(runif(n)) ; y. <- cumsum(abs(rnorm(n)))
plot(x., y.)
curve(splinefun(x., y.)(x), add = TRUE, col = 2, n = 1001)
curve(splinefun(x., y., method = "monoH.FC")(x), add = TRUE, col = 3, n = 1001)
curve(splinefun(x., y., method = "hyman")   (x), add = TRUE, col = 4, n = 1001)
legend("topleft",
       paste0("splinefun( \"", c("fmm", "monoH.FC", "hyman"), "\" )"),
       col = 2:4, lty = 1, bty = "n")

## and one from Fritsch and Carlson (1980), Dougherty et al (1989)
x. <- c(7.09, 8.09, 8.19, 8.7, 9.2, 10, 12, 15, 20)
f <- c(0, 2.76429e-5, 4.37498e-2, 0.169183, 0.469428, 0.943740,
       0.998636, 0.999919, 0.999994)
s0 <- splinefun(x., f)
s1 <- splinefun(x., f, method = "monoH.FC")
s2 <- splinefun(x., f, method = "hyman")
plot(x., f, ylim = c(-0.2, 1.2))
curve(s0(x), add = TRUE, col = 2, n = 1001) -> m0
curve(s1(x), add = TRUE, col = 3, n = 1001)
curve(s2(x), add = TRUE, col = 4, n = 1001)
legend("right",
       paste0("splinefun( \"", c("fmm", "monoH.FC", "hyman"), "\" )"),
       col = 2:4, lty = 1, bty = "n")

## they seem identical, but are not quite:
xx <- m0$x
plot(xx, s1(xx) - s2(xx), type = "l",  col = 2, lwd = 2,
     main = "Difference   monoH.FC - hyman"); abline(h = 0, lty = 3)

x <- xx[xx < 10.2] ## full range: x <- xx .. does not show enough
ccol <- adjustcolor(2:4, 0.8)
matplot(x, cbind(s0(x, deriv = 2), s1(x, deriv = 2), s2(x, deriv = 2))^2,
        lwd = 2, col = ccol, type = "l", ylab = quote({{f*second}(x)}^2),
        main = expression({{f*second}(x)}^2 ~" for the three 'splines'"))
legend("topright",
       paste0("splinefun( \"", c("fmm", "monoH.FC", "hyman"), "\" )"),
       lwd = 2, col  =  ccol, lty = 1:3, bty = "n")
## --> "hyman" has slightly smaller  Integral f''(x)^2 dx  than "FC",
## here, and both are 'much worse' than the regular fmm spline.
}
\keyword{math}
\keyword{dplot}
