This tutorial follows from our introduction to multivariate linear models, extending it by using multivariate linear mixed models.

Useful packages:

library(MCMCglmm)
library(lme4)
library(brms)
library(tidyr)
library(dplyr)
library(corrplot)
library(broom.mixed)
library(dotwhisker)
library(ggplot2); theme_set(theme_bw())

0.1 data

This is an old Drosophila melanogaster data set from Ian Dworkin’s PhD work (Dworkin 2005). This was from a study that was meant to test predictions of a model on how mutational and environmental variation can influence the overall structure of phenotypic variation. For this study Dworkin measured several traits (lengths) on the first leg as well as the number of sex comb teeth (a structure used to clasp females during copulation) for different wild type strains (line) reared at different developmental temperatures (temp), with and without a mutation that effects proximal-distal axis development in limbs (genotype).

Get data from https://doi.org/10.5061/dryad.8376 or this repo:

dll_data <- read.csv("../data/dll.csv")
## make temp a factor (25 vs 30 degrees)
dll_data$temp <- factor(dll_data$temp)
## scale relevant variables (fancier but less repetition than previously)
morph_vars <- c("femur","tibia","tarsus","SCT")
morph_vars_sc <- paste(morph_vars,"s",sep="_")
dll_data2 <- dll_data
## c() drops unwanted structure from the results of scale()
for (i in 1:length(morph_vars)) {
    dll_data2[[morph_vars_sc[i]]] <- c(scale(dll_data[[morph_vars[i]]]))
}

0.2 Mixed models

The previous expression for the multivariate model was

\[ \mathbf{Y} = \mathbf{XB} + \mathbf{E} \]

In contrast, the expression for the mixed model is

\[ y = \mathbf{X\beta} + \mathbf{Zb} + \epsilon \]

where \(\mathbf{b}\) is a set of Gaussian variables with a variance-covariance matrix \(\mathbf{\Sigma}\) which we estimate.


Suppose we have observations of \(m\) individuals, with \(p\) different observations (traits, or time points, or types of measurement, or …) for each individual. The way we’re going to make this work is to expand (“melt”, or gather() or pivot_longer() if we’re using tidyr) the data set to be \(mp\) observations long, then treat each individual (which was previously a single row of the data set but now comprises \(p\) rows) as a group (we’ll call this units):

0.3 A trick to do multivariate mixed models using lme4

lme4 does not (currently) have a natural syntax for multivariate responses, however, as I eluded to in class, there is an important relationship between multivariate response models and so called “repeated” measures (or longitudinal) models. As such we can use a few tricks to estimate the model in lme4. Below this, I will go through the same model using MCMCglmm, which is a library which has a more natural syntax for such multivariate responses, but is explicitly Bayesian, so you need to provide prior distributions.

0.3.1 melting code for lme4

What we need to first do (for lme4) is to generate a single column that represents the numeric values for our response traits, and then a second variable that stores the trait type.

melting

dll_melt <- (dll_data2
    %>% select(-c(femur,tibia,tarsus,SCT))  ## drop unscaled vars
    %>% mutate(units=factor(1:n()))
    %>% gather(trait,value, -c(units,replicate,line,genotype,temp))
    %>% drop_na()
    %>% arrange(units)
)
## saveRDS(dll_melt, file="dll_melt.rds")

(in case you’re familiar with current-day tidyverse: gather() is an earlier version of pivot_longer(), which is now recommended).


Look at how this has changed the structure of the data from a wide format:

head(dll_data) # original wide
##   replicate   line genotype temp femur tibia tarsus SCT
## 1         1 line-1      Dll   25 0.590 0.499  0.219   9
## 2         1 line-1      Dll   25 0.550 0.501  0.214  13
## 3         1 line-1      Dll   25 0.588 0.488  0.211  11
## 4         1 line-1      Dll   25 0.588 0.515  0.211  NA
## 5         1 line-1      Dll   25 0.596 0.502  0.207  12
## 6         1 line-1      Dll   25 0.577 0.499  0.207  14

