R bygroup mice imputation - mice.impute.bygroup

Viewed 400

I would like to group my data by state_id and species when I run mice::mice to impute values. I've got it grouped by state_id and results are looking much better than without the bygroup.

mice.impute.bygroup: Groupwise Imputation Function

Edit... improved, working code:

# Modify df name and method
init <- mice::mice(data, method = "pmm", maxit = 0) 
meth <- init$meth
pred <- init$pred

# Impute variables by group (state_id)
imputationFunction <- list("decimalLatitude" = meth["decimalLatitude"],
                           "decimalLongitude" = meth["decimalLongitude"])

meth[c("decimalLatitude", "decimalLongitude")] <- "bygroup"

group <- list("decimalLatitude" = "state_id", 
              "decimalLongitude" = "state_id")

# Remove variables as predictors but they can still be imputed.
pred[, c("coordinateUncertaintyInMeters", "geoprivacy_id")] <- 0

set.seed(500)
imp <- mice::mice(data, meth = meth, pred = pred, m = 1, 
                  group = group, imputationFunction = imputationFunction)
imp <- complete(imp)

This works too, but no bygroup:

imp <- mice(data, m = 1, maxit = 3, method = 'norm.predict', seed = 500)
imp <- complete(imp, 1)

So, one question remains.

  1. Can I group by multiple variables?

When I replace the variable state_id with species_id, I'm running into an error:

Error in lm.fit(x = x, y = y) : 0 (non-NA) cases

The problem seems to be that some species have zero or no values for lat and long data. I confirmed this for one state by removing all species with no lat and long data and the imputation by species was successful.

group <- list("decimalLatitude" = "species_id", 
              "decimalLongitude" = "species_id")
1 Answers

You shouldn't use mice.impute.bygroup directly. It is a function that gets called when you specify method["x"] <- "bygroup", just like you call mice.impute.norm.predict with "norm.predict" (see ?mice.impute.norm.predict).

Below is some example code on how to use bygroup.

Sample data

data <- iris
str(data)
# 'data.frame': 150 obs. of  5 variables:
#  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

data[, -5] <- mice::ampute(data[, -5])$amp

init <- mice::mice(data, maxit = 0)

Impute one variable (Petal.Width) by group (Species)

meth <- init$meth
pred <- init$pred

imputationFunction <- list("Petal.Width" = meth["Petal.Width"])
meth["Petal.Width"] <- "bygroup"
group <- list("Petal.Width" = "Species")

pred[, "Species"] <- 0

imp <- mice::mice(data, meth = meth, pred = pred, m = 1, 
                  group = group, imputationFunction = imputationFunction)

For all variables

meth <- init$meth
pred <- init$pred

imputationFunction <- as.list(meth[meth != ""])
meth[meth != ""] <- "bygroup"
group <- imputationFunction
group[] <- "Species"

pred[, "Species"] <- 0

imp <- mice::mice(data, meth = meth, pred = pred, m = 1, 
                  group = group, imputationFunction = imputationFunction)

Further considerations

The bygroup method does not allow you to group across multiple variables. You could make a new variable that simply covers all those groups. Internally, all that bygroup does is split the data into distinct groups, so that's not a problem.

However, at some point you have to consider whether this is a proper way of doing things. It may be more worthwhile to consider multi-level imputation.

Related