Nonlinear Regression (Oddi et al. LFMC) paper

knitr::opts_chunk$set(echo = TRUE, fig.width = 7, fig.height = 6)
library(lattice)
library(nlme)
library(ggplot2)
library(nlraa)
library(bbmle)
library(emmeans)

Steps in fitting a nonlinear mixed model

Part I.

Load data

data(lfmc)
lfmc.gd <- groupedData(lfmc ~ time | group, data = lfmc, order.groups=FALSE) 
## a groupedData class object is created 
lfmc.gd$plot = with(lfmc.gd, factor(plot, levels=c("4", "5", "6", "1", "2", "3")))
## "plot" is coded as factor in the groupedData object

Plot the data

ggplot(data = lfmc, aes(x = time, y = lfmc)) + 
  geom_point() + 
  geom_smooth(method = "loess") +
  facet_wrap(~leaf.type) + 
  xlab("Time (days)") + 
  ylab("LFMC (%)") # The LFMC temporal dynamics is plotted by "leaf type" 

NONLINEAR MIXED-EFFECTS MODEL (M1)

Nonlinear modeling proccess

## Fixed-effects model for each group usign the selfStart function ("SSdlf")
nlsL <- nlsList(lfmc ~ SSdlf(time, A, w, m, s), data = lfmc.gd) 
## one nls() model is fitted for each group ("plot" x "leaf type"). 
coef(nlsL) 
## estimated parameters (NAs indicate convergence problems)        
plot(intervals(nlsL)) 
## confidence intervals (wide intervals indicate convergence problems)

Random-Effects Mode

nl.re <- nlme(nlsL, control = nlmeControl(maxIter = 5000, msMaxIter = 1500)) 
## all effects on A, w, m, s are assumed as random. The model variance-covariance is unstructured.   

The previous model issues a warning. It is important to recognize that the unstructured variance-covariance meaning that 10 (4 + 3 + 2 + 1) parameters are tried to be estimated for the random effects. This is very likely to result in an over-parameterized model.

## Therefore, 
nl.re.1 <- update(nl.re, random = pdDiag(A + w + m + s ~ 1)) 
## a random-effects model assuming zero correlation among the random-effects is fitted 
## This converges easilty and this means the previous model (nl.re) is over parameterized. 
fxf <- fixef(nl.re.1) # the intercepts of the previous model are obtained to be used in the next step. 
## Mixed-effects model ("leaf type" is included as a fixed-effect on A, W, M, S) 
nl.me <- update(nl.re.1, fixed = list(A + w + m + s ~ leaf.type), 
                start = c(A=fxf[1],0,0,0, w=fxf[2],0,0,0, m=fxf[3],0,0,0, s=fxf[4],0,0,0)) 
## Error in (function (model, data = sys.frame(sys.parent()), fixed, random,  : 
##  Singularity in backsolve at level 0, block 1
## convergence problems
nl.re.1 # StdDev of "S" is small. 
## Therefore, the random-effects on "S" could be removed to achieve convergence 
nl.re.2 <- update(nl.re.1, random = pdDiag(A + w + m ~ 1))  
fxf <- fixef(nl.re.2)
## Now, the model including the fixed-effects of "leaf type" converges 
nl.me.1 <- update(nl.re.2, fixed = list(A + w + m + s ~ leaf.type),  
                  start = c(A=fxf[1],0,0,0, w=fxf[2],0,0,0, m=fxf[3],0,0,0, s=fxf[4],0,0,0))
## the covariance structure of the random effects is much smaller when the fixed-effects are modeled 
nl.me.1
nl.re.2
## ...suggesting parameters to vary with "leaf type"  
AICtab(nl.re.2, nl.me.1) # which is supported by the AIC comparison
## The small StdDev of "w" suggests to remove the random-effect of plot on this parameter  
nl.me.2 <- update(nl.me.1, random = pdDiag(A + m ~ 1))  
AICtab(nl.me.1, nl.me.2) # and the AIC comparison supports the simpler model
## in addition, if the random-effects on "m" is also removed   
nl.me.3 <- update(nl.me.2, random = pdDiag(A ~ 1)) 
AICtab(nl.me.2, nl.me.3) # the model fit is similar, supporting the simpler model

