Create new S3 class methods in R - r

I'm writing a package in which I would like to create a new generic method, called "analyze", wich do different things according to the argument class. Similar to print that has print.lm, print.aov etc.
In the R folder of my package, I created two files, "analyze.lm" and "analyze.aov" that contain eponym functions. However, if I run analyze(fit) on an lm object, it does nothing because R only recognizes analyze.lm and not the root function ("analyze" only).
I've tried adding an "analyze.R" file, which either contained setMethod() (but that errored), setGeneric("analyze", function(x) attributes(x)) (but that did not solve the issue) or an analyze() function that prints "NULL". However, if I then run analyze(fit) on an lm object, in prints NULL instead of running the analyze.lm class method.
How could I create a generic method, similar to base print, that behaves differently according to argument class, and than I maintained splitted in different files (analyze.lm.R, analyze.aov.R etc.). Thanks!

Add a generic function like this:
analyze <- function(object, ...){
UseMethod("analyze")
}

Related

How do I call the new function in R when the object is defined in another package?

Suppose I'm creating a package and I'd like to define a function that creates an object where the object is defined in another package.
For example:
get_empty_mtx <- function() return(new("dgCMatrix"))
If I type library(Matrix), this will work, but when I'm making my own package, I like to use :: when referencing things from other packages. I can't do Matrix::new("dgCMatrix") as new is not a function from the Matrix package.
You can use the getClassDef function to get a class definition from a specific package and then call new() on that. For example
new(getClassDef("dgCMatrix", getNamespace("Matrix")))
and new(getClassDef("dgCMatrix", "Matrix")) also seems to work despite the documtation saying where should be an environment.

R overload assignment operator (`<-`)

Hi I am experimenting with R's S3 classes. So I ran into a problem after following https://www.r-bloggers.com/object-oriented-programming-in-r/. In the class nc <- list2env(nc) is used to save the object in its own environment, I will be creating objects that will contain large datasets so I like the pointer style a lot. The problem comes in when you want to create a copy of the object (newObject<-oldObject), this only creates a copy of the pointer to the object and not a copy of the object. This is a problem because if I change something of either the old or new object both would register the same change.
I was thinking if I could overload the <- operator with my own function that copies the object into a new environment it would be great. Does anyone know how to do this?
I know I can clone the environment with:
newObject<- as.environment(as.list(oldObject, all.names=TRUE))
but when I try to overload the assignment operator it does not work, i.e.
'<-.MyClass'<-function(NOT SURE WHAT SHOULD COME HERE){
newObject<- as.environment(as.list(oldObject, all.names=TRUE))
return(newObject)
}
I have gotten it to work with other operators eg +, *

R function Call from Another Function

I need to modify the function gamGPDfit() in the package QRM to solve a problem. The function gamGPDfit() in turn calls other functions fit.GPD(​) and gamGPDfitUp() to compute the estimates of the parameters.
The structure of the function is shown below:
#######################################################
gamGPDfit<-function (..., init = fit.GPD(...) , ...)
{
...
Par<-gamGPDfitUp(...)
...
return (list(...))
}
<environment: namespace:QRM>
#######################################################
Now, when I call fit.GPD(​), I get the function on the command window to make the necessary modifications. However, the other function gamGPDfitUp​() returns
> gamGPDfitUp
Error: object 'gamGPDfitUp' not found
The question is, how do I get such an in-built function within another function? Does it have to do with the environment QRM? If so how do I obtain the function to modify it?.
I have attached the function and the call of the gamGPDfitUp() is indicated in colour red.
There's a couple of things that may come in handy.
One is help(":::") - Accessing exported and internal variables in a namespace. You can access GamGPDfitUp probably by prefixing it with QRM:::.
Another function is fixInNamespace, which allows you to modify functions inside packages. The help page for this one lists a few more interesting tools. Play around with this and it should solve most of your problems.

override S3 methods in base R

