# File src/library/stats/R/splinefun.R # Part of the R package, https://www.R-project.org # # Copyright (C) 1995-2019 The R Core Team # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # A copy of the GNU General Public License is available at # https://www.R-project.org/Licenses/ #### 'spline' and 'splinefun' are very similar --- keep in sync! #### also consider ``compatibility'' with 'approx' and 'approxfun' splinefun <- function(x, y = NULL, method = c("fmm", "periodic", "natural", "monoH.FC", "hyman"), ties = mean) { x <- regularize.values(x, y, ties, missing(ties)) # -> (x,y) numeric of same length y <- x$y x <- x$x nx <- length(x) # large vectors ==> non-integer if(is.na(nx)) stop(gettextf("invalid value of %s", "length(x)"), domain = NA) if(nx == 0) stop("zero non-NA points") method <- match.arg(method) if(method == "periodic" && y[1L] != y[nx]) { warning("spline: first and last y values differ - using y[1L] for both") y[nx] <- y[1L] } if(method == "monoH.FC") { n1 <- nx - 1L ## - - - "Data preprocessing" - - - dy <- y[-1L] - y[-nx] # = diff(y) dx <- x[-1L] - x[-nx] # = diff(x) Sx <- dy / dx # Sx[k] = \Delta_k = (y_{k+1} - y_k)/(x_{k+1} - x_k), k=1:n1 m <- c(Sx[1L], (Sx[-1L] + Sx[-n1])/2, Sx[n1]) ## 1. ## use C, as we need to "serially" progress from left to right: m <- .Call(C_monoFC_m, m, Sx) ## Hermite spline with (x,y,m) : return(splinefunH0(x0 = x, y0 = y, m = m, dx = dx)) } ## else iMeth <- match(method, c("periodic", "natural", "fmm", "monoH.FC", "hyman")) if(iMeth == 5L) { dy <- diff(y) if(!(all(dy >= 0) || all(dy <= 0))) stop("'y' must be increasing or decreasing") } z <- .Call(C_SplineCoef, min(3L, iMeth), x, y) if(iMeth == 5L) z <- spl_coef_conv(hyman_filter(z)) rm(x, y, nx, method, iMeth, ties) function(x, deriv = 0L) { deriv <- as.integer(deriv) if (deriv < 0L || deriv > 3L) stop("'deriv' must be between 0 and 3") if (deriv > 0L) { ## For deriv >= 2, using approx() should be faster, but doing it correctly ## for all three methods is not worth the programmer's time... z0 <- double(z$n) z[c("y", "b", "c")] <- switch(deriv, list(y = z$b , b = 2*z$c, c = 3*z$d), # deriv = 1 list(y = 2*z$c, b = 6*z$d, c = z0), # deriv = 2 list(y = 6*z$d, b = z0, c = z0)) # deriv = 3 z[["d"]] <- z0 } ## yout[j] := y[i] + dx*(b[i] + dx*(c[i] + dx* d_i)) ## where dx := (u[j]-x[i]); i such that x[i] <= u[j] <= x[i+1}, ## u[j]:= xout[j] (unless sometimes for periodic spl.) ## and d_i := d[i] unless for natural splines at left res <- .splinefun(x, z) ## deal with points to the left of first knot if natural ## splines are used (Bug PR#13132) if( deriv > 0 && z$method==2 && any(ind <- x<=z$x[1L]) ) res[ind] <- ifelse(deriv == 1, z$y[1L], 0) res } } ## avoid capturing internal calls .splinefun <- function(x, z) .Call(C_SplineEval, x, z) ## hidden : The exported user function is splinefunH() splinefunH0 <- function(x0, y0, m, dx = x0[-1L] - x0[-length(x0)]) { function(x, deriv=0, extrapol = c("linear","cubic")) { extrapol <- match.arg(extrapol) deriv <- as.integer(deriv) if (deriv < 0 || deriv > 3) stop("'deriv' must be between 0 and 3") i <- findInterval(x, x0, all.inside = (extrapol == "cubic")) if(deriv == 0) interp <- function(x, i) { h <- dx[i] t <- (x - x0[i]) / h ## Compute the 4 Hermite (cubic) polynomials h00, h01,h10, h11 t1 <- t-1 h01 <- t*t*(3 - 2*t) h00 <- 1 - h01 tt1 <- t*t1 h10 <- tt1 * t1 h11 <- tt1 * t y0[i] * h00 + h*m[i] * h10 + y0[i+1]* h01 + h*m[i+1]* h11 } else if(deriv == 1) interp <- function(x, i) { h <- dx[i] t <- (x - x0[i]) / h ## 1st derivative of Hermite polynomials h00, h01,h10, h11 t1 <- t-1 h01 <- -6*t*t1 # h00 = - h01 h10 <- (3*t - 1) * t1 h11 <- (3*t - 2) * t (y0[i+1] - y0[i])/h * h01 + m[i] * h10 + m[i+1]* h11 } else if (deriv == 2) interp <- function(x, i) { h <- dx[i] t <- (x - x0[i]) / h ## 2nd derivative of Hermite polynomials h00, h01,h10, h11 h01 <- 6*(1-2*t) # h00 = - h01 h10 <- 2*(3*t - 2) h11 <- 2*(3*t - 1) ((y0[i+1] - y0[i])/h * h01 + m[i] * h10 + m[i+1]* h11) / h } else # deriv == 3 interp <- function(x, i) { h <- dx[i] ## 3rd derivative of Hermite polynomials h00, h01,h10, h11 h01 <- -12 # h00 = - h01 h10 <- 6 h11 <- 6 ((y0[i+1] - y0[i])/h * h01 + m[i] * h10 + m[i+1]* h11) / h } if(extrapol == "linear" && any(iXtra <- (iL <- (i == 0)) | (iR <- (i == (n <- length(x0)))))) { ## do linear extrapolation r <- x if(any(iL)) r[iL] <- if(deriv == 0) y0[1L] + m[1L]*(x[iL] - x0[1L]) else if(deriv == 1) m[1L] else 0 if(any(iR)) r[iR] <- if(deriv == 0) y0[n] + m[n]*(x[iR] - x0[n]) else if(deriv == 1) m[n] else 0 ## For internal values, compute "as normal": ini <- !iXtra r[ini] <- interp(x[ini], i[ini]) r } else { ## use cubic Hermite polynomials, even for extrapolation interp(x, i) } } } splinefunH <- function(x, y, m) { ## Purpose: "Cubic Hermite Spline" ## ---------------------------------------------------------------------- ## Arguments: (x,y): points; m: slope at points, all of equal length ## ---------------------------------------------------------------------- ## Author: Martin Maechler, Date: 9 Jan 2008 n <- length(x) stopifnot(is.numeric(x), is.numeric(y), is.numeric(m), length(y) == n, length(m) == n) if(is.unsorted(x)) { i <- sort.list(x) x <- x[i] y <- y[i] m <- m[i] } dx <- x[-1L] - x[-n] if(anyNA(dx) || any(dx == 0)) stop("'x' must be *strictly* increasing (non - NA)") splinefunH0(x, y, m, dx=dx) }