3

In my summarytools package, I have successfully defined the print.by method. However, even though I've followed the exact same steps for print.list, the dispatch fails.

The method seems to have registered, along with the two other print methods defined in the package:

grep("print(\\.summarytools$|\\.by$|\\.list$)", methods("print"), value = TRUE)
[1] "print.by"           "print.list"         "print.summarytools"

In NAMESPACE, I have:

S3method(print,by)
S3method(print,list)
S3method(print,summarytools)

Example

devtools::install_github("dcomtois/summarytools", ref = "dev-current")
library(summarytools)
list_obj <- lapply(tobacco[,c(1,3)], freq))

## $gender
## For best results printing list objects with summarytools, use view(x, method = 'pander')
## Frequencies   
## tobacco$gender     
## Type: Factor   
## 
##               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
##           F    489     50.00          50.00     48.90          48.90
##           M    489     50.00         100.00     48.90          97.80
##        <NA>     22                               2.20         100.00
##       Total   1000    100.00         100.00    100.00         100.00
## 
## $age.gr
## Frequencies   
## tobacco$age.gr     
## Type: Factor   
## 
##               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
##       18-34    258     26.46          26.46     25.80          25.80
##       35-50    241     24.72          51.18     24.10          49.90
##       51-70    317     32.51          83.69     31.70          81.60
##        71 +    159     16.31         100.00     15.90          97.50
##        <NA>     25                               2.50         100.00
##       Total   1000    100.00         100.00    100.00         100.00

Compare with...

summarytools:::print.list(list_obj)

## Frequencies   
## tobacco$gender     
## Type: Factor   
## 
##               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
##           F    489     50.00          50.00     48.90          48.90
##           M    489     50.00         100.00     48.90          97.80
##        <NA>     22                               2.20         100.00
##       Total   1000    100.00         100.00    100.00         100.00
##   
## tobacco$age.gr    
## Type: Factor   
## 
##               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
##       18-34    258     26.46          26.46     25.80          25.80
##       35-50    241     24.72          51.18     24.10          49.90
##       51-70    317     32.51          83.69     31.70          81.60
##        71 +    159     16.31         100.00     15.90          97.50
##        <NA>     25                               2.50         100.00
##       Total   1000    100.00         100.00    100.00         100.00

Here is the contents of print.list.R:

#' Print Method for Objects of Class \dQuote{list}.
#'
#' Displays a list comprised of summarytools objects created with \code{lapply}. 
#'
#' @usage
#'  \method{print}{list}(x, method = "pander", file = "", 
#'   append = FALSE, report.title = NA, table.classes = NA, 
#'   bootstrap.css = st_options('bootstrap.css'), 
#'   custom.css = st_options('custom.css'), silent = FALSE, 
#'   footnote = st_options('footnote'), 
#'   escape.pipe = st_options('escape.pipe'), \dots)
#' 
#' @inheritParams print.summarytools
#' @method print list
#' @export
print.list <- function(x, method = "pander", file = "", append = FALSE, 
                       report.title = NA, table.classes = NA, 
                       bootstrap.css = st_options('bootstrap.css'), 
                       custom.css = st_options('custom.css'),
                       silent = FALSE, footnote = st_options('footnote'), 
                       escape.pipe = st_options('escape.pipe'), ...) {
  if (inherits(x[[1]], "summarytools")) {
    view(x, method = method, file = file, append = append, 
         report.title = report.title, table.classes = table.classes, 
         bootstrap.css = bootstrap.css, custom.css = custom.css,
         silent = silent, footnote = footnote, escape.pipe = escape.pipe,
         ...)
  } else {
    base::print.default(x, ...)
  }
}

I've read several documents having to do with generic functions and their methods, but I can't pinpoint the problem, nor see a solution. I looked at the setMethod() function and the "signature" parameter, but since the function will most likely be called without arguments, I don't see how this would help.

One difference between the two is that print.by exists in the base package, while print.list doesn't. But I couldn't establish whether that is relevant or not.

Further background information about how I came to use this type of function definition can be found in this question I asked earlier.

