# 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. – atiretoo 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..... ? – Robert Long 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. – atiretoo 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) 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), overall_tumor_grade=factor(sample(c(1,2,3,'X'),25,replace=TRUE)), 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,1),'tumor_size']=NA

# output the data
df

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. – Robert Long Jul 12 '12 at 21:27
• @JJM see my updated answer. Hope it helps ! – Robert Long 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? – atiretoo 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) – Robert Long Jul 13 '12 at 15:43