to a long format:

head(dll_melt)
##   replicate   line genotype temp units    trait  value
## 1         1 line-1      Dll   25     1  femur_s  1.514
## 2         1 line-1      Dll   25     1  tibia_s  0.597
## 3         1 line-1      Dll   25     1 tarsus_s  1.751
## 4         1 line-1      Dll   25     1    SCT_s -1.222
## 5         1 line-1      Dll   25     2  femur_s  0.138
## 6         1 line-1      Dll   25     2  tibia_s  0.654

0.4 lmer fit

We can now go ahead and fit the model where we need to include trait as a predictor variable (where each trait is now a “repeated measure” from a particular subject/individual)

t1 <- system.time(
    lmer1 <- lmer(value ~ trait:(genotype*temp) - 1 +
                      (trait-1|line) + (trait-1|units),
                  data=dll_melt,
                  control=lmerControl(
                      optimizer="bobyqa",
                      check.nobs.vs.nlev="ignore",
                      check.nobs.vs.nRE="ignore"))
)

0.4.1 lme4: fixed-effects formula

  • the system.time() business is just to keep track of how long the command takes to run (we could have kept the HTML output clean by hiding this in the .Rmd file, at the cost of making the .Rmd more complicated/confusing for those who were following along there)
  • it doesn’t make sense to consider effects that apply equally to all traits
  • so, let trait interact with all the other variables, but nothing else - use -1 to drop intercept
  • specification is equivalent to
    • trait:(1+genotype+temp+genotype:temp)
    • or trait + trait:genotype + trait:temp + trait:genotype:temp

0.4.2 lme4: random-effects formula

  • (trait-1|line) means “variance/covariances of traits among lines”
  • -1 so we consider traits, not differences among traits
  • (trait-1|units) specifies “var/cov of traits among units (individuals)”

0.4.3 lme4: control specificatons

  • lmer always includes a residual variance term. This is redundant because we have only one data point per individual per trait: lmerControl(...) tells lmer not complain
  • As a result, our within-individual correlation estimates will be slightly overestimated (not so for GLMMs)
  • optimizer="bobyqa" is the only choice of optimizers that does not give a convergence warning (ask about this if you feel like going down a rabbit hole)

0.5 Notes on lme4 results

0.6 lme4: random-effects var/cov

isSingular(lmer1)
## [1] FALSE
VarCorr(lmer1)
##  Groups   Name          Std.Dev. Corr          
##  units    traitfemur_s  0.649                  
##           traitSCT_s    0.724    0.06          
##           traittarsus_s 0.559    0.59 0.21     
##           traittibia_s  0.665    0.93 0.12 0.48
##  line     traitfemur_s  0.489                  
##           traitSCT_s    0.392    0.14          
##           traittarsus_s 0.462    0.81 0.37     
##           traittibia_s  0.473    0.87 0.22 0.83
##  Residual               0.470

0.7 lme4: correlation plot

par(mfrow=c(1,2))
vv1 <- VarCorr(lmer1)
## fix unit variance-covariance by adding residual variance:
diag(vv1$units) <- diag(vv1$units)+sigma(lmer1)^2
corrplot.mixed(cov2cor(vv1$line),upper="ellipse")
corrplot.mixed(cov2cor(vv1$units),upper="ellipse")

Correlations of traits across lines are stronger than correlations within individuals. In both cases correlations are all positive (i.e. first axis of variation is size variation?)

0.8 lme4: coefficient plot

cc1 <- tidy(lmer1,effect="fixed") %>%
  tidyr::separate(term,into=c("trait","fixeff"),extra="merge",
                  remove=FALSE)
dwplot(cc1)+facet_wrap(~fixeff,scale="free",ncol=2)+
  geom_vline(xintercept=0,lty=2)

These results tell approximately the same story (coefficients are consistent across traits within a fixed effect, e.g. effect of higher temperature is to reduce scores on all traits).

0.9 lme4: random effects

This is very slow, you probably don’t want to evaluate it on your computer

