# How to impute an ordinal variable with MICE but prevent it from taking one value?

I have an ordinal variable, overall_tumor_grade, that can take on values of 1, 2, 3, or X if the measurement is indeterminable. There are some NAs that I want to impute using the mice package in R, but I know that the missing values cannot be X because their tumor sizes are greater than 0. I want to impute overall_tumor_grade but force mice to only choose from 1, 2, or 3.

Here is sample code for you to use:

df=data.frame(age=c(24,37,58,65,70,84),
tumor_size=c(1.5,2.0,4.2,5.6,0,0.1))
imp=mice(df)
na_index=which(is.na(df$$overall_tumor_grade)) complete(imp)$$overall_tumor_grade[na_index]  #This can never be 'X'


@longrob suggested I temporarily remove the patients with X observations, impute, then add them back in to the full dataset. Would the imputation lose power by removing all of those observations? Along @longrob's suggestion, here is the work-around I have right now using a sample dataframe that is closer to what I'm really working with (several columns with missing values):

df=data.frame(age=rnorm(mean=45,sd=10,25),
tumor_size=runif(25)*10)
df[df$$overall_tumor_grade=='X','tumor_size']=0 #Patients with Grade 'X' have tumorsz=0 df[sample(1:25,3),'age']=NA ##Setting some observations to NA df[sample(1:25,5),'overall_tumor_grade']=NA df[sample(1:25,1),'tumor_size']=NA ################ Imputation imp=mice(df,meth=c('pmm',"",'pmm')) #Suppress imputation of overall_tumor_grade dfimp=complete(imp) dfimp2=dfimp[dfimp$$overall_tumor_grade!='X',]  #I don't want to impute grade 'X' tumors
#so I am trying to remove those observations here and then use droplevels(), but for
#a reason I can't figure out, that statement is setting all rows to NA where grade='X'


Any suggestions on how I accomplish what @longrob suggested?

• Can we assume that all instances of overall_tumor_grade == 'X' also have tumor_size == 1? I think the answer might be to construct a data.frame without any rows with 'X', and then do the imputation on that, but that only works if the above assumption is true. Jul 12 '12 at 18:00
• We cannot assume that because by definition grade 'X' means that the tumor size=0
– JJM
Jul 12 '12 at 19:25
• @atiretoo I don't see why that assumption is needed ? If there are any observations with tumor_size==0 and overall_tumor_grade==NA just recode those NAs as X. Then delete observations with overall_tumor_grade==NA and run the imputations..... ? Jul 12 '12 at 19:38
• @longrob all of the patients that have tumor sizes of 0 are already coded as grade 'X'. All of the missing overall_tumor_grade observations have tumor sizes >0, and the problem I'm having is that MICE is imputing some of those NAs as grade 'X'.
– JJM
Jul 12 '12 at 20:08
• Sorry, I just realized there's a typo in my comment, should be tumor size == 0, so your response answers the question, thanks. In your additional code the reason why rows with overall_tumor_grade == NA are set to NA, is that any comparison with NA results in NA, not TRUE or FALSE. You can get around that by picking out the rows with tumor_size == 0, hence my botched question about the data above. Jul 12 '12 at 22:58

The following code defines and calls a dedicated imputation function that separates imputation of cases with tumor_size == 0 from tumor_size > 0.

## How to impute an ordinal variable with MICE but prevent it from taking one value?

df <- data.frame(age = c(24,37,58,65,70,84),
tumor_size = c(1.5,2.0,4.2,5.6,0,0.1))

mice.impute.tumor <- function(y, ry, x, ...){
ymis <- y[!ry]
tmis <- x$tumor_size[!ry] > 0 t <- x$tumor_size > 0
y[!ry] <- NA
ymis[!tmis] <- "X"
ymis[tmis] <- mice.impute.polyreg(y[t, drop = TRUE], ry[t], x[t,], ...)
ymis
}

ini <- mice(df, maxit = 0)
meth <- ini$meth meth["overall_tumor_grade"] <- "tumor" imp <- mice(df, meth = meth, maxit = 1, m = 2)  • Versions prior to mice 3.0.0 used the now retired padModel() function to create the design matrix. That function returned a data frame. Since mice 3.0.0 we use base::model.matrix(), which returns a matrix, so the $ operator on x does not work anymore. Patrick Rockenschaub provided an update github.com/amices/mice/issues/224#issuecomment-693935305 Sep 17 '20 at 7:24

