Seq = function(a,b) { if (a<=b) return(a:b) else return(integer(0)) } Sign = function(x) { return(ifelse(x==0, 1, sign(x))) } Curve = function(f, a=0, b=1, N=100, ...) { x = seq(a,b,length=N) y = numeric(N) for (i in 1:N) y[i] = f(x[i]) plot(x, y, type="l", ...) } symmetrize = function(mat, lower=TRUE) { tmat = t(mat) if (lower) tmat[lower.tri(tmat)] = mat[lower.tri(mat)] else tmat[upper.tri(tmat)] = mat[upper.tri(mat)] return(tmat) } cond.num = function(A) { s = eigen(A,symmetric=TRUE,only.values=TRUE)$val return(max(s)/min(s)) } clockwise90 = function(a) { return(t(a[nrow(a):1,])) } enlist = function(...) { result = list(...) n = result[[1]] if ((nargs() == 1) & is.character(n)) { result = as.list(seq(n)) names(result) = n for (i in n) result[[i]] = get(i) } else { n = sys.call() n = as.character(n)[-1] n2 = names(result) if (!is.null(n2)) { which = n2 != "" n[which] = n2[which] } names(result) = n } return(result) } ## Falling factorial functions getT = function(n, k, x=1:n/n) { T = x if (k==0) T = T[-1] else if (k%%2==1) T = T[-c(1:((k+1)/2),(n-(k-1)/2):n)] else if (k%%2==0) T = T[-c(1:((k+2)/2),(n-(k-2)/2):n)] return(T) } getG = function(n, k, x=1:n/n) { T = getT(n,k,x) G = matrix(0,n,n) G[,1] = rep(1,n) for (j in 2:n) { if (j<=k+1) { G[,j] = x^(j-1)/factorial(j-1) } else { G[,j] = pmax(x-T[j-k-1],0)^k/factorial(k) } } return(G) } getH = function(n, k, x=1:n/n) { return(evalH(x,k,(k+1):(n-1),x)) } # Note: jj are the indices of the active knots in the x vector evalH = function(x0, k, jj, x=1:n/n) { n0 = length(x0) m = length(jj) N = m+k+1 H = matrix(0,n0,N) H[,1] = rep(1,n0) for (j in Seq(2,k+1)) { for (i in 1:n0) { H[i,j] = prod(x0[i]-x[1:(j-1)])/factorial(j-1) } } for (l in 1:length(jj)) { j = jj[l] for (i in 1:n0) { if (x0[i] > x[j]) { if (k==0) H[i,l+k+1] = 1 else H[i,l+k+1] = prod(x0[i]-x[(j-k+1):j])/factorial(k) } } } return(H) } library(Matrix) Id = function(n) { return(bandSparse(n, k=0, diagonals=list(rep(1,n)))) } # These are the "right" ones based on divided diffs getB = function(n, k, x=1:n/n) { I = Id(n) D = bandSparse(n, k=c(-1,0), diagonals=list(rep(-1,n-1), rep(1,n))) B = I for (i in Seq(1,k)) { wts = c(rep(1,i), i/(x[(i+1):n]-x[1:(n-i)])) M = bdiag(I[Seq(1,i-1),Seq(1,i-1)], D[1:(n-i+1),1:(n-i+1)]) B = (wts * M) %*% B } return(B) } getD = function(n, k, x=1:n/n) { getB(n,k,x)[-(1:k),] } # These were the ones used in previous TF papers getBtil = function(n, k, x=1:n/n) { c(rep(1,k),(x[(k+1):n]-x[1:(n-k)])/k) * getB(n,k,x) } getDtil = function(n, k, x=1:n/n) { getBtil(n,k,x)[-(1:k),] } ## Prediction functions predict.tf.fitted = function(newx,beta,x,k) { n = length(x) B = getBtil(n,k+1,x) alpha = B %*% beta return(predict.tf.basis(newx,alpha,x,k)) } predict.tf.basis = function(newx,alpha,x,k) { n = length(x) jj = (k+1):(n-1) H = evalH(newx,k,jj,x) return(H %*% alpha) } predict.tf.basis.sk = function(newx,alpha,x,k,jj) { H = evalH(newx,k,jj,x) return(H %*% alpha) } predict.tf.deriv = function(newx,beta,x,k,tol=1e-6) { i = which(abs(newx-x) < tol) if (length(i) > 0) return(median(beta[i])) if (max(x) <= newx) j0 = n else j0 = min(which(x > newx)) if (j0 < k+1) { x0 = c(x[1:j0],newx,x[(j0+1):(k+1)]) D0 = as.numeric(getD(k+2,k+1,x0)) return(-sum(D0[-(j0+1)]*beta[1:(k+1)]) / D0[j0+1]) } else { x0 = c(x[(j0-k):j0],newx) D0 = as.numeric(getD(k+2,k+1,x0)) return(-sum(D0[-(k+2)]*beta[(j0-k):j0]) / D0[k+2]) } } ## B-spline functions tpf = function(z, k) { return(function(x) (x-z)^k * (x>z)) } # Assumes length(z) >= 1 (so that k >= 1) tff = function(z) { return(Vectorize(function(x) prod(x-z) * (x>min(z)))) } tnp = function(z) { return(Vectorize(function(x) prod(x-z) * (x>max(z)))) } bs0 = function(z) { return(function(x) ifelse(x > z[1] && x <= x[2])) } bs = function(z) { k = length(z) - 2 D = getD(k+2,k+1,z) funs = sapply(z, function(z0,k) return(tpf(z0,k)), k) return(Vectorize(function(x0) return((-1)^(k+1) * (z[k+2]-z[1]) * (1/factorial(k+1)) * as.numeric(D %*% sapply(funs, function(f) f(x0)))))) } dbs.v = function(z, v) { k = length(z) - 2 if (k==0) return(bs(z)) # Just avoid indexing issues D = getD(k+2,k+1,z) funs = sapply(z, function(z0,k,v) return(tff(z0 + 0:(k-1)*v)), k, v) return(Vectorize(function(x0) return((-1)^(k+1) * (z[k+2]-z[1]) * (1/factorial(k+1)) * as.numeric(D %*% sapply(funs, function(f) f(x0)))))) } dbs = function(z, x) { k = length(z) - 2 if (k==0) return(bs(z)) # Just avoid indexing issues D = getD(k+2,k+1,z) v = rep(0,n) for (i in 1:n) { if (x[i] <= z[1]) v[i] = 0 else if (x[i] >= z[k+2]) v[i] = 0 else { vals = tnp(x[(i-k+1):i])(z) v[i] = (z[k+2]-z[1]) * (1/factorial(k+1)) * as.numeric(D %*% vals) } } return(dbs.fun(k,x,v)) } dbs.fun = function(k, x, v) { return(Vectorize(function(newx) predict.tf.deriv(newx,v,x,k))) } dbs.fun.sk = function(k, x, alpha, jj) { return(Vectorize(function(newx) predict.tf.basis.sk(newx,alpha,x,k,jj))) } dbs.Amat.sk = function(k, x, jj, xright=NULL) { n = length(x) m = length(jj) N = m+k+1 if (is.null(xright)) { dx = max(diff(x)) xright = max(x) + (1:(k+2))*dx } J = c(jj, n:(n+k)) X = c(x, xright) Amat = matrix(0,N+k+1,N) for (i in 1:(k+1)) { ii = c(Seq(1,i), Seq(J[i]-k+1,J[i]+1)) H = evalH(X[ii],k,J[1:i],X) y = rep(0,i+k+1); y[i] = 1 alpha = lsfit(H,y,intercept=FALSE)$coef Amat[1:(k+1),i] = alpha[1:(k+1)] # poly coefs Amat[k+1+(1:i),i] = alpha[-(1:(k+1))] # pp coefs } for (i in 1:m) { ii = c(Seq(J[i]-k, J[i]), J[i+1], Seq(J[i+k+1]-k+1, J[i+k+1]+1)) H = evalH(X[ii],k,J[i:(i+k+1)],X) y = rep(0,2*k+3); y[k+2] = 1 alpha = lsfit(H,y,intercept=FALSE)$coef Amat[1:(k+1),i+k+1] = alpha[1:(k+1)] # poly coefs Amat[k+1+(i:(i+k+1)),i+k+1] = alpha[-(1:(k+1))] # pp coefs } return(Amat[1:N,]) # cut off boundary knots } dbs.Bmat = function(k, x, a=NULL, b=NULL, xleft=NULL) { n = length(x) if (is.null(a) || is.null(b) || is.null(xleft)) { dx = min(diff(x)) a = min(x) - dx b = max(x) + dx xleft = a + Seq(-k+1,-1)*dx } X = Vectorize(function(i) { if (1 <= i && i <= n) return(x[i]) if (i==0) return(a) if (i==n+1) return(b) if (-k+1 <= i && i <= -1) return(xleft[i+k]) else return(NA) }) bvec = rep(1,n) if (k >= 1) { for (i in 1:n) { v = X(Seq(i-k+1,i)) z = c(X(i-k), v, X(i+1)) bvec[i] = (z[k+2]-z[1]) * (1/factorial(k+1)) * getD(k+2,k+1,z)[k+2] * tnp(v)(X(i+1)) } } return(bandSparse(n, k=0, diagonals=list(bvec))) } # Faster and more stable way of evaluating DB-splines at the design # points (in the sparse knot case) dbs.evals.sk = function(k, x, jj, xright=NULL, tol=1e-10) { n = length(x) m = length(jj) N = m+k+1 if (is.null(xright)) { dx = max(diff(x)) xright = max(x) + (1:(k+1))*dx } J = c(jj, n:(n+k)) X = c(x, xright) evals = Matrix(0,n+k+1,N+k+1) for (i in 1:(k+1)) { I = 1:(J[i]+1) jj = J[1:i] z = rep(0,length(I)); z[i] = 1 evals[I,i] = dbs.local.interp.left(k,jj,i,X[I],z,tol) } for (i in 1:m) { I = Seq(J[i]-k,J[i+k+1]+1) jj = J[i:(i+k+1)]-J[i]+k+1 z = rep(0,length(I)); z[jj[2]] = 1 evals[I,i+k+1] = dbs.local.interp(k,jj,X[I],z,tol) } return(evals[1:n,1:N]) # cut off boundary points and knots } # Local interpolation function for "left" DB-splines (first k+1) # Note: jj are the indices of the active knots in the x vector # Also: y is the observation vector, and we want to interpolate # between its ith element and last knot dbs.local.interp.left = function(k, jj, i, x, y, tol=1e-10) { n = length(x) B = getB(n,k+1,x) # Interpolate between point i+1 and last knot if (jj[i]-k+1 > i+1) { pts.ind = Seq(i+1,jj[i]-k) row.ind = setdiff((k+2):jj[i],jj[Seq(1,i-1)]+1) col.ind = 1:max(row.ind) I1 = pts.ind; I2 = setdiff(col.ind,I1) B1 = suppressWarnings(Matrix(B[row.ind,I1],ncol=length(I1))) B2 = suppressWarnings(Matrix(B[row.ind,I2],ncol=length(I2))) y[I1] = -solve(qr(B1,tol=tol), as.numeric(B2 %*% y[I2])) } return(y) } # Local interpolation function for DB-splines (last r functions) # Note: jj are the indices of the active knots in the x vector # Also: y is the observation vector, and we want to interpolate # between first and second knot, and second knot and last knot dbs.local.interp = function(k, jj, x, y, tol=1e-10) { n = length(x) B = getB(n,k+1,x) # Interpolate between first knot and second knot if (jj[2] > jj[1]+1) { # Grab a local part of discrete deriv matrix pts.ind = Seq(jj[1]+1,jj[2]-1) row.ind = pts.ind + 1 col.ind = Seq(min(row.ind)-k-1, max(row.ind)) # Solve for unknown function evaluations I1 = pts.ind; I2 = setdiff(col.ind,I1) B1 = suppressWarnings(Matrix(B[row.ind,I1],ncol=length(I1))) B2 = suppressWarnings(Matrix(B[row.ind,I2],ncol=length(I2))) y[I1] = -solve(qr(B1,tol=tol), as.numeric(B2 %*% y[I2])) } # Interpolate between second knot and last knot if (jj[k+2]-k+1 > jj[2]+1) { # Grab a local part of discrete deriv matrix pts.ind = Seq(jj[2]+1,jj[k+2]-k) knt.ind = jj[jj %in% pts.ind] reg.ind = setdiff(pts.ind,knt.ind) # Row indices are tricky to compute. Insight: for each knot that's in # jj[2]+1 to jj[k+2]-k, there's a point in jj[k+2]-k+1 to jj[k+2]-1 # that's a regular point (not a knot); this is because the total number # of knots in jj[2]+1 to jj[k+2]-1 must be exactly k+1 ii = Seq(jj[k+2]-k+1,jj[k+2]-1) row.ind = c(reg.ind, ii[!(ii %in% jj)]) + 1 col.ind = Seq(min(row.ind)-k-1, max(row.ind)) # Solve for unknown function evaluations I1 = pts.ind; I2 = setdiff(col.ind,I1) B1 = suppressWarnings(Matrix(B[row.ind,I1],ncol=length(I1))) B2 = suppressWarnings(Matrix(B[row.ind,I2],ncol=length(I2))) y[I1] = -solve(qr(B1,tol=tol), as.numeric(B2 %*% y[I2])) } return(y) } ## Discrete spline smoothing ff_deriv = function(z, d, knot=TRUE) { k = length(z) deriv_str = paste0(paste(paste0("(x-", letters[1:k]),collapse=")*"),")") deriv_expr = parse(text=deriv_str) for (i in Seq(1,d)) { deriv_expr = D(deriv_expr, "x") } list_str = paste0(paste0("z[",1:k),"]") list_str = paste(paste(letters[1:k], list_str, sep="="), collapse=", ") list_str = paste0("list(x=x, ", list_str, ")") if (!knot) { return(function(x) { eval(deriv_expr, envir=eval(parse(text=list_str))) / factorial(k) }) } else { return(function(x) { eval(deriv_expr, envir=eval(parse(text=list_str))) / factorial(k) * (x > max(z)) }) } } getM = function(n, m, x=1:n/n, a=x[1], b=x[n], del=1e-8) { k = 2*m-1 M = matrix(0,n,n) for (i in (m+1):n) { if (i <= k+1) zi = x[1:(i-1)] else zi = x[(i-k):(i-1)] for (j in (m+1):i) { if (j <= k+1) zj = x[1:(j-1)] else zj = x[(j-k):(j-1)] # First min(m,i) - 1 terms for (l in Seq(1,min(i-m,m)-1)) { hi_deriv = ff_deriv(zi,m+l-1,knot=i>k+1) hj_deriv = ff_deriv(zj,m-l,knot=j>k+1) M[i,j] = M[i,j] + (-1)^(l-1) * ( hi_deriv(b)*hj_deriv(b) - hi_deriv(x[i-1]+del)*hj_deriv(x[i-1]+del) + hi_deriv(x[i-1]-del)*hj_deriv(x[i-1]-del) - hi_deriv(a)*hj_deriv(a)) } # Last term if (i <= k+1) { hj_deriv = ff_deriv(zj,2*m-i,knot=j>k+1) M[i,j] = M[i,j] + (-1)^(i-m-1) * ( hj_deriv(b) - hj_deriv(a)) } else { hj_deriv = ff_deriv(zj,0,knot=j>k+1) M[i,j] = M[i,j] + (-1)^(m-1) * (hj_deriv(b) - hj_deriv(x[i-1]+del)) } } } return(symmetrize(M)) } getV = function(n, m, x=1:n/n, a=x[1], b=x[n]) { M = getM(n,m,x,a,b) Btil = getBtil(n,2*m,x) Htil = getH(n,m-1,x) %*% diag(c(rep(1,m), (x[(m+1):n]-x[1:(n-m)])/m)) U = t(Htil) %*% t(Btil) %*% M %*% Btil %*% Htil V = U[-(1:m),-(1:m)] V = band(V,-(m-1),(m-1)) # Manually zero out elements return(Matrix(V,sparse=TRUE)) } ## Example functions # Piecewise constant function pieconst = function(x) { n = length(x) d = floor(n/5) f = rep(c(8,0,10,3,4), times=c(rep(d,4),n-4*d)) return(f) } # Piecewise linear function pielin = function(x) { x0 = seq(min(x),max(x),length=100) y0 = pielin100() return(approx(x0,y0,x,rule=2)$y) } pielin100 = function() { knots = matrix(0,6,2) knots[,1] = c(1,20,45,60,85,100) knots[,2] = c(1,2,6,8,5,6) f = rep(0,100) for (i in 1:(nrow(knots)-1)) { for (j in knots[i,1]:(knots[i+1,1])) { f[j] = (knots[i+1,1]-j)/(knots[i+1,1]-knots[i,1])*knots[i,2] + (j-knots[i,1])/(knots[i+1,1]-knots[i,1])*knots[i+1,2] } } f = 10*(f-min(f))/(max(f)-min(f)) return(f) } # Piecewise quadratic function piequad = function(x) { x0 = seq(min(x),max(x),length=100) y0 = piequad100() return(splinefun(x0,y0)(x)) } piequad100 = function() { knots = matrix(0,4,2) knots[,1] = c(1,33,60,100) knots[,2] = c(8,6,2,4) f = rep(0,100); endval = 0 for (i in 1:(nrow(knots)-1)) { sgn = (-1)^(i+1) mid = (knots[i,1]+knots[i+1,1])/2 dif = knots[i+1,1]-knots[i,1] j = knots[i,1] intcp = endval - (sgn*n/5*(j-mid)^2/dif^2 + (knots[i+1,1]-j)/dif*knots[i,2] + (j-knots[i,1])/dif*knots[i+1,2]) for (j in knots[i,1]:(knots[i+1,1])) { f[j] = intcp + sgn*n/5*(j-mid)^2/dif^2 + (knots[i+1,1]-j)/dif*knots[i,2] + (j-knots[i,1])/dif*knots[i+1,2] } endval = f[j] } f = 10*(f-min(f))/(max(f)-min(f)) f = rev(max(f) - f + min(f)) return(f) } # Smooth and wiggly function smoothwiggly.fun = function(x) { f = function(a,b,c,x) return(a*(x-b)^2+c) fp = function(a,b,c,x) return(2*a*(x-b)) a=-1; b=1/4; c=1; if (x<=1/3) return(f(a,b,c,x)) aa=a; bb=b; cc=c; xx=1/3; a=1; b=xx-fp(aa,bb,cc,xx)/(2*a); c=f(aa,bb,cc,xx)-a*(xx-b)^2; if (x<=2/3) return(f(a,b,c,x)) aa=a; bb=b; cc=c; xx=2/3; b=0.7; a=fp(aa,bb,cc,xx)/(2*(xx-b)); c=f(aa,bb,cc,xx)-a*(xx-b)^2; if (x<=0.775) return(f(a,b,c,x)) aa=a; bb=b; cc=c; xx=0.775; b=0.8; a=fp(aa,bb,cc,xx)/(2*(xx-b)); c=f(aa,bb,cc,xx)-a*(xx-b)^2; if (x<=0.825) return(f(a,b,c,x)) aa=a; bb=b; cc=c; xx=0.825; b=0.85; a=fp(aa,bb,cc,xx)/(2*(xx-b)); c=f(aa,bb,cc,xx)-a*(xx-b)^2; if (x<=0.875) return(f(a,b,c,x)) aa=a; bb=b; cc=c; xx=0.875; b=0.9; a=fp(aa,bb,cc,xx)/(2*(xx-b)); c=f(aa,bb,cc,xx)-a*(xx-b)^2; if (x<=0.925) return(f(a,b,c,x)) aa=a; bb=b; cc=c; xx=0.925; b=0.95; a=fp(aa,bb,cc,xx)/(2*(xx-b)); c=f(aa,bb,cc,xx)-a*(xx-b)^2; if (x<=0.975) return(f(a,b,c,x)) aa=a; bb=b; cc=c; xx=0.975; b=1; a=fp(aa,bb,cc,xx)/(2*(xx-b)); c=f(aa,bb,cc,xx)-a*(xx-b)^2; return(f(a,b,c,x)) } smoothwiggly = function(x) { n = length(x); f = rep(0,n) for (i in 1:n) f[i] = smoothwiggly.fun(x[i]) f = 10*(f-min(f))/(max(f)-min(f)) return(f) }