t1_CI <- system.time(cc2 <- tidy(lmer1,
            effect = "ran_pars",
            conf.int = TRUE,
            conf.method = "profile",
            parallel="multicore",
            ncpus=8))
saveRDS(cc2, file="../data/cc2.rds")

(about 40 minutes to run on 8 cores on a fast machine)

1 MCMCglmm

Another option is the MCMCglmm package, which has a natural interface for general multivariate mixed models. It takes a while to get used to the interface, but here is an example. Please check out here for more information about the package, and here for an overview of how to use it.


First I find it easier (given the interface of MCMCglmm) to create a formula with the response variables and predictors. This is only for the fixed effects part of the model.

fmla.MMLM1  <- cbind(femur_s, tibia_s, tarsus_s, SCT_s) ~
    trait:(genotype*temp) - 1

Now we need to let MCMCglmm know which family (i.e. distribution) the response variables are. Since all are normal (Gaussian), we can specify it the following way.

fam.test <- rep("gaussian", 4 )

Since MCMCglmm is fundamentally a Bayesian approach, it needs a prior. If you provide no prior by default, it tries a “flat” prior, although this rarely works. In this case we provide not-quite-flat priors only for the random effects of line and for the residual variances (could also provide them for the fixed effects). Choosing priors for variances and covariances is tricky (and too technical for right now), so we will use the default prior distributions (inverse-Wishart). Ian Dworkin has some simple tutorials where he draws out the effects of various common priors for variances and covariances.

prior.model.1 <- list( R = list(V=diag(4)/4, nu=0.004),  
                       G = list(G1=list(V=diag(4)/4, nu=0.004)))

Let’s take a quick look at the prior

prior.model.1
## $R
## $R$V
##      [,1] [,2] [,3] [,4]
## [1,] 0.25 0.00 0.00 0.00
## [2,] 0.00 0.25 0.00 0.00
## [3,] 0.00 0.00 0.25 0.00
## [4,] 0.00 0.00 0.00 0.25
## 
## $R$nu
## [1] 0.004
## 
## 
## $G
## $G$G1
## $G$G1$V
##      [,1] [,2] [,3] [,4]
## [1,] 0.25 0.00 0.00 0.00
## [2,] 0.00 0.25 0.00 0.00
## [3,] 0.00 0.00 0.25 0.00
## [4,] 0.00 0.00 0.00 0.25
## 
## $G$G1$nu
## [1] 0.004

The R matrix prior is for the residuals. This is \(\mathbf{R}_{4,4}\) as we have 4 traits in the VCV matrix. We have have non-zero variances for each trait as the mode for the prior, and 0 for covariances. However this is a weak prior, so even small amounts of data will largely overcome the pull of the prior. Another sensible approach we have used is to make the prior proportional to the overall observed VCV, as there is a large literature on the proportionality of covariance matrices for morphology. This may not be sensible for other types of multivariate response measures.

##,depends.on=c("prior","mod_fm","get_data")}
t2 <- system.time(
    MMLM1.fit <- MCMCglmm(fmla.MMLM1,
                          random=~ us(trait):line, 
                          rcov=~ us(trait):units,
                          prior=  prior.model.1,
                          data= dll_data2, 
                          family = fam.test, 
                          nitt= 10000, burnin= 2000, thin=10)
)

1.1 MCMCglmm specification: fixed effects

  • fmla.MMLM1 is the formula object we created above
  • it looks like the lmer formula
  • the trait term is a reserved word in MCMCglmm, letting it know we want to fit a multivariate mixed model
  • MCMCglmm automatically melts the data for us (and assigns the name trait the same way we did manually above)

1.2 MCMCglmm: random effects

  • random=~us(trait):line asks MCMCglmm to fit an unstructured covariance matrix for the line term (i.e the different wild type genotypes we are examining).- “Unstructured” means we are estimating the complete 4 x 4 matrix of covariances (= 4*5/2 = 10 elements total)
  • equivalent to (trait-1|line) in lmer model
  • MCMCglmm also automatically adds a units
  • rcov=~us(trait):units = (trait-1|units)
  • MCMCglmm offers a few other options for variance-covariance structures

