Error from residual plot

I am trying to follow this example for Latent class analysis here:
https://www.rpubs.com/hlennon/LCTMtoolsvignette

But stucked when performing residual plot and got error message:
Error in dplyr::left_join():
! ... must be empty.
:heavy_multiplication_x: Problematic argument:
ā€¢ .by = nameofid

The code is pasted below, can anyone help fix the problem? Thank you.

# installing LCTMtools
install.packages("remotes")
remotes::install_github("hlennon/LCTMtools")

library( LCTMtools )
library(ggplot2)

data(bmi_long, package = "LCTMtools" )

# spaghetti plot
p <- ggplot(data = bmi_long, aes(x = age, y = bmi, group = id))
p + geom_line()

ggplot(bmi_long, aes(x = age, y= bmi)) + geom_line(aes(color = true_class, group = id)) + 
  xlab("Age") + ylab("BMI") + labs(color = "Class Assignment")

m1<- hlme(fixed=bmi ~ 1+age+I(age^2), random=~ 1, subject='id', data=data.frame(bmi_long))

# Step 1: Select the form of the random effect structure

library( lcmm )
# To fit a latent class modelel with no random effects, the lcmm R package this can be used with the 
# specification of random=~-1 .
m1<- hlme(fixed=bmi ~ 1+age+I(age^2), random=~ -1, subject='id', idiag = FALSE, data=data.frame(bmi_long))

model1 <- lcmm::hlme(fixed=bmi~1+age+I(age^2),
                     mixture = ~1+age+I(age^2),
                     random=~ -1,
                     subject="id", idiag = FALSE, 
                     ng=5,
                     nwg=FALSE, 
                     data=data.frame(bmi_long),
                     B=m1
)

residualplot_step1( model1, 
                    nameofoutcome="bmi",  nameofage = "age",
                    data = data.frame(bmi_long),
                    ylimit=c(-15,15)) 

Hi @Visiting
This is a known problem with this package which is not currently being maintained (see GitHub issue page).
However, the fix to the errant function is easy:

library(lcmm)
library(LCTMtools)
library(ggplot2)

data(bmi_long, package = "LCTMtools" )
#help(bmi_long)

# spaghetti plot
# p <- ggplot(data = bmi_long, aes(x = age, y = bmi, group = id))
# p + geom_line()
#
# ggplot(bmi_long, aes(x = age, y= bmi)) +
#   geom_line(aes(color = true_class, group = id)) +
#   xlab("Age") +
#   ylab("BMI") +
#   labs(color = "Class Assignment")

# Step 1: Select the form of the random effect structure
# To fit a latent class model with no random effects, the lcmm R package this can be used with the
# specification of random=~ -1 .
m1 <- hlme(fixed=bmi ~ 1+age+I(age^2), random=~ -1, subject="id", idiag = FALSE, data=bmi_long)

model1 <- lcmm::hlme(fixed=bmi~1+age+I(age^2),
                     mixture = ~1+age+I(age^2),
                     random=~ -1,
                     subject="id",
                     idiag = FALSE,
                     ng=5,
                     nwg=FALSE,
                     data=bmi_long,
                     B=m1)

# Fix a couple of issues in the function code (see GitHub issues)
new_residualplot_step1 <-
  function (model, nameofoutcome, nameofage, data, ylimit = c(-50,50)) {
    require(dplyr)
    k <- model$ng
    preds <- model$pred
    names(preds)[6] <- nameofoutcome
    nameofid <- names(model$pred)[1]
    test <- dplyr::left_join(preds, model$pprob, by = nameofid)      # Corrected .by
    test <- dplyr::left_join(test, data, by = c(nameofid, nameofoutcome))  # Corrected .by
    plotvalues <- NULL
    for (i in 1:k) {
        newplotvalues <- test %>% filter(class == i) %>% mutate(Residuals = get(nameofoutcome) -
            eval(parse(text = paste0("pred_ss", i))))
        plotvalues <- rbind(plotvalues, newplotvalues)
        plotvaluessub <- plotvalues %>% filter(class == i)
        pname <- paste0("p", i)
        assign(pname, ggplot2::ggplot(data = plotvaluessub, aes(x = get(nameofage),
            y = Residuals, group = class)) + theme(axis.text = element_text(size = 16),
            text = element_text(size = 16)) + geom_point() +
            stat_summary(fun.y = mean, geom = "line", size = 3,
                col = "CadetBlue", group = 1) + ggtitle("Residuals in class",
            i) + ylim(ylimit))
        print(eval(parse(text = (paste0("p", i)))))
        plotname <- paste0("p", i, ".png")
        ggplot2::ggsave(filename = plotname)
    }
    return(as.list(get(dput(paste0("p", 1:k)))))
}

# This now works
new_residualplot_step1(m1,
                   nameofoutcome="bmi",
                   nameofage = "age",
                   data = bmi_long)
#> Loading required package: dplyr
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
#> Warning: The `fun.y` argument of `stat_summary()` is deprecated as of ggplot2 3.3.0.
#> ā„¹ Please use the `fun` argument instead.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.
#> Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
#> ā„¹ Please use `linewidth` instead.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.

#> Saving 7 x 5 in image
#> "p1"

Created on 2024-12-01 with reprex v2.1.1

1 Like

Great! You are really expert! Thank you!