Maybe there is a better way, but the only way I know is to remove the observations with tumor size X from the dataset, then remove the unused level X from the variable definitions (with droplevels() for example) and then run mice()

Update1: OK, there was a typo in my comment last night - I meant to change that line to dfimp2=dfimp[dfimp$overall_tumor_grade!='X' | is.na(dfimp$overall_tumor_grade),] Sorry about that. Anyway, here's my approach to this problem, based on your simulated dataset:

set.seed(100)
df=data.frame(age=rnorm(mean=45,sd=10,25),
tumor_size=runif(25)*10)
df[df$overall_tumor_grade=='X','tumor_size']=0 #Patients with Grade 'X' have tumorsz=0 df[sample(1:25,3),'age']=NA ##Setting some observations to NA df[sample(1:25,5),'overall_tumor_grade']=NA df[sample(1:25,1),'tumor_size']=NA # output the data df age overall_tumor_grade tumor_size 1 NA 2 NA 2 46.31531 1 8.5665304 3 44.21083 1 7.7477889 4 53.86785 2 8.3402710 5 46.16971 3 0.9151028 6 48.18630 2 4.5952549 7 39.18209 1 5.9939816 8 52.14533 1 9.1972191 9 NA 3 9.8282408 10 NA <NA> 0.3780258 11 45.89886 2 5.7793740 12 45.96274 3 7.3331417 13 42.98366 X 0.0000000 14 52.39840 <NA> 3.0073652 15 46.23380 <NA> 7.3346670 16 44.70683 <NA> 9.0695438 17 41.11146 2 2.0981677 18 50.10856 2 3.5813799 19 35.86186 1 4.4829914 20 68.10297 3 9.0642643 21 40.61910 2 3.8943930 22 52.64061 <NA> 5.1745975 23 47.61961 3 1.2523909 24 52.73405 X 0.0000000 25 36.85621 3 7.7180549 # rows 13 and 24 have tumor grade X # vector for which rows do not have tumor grade X vx <- df$overall_tumor_grade!='X' | is.na(df$overall_tumor_grade) # remove those with tumor grade X dfimp <- df[vx,] # do the imputations dfimp$overall_tumor_grade <- droplevels(dfimp$overall_tumor_grade) imp=mice(dfimp, printFlag=F) # add back the rows we removed (new_df1 <- rbind(complete(imp,1),df[!vx,])) 1 40.61910 2 3.8943930 2 46.31531 1 8.5665304 3 44.21083 1 7.7477889 4 53.86785 2 8.3402710 5 46.16971 3 0.9151028 6 48.18630 2 4.5952549 7 39.18209 1 5.9939816 8 52.14533 1 9.1972191 9 45.89886 3 9.8282408 10 52.14533 3 0.3780258 11 45.89886 2 5.7793740 12 45.96274 3 7.3331417 14 52.39840 2 3.0073652 15 46.23380 3 7.3346670 16 44.70683 2 9.0695438 17 41.11146 2 2.0981677 18 50.10856 2 3.5813799 19 35.86186 1 4.4829914 20 68.10297 3 9.0642643 21 40.61910 2 3.8943930 22 52.64061 3 5.1745975 23 47.61961 3 1.2523909 25 36.85621 3 7.7180549 13 42.98366 X 0.0000000 24 52.73405 X 0.0000000  • I'm trying to figure out a simple way to do this but I'm hitting a road block because in my data set (i.e. not the sample I posted) there are multiple columns that I am imputing at once. I can suppress imputation of the overall_tumor_grade column while letting the others proceed, but I'm running into trouble trying to then impute overall_tumor_grade while removing the 'X' observations. I'll post some more code in my question to illustrate. – JJM Jul 12 '12 at 20:09 • @JJM, looking at your additional code, try changing the last line to dfimp2=dfimp[dfimp$overall_tumor_grade!='X' | is.na(df\$overall_tumor_grade),] - sorry I don't have time to test this or look in detail, as I have to go to bed now for an early rise (here in UK) but I'll check back on this question tomorrow. Jul 12 '12 at 21:27
• @JJM see my updated answer. Hope it helps ! Jul 13 '12 at 7:22
• Yeah, so that works as long as there are no missing values in other variables in rows with overall_tumor_grade == 'X'. I suppose you could run the imputation a second time after adding the rows back? Jul 13 '12 at 14:10
• @atiretoo , yes, or the other way around (which is what the OP was going to do in the "New addition" section of the question, I think, before running into the problem of selecting on NAs) Jul 13 '12 at 15:43