Nothing
###
### Ramsay, Hooker & Graves (2009)
### Functional Data Analysis with R and Matlab (Springer)
###
# Remarks and disclaimers
# These R commands are either those in this book, or designed to
# otherwise illustrate how R can be used in the analysis of functional
# data.
# We do not claim to reproduce the results in the book exactly by these
# commands for various reasons, including:
# -- the analyses used to produce the book may not have been
# entirely correct, possibly due to coding and accuracy issues
# in the functions themselves
# -- we may have changed our minds about how these analyses should be
# done since, and we want to suggest better ways
# -- the R language changes with each release of the base system, and
# certainly the functional data analysis functions change as well
# -- we might choose to offer new analyses from time to time by
# augmenting those in the book
# -- many illustrations in the book were produced using Matlab, which
# inevitably can imply slightly different results and graphical
# displays
# -- we may have changed our minds about variable names. For example,
# we now prefer "yearRng" to "yearRng" for the weather data.
# -- three of us wrote the book, and the person preparing these scripts
# might not be the person who wrote the text
# Moreover, we expect to augment and modify these command scripts from time
# to time as we get new data illustrating new things, add functionality
# to the package, or just for fun.
###
### ch. 8. Registration: Aligning Features
### for Samples of Curves
###
# load the fda package
library(fda)
# display the data files associated with the fda package
data(package='fda')
# start the HTML help system if you are connected to the Internet, in
# order to open the R-Project documentation index page in order to obtain
# information about R or the fda package.
help.start()
##
## Section 8.1 Amplitude and Phase Variation
##
# Figure 8.1 in this section requires that we firt do
# landmark registration first.
# This figure is therefore plotted in Section 8.3 below.
# Figure 8.2
# set up a fine mesh of t-values for plotting, and define mu and sigma
tvec = seq(-5,5,len=201)
mu = seq(-1,1,len= 5)
sigma = (1:5)/3
# Here is function Dgauss that we use below to compute values
# of the first derivative of a Gaussian density:
DGauss = function(tvec, mu, sigma)
{
var = as.matrix(sigma)^2
n = length(tvec)
m = length(mu)
if (length(sigma) != m)
stop('MU and SIGMA not of same length')
tvec = as.matrix(tvec)
mu = as.matrix(mu)
onesm = matrix(1,1,m)
onesn = matrix(1,n,1)
res = tvec %*% onesm - onesn %*% t(mu)
expon = res^2/(2*onesn %*% t(var))
DpG = -res*exp(-expon)
return(DpG)
}
# Data to be plotted in the left panel
DpGphase = matrix(0,201,5)
for (i in 1:5) DpGphase[,i] = DGauss(tvec+mu[i], 0, 1)
DpGphaseMean = apply(DpGphase,1,mean)
# Data to be plotted in the right panel
DpGampli = matrix(0,201,5)
for (i in 1:5) DpGampli[,i] = sigma[i]*DGauss(tvec, 0, 1)
DpGampliMean = apply(DpGampli,1,mean)
op = par(mfrow=c(2,1), cex=1, ask=FALSE)
# top panel
matplot(tvec, DpGphase, "l", lwd=1, col=1, lty=1,
xlim=c(-5,5), ylim=c(-0.8,0.8),
xlab="", ylab="")
lines(tvec, DpGphaseMean, col=1, lty=2, lwd=4)
lines(c(-5,5), c(0,0), col=1, lty=3, lwd=1)
# bottom panel
matplot(tvec, DpGampli, "l", lwd=1, col=1, lty=1,
xlim=c(-5,5), ylim=c(-1.2,1.2),
xlab="", ylab="")
lines(tvec, DpGampliMean, col=1, lty=2, lwd=4)
lines(c(-5,5), c(0,0), col=1, lty=3, lwd=1)
par(op)
# get eigenvalues for each panel
eigvecphase = svd(DpGphase)$d^2
(eigvecphase = eigvecphase/sum(eigvecphase))
# First three = 0.55, 0.39, and 0.05
eigvecampli = svd(DpGampli)$d^2
(eigvecampli = eigvecampli/sum(eigvecampli))
# First singular value = 1;
# all others are round-off
##
## Section 8.2 Time-Warping Functions and Registration
##
# ---------- Registration of the Berkeley female growth data -----------
# Figure 8.3 requires landmark registration, and is set up below
##
## Section 8.3: Landmark registration
##
# set up ages of measurement and an age mesh
age = growth$age
nage = length(age)
ageRng = range(age)
nfine = 101
agefine = seq(ageRng[1], ageRng[2], length=nfine)
# the data
hgtf = growth$hgtf
ncasef = dim(hgtf)[2]
# Set up functional data objects for the acceleration curves
# and their mean. Suffix UN means "unregistered".
# This step requires the functional data object hgtfhatfd computed
# from the monotone smooth of the Berkeley female data. Refer to
# Section 5.4.2.2 in the text. To create it here, we copy
# relevant lines from the script file fdarm-ch05.R
growbasis = create.bspline.basis(norder=6, breaks=age)
Lfdobj = 3 # penalize curvature of acceleration
lambda = 10^(-0.5) # smoothing parameter
cvecf = matrix(0, growbasis$nbasis, ncol(growth$hgtf))
dimnames(cvecf) = list(growbasis$names, dimnames(growth$hgtf)[[2]])
growfd0 = fd(cvecf, growbasis)
growfdPar = fdPar(growfd0, Lfdobj, lambda)
growthMon = smooth.monotone(age, hgtf, growfdPar)
hgtfhatfd = growthMon$yhatfd
accelfdUN = deriv.fd(hgtfhatfd, 2)
accelmeanfdUN = mean(accelfdUN)
# plot unregistered curves
plot(accelfdUN, xlim=c(1,18), ylim=c(-4,3), lty=1, lwd=2,
cex=2, xlab="Age", ylab="Acceleration (cm/yr/yr)")
# This is a MANUAL PGS spurt identification procedure requiring
# a mouse click at the point where the acceleration curve
# crosses the zero axis with a negative slope during puberty.
# Here we do this only for the first 10 children.
children = 1:10
PGSctr = rep(0,length(children))
for (icase in children) {
accveci = eval.fd(agefine, accelfdUN[icase])
plot(agefine,accveci,"l", ylim=c(-6,4),
xlab="Year", ylab="Height Accel.",
main=paste("Case",icase))
lines(c(1,18),c(0,0),lty=2)
PGSctr[icase] = locator(1)$x
# **** CLICK ON EACH ACCELERATION CURVE WHERE IT
# **** CROSSES ZERO WITH NEGATIVE SLOPE DURING PUBERTY
}
# This is an automatic PGS spurt identification procedure.
# A mouse click advances the plot to the next case.
# Compute PGS mid point for landmark registration.
# Downward crossings are computed within the limits defined
# by INDEX. Each of the crossings within this interval
# are plotted. The estimated PGS center is plotted as a vertical line.
# The choice of range of argument values (6--18) to consider
# for a potential mid PGS location is determined by previous
# analyses, where they have a mean of about 12 and a s.d. of 1.
# We compute landmarks for all 54 children
index = 1:102 # wide limits
nindex = length(index)
ageval = seq(8.5,15,len=nindex)
PGSctr = rep(0,ncasef)
op = par(ask=TRUE)
for (icase in 1:ncasef) {
accveci = eval.fd(ageval, accelfdUN[icase])
aup = accveci[2:nindex]
adn = accveci[1:(nindex-1)]
indx = (1:102)[adn*aup < 0 & adn > 0]
plot(ageval[2:nindex],aup,"p",
xlim=c(7.9,18), ylim=c(-6,4))
lines(c(8,18),c(0,0),lty=2)
for (j in 1:length(indx)) {
indxj = indx[j]
aupj = aup[indxj]
adnj = adn[indxj]
agej = ageval[indxj] + 0.1*(adnj/(adnj-aupj))
if (j == length(indx)) {
PGSctr[icase] = agej
lines(c(agej,agej),c(-4,4),lty=1)
} else {
lines(c(agej,agej),c(-4,4),lty=3)
}
}
title(paste('Case ',icase))
# ****** CLICK ON EACH PLOT TO ADVANCE TO THE NEXT
# ****** {par(ask=TRUE)}
}
par(op)
# We use the minimal basis function sufficient to fit 3 points
# remember that the first coefficient is set to 0, so there
# are three free coefficients, and the data are two boundary
# values plus one interior knot.
# Suffix LM means "Landmark-registered".
PGSctrmean = mean(PGSctr)
# Define the basis for the function W(t).
wbasisLM = create.bspline.basis(c(1,18), 4, 3, c(1,PGSctrmean,18))
WfdLM = fd(matrix(0,4,1),wbasisLM)
WfdParLM = fdPar(WfdLM,1,1e-12)
# Carry out landmark registration.
regListLM = landmarkreg(accelfdUN, PGSctr, PGSctrmean,
WfdParLM, TRUE)
accelfdLM = regListLM$regfd
accelmeanfdLM = mean(accelfdLM)
# plot registered curves
plot(accelfdLM, xlim=c(1,18), ylim=c(-4,3), lty=1, lwd=1,
cex=2, xlab="Age", ylab="Acceleration (cm/yr/yr)")
lines(accelmeanfdLM, col=1, lwd=2, lty=2)
lines(c(PGSctrmean,PGSctrmean), c(-4,3), lty=2, lwd=1.5)
# Figure 8.1
accelmeanfdUN10 = mean(accelfdUN[children])
accelmeanfdLM10 = mean(accelfdLM[children])
op = par(mfrow=c(2,1))
plot(accelfdUN[children], xlim=c(1,18), ylim=c(-3,1.5), lty=1, lwd=1,
cex=2, xlab="", ylab="Acceleration (cm/yr/yr)")
lines(accelmeanfdUN10, col=1, lwd=2, lty=2)
lines(c(PGSctrmean,PGSctrmean), c(-3,1.5), lty=2, lwd=1.5)
plot(accelfdLM[children], xlim=c(1,18), ylim=c(-3,1.5), lty=1, lwd=1,
cex=2, xlab="Age (Years)", ylab="Acceleration (cm/yr/yr)")
lines(accelmeanfdLM10, col=1, lwd=2, lty=2)
lines(c(PGSctrmean,PGSctrmean), c(-3,1.5), lty=2, lwd=1.5)
par(op)
# Figure 8.3
# plot warping functions for cases 3 and 7
warpfdLM = regListLM$warpfd
warpmatLM = eval.fd(agefine, warpfdLM)
op = par(mfrow=c(2,2))
plot(accelfdUN[3], xlim=c(1,18), ylim=c(-3,1.5), lty=1, lwd=2,
xlab="", ylab="")
lines(c(PGSctrmean,PGSctrmean), c(-3,1.5), lty=2, lwd=1.5)
plot(agefine, warpmatLM[,3], "l", lty=1, lwd=2, col=1, cex=1.2,
xlab="", ylab="")
lines(agefine, agefine, lty=2, lwd=1.5)
lines(c(PGSctrmean,PGSctrmean), c(1,18), lty=2, lwd=1.5)
text(PGSctrmean+0.1, warpmatLM[61,3]+0.3, "o", lwd=2)
plot(accelfdUN[7], xlim=c(1,18), ylim=c(-3,1.5), lty=1, lwd=2,
xlab="", ylab="")
lines(c(PGSctrmean,PGSctrmean), c(-3,1.5), lty=2, lwd=1.5)
plot(agefine, warpmatLM[,7], "l", lty=1, lwd=2, col=1, cex=1.2,
xlab="", ylab="")
lines(c(PGSctrmean,PGSctrmean), c(1,18), lty=2, lwd=1.5)
lines(agefine, agefine, lty=2, lwd=1.5)
text(PGSctrmean+0.1, warpmatLM[61,7]+0.2, "o", lwd=2)
par(op)
# Comparing unregistered to landmark registered curves
AmpPhasList = AmpPhaseDecomp(accelfdUN, accelfdLM, warpfdLM, c(3,18))
MS.amp = AmpPhasList$MS.amp
MS.pha = AmpPhasList$MS.pha
RSQRLM = AmpPhasList$RSQR
CLM = AmpPhasList$C
print(paste("Total MS =", round(MS.amp+MS.pha,2),
"Amplitude MS =", round(MS.amp,2),
"Phase MS =", round(MS.pha,2)))
# [1] "Total MS = 7.06 Amplitude MS = 2.12 Phase MS = 4.95"
print(paste("R-squared =", round(RSQRLM,3), ", C =", round(CLM,3)))
# "R-squared = 0.7 , C = 0.984"
##
## Section 8.4: Continuous registration
##
# Set up a cubic spline basis for continuous registration
nwbasisCR = 15
norderCR = 5
wbasisCR = create.bspline.basis(c(1,18), nwbasisCR, norderCR)
Wfd0CR = fd(matrix(0,nwbasisCR,ncasef),wbasisCR)
lambdaCR = 1
WfdParCR = fdPar(Wfd0CR, 1, lambdaCR)
# carry out the registration
registerlistCR = register.fd(accelmeanfdLM, accelfdLM, WfdParCR)
accelfdCR = registerlistCR$regfd
warpfdCR = registerlistCR$warpfd
WfdCR = registerlistCR$Wfd
# plot landmark and continuously registered curves for the
# first 10 children
accelmeanfdLM10 = mean(accelfdLM[children])
accelmeanfdCR10 = mean(accelfdCR[children])
op = par(mfrow=c(2,1))
plot(accelfdLM[children], xlim=c(1,18), ylim=c(-3,1.5), lty=1, lwd=1,
cex=2, xlab="Age (Years)", ylab="Acceleration (cm/yr/yr)")
lines(accelmeanfdLM10, col=1, lwd=2, lty=2)
lines(c(PGSctrmean,PGSctrmean), c(-3,1.5), lty=2, lwd=1.5)
plot(accelfdCR[children], xlim=c(1,18), ylim=c(-3,1.5), lty=1, lwd=1,
cex=2, xlab="Age (Years)", ylab="Acceleration (cm/yr/yr)")
lines(accelmeanfdCR10, col=1, lwd=2, lty=2)
lines(c(PGSctrmean,PGSctrmean), c(-3,1.5), lty=2, lwd=1.5)
par(op)
# plot all landmark and continuously registered curves
accelmeanfdCR = mean(accelfdCR)
op = par(mfrow=c(2,1))
plot(accelfdLM, xlim=c(1,18), ylim=c(-4,3), lty=1, lwd=1,
cex=2, xlab="Age (Years)", ylab="Acceleration (cm/yr/yr)")
lines(accelmeanfdLM, col=1, lwd=2, lty=2)
lines(c(PGSctrmean,PGSctrmean), c(-4,3), lty=2, lwd=1.5)
plot(accelfdCR, xlim=c(1,18), ylim=c(-4,3), lty=1, lwd=1,
cex=2, xlab="Age (Years)", ylab="Acceleration (cm/yr/yr)")
lines(accelmeanfdCR, col=1, lwd=2, lty=2)
lines(c(PGSctrmean,PGSctrmean), c(-4,3), lty=2, lwd=1.5)
par(op)
# Figure 8.4
par(mfrow=c(1,1))
plot(accelfdCR[children], xlim=c(1,18), ylim=c(-3,1.5), lty=1, lwd=1,
cex=2, xlab="Age (Years)", ylab="Acceleration (cm/yr/yr)")
lines(accelmeanfdCR10, col=1, lwd=2, lty=2)
lines(c(PGSctrmean,PGSctrmean), c(-3,1.5), lty=2, lwd=1.5)
par(op)
# Figure 8.5
accelmeanfdUN = mean(accelfdUN)
accelmeanfdLM = mean(accelfdLM)
accelmeanfdCR = mean(accelfdCR)
plot(accelmeanfdCR, xlim=c(1,18), ylim=c(-3,1.5), lty=1, lwd=2,
cex=1.2, xlab="Years", ylab="Height Acceleration")
lines(accelmeanfdLM, lwd=1.5, lty=1)
lines(accelmeanfdUN, lwd=1.5, lty=2)
##
## Section 8.5 A Decomposition into Amplitude and Phase Sums of Squares
##
# Comparing landmark to continuously registered curves
AmpPhasList = AmpPhaseDecomp(accelfdLM, accelfdCR, warpfdCR, c(3,18))
MS.amp = AmpPhasList$MS.amp
MS.pha = AmpPhasList$MS.pha
RSQRCR = AmpPhasList$RSQR
CCR = AmpPhasList$C
print(paste("Total MS =", round(MS.amp+MS.pha,2),
"Amplitude MS =", round(MS.amp,2),
"Phase MS =", round(MS.pha,2)))
# "Total MS = 1.5 Amplitude MS = 1.6 Phase MS = -0.1"
print(paste("R-squared =", round(RSQRCR,3), ", C =", round(CCR,3)))
# "R-squared = -0.067 , C = 1.001"
##
## 8.6 Registering the Chinese Handwriting Data
##
# No code for this section
##
## 8.7 Details for Functions landmarkreg and register.fd
##
help(landmarkreg)
help(register.fd)
##
## Section 8.8 Some Things to Try
##
# (exercises for the reader)
##
## Section 8.8 More to Read
##
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.