|
1 | 1 | # Contrasts from factors |
| 2 | + |
| 3 | +## Purpose |
| 4 | + |
| 5 | +When working with factorial experimental designs (e.g. two conditions |
| 6 | +crossed with three time points), specifying all the relevant contrasts |
| 7 | +by hand is tedious and error-prone. The `generate_contrasts` family of |
| 8 | +functions in prolfqua automates this by generating contrast |
| 9 | +specifications from factor levels. These contrast strings can then be |
| 10 | +passed directly to the `Contrasts` class for statistical testing. |
| 11 | + |
| 12 | +This vignette demonstrates how to: |
| 13 | + |
| 14 | +- Generate main effect, level-specific, and interaction contrasts for a |
| 15 | + two-factor design |
| 16 | +- Use `annotation_add_contrasts` to produce a combined annotation and |
| 17 | + contrast table ready for analysis |
| 18 | + |
| 19 | +## Group labelling convention |
| 20 | + |
| 21 | +All contrast generation functions assume that the group levels in the |
| 22 | +fitted model follow the naming convention `G_<primary>_<secondary>`, |
| 23 | +which is produced by |
| 24 | +[`group_label()`](https://wolski.github.io/prolfqua/reference/group_label.md): |
| 25 | + |
| 26 | +``` r |
| 27 | +library(prolfqua) |
| 28 | + |
| 29 | +group_label("MI", "T0") |
| 30 | +``` |
| 31 | + |
| 32 | + ## [1] "G_MI_T0" |
| 33 | + |
| 34 | +``` r |
| 35 | +group_label("MINOCA", "T300") |
| 36 | +``` |
| 37 | + |
| 38 | + ## [1] "G_MINOCA_T300" |
| 39 | + |
| 40 | +This means that before fitting a model, the data must contain a grouping |
| 41 | +column with levels in this format. The `annotation_add_contrasts` |
| 42 | +function creates such a column automatically using |
| 43 | +[`tidyr::unite`](https://tidyr.tidyverse.org/reference/unite.html). |
| 44 | + |
| 45 | +## Building contrasts step by step |
| 46 | + |
| 47 | +Consider a two-factor design with disease type (MI, MINOCA) and time |
| 48 | +point (T0, T150, T300): |
| 49 | + |
| 50 | +``` r |
| 51 | +primary_levels <- c("MI", "MINOCA") |
| 52 | +secondary_levels <- c("T0", "T150", "T300") |
| 53 | +``` |
| 54 | + |
| 55 | +### Main effect contrasts |
| 56 | + |
| 57 | +Main effects average across all levels of the secondary factor. For |
| 58 | +example, the main effect of MINOCA vs MI is the average difference |
| 59 | +across all time points: |
| 60 | + |
| 61 | +``` r |
| 62 | +me <- main_effect_contrasts(primary_levels, secondary_levels) |
| 63 | +data.frame(ContrastName = names(me), Contrast = unlist(me)) |
| 64 | +``` |
| 65 | + |
| 66 | + ## ContrastName |
| 67 | + ## MINOCA_vs_MI MINOCA_vs_MI |
| 68 | + ## Contrast |
| 69 | + ## MINOCA_vs_MI ( (G_MINOCA_T0 + G_MINOCA_T150 + G_MINOCA_T300)/3 - (G_MI_T0 + G_MI_T150 + G_MI_T300)/3 ) |
| 70 | + |
| 71 | +Swapping the roles of primary and secondary gives main effects for time |
| 72 | +points averaged across disease types: |
| 73 | + |
| 74 | +``` r |
| 75 | +me2 <- main_effect_contrasts(secondary_levels, primary_levels) |
| 76 | +data.frame(ContrastName = names(me2), Contrast = unlist(me2)) |
| 77 | +``` |
| 78 | + |
| 79 | + ## ContrastName |
| 80 | + ## T150_vs_T0 T150_vs_T0 |
| 81 | + ## T300_vs_T0 T300_vs_T0 |
| 82 | + ## T300_vs_T150 T300_vs_T150 |
| 83 | + ## Contrast |
| 84 | + ## T150_vs_T0 ( (G_T150_MI + G_T150_MINOCA)/2 - (G_T0_MI + G_T0_MINOCA)/2 ) |
| 85 | + ## T300_vs_T0 ( (G_T300_MI + G_T300_MINOCA)/2 - (G_T0_MI + G_T0_MINOCA)/2 ) |
| 86 | + ## T300_vs_T150 ( (G_T300_MI + G_T300_MINOCA)/2 - (G_T150_MI + G_T150_MINOCA)/2 ) |
| 87 | + |
| 88 | +### Level-specific contrasts |
| 89 | + |
| 90 | +These compare primary factor levels at each individual level of the |
| 91 | +secondary factor: |
| 92 | + |
| 93 | +``` r |
| 94 | +ls <- level_specific_contrasts(primary_levels, secondary_levels) |
| 95 | +data.frame(ContrastName = names(ls), Contrast = unlist(ls)) |
| 96 | +``` |
| 97 | + |
| 98 | + ## ContrastName Contrast |
| 99 | + ## MINOCA_vs_MI_at_T0 MINOCA_vs_MI_at_T0 G_MINOCA_T0 - G_MI_T0 |
| 100 | + ## MINOCA_vs_MI_at_T150 MINOCA_vs_MI_at_T150 G_MINOCA_T150 - G_MI_T150 |
| 101 | + ## MINOCA_vs_MI_at_T300 MINOCA_vs_MI_at_T300 G_MINOCA_T300 - G_MI_T300 |
| 102 | + |
| 103 | +### Interaction contrasts |
| 104 | + |
| 105 | +Interaction contrasts test whether the difference between primary levels |
| 106 | +changes across secondary levels (difference of differences): |
| 107 | + |
| 108 | +``` r |
| 109 | +ic <- interaction_contrasts(primary_levels, secondary_levels) |
| 110 | +data.frame(ContrastName = names(ic), Contrast = unlist(ic)) |
| 111 | +``` |
| 112 | + |
| 113 | + ## ContrastName |
| 114 | + ## interaction_MINOCA_vs_MI_at_T150_vs_T0 interaction_MINOCA_vs_MI_at_T150_vs_T0 |
| 115 | + ## interaction_MINOCA_vs_MI_at_T300_vs_T0 interaction_MINOCA_vs_MI_at_T300_vs_T0 |
| 116 | + ## interaction_MINOCA_vs_MI_at_T300_vs_T150 interaction_MINOCA_vs_MI_at_T300_vs_T150 |
| 117 | + ## Contrast |
| 118 | + ## interaction_MINOCA_vs_MI_at_T150_vs_T0 (G_MINOCA_T150 - G_MI_T150) - (G_MINOCA_T0 - G_MI_T0) |
| 119 | + ## interaction_MINOCA_vs_MI_at_T300_vs_T0 (G_MINOCA_T300 - G_MI_T300) - (G_MINOCA_T0 - G_MI_T0) |
| 120 | + ## interaction_MINOCA_vs_MI_at_T300_vs_T150 (G_MINOCA_T300 - G_MI_T300) - (G_MINOCA_T150 - G_MI_T150) |
| 121 | + |
| 122 | +### Single-factor contrasts |
| 123 | + |
| 124 | +For a one-factor design, `generate_contrasts_for_factor` generates all |
| 125 | +pairwise comparisons: |
| 126 | + |
| 127 | +``` r |
| 128 | +group_levels <- c("CondA", "CondB", "CondC") |
| 129 | +sf <- generate_contrasts_for_factor(group_levels) |
| 130 | +data.frame(ContrastName = names(sf), Contrast = unlist(sf)) |
| 131 | +``` |
| 132 | + |
| 133 | + ## ContrastName Contrast |
| 134 | + ## CondB_vs_CondA CondB_vs_CondA CondB - CondA |
| 135 | + ## CondC_vs_CondA CondC_vs_CondA CondC - CondA |
| 136 | + ## CondC_vs_CondB CondC_vs_CondB CondC - CondB |
| 137 | + |
| 138 | +## Generating all contrasts at once |
| 139 | + |
| 140 | +`generate_contrasts` combines main effects, level-specific, and |
| 141 | +interaction contrasts into a single data frame: |
| 142 | + |
| 143 | +``` r |
| 144 | +all_contrasts <- generate_contrasts(primary_levels, secondary_levels) |
| 145 | +knitr::kable(all_contrasts, row.names = FALSE) |
| 146 | +``` |
| 147 | + |
| 148 | +| ContrastName | Contrast | |
| 149 | +|:-----------------------------------------|:------------------------------------------------------------------------------------------| |
| 150 | +| MINOCA_vs_MI | ( (G_MINOCA_T0 + G_MINOCA_T150 + G_MINOCA_T300)/3 - (G_MI_T0 + G_MI_T150 + G_MI_T300)/3 ) | |
| 151 | +| MINOCA_vs_MI_at_T0 | G_MINOCA_T0 - G_MI_T0 | |
| 152 | +| MINOCA_vs_MI_at_T150 | G_MINOCA_T150 - G_MI_T150 | |
| 153 | +| MINOCA_vs_MI_at_T300 | G_MINOCA_T300 - G_MI_T300 | |
| 154 | +| interaction_MINOCA_vs_MI_at_T150_vs_T0 | (G_MINOCA_T150 - G_MI_T150) - (G_MINOCA_T0 - G_MI_T0) | |
| 155 | +| interaction_MINOCA_vs_MI_at_T300_vs_T0 | (G_MINOCA_T300 - G_MI_T300) - (G_MINOCA_T0 - G_MI_T0) | |
| 156 | +| interaction_MINOCA_vs_MI_at_T300_vs_T150 | (G_MINOCA_T300 - G_MI_T300) - (G_MINOCA_T150 - G_MI_T150) | |
| 157 | + |
| 158 | +To exclude interaction contrasts: |
| 159 | + |
| 160 | +``` r |
| 161 | +no_int <- generate_contrasts(primary_levels, secondary_levels, interactions = FALSE) |
| 162 | +knitr::kable(no_int, row.names = FALSE) |
| 163 | +``` |
| 164 | + |
| 165 | +| ContrastName | Contrast | |
| 166 | +|:---------------------|:------------------------------------------------------------------------------------------| |
| 167 | +| MINOCA_vs_MI | ( (G_MINOCA_T0 + G_MINOCA_T150 + G_MINOCA_T300)/3 - (G_MI_T0 + G_MI_T150 + G_MI_T300)/3 ) | |
| 168 | +| MINOCA_vs_MI_at_T0 | G_MINOCA_T0 - G_MI_T0 | |
| 169 | +| MINOCA_vs_MI_at_T150 | G_MINOCA_T150 - G_MI_T150 | |
| 170 | +| MINOCA_vs_MI_at_T300 | G_MINOCA_T300 - G_MI_T300 | |
| 171 | + |
| 172 | +## Working with annotation tables |
| 173 | + |
| 174 | +In a typical prolfquapp workflow, you start with a sample annotation |
| 175 | +table that has columns for the two factors. `annotation_add_contrasts` |
| 176 | +creates a united Group column, generates all contrasts, and binds them |
| 177 | +alongside the annotation: |
| 178 | + |
| 179 | +``` r |
| 180 | +# x5463yzwer453bbb is a bundled example annotation table |
| 181 | +# with factor_A (MI / MINOCA) and factor_B (T0 / T150 / T300) |
| 182 | +head(prolfqua::x5463yzwer453bbb[, c("Name", "Group", "factor_A", "factor_B")]) |
| 183 | +``` |
| 184 | + |
| 185 | + ## # A tibble: 6 × 4 |
| 186 | + ## Name Group factor_A factor_B |
| 187 | + ## <chr> <chr> <chr> <chr> |
| 188 | + ## 1 MI_150_6 MI_T150 MI T150 |
| 189 | + ## 2 MINOCA_0_3 MINOCA_T0 MINOCA T0 |
| 190 | + ## 3 MINOCA_300_5 MINOCA_T300 MINOCA T300 |
| 191 | + ## 4 MI_150_5 MI_T150 MI T150 |
| 192 | + ## 5 MI_150_1 MI_T150 MI T150 |
| 193 | + ## 6 MI_0_3 MI_T0 MI T0 |
| 194 | + |
| 195 | +``` r |
| 196 | +result <- annotation_add_contrasts( |
| 197 | + prolfqua::x5463yzwer453bbb, |
| 198 | + primary_col = "factor_A", |
| 199 | + secondary_col = "factor_B", |
| 200 | + prefix = "primary" |
| 201 | +) |
| 202 | + |
| 203 | +# The annotation with united Group and contrast columns |
| 204 | +knitr::kable(head(result$annot[, c("Name", "Group", "ContrastName", "Contrast")], 10), |
| 205 | + row.names = FALSE) |
| 206 | +``` |
| 207 | + |
| 208 | +| Name | Group | ContrastName | Contrast | |
| 209 | +|:-------------|:------------|:-----------------------------------------|:------------------------------------------------------------------------------------------| |
| 210 | +| MI_150_6 | MI_T150 | MINOCA_vs_MI | ( (G_MINOCA_T0 + G_MINOCA_T150 + G_MINOCA_T300)/3 - (G_MI_T0 + G_MI_T150 + G_MI_T300)/3 ) | |
| 211 | +| MINOCA_0_3 | MINOCA_T0 | MINOCA_vs_MI_at_T0 | G_MINOCA_T0 - G_MI_T0 | |
| 212 | +| MINOCA_300_5 | MINOCA_T300 | MINOCA_vs_MI_at_T150 | G_MINOCA_T150 - G_MI_T150 | |
| 213 | +| MI_150_5 | MI_T150 | MINOCA_vs_MI_at_T300 | G_MINOCA_T300 - G_MI_T300 | |
| 214 | +| MI_150_1 | MI_T150 | interaction_MINOCA_vs_MI_at_T150_vs_T0 | (G_MINOCA_T150 - G_MI_T150) - (G_MINOCA_T0 - G_MI_T0) | |
| 215 | +| MI_0_3 | MI_T0 | interaction_MINOCA_vs_MI_at_T300_vs_T0 | (G_MINOCA_T300 - G_MI_T300) - (G_MINOCA_T0 - G_MI_T0) | |
| 216 | +| MINOCA_150_1 | MINOCA_T150 | interaction_MINOCA_vs_MI_at_T300_vs_T150 | (G_MINOCA_T300 - G_MI_T300) - (G_MINOCA_T150 - G_MI_T150) | |
| 217 | +| MI_300_2 | MI_T300 | NA | NA | |
| 218 | +| MINOCA_150_5 | MINOCA_T150 | NA | NA | |
| 219 | +| MI_300_3 | MI_T300 | NA | NA | |
| 220 | + |
| 221 | +``` r |
| 222 | +# Suggested output file name |
| 223 | +result$name |
| 224 | +``` |
| 225 | + |
| 226 | + ## [1] "DEA_primary_dataset.csv" |
| 227 | + |
| 228 | +Swapping primary and secondary factors gives contrasts from the other |
| 229 | +perspective: |
| 230 | + |
| 231 | +``` r |
| 232 | +result2 <- annotation_add_contrasts( |
| 233 | + prolfqua::x5463yzwer453bbb, |
| 234 | + primary_col = "factor_B", |
| 235 | + secondary_col = "factor_A", |
| 236 | + prefix = "secondary" |
| 237 | +) |
| 238 | +knitr::kable(head(result2$annot[, c("Name", "Group", "ContrastName", "Contrast")], 10), |
| 239 | + row.names = FALSE) |
| 240 | +``` |
| 241 | + |
| 242 | +| Name | Group | ContrastName | Contrast | |
| 243 | +|:-------------|:------------|:---------------------------------------|:------------------------------------------------------------------| |
| 244 | +| MI_150_6 | T150_MI | T150_vs_T0 | ( (G_T150_MI + G_T150_MINOCA)/2 - (G_T0_MI + G_T0_MINOCA)/2 ) | |
| 245 | +| MINOCA_0_3 | T0_MINOCA | T300_vs_T0 | ( (G_T300_MI + G_T300_MINOCA)/2 - (G_T0_MI + G_T0_MINOCA)/2 ) | |
| 246 | +| MINOCA_300_5 | T300_MINOCA | T300_vs_T150 | ( (G_T300_MI + G_T300_MINOCA)/2 - (G_T150_MI + G_T150_MINOCA)/2 ) | |
| 247 | +| MI_150_5 | T150_MI | T150_vs_T0_at_MI | G_T150_MI - G_T0_MI | |
| 248 | +| MI_150_1 | T150_MI | T150_vs_T0_at_MINOCA | G_T150_MINOCA - G_T0_MINOCA | |
| 249 | +| MI_0_3 | T0_MI | T300_vs_T0_at_MI | G_T300_MI - G_T0_MI | |
| 250 | +| MINOCA_150_1 | T150_MINOCA | T300_vs_T0_at_MINOCA | G_T300_MINOCA - G_T0_MINOCA | |
| 251 | +| MI_300_2 | T300_MI | T300_vs_T150_at_MI | G_T300_MI - G_T150_MI | |
| 252 | +| MINOCA_150_5 | T150_MINOCA | T300_vs_T150_at_MINOCA | G_T300_MINOCA - G_T150_MINOCA | |
| 253 | +| MI_300_3 | T300_MI | interaction_T150_vs_T0_at_MINOCA_vs_MI | (G_T150_MINOCA - G_T0_MINOCA) - (G_T150_MI - G_T0_MI) | |
0 commit comments