5

I'm new to R package development and stack overflow, but I've been unable to find this information anywhere.

I'm trying to load the R package mice, without it polluting my namespace. I've tried importing just the functions I'm using, but that hasn't worked. So I'll settle for loading the entire package in one specific environment as follows:

e <- new.env()
load_package_into_environment(e, package = "mice")
eval(mice(data, m = m, maxit = max.iter, printFlag = F), envir = e)

However, I haven't been able to find the actual function to replace the "load_package_into_environment" placeholder. What function, if any, would accomplish this?

EDIT: Here are the files I'm working with and the problem I'm having to give more detail.

File: DESCRIPTION

Package: bug.example2
Title: Example of Package Loading Bug
Version: 0.0.0.9000
Authors@R: person("R", "Woodbridge", email = "example@gmail.com", role = c("aut", "cre"))
Description: Creates a wrapper function for mice::mice function.
Depends:
    R (>= 3.2.3),
    data.table (>= 1.9.6)
License:
LazyData: true
Imports: mice
RoxygenNote: 5.0.1

File: NAMSPACE (automatically generated by roxygen)

import(data.table)
importFrom(mice,mice)
importFrom(mice,mice.impute.logreg)
importFrom(mice,mice.impute.pmm)
importFrom(mice,mice.impute.polr)
importFrom(mice,mice.impute.polyreg)

File: impute.R (uses the mice function from the mice package)

#' @import data.table
#' @importFrom mice mice
#' @importFrom mice mice.impute.pmm
#' @importFrom mice mice.impute.logreg
#' @importFrom mice mice.impute.polyreg
#' @importFrom mice mice.impute.polr
#' @export
impute <- function(data, m = 5, max.iter = 5){

mice_environment <- new.env()


#Impute missing data using mice function, output format is mids object
mice.out <- mice(data, m = m, maxit = max.iter, printFlag = F)

#save the m imputed data.frames as a list of data.tables
return.list <- lapply(1:m, function(x){
                                    as.data.table(complete(mice.out, x))
                                      })
names(return.list) <- paste0("imp.",1:m)
return.list
}

File: test-impute.R (uses testthat package to test impute function)

context("Impute missing values")
test_that("Output format is a list of lenght m and each element is a data.table",{
#Set up data
set.seed(200)
data <- iris
data$Species[runif(nrow(data)) < .1] <- NA
data$Sepal.Width[runif(nrow(data)) < .2] <- NA
setDT(data)

#Create imputed data
M <- 5
impute.output <- impute(data, m = M)

#Test output format
expect_is(impute.output, "list")
expect_equal(length(impute.output), M)
lapply(impute.output,expect_is, "data.table")
})

Error output from testthat

1. Error: Output format is a list of lenght m and each element is a data.table -
The following functions were not found: mice.impute.pmm, mice.impute.polyreg
1: withCallingHandlers(eval(code, new_test_environment), error = capture_calls, message = function(c) invokeRestart("muffleMessage"))
2: eval(code, new_test_environment)
3: eval(expr, envir, enclos)
4: impute(data, m = M) at test-impute.R:12
5: mice(data, m = m, maxit = max.iter, printFlag = F) at          C:\repos\bug.example2/R/impute.R:11
6: check.method(setup, data)
7: stop(paste("The following functions were not found:", paste(fullNames[notFound], 
   collapse = ", ")))
  • A package *is* loaded into a dedicated environment. What do you mean by "polluting"? – nicola Mar 16 '16 at 23:26
  • `importFrom` was my first approach but I've imported the one function I call all and all the dependent functions thereof, but I still get a message that some these dependent functions are not found when I try to build the package. I think it's a result of how mice is built. – r.woodbridge Mar 17 '16 at 14:14
  • Please see http://stackoverflow.com/a/40830959/4468078 and read about the `import` package which (almost) exactly does what you want. The only thing I have not yet tried is whether you can create your own environment instead of specifying the name of the new environment. Another difference could be that `import` attaches the environment into the search path which is possible not what you want (and you can fix by detaching it). – R Yoda Nov 27 '16 at 16:12

2 Answers2

1

Package 'mice' internally is calling imputations methods from the global environment. According to the authors this is to allow providing your own, custom methods for imputations. As such, the package must expose their default implementations in the global environment as well. I recon this is a nice example of pure judgement - internal methods are now recognized by the package only through the global environment. This is defeating the purpose of packaging code in the first place. If one wants to allow external functions to be used by your package, then simply provide an API for passing them to the package.

The error message you see is triggered by function 'mice:::check_method':

notFound <- !vapply(fullNames, exists, logical(1), mode = "function", inherits = TRUE)

My workaround is to re-export methods used internally by 'mice' from my own package (which internally uses 'mice', just like yours). Simply put in one of your R files and run roxygen2 on it:

#' @importFrom mice mice

#' @export
mice.impute.pmm <- mice::mice.impute.pmm

#' @export
mice.impute.polyreg <- mice::mice.impute.polyreg

Of course you'll need to export other methods if they are used on your data.

My stand point is that if I need to pollute the global environment, I'll pollute it minimally, only with functions required for 'mice' to work.

0

I got the same error yesterday and open a similar question (that has been already downvoted btw). Today I found a solution, I hope it works for you too and maybe you (or others) can shine a bit more light on it.

I am developing an R package and using roxygen2 to document the functions. I've listed mice in my imports section in the description file since I have to use the mice::mice function. When building and checking the package, everything runs smooth until I actually run the function that calls mice::mice, at that point I get the very same error that you get.

As far as my understanding goes this is what causing the problem: In the documentation part of the function that is using mice, you need to add this bit:

#' @importMethodsFrom mice
#' @importFrom mice mice

Note that with most packages I've used so far, the following line would have been more than enough:

#' @importFrom mice mice

Apparently mice requires you to add the @importMethodsFrom directive too. My guess is that this is because it is using S4 classes but I know little about them, so I just know that this way it works.

I found out this by reading the "S4" section at this page: http://r-pkgs.had.co.nz/namespace.html#imports

So, to get back at your specific case, my guess is that the docs for your function should look something like this

#' @import data.table
#' @importFrom mice mice
#' @importMethodsFrom mice
#' @export
impute <- function(data, m = 5, max.iter = 5){...}

This worked fine for me. Hope it helps for you too.

mickkk
  • 1,172
  • 2
  • 17
  • 38