Skip to content

Commit 8477a85

Browse files
committed
intial idea
1 parent 474c09c commit 8477a85

File tree

3 files changed

+156
-196
lines changed

3 files changed

+156
-196
lines changed

R/ancova.R

Lines changed: 0 additions & 196 deletions
This file was deleted.

design/anova/anova.Rmd

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
---
2+
title: "Anova for MMRM"
3+
output: html_document
4+
editor_options:
5+
chunk_output_type: console
6+
markdown:
7+
wrap: 72
8+
---
9+
10+
11+
# Backgroun
12+
13+
This [blog](https://www.r-bloggers.com/2011/03/anova-%E2%80%93-type-iiiiii-ss-explained/) has a nice summary of it.
14+
The original [post](http://goanna.cs.rmit.edu.au/fscholer/anova.php) is not accessible now.
15+
16+
And we can also refer to this [page](https://agstats.io/tutorials/sas-proc-mixed)
17+
18+
Some available code from [glmmTMB](https://github.com/glmmTMB/glmmTMB/blob/master/glmmTMB/R/Anova.R)
19+
20+
SAS also has some paper on this topic, but from a different angle.
21+
It uses estimable functions to construct the three types of hypothesis.
22+
23+
See paper https://doi.org/10.1080/03610928008827869 and https://support.sas.com/documentation/onlinedoc/stat/131/introglmest.pdf.
24+
25+
## SAS results
26+
27+
Using `anova.sas` we have the following results.
28+
Please note that type 1 result is dependent on the order of testing so it is not included here for simplicity.
29+
Type 2 and 3 tests are independent of the order of model parameter provided to the procedure.
30+
31+
32+
| type 2 | df| ddf | F value | Pr > F |
33+
|--------------|---|------------------|-------------------|----------------------|
34+
| ARMCD | 1 | 177.578206251403 | 33.1870413448661 | 3.62415957242791e-08 |
35+
| AVISIT | 3 | 336.480231426766 | 116.732733882531 | 7.97333625254255e-52 |
36+
| AVISIT*ARMCD | 3 | 335.929182055453 | 0.345902681482862 | 0.792143200158668 |
37+
38+
39+
| type 3 | df| ddf | F value | Pr > F |
40+
|--------------|---|------------------|-------------------|----------------------|
41+
| ARMCD | 1 | 177.848284237308 | 33.0036689632883 | 3.91621392761811e-08 |
42+
| AVISIT | 3 | 335.929182055453 | 116.539400933677 | 9.71251157971289e-52 |
43+
| AVISIT*ARMCD | 3 | 335.368828514443 | 0.345902681482865 | 0.792143224823356 |
44+
45+
46+
## emmeans joint_tests
47+
48+
```{r}
49+
library(mmrm)
50+
fit <- mmrm(FEV1 ~ ARMCD + AVISIT + ARMCD * AVISIT + ar1(AVISIT | USUBJID), data = fev_data)
51+
emmeans::joint_tests(fit)
52+
```
53+
54+
55+
however, the result do not agree well between SAS and R.
56+
57+
Using "satterthwaite df" the F statistic is close, but the d.f. is not.
58+
59+
```
60+
model term df1 df2 F.ratio p.value
61+
ARMCD 1 177.85 33.004 <.0001
62+
AVISIT 3 309.60 116.540 <.0001
63+
ARMCD:AVISIT 3 309.60 0.346 0.7921
64+
```
65+
66+
## Finding suitable contrasts
67+
68+
similar to emmeans::joint_tests() obtain a contrasts. In `emmeans` the contrasts can be obtained for Type 3, and
69+
use these contrasts to send to `df_md` we get exactly the same results with SAS.
70+
71+
```{r}
72+
args <- emmeans:::.zap.args(object = fit, cov.reduce = emmeans:::meanint, omit = "submodel")
73+
object <- do.call(emmeans:::ref_grid, args)
74+
by <- NULL
75+
facs <- setdiff(names(object@levels), c(by, "1"))
76+
emm <- emmeans::emmeans(fit, facs[2], by = by)
77+
emmgrid <- emmeans::contrast(emm, interaction = "consec", by = union(by, NULL))
78+
tst <- emmeans::test(emmgrid, by = by, joint = TRUE, status = TRUE)
79+
ef <- attr(tst, "est.fcns")
80+
```
81+
82+
```
83+
$ARMCD
84+
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
85+
[1,] 0 -0.9176629 0 0 0 -0.2294157 -0.2294157 -0.2294157
86+
87+
$`ARMCD:AVISIT`
88+
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
89+
[1,] 0 0 0 0 0 -1 0 0
90+
[2,] 0 0 0 0 0 0 -1 0
91+
[3,] 0 0 0 0 0 0 0 -1
92+
93+
$AVISIT
94+
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
95+
[1,] 0 0 -0.8944272 0.0000000 0.0000000 -0.4472136 0.0000000
96+
[2,] 0 0 0.0000000 -0.8944272 0.0000000 0.0000000 -0.4472136
97+
[3,] 0 0 0.0000000 0.0000000 -0.8944272 0.0000000 0.0000000
98+
[,8]
99+
[1,] 0.0000000
100+
[2,] 0.0000000
101+
[3,] -0.4472136
102+
```
103+
104+
### Obtain the contrasts
105+
106+
```
107+
df_md(fit, contrasts)
108+
```
109+
110+
### Initial thoughts
111+
112+
Using doolittle method to obtain the contrasts seems plausible? but this is initial.
113+
114+
```{r}
115+
doolittle <- function(m) {
116+
assert_matrix(m)
117+
for (i in seq_len(nrow(m))) {
118+
for (j in seq_len(i - 1)) {
119+
m[i, ] <- m[i, ] - m[i, j] * m[j, ]
120+
}
121+
if (m[i, i] == 0) {
122+
m[i, ] <- 0
123+
} else {
124+
m[i, ] <- m[i, ] / m[i, i]
125+
}
126+
}
127+
return(m)
128+
}
129+
130+
normalize_mat <- function(m) {
131+
m / rowSums(m^2)
132+
}
133+
134+
mx <- model.matrix(fit)
135+
dl <- doolittle(t(mx) %*% mx)
136+
df_md(fit, dl[6:8, ])
137+
df_md(fit, normalize_mat(dl[6:8, ]))
138+
df_md(fit, dl[2, ]) # d.f. do not match, stat matches a lot ()
139+
```

design/anova/anova.sas

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
%xpt2loc(libref = work, filespec = 'data-raw/fev.xpt');
2+
ods output Tests1 = Tests1 Tests2 = Tests2 Tests3 = Tests3;
3+
4+
PROC MIXED DATA = fev cl method=reml;
5+
CLASS RACE(ref = '3') AVISIT(ref = '4') SEX(ref = '2') ARMCD(ref = '2') USUBJID;
6+
MODEL FEV1 = ARMCD*AVISIT ARMCD AVISIT / ddfm=satterthwaite htype=1,2,3;
7+
REPEATED AVISIT / subject=USUBJID type=ar(1);
8+
RUN;
9+
10+
libname mm 'data-raw';
11+
data mm.test1;
12+
set tests1;
13+
data mm.test2;
14+
set tests2;
15+
data mm.test3;
16+
set tests3;
17+
run;

0 commit comments

Comments
 (0)