Plotting the residuals

# Residual checking:
plot(nl.me.3) # heterocedasticity is clear
hist(resid(nl.me.3, type="normalized"), main="", xlab="Residuals") 
qqnorm(resid(nl.me.3, type="normalized"))
qqline(resid(nl.me.3, type="normalized"))

Variance modeling

# Heterocedasticity: variance modeling ---- 
nl.me.3.vm <- update(nl.me.3, weights = varIdent(form=~1|leaf.type)) 
AICtab(nl.me.3, nl.me.3.vm) 
## the fit is clearly improved when variances are modeled

## Residual checking
plot(nl.me.3.vm)
hist(resid(nl.me.3.vm, type="normalized"), main="", xlab="Residuals") 
qqnorm(resid(nl.me.3.vm, type="normalized"))
qqline(resid(nl.me.3.vm, type="normalized"))
## the residuals look fine now

# Temporal correlation
plot(ACF(nl.me.3.vm), alpha = 0.01) 
## This plot suggests there is no need for modeling the correlation of the residuals

Evaluation of Fixed Effects

# Evaluation of the fixed-effects
nl.me.ml <- update(nl.me.3.vm, method ="ML") 
## the model is fitted by Maximum likelihood 
nl.me.ml.1 <- update(nl.me.ml, fixed = list(A + w + m ~ leaf.type, s ~ 1),  
                     start = c(A=fxf[1],0,0,0, w=fxf[2],0,0,0, m=fxf[3],0,0,0, s=fxf[4])) 
## the model is reduced removing the fixed-effect of "leaf type" on "s" 
AICtab(nl.me.ml, nl.me.ml.1) 
## the AIC comparison suggests that the more complex model is not justified (fits are similar) 
nl.me.ml.2 <- update(nl.me.ml, fixed = list(A + w ~ leaf.type, m + s ~ 1),  
                     start = c(A=fxf[1],0,0,0, w=fxf[2],0,0,0, m=fxf[3], s=fxf[4])) 
## the model is reduced removing the fixed-effect of "leaf type" on "m" 
AICtab(nl.me.ml.1, nl.me.ml.2) 
## the more complex model is not justified   
nl.me.ml.3 <- update(nl.me.ml, fixed = list(A ~ leaf.type, w + m + s  ~ 1),  
                     start = c(A=fxf[1],0,0,0, w=fxf[2], m=fxf[3], s=fxf[4])) 
## the model is reduced removing the fixed-effect of "leaf type" on "W"                    
AICtab(nl.me.ml.2, nl.me.ml.3) 
## the AIC comparison suggests to "w" to be modeled as a function of "leaf type" 
nl.me.ml.4 <- update(nl.me.ml, fixed = list(w ~ leaf.type, A + m + s  ~ 1),  
                     start = c(A=fxf[1], w=fxf[2],0,0,0, m=fxf[3], s=fxf[4])) 
## the model is reduced removing the fixed-effect of "leaf type" on "A"                    
AICtab(nl.me.ml.2, nl.me.ml.4) 
## the AIC comparison suggests to "A" to be modeled as a function of "leaf type" 

Final Model

This model assumes:

M1 <- update(nl.me.ml.2, method = "REML")
summary(M1)
fixef(M1) # Fixed effects 
ranef(M1) # Random effects
intervals(M1) # Confidence intervals

Part II

The script below is identical to the portion that was distributed as the additional supporting information with the Oddi et al. publication.

