I struggle with the analysis of my very skewed data with linear mixed models in R. Since the original data is for actual research, I can't share it with you, but I have created a fake dataset, that resembles the distribution of my data: Let's assume, we give 1000 amateur dart players 4 throws and measure, if they can hit the board. Then, they have to drink 3 shots and try again (so we have 8 throws, 4 sober, 4 drunk). This would result in a dataset like this:
EDIT: Forgot to include the libraries
library(tidyverse)
library(lmerTest)
set.seed(1)
size <- 1000
dart_wide <- tibble(
ID = factor(paste0('ID_', seq(size))),
sober = sample(c(.75, 1), size = size, replace = TRUE, prob = c(.1, .9)),
drunk = sapply(sober, simplify = TRUE, FUN = function(x) {
if (x == 0.75) sample(c(0, .25, .75, 1), size = 1, prob = c(.01, .01, .02, .06))
else if (x == 1) sample(c(0, .25, .5, .75, 1), size = 1, prob = c(.05, .05, .05, .15, .6))
}),
difference = drunk - sober
)
dart <- pivot_longer(dart_wide, sober:drunk, names_to = 'condition', values_to = 'score')
dart <- dart %>% rbind(dart) %>% rbind(dart) %>% rbind(dart) %>%
group_by(ID, condition) %>%
mutate(
condition = factor(condition, levels = c('sober', 'drunk')),
throw = seq(n()),
hit = as.numeric(score >= (throw/4))
) %>%
ungroup %>%
select(ID, condition, throw, hit) %>%
arrange(ID, condition, throw)
dart
> # A tibble: 8,000 x 4
> ID condition throw hit
> <fct> <fct> <int> <dbl>
> 1 ID_1 sober 1 1
> 2 ID_1 sober 2 1
> 3 ID_1 sober 3 1
> 4 ID_1 sober 4 1
> 5 ID_1 drunk 1 1
> 6 ID_1 drunk 2 1
> 7 ID_1 drunk 3 1
> 8 ID_1 drunk 4 1
> 9 ID_10 sober 1 1
> 10 ID_10 sober 2 1
> # ... with 7,990 more rows
In which most of the players perform worse in the "drunk" condition, compared to the "sober" condition
mean(dart_wide$difference)
> [1] -0.146
However the distribution does not even resemble a normal distribution:
dart %>%
group_by(ID, condition) %>%
summarize(accuracy = mean(hit), .groups = 'drop') %>%
ggplot(aes(accuracy)) +
facet_wrap(~condition) +
geom_bar()
If I now analyze the data with a generalized linear mixed model using only a random intercept, I get the expected results: The accuracy of the participants was lower when drunk (β = -2.4982):
dart_model_intercept <- glmer(
hit ~ condition + (1|ID),
data = dart,
family = binomial(link = 'logit')
)
summary(dart_model_intercept)
> Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
> Family: binomial ( logit )
> Formula: hit ~ condition + (1 | ID)
> Data: dart
>
> AIC BIC logLik deviance df.resid
> 4217.8 4238.7 -2105.9 4211.8 7997
>
> Scaled residuals:
> Min 1Q Median 3Q Max
> -7.7350 0.0632 0.1999 0.2203 1.3237
>
> Random effects:
> Groups Name Variance Std.Dev.
> ID (Intercept) 3.117 1.765
> Number of obs: 8000, groups: ID, 1000
>
> Fixed effects:
> Estimate Std. Error z value Pr(>|z|)
> (Intercept) 4.8973 0.1597 30.67 <2e-16 ***
> conditiondrunk -2.4982 0.1221 -20.46 <2e-16 ***
> ---
> Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
>
> Correlation of Fixed Effects:
> (Intr)
> conditndrnk -0.763
Okay, great. HOWEVER, if I include the random slope for condition, the model estimates a contra-intuitive result: apparently, participants perform better when drunk than when sober (β = 0.6111)...
dart_model_slope <- glmer(
hit ~ condition + (condition|ID),
data = dart,
family = binomial(link = 'logit')
)
summary(dart_model_slope)
> Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
> Family: binomial ( logit )
> Formula: hit ~ condition + (condition | ID)
> Data: dart
>
> AIC BIC logLik deviance df.resid
> 3963.2 3998.1 -1976.6 3953.2 7995
>
> Scaled residuals:
> Min 1Q Median 3Q Max
> -5.9729 0.0952 0.1672 0.1674 1.4121
>
> Random effects:
> Groups Name Variance Std.Dev. Corr
> ID (Intercept) 0.004656 0.06824
> conditiondrunk 13.328751 3.65086 1.00
> Number of obs: 8000, groups: ID, 1000
>
> Fixed effects:
> Estimate Std. Error z value Pr(>|z|)
> (Intercept) 3.5679 0.0970 36.782 <2e-16 ***
> conditiondrunk 0.6111 0.4150 1.472 0.141
> ---
> Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
>
> Correlation of Fixed Effects:
> (Intr)
> conditndrnk -0.234
However, this is not the case, as we have seen that overall, participants perform worse (direct comparison of performance using mean()
)
Apparently, the model estimates positive slopes for participants, that don't change their performance between conditions
dart_wide_with_slope <- coef(dart_model_slope)$ID %>%
rownames_to_column('ID') %>%
select(ID, random_slope = conditiondrunk) %>%
left_join(dart_wide %>% select(ID, difference), by = 'ID')
dart_wide_with_slope %>% as_tibble
> # A tibble: 1,000 x 3
> ID random_slope difference
> <chr> <dbl> <dbl>
> 1 ID_1 1.13 0
> 2 ID_10 1.13 0
> 3 ID_100 -4.15 -0.75
> 4 ID_1000 1.13 0
> 5 ID_101 -2.12 -0.25
> 6 ID_102 1.13 0
> 7 ID_103 1.13 0
> 8 ID_104 0.964 0.25
> 9 ID_105 1.13 0
> 10 ID_106 1.13 0
> # ... with 990 more rows
So this then results in a positive overall weight for condition: drunk. But this is just objectively not the case/correct.
Now finally I reached my question:
- From my understanding, random slopes are calculated by distributing the performance around the mean (super simplified). Is there a way to prevent this? This should probably resolve this issue
- Is there a better analysis I can use (maybe use a different
link
in thebinomial
function? - Does it even make sense to use a GLMM for this distribution of data and if not, what would you propose?