3
$\begingroup$

I'm trying to plot the following lmer:

mod1 <- lmer(SCORE ~ X1_c * X2 + (1|PARTICIPANT), data = data)

With THIS dataset. (this is a Git link)

However, I can't go past the residual-normality assumption, since no matter what I do, I don't get a normal-distributed residual distribution.

qqnorm(resid(mod1))
qqline(resid(mod1))

qqplotofresiduals

### Model's residuals test (1)
shapiro.test(resid(mod1))

Shapiro-Wilk normality test

data:  resid(mod1)
W = 0.95616, p-value = 0.0001235

### Model's residuals test (2)

shapiro_test(resid(mod1))

variable    statistic  p.value
  <chr>           <dbl>    <dbl>
1 resid(mod1)     0.956 0.000123

** It's important to note that: **

  • I've already tried to exclude outliers
  • I've already tried to exclude Participants with Cook's Distance highier than 1
  • I've seen MANY MANY posts here on this issue, but I hope this isn't a duplicate since I couldn't apply the other posts' answers to my data
  • I've seen some people say that Shapiro-Wilk test don't matter for "large samples", even Andy Field himself in his book and in a video . But what is "big enough" ? I have a total of 148 non-NA paired values. If I can ignore Shapiro-Wilk's results, on WHAT basis can I do that? How could I justify this decision?
  • I've seen some people talk about robust modeling with this package or OLS , but I'm a beginner, I'm not familiar with these tests and though I'm really willing to learn about them, I need to return this analysis asap, so I can't do that for the present moment (but I will, any suggestions will be much appreciated)
  • Log-transformations of Y or X1_c, in this particular case, wouldn't make much of a sense theorically since both variables are exam/tests scores
  • The random intercept normality is ok:
### Random intercept normality test:
r_int<- ranef(mod1)$PARTICIPANT$`(Intercept)`
qqnorm(r_int)
qqline(r_int)
shapiro.test(r_int)

p > 0.05
  • This is the plot(fitted(mod1), resid(mod1))

fittedplot

  • I've been trying to decide if I should/can continue with this model within the last 72hours and, honestly, I just wanna to sit down and cry. I really mean that any thoughts would be much appreciated. So if you think that you can help me, I already thank you in advance!

Obs: the dataset I've put on git is the raw data-set with all outliers and Nas. The only thing is that X1_c is a centered predictor around its own mean.

$\endgroup$
2
  • $\begingroup$ it looks like you're checking the distribution of the random effect, not the residuals. People don't really check that the random effect is normally distributed. It doesn't really affect the model. Even then, the random effects looks good to me except in the right tail. $\endgroup$
    – Eli
    Commented Sep 9, 2022 at 1:44
  • $\begingroup$ @Eli , sthank you for your answer, so I shouldn't use shapiro.test(resid(mod1)) ? What should I use instead to get the model's residuals? (I'm used to lm$residuals, but it doesn't seem to work with lmer) $\endgroup$ Commented Sep 9, 2022 at 1:50

3 Answers 3

4
$\begingroup$

First let's look at the data to gain a bit of understanding about the problem.

data
#> # A tibble: 236 × 4
#>    PARTICIPANT    X1 X2    SCORE
#>    <chr>       <dbl> <chr> <dbl>
#>  1 1            26.6 Test1    21
#>  2 1            26.6 Test2    NA
#>  3 2            88.4 Test1    21
#>  4 2            88.4 Test2    20
#>  5 3            NA   Test1    15
#>  6 3            NA   Test2    17
#>  7 4            NA   Test1    26
#>  8 4            NA   Test2    19
#>  9 5            76.4 Test1    52
#> 10 5            76.4 Test2    21
#> # … with 226 more rows

So this is test score data for two tests, Test1 and Test2, and X1 is a participant-level predictor.

Quite a bit of data is missing, esp. in the X1 predictor which is missing for 38 out of 118 (32%) participants. There are some missing test scores as well.

Without imputation (which would be challenging with only X1 and the scores), we lose 37% of the observations (rows) because of NA's. Of the remaining 80 participants, 12 participants (15%) have only their Test1 score; their Test2 is missing. This is important because it's difficult to estimate a participant random effect with just one observation from a participant.

In fact, you should be very careful about the missing data and account for it, as some of it (the missing Test2 score in particular) may not be missing at random.

Now let's make some plots.

To start with, the Test1 and Test2 scores have very different distributions. Keep in mind that X1 cannot explain these differences because X1 is a participant-level predictor, not a test-level predictor.