# 2.2.1 - Overall predictions (Table 3) ---- 
Agw <- fixef(M1)[1] # "A" for grasses in the W site (GW) 
Age <- fixef(M1)[1]+fixef(M1)[2] # "A" for grasses in the E site (GE)
Asm <- fixef(M1)[1]+fixef(M1)[3] # "A" for Mullinum spinosum (SM)
Ass <- fixef(M1)[1]+fixef(M1)[4] # "A" for Senecio filaginoides (SS)
Wgw <- fixef(M1)[5] # "w" for grasses in the W site (GW) 
Wge <- fixef(M1)[5]+fixef(M1)[6] # "w" for grasses in the E site (GE) 
Wsm <- fixef(M1)[5]+fixef(M1)[7] # "w" for Mullinum spinosum (SM)
Wss <- fixef(M1)[5]+fixef(M1)[8] # "w" for Senecio filaginoides (SS)
M <- fixef(M1)[9] # "m" for all the leaf types
S <- fixef(M1)[10] # "s" for all the leaf types

GW <- subset(lfmc, leaf.type == "Grass W")
GE <- subset(lfmc, leaf.type == "Grass E")
SM <- subset(lfmc, leaf.type == "M. spinosum")
SS <- subset(lfmc, leaf.type == "S. bracteolactus")

par(mfrow=c(2,2), cex=0.75)
# Grasses W
plot(GW$time, GW$lfmc, xlab="", ylab="LFMC (%)", ylim=c(0,100), main="Grasses W site", col="darkgreen")
curve(((Agw-Wgw)/(1 + exp((M-x)/S))+Wgw), lwd=2, lty=1, add=T, col="darkgreen") 
# Grasses E
plot(GE$time, GE$lfmc, xlab="", ylab="LFMC (%)", ylim=c(0,100), main="Grasses E site", col="green")
curve(((Age-Wge)/(1 + exp((M-x)/S))+Wge), lwd=2, lty=1, add=T, col="green")
# M. Spinosum
plot(SM$time, SM$lfmc, xlab="Time (days)", ylab="LFMC (%)", ylim=c(0,350), main="M. spinosum", font.main=4, col="darkorange")
curve(((Asm-Wsm)/(1 + exp((M-x)/S))+Wsm), lwd=2, lty=1, add=T, col="darkorange") 
# S. filaginoides 
plot(SS$time, SS$lfmc, xlab="Time (days)", ylab="LFMC (%)", ylim=c(0,350), main="S. bracteolactus", font.main=4, col="darkred")
curve(((Ass-Wss)/(1 + exp((M-x)/S))+Wss), lwd=2, lty=1, add=T, col="darkred") 


# 2.2.2 - Predictions at plot level (Table 3) ----
Agw.p4 <- fixef(M1)[1]+ranef(M1)[1,1] # "A" for grasses in the plot 4 (GW) 
Agw.p5 <- fixef(M1)[1]+ranef(M1)[2,1] # "A" for grasses in the plot 5 (GW) 
Agw.p6 <- fixef(M1)[1]+ranef(M1)[3,1] # "A" for grasses in the plot 6 (GW) 
Age.p1 <- fixef(M1)[1]+fixef(M1)[2]+ranef(M1)[4,1] # "A" for grasses in the plot 1 (GW) 
Age.p2 <- fixef(M1)[1]+fixef(M1)[2]+ranef(M1)[7,1] # "A" for grasses in the plot 2 (GW) 
Age.p3 <- fixef(M1)[1]+fixef(M1)[2]+ranef(M1)[10,1] # "A" for grasses in the plot 3 (GW) 
Asm.p1 <- fixef(M1)[1]+fixef(M1)[3]+ranef(M1)[5,1] # "A" for M. spinosum in the plot 1 (SM) 
Asm.p2 <- fixef(M1)[1]+fixef(M1)[3]+ranef(M1)[8,1] # "A" for M. spinosum in the plot 2 (SM)
Asm.p3 <- fixef(M1)[1]+fixef(M1)[3]+ranef(M1)[11,1] # "A" for M. spinosum in the plot 3 (SM)
Ass.p1 <- fixef(M1)[1]+fixef(M1)[4]+ranef(M1)[6,1] # "A" for S. filaginoides in the plot 1 (SM)   
Ass.p2 <- fixef(M1)[1]+fixef(M1)[4]+ranef(M1)[9,1] # "A" for S. filaginoides in the plot 2 (SM) 
Ass.p3 <- fixef(M1)[1]+fixef(M1)[4]+ranef(M1)[12,1] # "A" for S. filaginoides in the plot 3 (SM)