1.3 MCMCglmm: other options

The nitt is how many iterations for the MCMC we want to perform, and the burnin is how many should be ignored at the beginning of the random walk.

1.4 MCMCglmm: diagnostics

The fit takes 12 seconds.

Normally we would spend a fair bit of time on diagnostics of the MCMC, but for now we will just quickly check the trace plots and autocorrelation.

In the fitted object $Sol is for solution, which is the term used for fixed effects in MCMCglmm. $VCV is for the variance-covariance matrix.

library(lattice)
xyplot(MMLM1.fit$Sol[,1:4])

xyplot(MMLM1.fit$Sol[,13:16])

acf(MMLM1.fit$Sol[,1:2])

xyplot(MMLM1.fit$VCV[,1:4])

acf(MMLM1.fit$VCV[,1:3])

Nothing terribly worrying.

summary(MMLM1.fit)
## 
##  Iterations = 2001:9991
##  Thinning interval  = 10
##  Sample size  = 800 
## 
##  DIC: 17450 
## 
##  G-structure:  ~us(trait):line
## 
##                                  post.mean l-95% CI u-95% CI eff.samp
## traitfemur_s:traitfemur_s.line      0.2976   0.1433    0.496      864
## traittibia_s:traitfemur_s.line      0.2487   0.1035    0.418      923
## traittarsus_s:traitfemur_s.line     0.2272   0.0947    0.389      940
## traitSCT_s:traitfemur_s.line        0.0318  -0.0891    0.148      800
## traitfemur_s:traittibia_s.line      0.2487   0.1035    0.418      923
## traittibia_s:traittibia_s.line      0.2739   0.1379    0.461      932
## traittarsus_s:traittibia_s.line     0.2227   0.0974    0.391      969
## traitSCT_s:traittibia_s.line        0.0493  -0.0414    0.174      800
## traitfemur_s:traittarsus_s.line     0.2272   0.0947    0.389      940
## traittibia_s:traittarsus_s.line     0.2227   0.0974    0.391      969
## traittarsus_s:traittarsus_s.line    0.2644   0.1159    0.443      959
## traitSCT_s:traittarsus_s.line       0.0824  -0.0232    0.213      800
## traitfemur_s:traitSCT_s.line        0.0318  -0.0891    0.148      800
## traittibia_s:traitSCT_s.line        0.0493  -0.0414    0.174      800
## traittarsus_s:traitSCT_s.line       0.0824  -0.0232    0.213      800
## traitSCT_s:traitSCT_s.line          0.1866   0.0891    0.313      800
## 
##  R-structure:  ~us(trait):units
## 
##                                   post.mean l-95% CI u-95% CI eff.samp
## traitfemur_s:traitfemur_s.units      0.6448  0.60283   0.6818      800
## traittibia_s:traitfemur_s.units      0.4017  0.37158   0.4364      800
## traittarsus_s:traitfemur_s.units     0.2155  0.18867   0.2439      715
## traitSCT_s:traitfemur_s.units        0.0261 -0.00694   0.0561      800
## traitfemur_s:traittibia_s.units      0.4017  0.37158   0.4364      800
## traittibia_s:traittibia_s.units      0.6643  0.62163   0.7062      800
## traittarsus_s:traittibia_s.units     0.1797  0.15021   0.2059      800
## traitSCT_s:traittibia_s.units        0.0604  0.02658   0.0910      800
## traitfemur_s:traittarsus_s.units     0.2155  0.18867   0.2439      715
## traittibia_s:traittarsus_s.units     0.1797  0.15021   0.2059      800
## traittarsus_s:traittarsus_s.units    0.5355  0.50492   0.5696     1108
## traitSCT_s:traittarsus_s.units       0.0840  0.05372   0.1133      800
## traitfemur_s:traitSCT_s.units        0.0261 -0.00694   0.0561      800
## traittibia_s:traitSCT_s.units        0.0604  0.02658   0.0910      800
## traittarsus_s:traitSCT_s.units       0.0840  0.05372   0.1133      800
## traitSCT_s:traitSCT_s.units          0.7457  0.69798   0.7884     1156
## 
##  Location effects: cbind(femur_s, tibia_s, tarsus_s, SCT_s) ~ trait:(genotype * temp) - 1 
## 
##                                 post.mean l-95% CI u-95% CI eff.samp  pMCMC   
## traitfemur_s:genotypeDll          0.49979  0.28116  0.71626      800 <0.001 **
## traittibia_s:genotypeDll          0.63199  0.42890  0.86094      874 <0.001 **
## traittarsus_s:genotypeDll         0.60341  0.42113  0.83665      779 <0.001 **
## traitSCT_s:genotypeDll            0.67562  0.47546  0.85978      800 <0.001 **
## traitfemur_s:genotypewt           0.16897 -0.03813  0.38846      960 0.1225   
## traittibia_s:genotypewt           0.00838 -0.18883  0.22347      800 0.9350   
## traittarsus_s:genotypewt          0.43649  0.24563  0.63703      796 0.0025 **
## traitSCT_s:genotypewt            -0.08833 -0.28673  0.07674      800 0.3050   
## traitfemur_s:temp30              -0.83506 -0.94664 -0.72345      800 <0.001 **
## traittibia_s:temp30              -0.81013 -0.92652 -0.71148      674 <0.001 **
## traittarsus_s:temp30             -1.23443 -1.33083 -1.14002      800 <0.001 **
## traitSCT_s:temp30                -0.84197 -0.97720 -0.73130      800 <0.001 **
## traitfemur_s:genotypewt:temp30    0.36022  0.21096  0.49195      800 <0.001 **
## traittibia_s:genotypewt:temp30    0.47518  0.33737  0.61969      714 <0.001 **
## traittarsus_s:genotypewt:temp30   0.51164  0.37837  0.63692      905 <0.001 **
## traitSCT_s:genotypewt:temp30      0.73084  0.57630  0.90538      800 <0.001 **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Sometimes it is easier to look at the fixed and random effects separately.