I see a strong linear correlation between the Test1 and the Test2 score of a participant. But the range of the Test1 scores seems to be [0, 120] while the range of the Test2 scores seems to be [0, 60]. Now I have follow-up questions about whether the two tests are even measuring the same thing and how comparable Test1 and Test2 score are...

Next I plot the score as a function of the continuous predictor X1 and I add two smooth (loess) curves, to help me get an idea what model might be appropriate for this data.

We learn a lot from plot, though it's mostly not good news.

  • The relationship between X1 and SCORE is not really linear: Higher X1 is associated with higher SCORE when X1 < 40 or X1 > 80 (or so). In the mid-range 40 ≤ X1 ≤ 80 the curve are flat (there doesn't seem to be a relationship).
  • The variance in Test1 is higher (about twice as high?) as the variance in the Test scores. Your current model assumes constant variance, so cannot model this pattern in the variability of test scores.

Based on these observations, I decide to fit a linear model using Generalized Least Squares (GLS).

  • I use restricted cubic splines (with 4 knots) to model the non-linear relationship between X1 and SCORE.
  • I don't interact X1 and X2 because the shape of the curves isn't that different. It seems enough to have different intercepts for the two tests, effectively shifting Test1 scores "up" compared to Test2 scores.
library("rms")

dd <- datadist(data)
options(datadist = "dd")

model <- Gls(
  SCORE ~ rcs(X1, 4) + X2,
  data = data,
  correlation = corSymm(form = ~ 1 | PARTICIPANT),
  weights = varIdent(form = ~ 1 | X2)
)

ggplot(Predict(model, X1, X2))

anova(model)
#>                 Wald Statistics          Response: SCORE 
#> 
#>  Factor     Chi-Square d.f. P     
#>  X1         19.09      3    0.0003
#>   Nonlinear  4.68      2    0.0962
#>  X2         68.77      1    <.0001
#>  TOTAL      86.25      4    <.0001

The model does explain some of the variability in test scores. But the non-normality is still present.

And the heteroscedasticity (the residual variance increases with the response) is also still present.

There might be nothing we can do about this however: the residual variance is not a function of X1.

Log-transforming the scores as suggested by @Lachlan helps with the heteroskedasticity since the log is a variance stabilizing transformation; the QQ plot looks "more Normal". However, it cannot help with the fact that there isn't much of a relationship between X1 and test scores, except for "extreme" X1 values (very low X1 or very high X1).


The R code to reproduce the figures and the analysis:

library("naniar")
library("rms")
library("nlme")
library("tidyverse")

data <-
  here::here("data", "588224.csv") %>%
  read_csv(
    col_types = cols(PARTICIPANT = col_character())
  ) %>%
  rename(
    X1 = X1_notCentered
  ) %>%
  select(
    PARTICIPANT, X1, X2, SCORE
  )
data

data %>%
  pivot_wider(
    id_cols = c(PARTICIPANT, X1),
    names_from = X2,
    values_from = SCORE
  ) %>%
  select(
    -PARTICIPANT
  ) %>%
  vis_miss()

data <- drop_na(data)

data %>%
  pivot_wider(
    id_cols = c(PARTICIPANT, X1),
    names_from = X2,
    values_from = SCORE
  ) %>%
  ggplot(
    aes(
      Test1, Test2,
      color = X1
    )
  ) +
  geom_point(
    shape = 1,
    size = 2,
    stroke = 2
  ) +
  scale_x_continuous(
    limits = c(0, 120)
  ) +
  scale_y_continuous(
    limits = c(0, 60)
  )

data %>%
  ggplot(
    aes(SCORE, fill = X2)
  ) +
  geom_histogram(
    bins = 33,
    alpha = 0.5
  ) +
  scale_x_continuous(
    limits = c(0, 120)
  )

# Log-transform the test scores?
data <- data %>%
  mutate(
    # No:
    # Y = SCORE,
    
    # Yes:
    Y = log2(SCORE)
  )

data %>%
  ggplot(
    aes(
      X1, Y,
      color = X2,
      shape = X2,
      group = X2,
      fill = X2
    )
  ) +
  geom_smooth(
    show.legend = FALSE
  ) +
  geom_point(
    size = 2,
    stroke = 2
  ) +
  scale_shape_manual(
    values = c(1, 2)
  )

dd <- datadist(data)
options(datadist = "dd")

model <- Gls(
  Y ~ rcs(X1, 4) + X2,
  data = data,
  correlation = corSymm(form = ~ 1 | PARTICIPANT),
  weights = varIdent(form = ~ 1 | X2)
)

ggplot(Predict(model, X1, X2))
anova(model)

data <- data %>%
  mutate(
    .fitted = fitted(model),
    .resid = resid(model)
  )

data %>%
  ggplot(
    aes(sample = .resid)
  ) +
  stat_qq() +
  stat_qq_line() +
  theme(
    aspect.ratio = 1
  )

data %>%
  ggplot(
    aes(
      .fitted, .resid,
      color = X2
    )
  ) +
  geom_point(
    size = 2,
    shape = 1,
    stroke = 2
  )

data %>%
  ggplot(
    aes(
      X1, .resid,
      color = X2
    )
  ) +
  geom_point(
    size = 2,
    shape = 1,
    stroke = 2
  )
$\endgroup$
21
  • $\begingroup$ I can't thank you enough for this, really. First: I've noticed the NAs afterwards and then I made this post: stats.stackexchange.com/questions/588343/… , but I didn't get any answers, so i'm still very confused about that. I'll continue in a follow-up comment below: $\endgroup$ Commented Sep 10, 2022 at 13:42
  • $\begingroup$ 2nd, you've asked for clarification, I'll try my best: Y has the scores of X2, which do measure the same thing (a grammar test in participant's second language), but in different ways (test1 is a written and text2 is an oral test) and X1 is a proficiency test very much like this (stats.stackexchange.com/questions/586990/…) . So, basically: I wanna see if the the type of test (X2) will influence the scores (Y) and if proficiency has a whole in it acting as a moderator (thus, the interaction) (X1). Am I clearer now? $\endgroup$ Commented Sep 10, 2022 at 13:48
  • $\begingroup$ Your explanation clarifies what we see in the data: Test1 and Test2 are strongly (& linearly) correlated but the scale is not the same. That doesn't seem to be much of a problem for the GLS. To answer your questions: a) You don't really need the interaction between X1 and X2 (you can formally test for this with an anova). b) X1 seems to say something about performance on Test1 and Test2 only for students that did very poorly or very well on X1. What that means I don't know. But I don't think it would make sense to model the relationship as a straight line. $\endgroup$
    – dipetkov
    Commented Sep 10, 2022 at 15:43
  • $\begingroup$ wow. this is SO amazing. you're definitely expanding my knowledge to "the next level", I can't thank you enough. I've been studying the GLS model since I've read your answer and I'm trying to understand how things are working (no success yet, but i'm trying!), this is all very new to me. Do you have any recommendations on materials of how one can move from understanding lm and lmers to a gls approach? (youtube.com/watch?v=j_WQQfaJQLI) this video helped at first, but then I got a bit lost $\endgroup$ Commented Sep 10, 2022 at 19:55
  • $\begingroup$ hi! from what I understood from your answers and from (projectguru.in/conduct-generalized-least-squares-test) this post, GLS still requires a normal residual distribuition, doesn't it? It accounts for heteroskedastic, but it still requires that errors are normally distributed, right? @dipetkov $\endgroup$ Commented Sep 10, 2022 at 23:23