par(mfrow=c(2,2), cex=0.75) # (Figure 4)
# Grasses W site 
plot(GW$time, GW$lfmc, xlab="", ylab="LFMC (%)", ylim=c(0,100), main="Grasses W site", col="darkgreen")
curve(((Agw.p4-Wgw)/(1 + exp((M-x)/S))+Wgw), lwd=1, lty=2, add=T, col="darkgreen") # plot 4
curve(((Agw.p5-Wgw)/(1 + exp((M-x)/S))+Wgw), lwd=1, lty=2, add=T, col="darkgreen") # plot 5
curve(((Agw.p6-Wgw)/(1 + exp((M-x)/S))+Wgw), lwd=1, lty=2, add=T, col="darkgreen") # plot 6
# Grasses E site
plot(GE$time, GE$lfmc, xlab="", ylab="LFMC (%)", ylim=c(0,100), main="Grasses E site", col="green")
curve(((Age.p1-Wge)/(1 + exp((M-x)/S))+Wge), lwd=1, lty=2, add=T, col="green") # plot 1
curve(((Age.p2-Wge)/(1 + exp((M-x)/S))+Wge), lwd=1, lty=2, add=T, col="green") # plot 2
curve(((Age.p3-Wge)/(1 + exp((M-x)/S))+Wge), lwd=1, lty=2, add=T, col="green") # plot 3
# M. Spinosum (E site) 
plot(SM$time, SM$lfmc, xlab="Time (days)", ylab="LFMC (%)", ylim=c(0,350), main="M. spinosum", font.main=4, col="darkorange")
curve(((Asm.p1-Wsm)/(1 + exp((M-x)/S))+Wsm), lwd=1, lty=2, add=T, col="darkorange") # plot 1
curve(((Asm.p2-Wsm)/(1 + exp((M-x)/S))+Wsm), lwd=1, lty=2, add=T, col="darkorange") # plot 2
curve(((Asm.p3-Wsm)/(1 + exp((M-x)/S))+Wsm), lwd=1, lty=2, add=T, col="darkorange") # plot 3
# S. filaginoides (E site)
plot(SS$time, SS$lfmc, xlab="Time (days)", ylab="LFMC (%)", ylim=c(0,350), main="S. bracteolactus", font.main=4, col="darkred")
curve(((Ass.p1-Wss)/(1 + exp((M-x)/S))+Wss), lwd=1, lty=2, add=T, col="darkred") # plot 1
curve(((Ass.p2-Wss)/(1 + exp((M-x)/S))+Wss), lwd=1, lty=2, add=T, col="darkred") # plot 2
curve(((Ass.p3-Wss)/(1 + exp((M-x)/S))+Wss), lwd=1, lty=2, add=T, col="darkred") # plot 3


# 2.2.3 - Derivatives (drying speed) ---- 
lfmc.GW <- expression((Agw-Wgw)/(1 + exp((M-x)/S))+Wgw) # LFMC dynamics for grasses in the W site
ds.GW <- deriv(lfmc.GW, "x", function.arg = TRUE) # Drying speed for grasses in the W site

lfmc.GE <- expression((Age-Wge)/(1 + exp((M-x)/S))+Wge) # LFMC dynamics for grasses in the E site
ds.GE <- deriv(lfmc.GE, "x", function.arg = TRUE) # Drying speed for grasses in the E site

lfmc.SM <- expression((Asm-Wsm)/(1 + exp((M-x)/S))+Wsm) # LFMC dynamics for M. spinosum
ds.SM <- deriv(lfmc.SM, "x", function.arg = TRUE) # Drying speed for M. spinosum

lfmc.SS <- expression((Ass-Wss)/(1 + exp((M-x)/S))+Wss) # LFMC dynamics for S. filaginoides
ds.SS <- deriv(lfmc.SS, "x", function.arg = TRUE) # # Drying speed for S. filaginoides

xvec <- seq(0, 100, length = 1000)
y.ds.GW <- ds.GW(xvec)
y.ds.GE <- ds.GE(xvec)
y.ds.SM <- ds.SM(xvec)
y.ds.SS <- ds.SS(xvec)

