# File src/library/stats/R/spline.R # Part of the R package, https://www.R-project.org # # Copyright (C) 1995-2019 The R Core Team # 2002 Simon N. Wood # # 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! #### --------- has more #### also consider ``compatibility'' with 'approx' and 'approxfun' spline <- function(x, y = NULL, n = 3*length(x), method = "fmm", xmin = min(x), xmax = max(x), xout, ties = mean) { method <- pmatch(method, c("periodic", "natural", "fmm", "hyman")) if(is.na(method)) stop("invalid interpolation method") 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") if(method == 1L && y[1L] != y[nx]) { # periodic warning("spline: first and last y values differ - using y[1] for both") y[nx] <- y[1L] } if(method == 4L) { dy <- diff(y) if(!(all(dy >= 0) || all(dy <= 0))) stop("'y' must be increasing or decreasing") } if(missing(xout)) xout <- seq.int(xmin, xmax, length.out = n) else n <- length(xout) if (n <= 0L) stop("'spline' requires n >= 1") xout <- as.double(xout) z <- .Call(C_SplineCoef, min(3L, method), x, y) if(method == 4L) z <- spl_coef_conv(hyman_filter(z)) list(x = xout, y = .Call(C_SplineEval, xout, z)) } ### Filters cubic spline function to yield co-monotonicity in accordance ### with Hyman (1983) SIAM J. Sci. Stat. Comput. 4(4):645-654, z$x is knot ### position z$y is value at knot z$b is gradient at knot. See also ### Dougherty, Edelman and Hyman 1989 Mathematics of Computation 52:471-494. ### Contributed by Simon N. Wood, improved by R-core. ### https://stat.ethz.ch/pipermail/r-help/2002-September/024890.html hyman_filter <- function(z) { n <- length(z$x) ss <- diff(z$y) / diff(z$x) S0 <- c(ss[1L], ss) S1 <- c(ss, ss[n-1L]) t1 <- pmin(abs(S0), abs(S1)) sig <- z$b ind <- S0*S1 > 0 sig[ind] <- S1[ind] ind <- sig >= 0 if(sum(ind)) z$b[ind] <- pmin(pmax(0, z$b[ind]), 3*t1[ind]) ind <- !ind if(sum(ind)) z$b[ind] <- pmax(pmin(0, z$b[ind]), -3*t1[ind]) z } ### Takes an object z containing equal-length vectors ### z$x, z$y, z$b, z$c, z$d defining a cubic spline interpolating ### z$x, z$y and forces z$c and z$d to be consistent with z$y and ### z$b (gradient of spline). This is intended for use in conjunction ### with Hyman's monotonicity filter. ### Note that R's spline routine has s''(x)/2 as c and s'''(x)/6 as d. ### Contributed by Simon N. Wood, improved by R-core. spl_coef_conv <- function(z) { n <- length(z$x) h <- diff(z$x); y <- -diff(z$y) b0 <- z$b[-n]; b1 <- z$b[-1L] cc <- -(3*y + (2*b0 + b1)*h) / h^2 c1 <- (3*y[n-1L] + (b0[n-1L] + 2*b1[n-1L])*h[n-1L]) / h[n-1L]^2 z$c <- c(cc, c1) dd <- (2*y/h + b0 + b1) / h^2 z$d <- c(dd, dd[n-1L]) z }