2
$\begingroup$

You could consider log-transforming your outcome variable. This would imply a multiplicative relationship between your predictors and $score$. I.e., changing your predictor variables changes score by a certain % amount, rather than by a constant (e.g. 5 points). This can often make sense but it does depend on the scientific context and your beliefs about the manner in which the variables relate to each other.

Alternatively, you could ignore the non-normality of the residuals. According to Gelman and Hill, normality of errors is usually the least important of the regression assumptions. Violations of this assumption, especially of the quite modest type which you show above, have little or no influence on the estimation of the regression coefficients. Only if you wish to use the model to make predictions does this assumption become important.

$\endgroup$
8
  • $\begingroup$ thank you for your answer! I forgot to mention it (I'll add an edit to the post). In this case it doesn't make much of a sense theorically to log transform it since the outcome is composed by tests/exam scores and X1 is another exam score. "Violations of this assumption, especially of the quite modest type which you show above,". From the q-q plot I get the sense that the violation is small, but from Shapiro-Wilk's results (p = 000.1), I don't see how I can still say that? Would you elaborate on it? $\endgroup$ Commented Sep 9, 2022 at 2:05
  • $\begingroup$ Yeah, right, the log transform probably doesn't make a great deal of theoretical sense in that case. If I were you, I would ignore the results of the Shapiro-Wilk's test. Looking at diagnostic plots, like the QQ-plot, is probably more reliable, given that it's not sensitive to sample size. Either way, if your aim is to estimate regression coefficients, to describe the relationship between your predictors and the outcome, then the normality of errors assumption is really not too material. $\endgroup$
    – Lachlan
    Commented Sep 9, 2022 at 2:14
  • $\begingroup$ thank you agan! I'm sorry If I'm being a bit persistant on the topic of the Shapiro test, how can I "justify" ignoring its results if a later reviewer asks me that, for example? Btw, I forgot to add the plot(fitted(mod1), resid(mod1)) plot to the post, I've just added that . I've seen some people talking about "large" samples and the possibility of ignoring the results if the sample is large, but, still, what would be "large" ? $\endgroup$ Commented Sep 9, 2022 at 2:21
  • 3
    $\begingroup$ If a reviewer were to pose that question to me, I would point them to the relevant section of the Gelman and Hill text cited above. Particularly this quote: 'In fact, for the purpose of estimating the regression line (as compared to predicting individual data points), the assumption of normality [of residuals] is barely important at all'. I'm also confident that if you searched scholar, you could find many papers criticizing the use of the Shapiro test as a diagnostic. $\endgroup$
    – Lachlan
    Commented Sep 9, 2022 at 2:28
  • 1
    $\begingroup$ Whether the model is generalizable to other samples is a different question. That's less about model diagnostics and more a question of sample selection, effect heterogeneity, external validity etc. The primary context in which this deviation from residual normality might be problematic is if you were to use your model to make predictions. E.g., you were trying to predict future exam scores for individual students with high accuracy. If you are simply describing the relationships between the predictor variables and test score (via the estimated coefficients) it's really not an issue. $\endgroup$
    – Lachlan
    Commented Sep 9, 2022 at 3:10
1
$\begingroup$

For qq plots, use standardized and normalized residuals, and compare with a diagonal line. The raw residual type = "r" will not align with the diagonal y = x.

qqnorm(residuals(mod1 , type = "p")) # pearson residual
qqline(residuals(mod1 , type = "p")) # add trend line
abline(0, 1)
qqnorm(residuals(mod1 , type = "n")) # standardized residual
qqline(residuals(mod1 , type = "n")) # add trend line
abline(0, 1)

Mishra, P., Pandey, C. M., Singh, U., Gupta, A., Sahu, C., & Keshri, A. (2019). Descriptive statistics and normality tests for statistical data. Annals of Cardiac Anaesthesia, 22(1), 67–72. https://doi.org/10.4103/aca.ACA_157_18 says that "Shapiro–Wilk test is more appropriate method for small sample sizes (<50 samples)...while Kolmogorov–Smirnov test is used for n ≥50." Although nonnormality in residuals does not affect unbiased point estimates of coefficients, it does affect inference, such as p value and confidence intervals. For that, consider cluster-robust standard errors to replace those in standard lmer(). See vcovCR() in package {clubSandwich}.

Despite the loess curves built by dipetkov, the relationship between SCORE and X1 is approximately linear. However, you should still try other function forms of X1, such as log(X1), I(X1^2), and I(X1^3) and interact with X2 to see which fixed-effect specification is most likely (using ML for maximum likelihood estimator instead of REML). With the correct function form, residual distribution problems should be alleviated.

Both residuals() ~ fitted() plots made by you and dipetkov show that the RAW residual variance increases with the response. However, you should check whether the standardized Pearson residual and normalized residual after error correlation are still heterogeneous.

plot(Model_Temp[[1]], resid(., type = "r") ~ fitted(.), abline = 0)
plot(Model_Temp[[1]], resid(., type = "p") ~ fitted(.), abline = 0)
plot(Model_Temp[[1]], resid(., type = "n") ~ fitted(.), abline = 0)

If all residual variances increase with the response no matter the type, they should also increase with X1, as the response increases with X1. Therefore, you can model the residual heterogeneity using X1 in nlme::lme(), possibly by different relationship depending on X2. This gives freedom to incorporate random effects, as opposed to Gls() in {rms} that builds upon nlme::gls(). You could also try a random effect of X1 and see if it correlated with random intercept, such as

model <- lme(
  SCORE ~ (X1 + I(X1^2)) * X2,
  data = data,
  random = ~ 1 + X1 | PARTICIPANT, 
  correlation = corSymm(form = ~ 1 | PARTICIPANT),
  weights = varPower(form = ~ X1 | X2))

You should compare different variance specification such as varPower(form = ~ log(X1) | X2), varExp(form = ~ X1 | X2), varConstPower(form = ~ X1 | X2), and varConstPropvarExp(form = ~ X1 | X2), and see whether the variance parameters differ by X2 through a likelihood ratio test by anova(). If the random effects of intercept and X1 have nonsignificant correlation by checking intervals(model) to have a CI of the correlation containing zero, then a restrictive model coercing uncorrelated random effects has random = list(PARTICIPANT = pdDiag(~ 1 + X1)).

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.