par(mfrow=c(2,2), cex=0.75) # (Figure 4)
plot(xvec, y.ds.GW, xlim=c(0,80), ylim=c(0,4), 
     ylab="Drying speed", xlab="Time (days)", type="n", main="Grasses in the W site")
lines(xvec, -1*(attr(y.ds.GW, "grad")), lwd=2, lty=1, col="darkgreen")
plot(xvec, y.ds.GE, xlim=c(0,80), ylim=c(0,4), 
     ylab="Drying speed", xlab="Time (days)", type="n", main="Grasses in the E site")
lines(xvec, -1*(attr(y.ds.GE, "grad")), lwd=2, lty=1, col="green")
plot(xvec, y.ds.SM, xlim=c(0,80), ylim=c(0,4), 
     ylab="Drying speed", xlab="Time (days)", type="n", main="M. spinosum", font.main=4)
lines(xvec, -1*(attr(y.ds.SM, "grad")), lwd=2, lty=1, col="darkorange")
plot(xvec, y.ds.SS, xlim=c(0,80), ylim=c(0,4), 
     ylab="Drying speed", xlab="Time (days)", type="n", main="S. filaginoides", font.main=4)
lines(xvec, -1*(attr(y.ds.SS, "grad")), lwd=2, lty=1, col="darkred")


# 2.3 ---- Testing differences among leaf types ------------------------------------------- 

# 2.3.1 - Parameter "A" ----
contrast(emmeans(M1, ~leaf.type, param = "A"), "pairwise")

# .- Maximum LFMC in grasses from the W site is lower than grasses from the E site (Grass W - Grass E)

# .- Maximum LFMC in grasses is lower than shrubs (Grass W - M. spinosum)
#                                                 (Grass W - S. bracteolactus)
#                                                 (Grass E - M. spinosum)
#                                                 (Grass E - S. bracteolactus)

# .- There are not difference between the two shrub species (M. spinosum - S. bracteolactus).


# 2.3.2 - Parameter "w" ---- 
contrast(emmeans(M1, ~leaf.type, param = "w"), "pairwise")

# .- Minimum LFMC in grasses from the W site is higher than grasses from the E site (Grass W - Grass E)

# .- Minimum LFMC in grasses is lower than shrubs (Grass W - M. spinosum)
#                                                 (Grass W - S. bracteolactus)
#                                                 (Grass E - M. spinosum)
#                                                 (Grass E - S. bracteolactus)

# .- There are not difference between the two shrub species (M. spinosum - S. bracteolactus).


# 3) NONLINEAR FIXED-EFFECTS MODEL (M2)  #####################################################################

# Response function:  lfmc = (A - w) / (1 + exp((m - time)/s))) + w 


# 3.1 ---- Fit of the nonlinear fixed-effects model ---------------------------------- 

# 3.1.1 - Base nonlinear model ----
nl.fe.0 <- nls(lfmc ~ ((A - w) / (1 + exp((m - time)/s))) + w, 
               start = c(A=15, m=30, s=-17, w=5),
               data = lfmc) # here, time is the only predictor variable 

b <- coef(nl.fe.0) # coefficients of the base model

# 3.1.2 - Leaf type fixed effect ----
nl.fe.1 <- nls(lfmc ~ ((A[leaf.type] - w[leaf.type])/(1 + exp((m - time)/s))) + w[leaf.type], 
              start = list(A=rep(b[1], 4), m=25, s=-10, w=rep(b[4], 4)),
              data = lfmc) # and fit is made using the base model's coefficients as start values

b1 <- coef(nl.fe.1) # coefficients of the model with leaf type and time as predictor variables

# 3.1.3 - Plot fixed effect ---- 
nl.fe.2 <- nls(lfmc ~ ((A[group] - w[group])/(1 + exp((m - time)/s))) + w[group], 
               start = list(A=rep(c(b1[1],b1[2],b1[3],b1[4]),3), m=25, s=-10, w=rep(c(b1[7],b1[8],b1[9],b1[10]),3)),
               data = lfmc) # the model is fit using "b1" as the start values