I am attempting to over-ride the print.anova() function from the R stats package within a local package that I use when teaching. Basically, I want to remove the printing of the heading and add a "total" row without creating a new function (e.g., ANOVA()) with a new class.
The function looks like the following:
print.anova <- function(x,digits=max(getOption("digits")-2,3),
signif.stars=getOption("show.signif.stars"),totalSS=TRUE,rm.heading=TRUE,...) {
if (!any(grepl("Res.Df",colnames(x)))) { # exclusion for multiple lm objects
if (!any(grepl("Levene",attr(x,"heading")))) { # exclusion for levenes.test
if (totalSS) { # add total SS row
x <- rbind(x,c(sum(x$Df),sum(x[,"Sum Sq"]),NA,NA,NA))
row.names(x)[dim(x)[1]] <- "Total"
}
}
}
if (rm.heading) attr(x,"heading") <- NULL # remove heading
stats::print.anova(x,digits=digits,signif.stars=signif.stars,...)
invisible(x)
}
My problem is that I am not sure whether to export this as a function, a method, an S3method, some combination of those, or something else entirely. For example, when I try this (part of roxygenize code):
#'#export
I get the following warning when running Rcmd check:
S3 methods shown with full name in documentation object 'print.anova':
'print.anova'
but the function works as expected when I load my package.
However, if I try this:
#'#method print anova
#'#S3method print anova
I dont' get any warnings or errors with Rcmd check but when I try to use the function in R it finds the original function in the stats package namespace. Furthermore, if I do this
getAnywhere(print.anova)
I get this
2 differing objects matching ‘print.anova’ were found in the following places
package:stats
registered S3 method for print from namespace stats
namespace:NCStats
namespace:stats
Finally, for this version (not using export, but using method and S3method), my roxygen-developed namespace has the following item in it
S3method(print,anova)
Leading to my confusion is that I seem to have had success doing something similar with other functions (e.g., using the method and S3method version with print.summary.lm).
I would appreciate any help in my understanding what I am doing wrong here (or how I can ultimately accomplish this goal). Thank you in advance for any help.
p.s., for what it is worth, I am on Windows 7 (32-bit), R 2.15.2, and using RStudio.
Instead of trying to override the print.anova function you could create your own class which is essentially identical to the anova class. Create an as.myanova function which will turn an anova object into an object of mynanova then write your print.myanova function.

Protecting function names in R

Is it possible in R to protect function names (or variables in general) so that they cannot be masked.
I recently spotted that this can be a problem when creating a data frame with the name "new", which masked a function used by lmer and thus stopped it working. (Recovery is easy once you know what the problem is, here "rm(new)" did it.)
There is an easy workaround for your problem, without worrying about protecting variable names (though playing with lockBinding does look fun). If a function becomes masked, as in your example, it is still possible to call the masked version, with the help of the :: operator.
In general, the syntax is packagename::variablename.
(If the function you want has not been exported from the package, then you need three colons instead, :::. This shouldn't apply in this case however.)
Maybe use environments! This is a great way to separate namespaces. For example:
> a <- new.env()
> assign('printer', function(x) print(x), envir=a)
> get('printer', envir=a)('test!')
[1] "test!"
#hdallazuanna recommends (via Twitter)
new <- 1
lockBinding('new', globalenv())
this makes sense when the variable is user created but does not, of course, prevent overwriting a function from a package.
I had the reverse problem from the OP, and I wanted to prevent my custom functions in .Rprofile from being overridden when I defined a variable with the same name as a function, but I ended up putting my functions to ~/.R.R and I added these lines to .Rprofile:
if("myfuns"%in%search())detach("myfuns")
source("~/.R.R",attach(NULL,name="myfuns"))
From the help page of attach:
One useful ‘trick’ is to use ‘what = NULL’ (or equivalently a
length-zero list) to create a new environment on the search path
into which objects can be assigned by assign or load or
sys.source.
...
## create an environment on the search path and populate it
sys.source("myfuns.R", envir = attach(NULL, name = "myfuns"))

Resources