Skip to content

Commit 3b39afb

Browse files
20250726 - quadratic and spline models
1 parent f8e5aa7 commit 3b39afb

1 file changed

Lines changed: 227 additions & 0 deletions

File tree

hlm.Rmd

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ mydata$ageYears <- mydata$age / 12
7676
mydata$ageMonthsCentered <- mydata$age - min(mydata$age, na.rm = TRUE)
7777
7878
mydata$ageYearsCentered <- mydata$ageMonthsCentered / 12
79+
mydata$ageYearsCenteredSquared <- mydata$ageYearsCentered ^ 2
7980
```
8081

8182
# Estimator: ML or REML
@@ -217,8 +218,234 @@ icc(linearMixedModel_nlme)
217218

218219
### Quadratic Growth Curve Model {#quadraticGCM}
219220

221+
When using higher-order polynomials, we could specify contrast codes for time to reduce multicollinearity between the linear and quadratic growth factors: https://tdjorgensen.github.io/SEM-in-Ed-compendium/ch27.html#saturated-growth-model
222+
223+
```{r}
224+
factorLoadings <- poly(
225+
x = c(0,1,2,3), # times (can allow unequal spacing)
226+
degree = 2)
227+
228+
factorLoadings
229+
230+
linearLoadings <- factorLoadings[,1]
231+
quadraticLoadings <- factorLoadings[,2]
232+
233+
linearLoadings
234+
quadraticLoadings
235+
```
236+
237+
#### Fit Model
238+
239+
```{r}
240+
quadraticGCM <- lmer(
241+
math ~ female + ageYearsCentered + ageYearsCenteredSquared + female:ageYearsCentered + female:ageYearsCenteredSquared + (1 + ageYearsCentered | id), # random intercepts and linear slopes; fixed quadratic slopes; sex as a fixed-effect predictor of the intercepts and slopes
242+
data = mydata,
243+
REML = FALSE, #for ML
244+
na.action = na.exclude,
245+
control = lmerControl(optimizer = "bobyqa"))
246+
247+
summary(quadraticGCM)
248+
```
249+
250+
This is equivalent to:
251+
252+
```{r}
253+
quadraticGCM <- lmer(
254+
math ~ female + ageYearsCentered + I(ageYearsCentered^2) + female:ageYearsCentered + female:I(ageYearsCentered^2) + (1 + ageYearsCentered | id), # random intercepts and slopes; sex as a fixed-effect predictor of the intercepts and slopes
255+
data = mydata,
256+
REML = FALSE, #for ML
257+
na.action = na.exclude,
258+
control = lmerControl(optimizer = "bobyqa"))
259+
260+
summary(quadraticGCM)
261+
```
262+
263+
#### Protoypical Growth Curve
264+
265+
```{r}
266+
newData <- expand.grid(
267+
female = c(0, 1),
268+
ageYears = seq(from = min(mydata$ageYears, na.rm = TRUE), to = max(mydata$ageYears, na.rm = TRUE), length.out = 10000))
269+
270+
newData$ageYearsCentered <- newData$ageYears - min(newData$ageYears)
271+
newData$ageYearsCenteredSquared <- newData$ageYearsCentered ^ 2
272+
273+
newData$sex <- NA
274+
newData$sex[which(newData$female == 0)] <- "male"
275+
newData$sex[which(newData$female == 1)] <- "female"
276+
newData$sex <- as.factor(newData$sex)
277+
278+
newData$predictedValue <- predict( # predict.merMod
279+
quadraticGCM,
280+
newdata = newData,
281+
re.form = NA
282+
)
283+
284+
ggplot(
285+
data = newData,
286+
mapping = aes(
287+
x = ageYears,
288+
y = predictedValue,
289+
color = sex)) +
290+
xlab("Age (Years)") +
291+
ylab("Math Score") +
292+
geom_line()
293+
```
294+
295+
#### Individuals' Growth Curves
296+
297+
```{r}
298+
mydata$predictedValue <- predict(
299+
quadraticGCM,
300+
newdata = mydata,
301+
re.form = NULL
302+
)
303+
304+
ggplot(
305+
data = mydata,
306+
mapping = aes(
307+
x = ageYears,
308+
y = predictedValue,
309+
group = factor(id))) +
310+
xlab("Age (Years)") +
311+
ylab("Math Score") +
312+
geom_line()
313+
```
314+
315+
#### Individuals' Trajectories Overlaid with Prototypical Trajectory
316+
317+
```{r}
318+
ggplot(
319+
data = mydata,
320+
mapping = aes(
321+
x = ageYears,
322+
y = predictedValue,
323+
group = factor(id))) +
324+
xlab("Age (Years)") +
325+
ylab("Math Score") +
326+
geom_line() +
327+
geom_line(
328+
data = newData,
329+
mapping = aes(
330+
x = ageYears,
331+
y = predictedValue,
332+
group = sex,
333+
color = sex),
334+
linewidth = 2)
335+
```
336+
337+
#### Extract Random Effects
338+
339+
```{r}
340+
ranef(quadraticGCM)
341+
```
342+
220343
### Spline Growth Curve Model {#splineGCM}
221344

345+
#### Create Knot
346+
347+
```{r}
348+
mydata$knot <- NA
349+
mydata$knot[which(mydata$ageYears <= 11)] <- 0
350+
mydata$knot[which(mydata$ageYears > 11)] <- 1
351+
```
352+
353+
#### Fit Model
354+
355+
```{r}
356+
splineGCM <- lmer(
357+
math ~ female + ageYearsCentered + female:ageYearsCentered + knot + knot:ageYearsCentered + female:knot + female:knot:ageYearsCentered + (1 + ageYearsCentered | id), # random intercepts and linear slopes; fixed quadratic slopes; sex as a fixed-effect predictor of the intercepts and slopes
358+
data = mydata,
359+
REML = FALSE, #for ML
360+
na.action = na.exclude,
361+
control = lmerControl(optimizer = "bobyqa"))
362+
363+
summary(splineGCM)
364+
```
365+
366+
#### Protoypical Growth Curve
367+
368+
```{r}
369+
newData <- expand.grid(
370+
female = c(0, 1),
371+
ageYears = seq(from = min(mydata$ageYears, na.rm = TRUE), to = max(mydata$ageYears, na.rm = TRUE), length.out = 10000))
372+
373+
newData$ageYearsCentered <- newData$ageYears - min(newData$ageYears)
374+
375+
newData$knot <- NA
376+
newData$knot[which(newData$ageYears <= 11)] <- 0
377+
newData$knot[which(newData$ageYears > 11)] <- 1
378+
379+
newData$sex <- NA
380+
newData$sex[which(newData$female == 0)] <- "male"
381+
newData$sex[which(newData$female == 1)] <- "female"
382+
newData$sex <- as.factor(newData$sex)
383+
384+
newData$predictedValue <- predict( # predict.merMod
385+
splineGCM,
386+
newdata = newData,
387+
re.form = NA
388+
)
389+
390+
ggplot(
391+
data = newData,
392+
mapping = aes(
393+
x = ageYears,
394+
y = predictedValue,
395+
color = sex)) +
396+
xlab("Age (Years)") +
397+
ylab("Math Score") +
398+
geom_line()
399+
```
400+
401+
#### Individuals' Growth Curves
402+
403+
```{r}
404+
mydata$predictedValue <- predict(
405+
splineGCM,
406+
newdata = mydata,
407+
re.form = NULL
408+
)
409+
410+
ggplot(
411+
data = mydata,
412+
mapping = aes(
413+
x = ageYears,
414+
y = predictedValue,
415+
group = factor(id))) +
416+
xlab("Age (Years)") +
417+
ylab("Math Score") +
418+
geom_line()
419+
```
420+
421+
#### Individuals' Trajectories Overlaid with Prototypical Trajectory
422+
423+
```{r}
424+
ggplot(
425+
data = mydata,
426+
mapping = aes(
427+
x = ageYears,
428+
y = predictedValue,
429+
group = factor(id))) +
430+
xlab("Age (Years)") +
431+
ylab("Math Score") +
432+
geom_line() +
433+
geom_line(
434+
data = newData,
435+
mapping = aes(
436+
x = ageYears,
437+
y = predictedValue,
438+
group = sex,
439+
color = sex),
440+
linewidth = 2)
441+
```
442+
443+
#### Extract Random Effects
444+
445+
```{r}
446+
ranef(splineGCM)
447+
```
448+
222449
# Generalized Linear Mixed Models {#generalized}
223450

224451
https://bbolker.github.io/mixedmodels-misc/glmmFAQ.html (archived at https://perma.cc/9RFS-BCE7; source code: https://github.com/bbolker/mixedmodels-misc/blob/master/glmmFAQ.rmd)

0 commit comments

Comments
 (0)