AICtab(nl.fe.0, nl.fe.1, nl.fe.2) # including leaf type and plot as predictor variables imrpoves the model fit 

# Residual checking:
par(mfrow=c(1,1))
plot(nl.fe.2) # heterocedasticity in the residuals
hist(resid(nl.fe.2), main="", xlab="Residuals") # slightly skew distribution   
qqnorm(resid(nl.fe.2))
qqline(resid(nl.fe.2))


# 3.2 ---- Final model ---------------------------------- 

M2 <- nl.fe.2
summary(M2)

# This model assumes:

# .- "A" and "w" vary with leaf type and plot (fixed-effects).
# .- "m" and "s" are unique for all the plots and leaf types. 
# .- Variance is homogeneous
# .- There is no temporal dependence 
# .- Residuals following a normal distribution


# 4 - LINEAR MIXED-EFFECTS MODEL (M3) ####################################################################### 

# Response function:  lfmc = β0 + β1xTime + β2xGE + β3xSM + β4xSS
#                            β5xGExTime + β6xSMxTime + β7xSSxTime

# where GE, SM, and SS are dummy variables (0 or 1) created to indicate differences between 
# the base level of "leaf type", i.e., grasses from the W site [GW], and the remaining
# levels. 


# 4.1 ---- Modeling process of the linear mixed-effects model ---------------------------------- 

# 4.1.1 - Base linear mixed-effect model ----
l.me <- lme(lfmc ~ time * leaf.type, random = ~1 | plot, data = lfmc)

# Residual checking:
plot(l.me) # heterocedasticity in the residuals
plot(ACF(l.me, resType = "normalized", na.action=na.omit), alpha=0.05, grid=TRUE) # temporal correlation is observed at lag 1


# 4.1.2 - Variance modelling ---- 
l.me.vm <- update(l.me, weights = varIdent(form=~ 1 | leaf.type))

# Residual checking:
plot(l.me.vm) # variances there seem to be well modeled
AIC(l.me, l.me.vm) # modeling the variance improves the model fit 


# 4.1.3 - Temporal correlation modelling ----  
l.me.vm.tm <- update(l.me.vm, correlation=corARMA(p=1,q=1, form=~1))
plot(ACF(l.me.vm.tm, resType = "normalized", na.action=na.omit), alpha=0.05, grid=TRUE) # the temporal dependence is modeled
AIC(l.me.vm, l.me.vm.tm) # modeling the temporal correlation improves the model fit

# Residual checking:
plot(l.me.vm.tm) 
plot(ACF(l.me.vm.tm, resType = "normalized", na.action=na.omit), alpha=0.05, grid=TRUE) 
hist(resid(l.me.vm.tm, type="normalized"), main="", xlab="Residuals") 
qqnorm(resid(l.me.vm.tm, type="normalized"))
qqline(resid(l.me.vm.tm, type="normalized"))
# the residuals look fine


# 4.1.4 - Evaluation of the fixed-effects ---- 
l.me.ml <- update(l.me.vm.tm, method ="ML") # the model is fitted by Maximum likelihood 
l.me.ml.1 <- update(l.me.ml, ~.-time:leaf.type) # the model is reduced removing the interaction "time x leaf type" 
AICtab(l.me.ml, l.me.ml.1) # the AIC comparison suggests the interaction term to be important  


# 4.2 ---- Final model ---------------------------------- 

M3 <- l.me.vm.tm 
summary(M3)

# This model assumes:

# .- β0 vary with plots as a random-effects (random intercept model)
# .- The residual variance depends on "leaf type"
# .- Temporal correlation
# .- Residuals follow a normal distribution


# 5 - CLASICAL REGRESSION (M4) ##################################################################################

M4 <- lm(lfmc ~ time * group, data = lfmc)
summary(M4)

# This model assumes:

# .- The residual variance depends on "leaf type"
# .- Temporal correlation
# .- Residuals following a normal distribution


# Residual checking:
par(mfrow=c(2,2))
plot(M4) # a clear pattern in the residuals is observed (model assumptions are not met) 