summary(MMLM1.fit$Sol)
## 
## Iterations = 2001:9991
## Thinning interval = 10 
## Number of chains = 1 
## Sample size per chain = 800 
## 
## 1. Empirical mean and standard deviation for each variable,
##    plus standard error of the mean:
## 
##                                     Mean     SD Naive SE Time-series SE
## traitfemur_s:genotypeDll         0.49979 0.1131  0.00400        0.00400
## traittibia_s:genotypeDll         0.63199 0.1120  0.00396        0.00379
## traittarsus_s:genotypeDll        0.60341 0.1061  0.00375        0.00380
## traitSCT_s:genotypeDll           0.67562 0.0975  0.00345        0.00345
## traitfemur_s:genotypewt          0.16897 0.1109  0.00392        0.00358
## traittibia_s:genotypewt          0.00838 0.1085  0.00384        0.00384
## traittarsus_s:genotypewt         0.43649 0.1030  0.00364        0.00365
## traitSCT_s:genotypewt           -0.08833 0.0909  0.00321        0.00321
## traitfemur_s:temp30             -0.83506 0.0571  0.00202        0.00202
## traittibia_s:temp30             -0.81013 0.0566  0.00200        0.00218
## traittarsus_s:temp30            -1.23443 0.0513  0.00181        0.00181
## traitSCT_s:temp30               -0.84197 0.0629  0.00222        0.00222
## traitfemur_s:genotypewt:temp30   0.36022 0.0748  0.00264        0.00264
## traittibia_s:genotypewt:temp30   0.47518 0.0736  0.00260        0.00275
## traittarsus_s:genotypewt:temp30  0.51164 0.0667  0.00236        0.00222
## traitSCT_s:genotypewt:temp30     0.73084 0.0822  0.00290        0.00290
## 
## 2. Quantiles for each variable:
## 
##                                    2.5%     25%      50%     75%   97.5%
## traitfemur_s:genotypeDll         0.2807  0.4217  0.50215  0.5751  0.7148
## traittibia_s:genotypeDll         0.3970  0.5556  0.63697  0.7069  0.8385
## traittarsus_s:genotypeDll        0.3974  0.5338  0.60326  0.6711  0.8178
## traitSCT_s:genotypeDll           0.4860  0.6108  0.67354  0.7383  0.8741
## traitfemur_s:genotypewt         -0.0383  0.0941  0.17282  0.2422  0.3884
## traittibia_s:genotypewt         -0.2054 -0.0618  0.00912  0.0826  0.2133
## traittarsus_s:genotypewt         0.2315  0.3725  0.43521  0.5043  0.6334
## traitSCT_s:genotypewt           -0.2732 -0.1475 -0.08866 -0.0313  0.0908
## traitfemur_s:temp30             -0.9451 -0.8755 -0.83602 -0.7953 -0.7214
## traittibia_s:temp30             -0.9201 -0.8474 -0.81226 -0.7731 -0.6979
## traittarsus_s:temp30            -1.3361 -1.2698 -1.23478 -1.1973 -1.1439
## traitSCT_s:temp30               -0.9683 -0.8820 -0.84183 -0.7999 -0.7183
## traitfemur_s:genotypewt:temp30   0.2117  0.3112  0.36177  0.4157  0.4920
## traittibia_s:genotypewt:temp30   0.3296  0.4233  0.47515  0.5253  0.6151
## traittarsus_s:genotypewt:temp30  0.3796  0.4673  0.51155  0.5585  0.6385
## traitSCT_s:genotypewt:temp30     0.5664  0.6789  0.72766  0.7836  0.9006

