-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathfdarm-ch08.R
451 lines (347 loc) · 14 KB
/
fdarm-ch08.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
###
### 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
##