require("splines") ## Bug report PR#16549 - 'bad value from splineDesign' ## Date: Wed, 30 Sep 2015 12:12:47 +0000 ## https://bugs.r-project.org/bugzilla/show_bug.cgi?id=16549 ## Reporter: roconnor@health.usf.edu knots <- c(0,0,0,0,1,1,1,1) m1 <- rbind(c(1, 0,0,0)) stopifnot( ## the first gave (0 0 0 0) instead of m1 : all.equal(splineDesign(knots, c(0,2), outer.ok = TRUE), rbind(m1, 0)), all.equal(splineDesign(knots, 0, outer.ok = TRUE), m1), all.equal(splineDesign(knots, 0), m1), TRUE) ## The original fix proposal introduced a new bug, visible here: S <- splineDesign(c(-3, -3, -2, 0, 2, 3, 3), x= -3:3, outer.ok=TRUE) ## (had a NaN in the lower-right corner) stopifnot(all.equal(S, rbind(0, c(22,3,0)/45, c(193, 6*23, 9)/360, c(1,3,1)/5, c(9, 6*23, 193)/360, c(0,3,22)/45, 0), tolerance = 1e-14)) chkSum.Ok <- TRUE ## for check chkSum <- function(knots, n = 1 + 2^9, ord = 4) { stopifnot(is.numeric(knots), !is.unsorted(knots), (n.k <- length(knots)) >= ord) dk <- diff(rk <- range(knots)) d.x <- dk / (2*n.k) x <- seq(min(knots)-d.x, max(knots)+d.x, length.out = n) bb <- splineDesign(knots, x = x, ord = ord, outer.ok = TRUE) is.x.in <- knots[ord] <= x & x < knots[n.k-(ord-1)] ## ~~~~ ~~~ same as in splineDesign(*, outer.ok=TRUE) sumB <- rowSums(bb) if(any(iBad <- !is.finite(sumB))) { chkSum.Ok <<- FALSE ## for check cat("** _FIXME_ NON-finite values in sumB: ord = ", ord, "; |knots| =", n.k,"\n") cat("knots <- "); dput(knots) cat("non-finite at x = "); dput(x[iBad]) } else { eps <- 2*.Machine$double.eps stopifnot(abs(1 - sumB[is.x.in]) <= 2*eps, 0 <= sumB+eps, sumB-eps <= 1) ## TODO: now also check derivatives } invisible(bb) } ## from ../man/splineDesign.Rd : knots <- c(1,1.8,3:5,6.5,7,8.1,9.2,10) # 10 => 10-4 = 6 Basis splines chkSum(knots) chkSum(knots, ord=3) chkSum(knots, ord=2) chkSum(knots, ord=1) set.seed(17) for(n in 1:1000) { if(n %% 50 == 0) cat(sprintf("n = %4d\n",n)) kn <- sort.int(round(10* rnorm(4 + rpois(1, lambda=4)))) for(oo in 1:4) chkSum(kn, ord = oo) } ## One of the cases with NaN {when used ( . <= x & x <= . )}: bb <- chkSum(c(-14, -4, 3, 5, 6, 15, 15)) which(!is.finite(rowSums(bb))) ## now integer(0) stopifnot(chkSum.Ok) proc.time() ###----------------- sparse / dense interpSpline() --------------------------- ## from help(interpSpline) -- ../man/interpSpline.Rd ispl <- interpSpline( women$height, women$weight) isp. <- interpSpline( women$height, women$weight, sparse=TRUE) stopifnot(all.equal(ispl, isp., tol = 1e-12)) # seen 1.65e-14 ipStime <- function(n) { # and using 'ispl' h <- seq(55, 75, length.out = n) w1k <- data.frame(height = h, weight = predict(ispl, h)$y) c.d <- system.time(is.d <- interpSpline(weight ~ height, w1k, sparse=FALSE)) c.s <- system.time(is.s <- interpSpline(weight ~ height, w1k, sparse=TRUE )) stopifnot(all.equal(is.d, is.s, tol = 1e-7)) # seen 9.4e-10 (n=1000), 1.3e-7 (n=5000) list(d.time = c.d, s.time = c.s) } n.s <- 25 * round(2^seq(1,6, by=.5)) (ipL <- lapply(setNames(n.s, paste0("n=",n.s)), ipStime)) ## sparse is *an order of magnitude* faster for n ~= 1000 but somewhat slower for n ~< 200: sapply(ipL, function(ip) round(ip$d.time / ip$s.time, 1)[c(1,3)]) ## n=50 n=75 n=100 n=150 n=200 n=275 n=400 n=575 n=800 n=1125 n=1600 -- nb-mm4, i7-5600U ## user.self 0.5 0.5 0.5 0.5 0.7 2.5 4.3 12.3 33.7 70.5 116.1 ## elapsed 0.5 0.3 0.5 0.7 1.0 2.5 4.3 13.0 26.2 57.4 117.3 ###---- "Bug report" (to R-core) from Trevor Hastie --- ###----> bs(*, Boundary.knots = .) ### needing boundary ajustment for correct extrapolation ## Trevor's Example, slightly modified and extended: x <- seq(1.5, 8.5, by = 1/4) set.seed(13) y <- x + .01*(x - 5)^3 + rnorm(x) fit0 <- lm(y ~ bs(x, degree=3, knots=4)) fit0.<- lm(y ~ bs(x, degree=3, knots=4, Boundary.knots=c(1,9)))# *NOT* outside fit1 <- lm(y ~ bs(x, degree=3, knots=4, Boundary.knots=c(1,8)))# warning fit2 <- lm(y ~ bs(x, degree=3, knots=4, Boundary.knots=c(2,8)))# warning "2 x" jx <- seq(from=-2,to=12, by=0.1) p0 <- predict(fit0, list(x=jx)) p0.<- predict(fit0, list(x=jx)) p1 <- predict(fit1, list(x=jx)) p2 <- predict(fit2, list(x=jx)) stopifnot(all.equal(p0, p0.,tol=1e-14), all.equal(p0, p1, tol=1e-14), all.equal(p0, p2, tol=1e-14)) ## ^^ p1 and p2 differed from p0 in R <= 3.2.2 ## See numerical fuzz: all.equal(p0, p0., tol=0) all.equal(p0, p1, tol=0) all.equal(p0, p2, tol=0) all.equal(p1, p2, tol=0)# interestingly almost the same