And for the random effects.

summary(MMLM1.fit$VCV)
## 
## Iterations = 2001:9991
## Thinning interval = 10 
## Number of chains = 1 
## Sample size per chain = 800 
## 
## 1. Empirical mean and standard deviation for each variable,
##    plus standard error of the mean:
## 
##                                     Mean     SD Naive SE Time-series SE
## traitfemur_s:traitfemur_s.line    0.2976 0.1044 0.003690       0.003550
## traittibia_s:traitfemur_s.line    0.2487 0.0951 0.003362       0.003130
## traittarsus_s:traitfemur_s.line   0.2272 0.0847 0.002994       0.002762
## traitSCT_s:traitfemur_s.line      0.0318 0.0597 0.002112       0.002112
## traitfemur_s:traittibia_s.line    0.2487 0.0951 0.003362       0.003130
## traittibia_s:traittibia_s.line    0.2739 0.0965 0.003412       0.003162
## traittarsus_s:traittibia_s.line   0.2227 0.0824 0.002912       0.002646
## traitSCT_s:traittibia_s.line      0.0493 0.0566 0.002001       0.002001
## traitfemur_s:traittarsus_s.line   0.2272 0.0847 0.002994       0.002762
## traittibia_s:traittarsus_s.line   0.2227 0.0824 0.002912       0.002646
## traittarsus_s:traittarsus_s.line  0.2644 0.0889 0.003144       0.002870
## traitSCT_s:traittarsus_s.line     0.0824 0.0611 0.002159       0.002159
## traitfemur_s:traitSCT_s.line      0.0318 0.0597 0.002112       0.002112
## traittibia_s:traitSCT_s.line      0.0493 0.0566 0.002001       0.002001
## traittarsus_s:traitSCT_s.line     0.0824 0.0611 0.002159       0.002159
## traitSCT_s:traitSCT_s.line        0.1866 0.0644 0.002276       0.002276
## traitfemur_s:traitfemur_s.units   0.6448 0.0204 0.000722       0.000722
## traittibia_s:traitfemur_s.units   0.4017 0.0173 0.000612       0.000612
## traittarsus_s:traitfemur_s.units  0.2155 0.0145 0.000514       0.000543
## traitSCT_s:traitfemur_s.units     0.0261 0.0165 0.000582       0.000582
## traitfemur_s:traittibia_s.units   0.4017 0.0173 0.000612       0.000612
## traittibia_s:traittibia_s.units   0.6643 0.0215 0.000760       0.000760
## traittarsus_s:traittibia_s.units  0.1797 0.0146 0.000518       0.000518
## traitSCT_s:traittibia_s.units     0.0604 0.0168 0.000593       0.000593
## traitfemur_s:traittarsus_s.units  0.2155 0.0145 0.000514       0.000543
## traittibia_s:traittarsus_s.units  0.1797 0.0146 0.000518       0.000518
## traittarsus_s:traittarsus_s.units 0.5355 0.0173 0.000612       0.000520
## traitSCT_s:traittarsus_s.units    0.0840 0.0154 0.000543       0.000543
## traitfemur_s:traitSCT_s.units     0.0261 0.0165 0.000582       0.000582
## traittibia_s:traitSCT_s.units     0.0604 0.0168 0.000593       0.000593
## traittarsus_s:traitSCT_s.units    0.0840 0.0154 0.000543       0.000543
## traitSCT_s:traitSCT_s.units       0.7457 0.0236 0.000836       0.000695
## 
## 2. Quantiles for each variable:
## 
##                                       2.5%      25%    50%    75%  97.5%
## traitfemur_s:traitfemur_s.line     0.16084  0.22444 0.2783 0.3495 0.5441
## traittibia_s:traitfemur_s.line     0.13011  0.18500 0.2276 0.2909 0.4706
## traittarsus_s:traitfemur_s.line    0.10966  0.16958 0.2098 0.2671 0.4273
## traitSCT_s:traitfemur_s.line      -0.08423 -0.00196 0.0287 0.0628 0.1582
## traitfemur_s:traittibia_s.line     0.13011  0.18500 0.2276 0.2909 0.4706
## traittibia_s:traittibia_s.line     0.15258  0.20873 0.2533 0.3196 0.5144
## traittarsus_s:traittibia_s.line    0.11239  0.16613 0.2073 0.2640 0.4292
## traitSCT_s:traittibia_s.line      -0.05043  0.01582 0.0431 0.0793 0.1719
## traitfemur_s:traittarsus_s.line    0.10966  0.16958 0.2098 0.2671 0.4273
## traittibia_s:traittarsus_s.line    0.11239  0.16613 0.2073 0.2640 0.4292
## traittarsus_s:traittarsus_s.line   0.14260  0.20440 0.2471 0.3077 0.4784
## traitSCT_s:traittarsus_s.line     -0.01906  0.04329 0.0727 0.1120 0.2304
## traitfemur_s:traitSCT_s.line      -0.08423 -0.00196 0.0287 0.0628 0.1582
## traittibia_s:traitSCT_s.line      -0.05043  0.01582 0.0431 0.0793 0.1719
## traittarsus_s:traitSCT_s.line     -0.01906  0.04329 0.0727 0.1120 0.2304
## traitSCT_s:traitSCT_s.line         0.09856  0.13910 0.1763 0.2180 0.3360
## traitfemur_s:traitfemur_s.units    0.60552  0.63101 0.6448 0.6584 0.6856
## traittibia_s:traitfemur_s.units    0.37126  0.38921 0.4004 0.4138 0.4362
## traittarsus_s:traitfemur_s.units   0.18928  0.20537 0.2145 0.2259 0.2448
## traitSCT_s:traitfemur_s.units     -0.00598  0.01570 0.0269 0.0368 0.0577
## traitfemur_s:traittibia_s.units    0.37126  0.38921 0.4004 0.4138 0.4362
## traittibia_s:traittibia_s.units    0.62251  0.64904 0.6638 0.6781 0.7074
## traittarsus_s:traittibia_s.units   0.15242  0.16924 0.1793 0.1896 0.2089
## traitSCT_s:traittibia_s.units      0.02892  0.04872 0.0601 0.0716 0.0944
## traitfemur_s:traittarsus_s.units   0.18928  0.20537 0.2145 0.2259 0.2448
## traittibia_s:traittarsus_s.units   0.15242  0.16924 0.1793 0.1896 0.2089
## traittarsus_s:traittarsus_s.units  0.50489  0.52336 0.5354 0.5472 0.5694
## traitSCT_s:traittarsus_s.units     0.05500  0.07349 0.0837 0.0944 0.1146
## traitfemur_s:traitSCT_s.units     -0.00598  0.01570 0.0269 0.0368 0.0577
## traittibia_s:traitSCT_s.units      0.02892  0.04872 0.0601 0.0716 0.0944
## traittarsus_s:traitSCT_s.units     0.05500  0.07349 0.0837 0.0944 0.1146
## traitSCT_s:traitSCT_s.units        0.70056  0.72938 0.7457 0.7632 0.7920