# 6 - NULL MODEL (M5) ######################################################################################

M5 <- lm(lfmc ~ 1, data = lfmc)
summary(M5)


# 7 - MODEL COMPARISON ############################################################################

# Models:  

# M1: Nonlinear mixed-effects model
# M2: Nonlinear fixed-effects model
# M3: Linear mixed-effetcs model  
# M4: Lnear fixed-effects model (clasical regression) 
# M5: Null model

# 7.1 ---- AIC comparison (Table 1) ----------------------------------------------  

AICtab(M1, M2, M3, M4, M5,  
       weights = T, delta = TRUE, base=T, sort = TRUE) # the nonlinear mixed-effects model is clearly the best

# 7.2 ---- Residual analysis -------------------------------------------

par(mfrow=c(2,2), cex = 0.65) # (Figure 5)
v1 <- c(1, 2, 3, 4)
v2 <- c("GW", "GE", "SM", "SS")

# Nonlinear mixed-effects model (M1) 
rM1 <- resid(M1, type = "normalized")
fM1 <- fitted(M1)
plot(fM1, rM1, xlab="Fitted LFMC (%)", ylab="Residuals", ylim=c(-3,3))
abline(a=0, b=0, lwd=2)  
boxplot(rM1 ~ lfmc$leaf.type, ylab="Residuals", xlab="Leaf type", xaxt="n", ylim=c(-3,3))
axis(side=1, at=v1, labels=v2, las=1)
plot(rM1 ~ lfmc$time, ylab="Residuals", xlab="Time (days)", ylim=c(-3,3))
abline(a=0,b=0, lw=2)
qqnorm(rM1, main="", xlab="Theoretical quantiles", ylab="Sample quantiles")
qqline(rM1)

# Nonlinear fixed-effects model (M2)
rM2 <- resid(M2)
fM2 <- fitted(M2)
plot(fM2, rM2, xlab="Fitted LFMC (%)", ylab="Residuals")
abline(a=0, b=0, lwd=2)  
boxplot(rM2 ~ lfmc$leaf.type, ylab="Residuals", xlab="Leaf type", xaxt="n")
axis(side=1, at=v1, labels=v2, las=1)
plot(rM2 ~ lfmc$time, ylab="Residuals", xlab="Time (days)")
abline(a=0,b=0, lw=2)
qqnorm(rM2, main="", xlab="Theoretical quantiles", ylab="Sample quantiles")
qqline(rM2)

# Linear mixed-effects model (M3) 
rM3 <- resid(M3, type = "normalized")
fM3 <- fitted(M3)
plot(fM3, rM3, xlab="Fitted LFMC (%)", ylab="Residuals", ylim=c(-3,3))
abline(a=0, b=0, lwd=2)  
boxplot(rM3 ~ lfmc$leaf.type, ylab="Residuals", xlab="Leaf type", xaxt="n", ylim=c(-3,3))
axis(side=1, at=v1, labels=v2, las=1)
plot(rM3 ~ lfmc$time, ylab="Residuals", xlab="Time (days)", ylim=c(-3,3))
abline(a=0,b=0, lw=2)
qqnorm(rM3, main="", ylab="Sample quantiles", xlab="Theoretical quantiles")
qqline(rM3)

# Clasical regression (M4) 
rM4 <- resid(M4)
fM4 <- fitted(M4)
plot(fM4, rM4, xlab="Fitted LFMC (%)", ylab="Residuals")
abline(a=0, b=0, lwd=2)  
boxplot(rM4 ~ lfmc$leaf.type, ylab="Residuals", xlab="", xaxt="n")
axis(side=1, at=v1, labels=v2, las=1)
plot(rM4 ~ lfmc$time, ylab="Residuals", xlab="Time (days)")
abline(a=0,b=0, lw=2)
qqnorm(rM4, main="", xlab="Theoretical quantiles", ylab="Sample quantiles")
qqline(rM4)


Try the nlraa package in your browser

Any scripts or data that you put into this service are public.

nlraa documentation built on April 22, 2021, 1:06 a.m.