Search code examples
rgroupinglapplysummarytools

R - Setting the class of an object created with by()


First a little bit of context:

In my package summarytools, I've defined a print method for objects of classs "summarytools". I have also created a function view() that handles objects created using by() or lapply() in such a way that the output doesn't include the lines stating the group -- or the variable in the case of lapply(); summarytools displays its own headings containing that information, so there is some redundancy when using print. Also, the main headings are not repeated when using view().

Here's an example. Note that in this version (in development), I included a message advising the use of view():

> library(summarytools)
> (tmp <- with(tobacco, by(smoker, gender, freq)))
gender: F
For best results printing list objects with summarytools, use view(x, method = 'pander')
Frequencies   
tobacco$smoker     
Type: Factor    
Group: gender = M   

              Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
----------- ------ --------- -------------- --------- --------------
        Yes    147     30.06          30.06     30.06          30.06
         No    342     69.94         100.00     69.94         100.00
       <NA>      0                               0.00         100.00
      Total    489    100.00         100.00    100.00         100.00
------------------------------------------------------------------ 
gender: M
Frequencies   
tobacco$smoker     
Type: Factor    
Group: gender = F   

              Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
----------- ------ --------- -------------- --------- --------------
        Yes    143     29.24          29.24     29.24          29.24
         No    346     70.76         100.00     70.76         100.00
       <NA>      0                               0.00         100.00
      Total    489    100.00         100.00    100.00         100.00

And now using view():

> view(tmp, method = "pander")
Frequencies   
tobacco$smoker     
Type: Factor    
Group: gender = M   

              Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
----------- ------ --------- -------------- --------- --------------
        Yes    147     30.06          30.06     30.06          30.06
         No    342     69.94         100.00     69.94         100.00
       <NA>      0                               0.00         100.00
      Total    489    100.00         100.00    100.00         100.00

Group: gender = F   

              Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
----------- ------ --------- -------------- --------- --------------
        Yes    143     29.24          29.24     29.24          29.24
         No    346     70.76         100.00     70.76         100.00
       <NA>      0                               0.00         100.00
      Total    489    100.00         100.00    100.00         100.00

I've thought about ways through which the objects of class "by" would automatically be dispatched to view() instead of print(). If I add the class "summarytools" to those objects, the print() method could redirect the call to view(), making it simpler for users to get proper, optimal outputs.

The solutions I've thought of, so far, are the following:

  1. Adding a "by" argument to the functions so that I have full control on the created objects' proporties. I'm not fond of this solution, since 1) I try to rely on base R functions that people are familiar with rather than introducing new parameters, and 2) I'd still have a similar issue when objects are created with lapply().
  2. Redefining by() so that when it's called from one of summarytools' functions, it appends the desired class to the created objects. I've avoided this because I'm hesitant to redefine base functions. I'd rather not see messages to the effect that objects have been masked when the package is loaded.
  3. Defining a package-specific by(), such as by_st(); I could use basically the same code as by.default() and by.data.frame(), the only difference being that I'd add the "summarytools" class to the created objects. This is a sort of compromise that I'm considering.

My question is the following: could there be other, maybe better solutions I'm not seeing?


Solution

  • You could use S3 method for print.by to dispatch to your custom function:

    old.print.by = print.by # save the original function so we can restore it later
    print.by = summarytools::view # redefine print.by to dispatch to custom function
    tmp
    

    enter image description here

    To restore original function later you can do print.by = old.print.by.

    If you only want your new function to operate on lists that contain objects of class "summarytools", you can use

    print.by = function(x, method = 'pander', ...) {
      if ("summarytools" %in% class(x[[1]])) {
        summarytools::view(x, method, ...)
      } else {
        old.print.by(x, ...)
      }
    }