This is not the most friendly output, and it takes a while to get used to. However, we can see that we still have evidence for something interesting going, and we could extract the vectors of effects as we did above.

let’s look at the VCV matrix for the random effects of line in a slightly clearer way (as a matrix)

##' extract variance-covariance matrices for MCMCglmm objects
##' does not (yet) set cor, stddev, residual-var attributes
##' may be fragile: depends on group vars not having dots in them?
VarCorr.MCMCglmm <- function(object, ...) {
    s <- summary(object$VCV)$statistics[,"Mean"]
    grps <- gsub("^[^.]+\\.([[:alnum:]]+)$","\\1",names(s))
    ss <- split(s,grps)
    getVC <- function(x) {
        nms <- gsub("^([^.]+)\\.[[:alnum:]]+$","\\1",names(x))
        n <- length(nms)
        L <- round(sqrt(n))
        dimnms <- gsub("^([^:]+):.*$","\\1",nms[1:L])
        return(matrix(x,dimnames=list(dimnms,dimnms),
                      nrow=L))
    }
    r <- setNames(lapply(ss,getVC),unique(grps))
    return(r)
}
vv <- VarCorr(MMLM1.fit)
par(mfrow=c(1,2))
corrplot.mixed(cov2cor(vv$line),upper="ellipse")
corrplot.mixed(cov2cor(vv$units),upper="ellipse")

  • among-line effects are very similar to lme4 estimates (variables are in a different order)
  • among-individual effects a

