Question

I am trying to use a S3 method in a package and thought I understood how to set it up after asking a question here: S3 method consistency warning when building R package with Roxygen

But now I get results I don't expect. If I run the code below directly in R it gives me the expected results but if I compile it into a package I don't get the correct results (notice the word the gets counted twice when it should take only unique words from vector a). I'm not sure what I'm setting up incorrectly.

The .R file:

#' Find Common Words Between Groups
#' 
#' Find common words between grouping variables (e.g. people).
#' 
#' @param word.list A list of names chacter vectors.
#' @param overlap Minimum/exact amount of overlap.
#' @param equal.or A character vector of c(\code{"equal"}, \code{"greater"}, 
#' \code{"more"}, \code{"less"}).
#' @param \dots In liu of word.list the user may input n number of character 
#' vectors.
#' @rdname common
#' @return Returns a dataframe of all words that match the criteria set by 
#' \code{overlap} and \code{equal.or}.
#' @export
#' @examples
#' \dontrun{
#' a <- c("a", "cat", "dog", "the", "the")                                                              
#' b <- c("corn", "a", "chicken", "the")                                                                
#' d <- c("house", "feed", "a", "the", "chicken")                                                       
#' common(a, b, d, overlap=2)  
#' common(a, b, d, overlap=3)                                                                          
#'                                                                                                      
#' r <- list(a, b, d)  
#' common(r)                                                                                 
#' common(r, overlap=2)                                                                                            
#'                                                                                                     
#' common(word_list(DATA$state, DATA$person)$cwl, overlap = 2) 
#' } 
common <-
function(word.list, ...){
    UseMethod("common")
}

#' @return \code{NULL}
#'
#' @rdname common
#' @method common list
common.list <-
function(word.list, overlap = "all", equal.or = "more", ...){
    if(overlap=="all") {
        OL <- length(word.list) 
    } else {
        OL <- overlap
    }
    LIS <- sapply(word.list, unique)
    DF <- as.data.frame(table(unlist(LIS)), stringsAsFactors = FALSE)
    names(DF) <- c("word", "freq")
    DF <- DF[order(-DF$freq, DF$word), ]
    DF <- switch(equal.or,
        equal = DF[DF$freq == OL, ],
        greater = DF[DF$freq > (OL - 1), ],
        more = DF[DF$freq > (OL - 1), ],
        less = DF[DF$freq < (OL + 1), ])
    rownames(DF) <- 1:nrow(DF)
    return(DF)
}

#' @return \code{NULL}
#'
#' @rdname common
#' @method common default
#' @S3method common default  
common.default <-
    function(..., overlap = "all", equal.or = "more", word.list){
        LIS <- list(...)
        return(common.list(LIS, overlap, equal.or))
}


a <- c("a", "cat", "dog", "the", "the")                                                              
b <- c("corn", "a", "chicken", "the")                                                                
d <- c("house", "feed", "a", "the", "chicken")                                                       
common(a, b, d, overlap=2)  


r <- list(a, b, d)                                                                                   
common(r, overlap=2)                                                                                            

Running the code from the command line (expected behavior):

> common(a, b, d, overlap=2)  
     word freq
1       a    3
2     the    3
3 chicken    2
>                                                                           
>                                                                                                      
> r <- list(a, b, d)                                                                                   
> common(r, overlap=2)                                                                                            
     word freq
1       a    3
2     the    3
3 chicken    2

Output after package compile:

> a <- c("a", "cat", "dog", "the", "the")                                                              
> b <- c("corn", "a", "chicken", "the")                                                                
> d <- c("house", "feed", "a", "the", "chicken")                                                       
> common(a, b, d, overlap=2)  
     word freq
1       a    3
2     the    3
3 chicken    2
>                                                                           
>                                                                                                      
> r <- list(a, b, d)                                                                                   
> common(r, overlap=2)                                                                                            
     word freq
1     the    4
2       a    3
3 chicken    2
Was it helpful?

Solution

The bug you are seeing is mostly likely caused because the list method of the common generic is not being exported, so common.default gets called instead. You can pick up this problem using devtools::missing_s3 - the function is a bit heuristic, so you may get a few false positives (e.g. it can't currently tell that is.list isn't a method). This is an incredibly common problem (it has caught me so many times), and the next iteration of roxygen will do more to prevent it.

Currently, to correctly export S3 methods with roxygen you need to do either:

  • @S3method generic class (and nothing else) if you don't want to document the method
  • @method generic class and @export if you want to export and document it.

You should never have @S3method and @method in the same documentation block.

Update for roxygen2 >3.0.0

Now roxygen automatically figures out if a function is an S3 method so:

  • Never use @S3method or @method
  • Use @export if you want the method to be exported (which you normally do, even if the generic isn't)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top