Search code examples
rdplyrr-mice

mice: Convert long completed data to a mids object


I have a longitudinal dataframe in wide format with waves separated by suffixes (e.g., "_Wave1", "_Wave2", etc.) that I would like to multiply impute and convert to long format. Similar unanswered question here.

First, I tried imputing the data after converting from wide to long, but I realized that mice would impute data that shouldn't have been imputed - for example, it would impute Scale 1 for the first wave of data collection ("Wave 1"), but Scale 1 was never completed at Wave 1, so it shouldn't have been imputed for that wave. At this point, I tried editing the prediction matrix, but since I've already converted to long format after imputation, all information about waves was lost.

Then, I tried imputing before converting from wide to long, but encountered an error with as.mids. Converting from wide to long ends up making .id repeat, which causes as.mids to error.

I would appreciate any insight or advice.

library(mice, warn.conflicts = FALSE)
library(tidyverse)
library(missMethods)
set.seed(123)

studyid <- 1:10
color <- sample(c("red","blue","yellow"), 10, replace=TRUE, prob=c(0.25, 0.25, 0.5))
#scale_1_Wave1 <- NA
scale_2_Wave1 <- sample(1:25, 10, replace=TRUE)
scale_3_Wave1 <- sample(1:25, 10, replace=TRUE)
#scale_1_Wave2 <- NA
scale_2_Wave2 <- sample(1:25, 10, replace=TRUE)
scale_3_Wave2 <- sample(1:25, 10, replace=TRUE)
scale_1_Wave3 <- sample(1:25, 10, replace=TRUE)
scale_2_Wave3 <- sample(1:25, 10, replace=TRUE)
#scale_3_Wave3 <- NA
# Scale 1 wasn't given at Waves 1 or 2, only Wave 3
# Scale 3 wasn't given at Wave 3

df <- data.frame(studyid, color,
                 scale_2_Wave1, scale_3_Wave1,
                 scale_2_Wave2, scale_3_Wave2,
                 scale_1_Wave3, scale_2_Wave3)

# make some missingness
df <- delete_MCAR(df, p = 0.2, cols_mis = c(3:8))

rm(list=setdiff(ls(), "df"))

## Attempt #1: convert wide to long, then impute

df_long <- df %>%
  pivot_longer(contains("Wave"), names_to = "scalename", values_to = "scalevalue") %>%
  separate(scalename, c("scale", "Wave"), "_Wave") %>%
  pivot_wider(names_from = scale, values_from = scalevalue)

imp1 <- mice(df_long, m = 5, print = FALSE)
#> Warning: Number of logged events: 2
complete1 <- complete(imp1, action = "long", include = TRUE)

# Scale 1 shouldn't have any data at Waves 1 or 2, but it does
ggplot(complete1, aes(x = Wave, y = scale_1)) + geom_boxplot()
#> Warning: Removed 22 rows containing non-finite values (stat_boxplot).

## Attempt #2: impute, then convert wide to long

imp2 <- mice(df, m = 5, print = FALSE)
#> Warning: Number of logged events: 2
complete2 <- complete(imp2, action = "long", include = TRUE)

# convert imputed data from wide to long
complete_long <- complete2 %>%
  pivot_longer(contains("Wave"), names_to = "scalename", values_to = "scalevalue") %>%
  separate(scalename, c("scale", "Wave"), "_Wave") %>%
  pivot_wider(names_from = scale, values_from = scalevalue)

# now try to convert it back to mids
imp_long <- as.mids(complete_long, .imp = 1, .id = 2)
#> Warning: non-unique values when setting 'row.names': '1', '2', '3', '4', '5',
#> '6', '7', '8', '9', '10'
#> Error in `.rowNamesDF<-`(x, value = value): duplicate 'row.names' are not allowed
Created on 2022-01-05 by the reprex package (v2.0.1)

Solution

  • This post helped me write a solution for the second option (which I prefer as it gives me more control over what does and doesn't get imputed).

    Also, if you want to calculate anything post-lengthening (e.g., a new variable), do it after pivot_wider but before mutate(.id = 1:nrow(.)).

    library(mice, warn.conflicts = FALSE)
    library(tidyverse)
    library(missMethods)
    library(miceadds)
    #> * miceadds 3.11-6 (2021-01-21 11:48:47)
    set.seed(123)
    
    df <- structure(list(studyid = 1:10, 
                         color = c("yellow", "red", "yellow", "red", "red", "yellow", "blue", "red", "blue", "yellow"), 
                         scale_2_Wave1 = c(NA, NA, 22L, 25L, 5L, 19L, 25L, 25L, 9L, 3L), 
                         scale_3_Wave1 = c(8L, NA, 10L, NA, 19L, 4L, 14L, 17L, 11L, 7L), 
                         scale_2_Wave2 = c(21L, 12L, 15L, 10L, NA, NA, 9L, 9L, 10L, 23L), 
                         scale_3_Wave2 = c(21L, 7L, NA, 6L, 25L, 2L, 5L, 8L, NA, 13L), 
                         scale_1_Wave3 = c(18L, 1L, 25L, NA, 6L, NA, 15L, 9L, 15L, 16L), 
                         scale_2_Wave3 = c(20L, 6L, 11L, 8L, 22L, 22L, 7L, 16L, NA, NA)), 
                    row.names = c(NA, -10L ), class = "data.frame")
    
    imp <- mice(df, m = 5, print = FALSE)
    #> Warning: Number of logged events: 1
    
    complete <- mice::complete(imp, action = "long", include = TRUE)
    
    working_dats <- list()
    
    for(i in 0:max(complete$.imp)) {
      working_dats[[i+1]] <- 
        complete %>%
        subset(.imp == i) %>%
        pivot_longer(contains("Wave"), names_to = "scalename", values_to = "scalevalue") %>%
        separate(scalename, c("scale", "Wave"), "_Wave") %>%
        pivot_wider(names_from = scale, values_from = scalevalue) %>%
        mutate(.id = 1:nrow(.))
    }
    
    imputed_long <- as.mids(do.call(rbind, working_dats))
    write.mice.imputation(mi.res = imputed_long, name = "imputed_long", mids2spss = FALSE)