This is only a taste of what to do, and after this we could start asking questions about this genetic covariance matrix.

Compare fixed effects:

tt <- tidy(MMLM1.fit)
tt2 <- tidy(lmer1)
tt_comb <- bind_rows(lmer=tt,MCMCglmm=tt2,.id="model") %>%
    filter(effect=="fixed")
dwplot(tt_comb)+geom_vline(xintercept=0,lty=2)

1.5 brms

see vignette("brms_multivariate")

SLOW

library(brms)
t3 <- system.time(
    brmsfit <- brm(
        mvbind(femur_s, tibia_s, tarsus_s, SCT_s) ~ genotype*temp + (1|p|line) + (1|q|replicate),
        data = dll_data2, chains = 4, cores = 4)
)
saveRDS(brmsfit,file="../data/multiv_brmsfit.rda")

This takes a long time! (\(\approx 40\) minutes, even computing the chains in parallel)

1.6 Pros and cons

  • MCMCglmm: Bayesian
    • easier to get various kinds of confidence intervals
    • flexible priors
    • more flexible variance structures
    • multiple response types within a unit (e.g. Gaussian + Poisson)
  • lme4: frequentist
    • faster
    • simpler interface (??)
    • more convenient outputs
  • other options: glmmTMB, brms

FIXME:

  • sort out random effects tidying
  • set wildtype as baseline level
  • check out convergence warnings with nloptwrap vs bobyqa (why is allFit not working?)

References

Dworkin, Ian. 2005. “Evidence for Canalization of Distal-Less Function in the Leg of Drosophila Melanogaster.” Evolution & Development 7 (2): 89–100. https://doi.org/10.1111/j.1525-142X.2005.05010.x.