Edit: I tried a few others things which didn't work...

  • Redefining print.default instead of defining print.list, as suggested here, but it still doesn't work.
  • Adding a call to setMethod after the function definition (setMethod(f = "print", signature = "list", definition = print.list)); still no good results (I'm not too sure I get what the "signature" argument is supposed to be. I find the documentation about it rather confusing).

I'm starting to think there might be a little twist I need to do with Roxygen for it to work... But what twist, I don't know.

Any help much appreciated.

Dominic Comtois
  • 10,230
  • 1
  • 39
  • 61
  • I deleted my answer, as I'm not sure now that it's correct. According to ?class, S3 generics *should* dispatch to an implicit class if no explicit class exists. For some reason, which I'm currently unsure of, that does not seem to happen for print.list. – dww Dec 30 '18 at 22:25
  • Darn. Back to trying to make sense of it! :) – Dominic Comtois Dec 30 '18 at 22:32
  • 1
    Even though I can't explain the behaviour right now, I still stand by my comment on the deleted answer that your best option may be to create wrapper functions in the package that attach an explicit class to objects created with `by` or `lapply`. And then provide an S3 method for this explicit class. – dww Dec 31 '18 at 00:43
  • I took good note of it. For `by()`, I'm tempted to push it to the master branch on GitHub for a while and then if there's no signs of bugs, push it to CRAN. Would make things much simpler for users. For `lapply()`, I might go your way, or modify the functions themselves so they can accept dataframes as well as vectors (actually I've started working on that and so far, it seems to go well). Now the only thing I'm concerned about is if another package redefines `print.by()`... But how likely is that? I'll try to search for code like `print.by <- function(` on GitHub and see. – Dominic Comtois Dec 31 '18 at 01:04

1 Answers1

1

As explained (somewhat cryptically) in comments in the source code (available here) and stated (more explicitly) in this Stack Overflow answer that quotes them, "auto-printing" (which occurs, for example, with (list_obj <- lapply(tobacco[,c(1,3)], freq))) can only dispatch on explicit classes, and so won't work with a list. It will work with any call to print, though:

devtools::install_github("dcomtois/summarytools", ref = "dev-current", quiet = TRUE)
library(summarytools)
# For best results, consider updating pander to its most recent version. You can do so
# by using devtools::install_github('rapporter/pander')
list_obj <- lapply(tobacco[,c(1,3)], freq)
list_obj # will not work since it uses auto-printing
# $gender
# For best results printing list objects with summarytools, use view(x, method =
# 'pander')
# Frequencies   
# tobacco$gender     
# Type: Factor   
# 
#               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
# ----------- ------ --------- -------------- --------- --------------
#           F    489     50.00          50.00     48.90          48.90
#           M    489     50.00         100.00     48.90          97.80
#        <NA>     22                               2.20         100.00
#       Total   1000    100.00         100.00    100.00         100.00
# 
# $age.gr
# Frequencies   
# tobacco$age.gr     
# Type: Factor   
# 
#               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
# ----------- ------ --------- -------------- --------- --------------
#       18-34    258     26.46          26.46     25.80          25.80
#       35-50    241     24.72          51.18     24.10          49.90
#       51-70    317     32.51          83.69     31.70          81.60
#        71 +    159     16.31         100.00     15.90          97.50
#        <NA>     25                               2.50         100.00
#       Total   1000    100.00         100.00    100.00         100.00
print(list_obj) # will work
# Frequencies   
# tobacco$gender     
# Type: Factor   
# 
#               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
# ----------- ------ --------- -------------- --------- --------------
#           F    489     50.00          50.00     48.90          48.90
#           M    489     50.00         100.00     48.90          97.80
#        <NA>     22                               2.20         100.00
#       Total   1000    100.00         100.00    100.00         100.00
#   
# tobacco$age.gr    
# Type: Factor   
# 
#               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
# ----------- ------ --------- -------------- --------- --------------
#       18-34    258     26.46          26.46     25.80          25.80
#       35-50    241     24.72          51.18     24.10          49.90
#       51-70    317     32.51          83.69     31.70          81.60
#        71 +    159     16.31         100.00     15.90          97.50
#        <NA>     25                               2.50         100.00
#       Total   1000    100.00         100.00    100.00         100.00
duckmayr
  • 16,303
  • 3
  • 35
  • 53