I'm trying to create a method for my class, which inherits from data.frame. I was originally hoping just to inherit the 'show' method from data.frame as well, but I'm also fine with writing my own. I defined my class and 'show' method as follows:
setClass("SCvec", representation(auth = "character",
dev = "character",
sensor = "character",
channel = "character",
starttime = "character",
endtime = "character"),
contains="data.frame")
setMethod("show", signature(x="SCvec"), function(x) print(x))
when I type show in the R console, it prints out:
standardGeneric for "show" defined from package "methods"
function (object)
standardGeneric("show")
<bytecode: 0x0396bee8>
<environment: 0x0393ab60>
Methods may be defined for arguments: object
Use showMethods("show") for currently available ones.
(This generic function excludes non-simple inheritance; see ?setIs)
So it sure looks like I don't need to turn it into a generic using StandardGeneric() myself. but when I run my setMethod("show", signature(x="SCvec"), function(x) print(x)) line, I get the error
Error in match.call(definition, call, expand.dots) :
unused argument(s) (x = c("SCvec", ""))
I've defined this method just like I would define any other one. Why does this method definition not work? Is 'show' different than other generic functions?
The function show takes an argument object, so you would need to define your signature and function definition with respect to that formal argument:
setMethod("show", signature(object="SCvec"), function(object) print(object))
You can also see other methods that are defined for show by typing in
showMethods(show)
And this shows you that all the other methods are defined in terms of the class of object as well.
Related
Many languages have special ways to handle unknown methods (examples). The one I'm most familiar with is Python's __getattr__. If someone calls a method you haven't defined for the class, __getattr__ acts as a catch-all and does something.
I've been reading up on S4 and a little on R6, but I haven't found how to do it in R. Is it possible?
No there is no standard way of doing this from inside your class definition as you would do in python.
In python you would do something like MyObject.my_method() while in R with S3 or S4 this would be my_method(MyObject) so it looks exactly like my_function(MyObject). The only difference is that under the hood the function you called dispatches the call to the adequate method. Defining these methods for multiple classes is done as follows:
mean <- function (x, ...) UseMethod("mean", x)
mean.numeric <- function(x, ...) sum(x) / length(x)
mean.data.frame <- function(x, ...) sapply(x, mean, ...)
mean.matrix <- function(x, ...) apply(x, 2, mean)
mean.default <- function(x, ...) {
# do something
}
However, if you call the mean function on a class for which no method has been defined, it is up to the function to handle this, not to the class.
Then you have RC and S6 objects which have a more python-like syntax (MyObject$my_method()), however they would just throw an error that there is no corresponding field or method for the class you used.
Error in envRefInferField(x, what, getClass(class(x)), selfEnv) :
"my_method" is not a valid field or method name for reference class “MyObject”
Here some infos about OO-programing in R.
Winston Chang provided great info here:
https://github.com/r-lib/R6/issues/189#issuecomment-506405998
He explains how you can create an S3 generic function $ for your class to catch unknown methods. Read his full reply for more details, but the key function is below (Counter is the name of the class).
`$.Counter` <- function(x, name) {
if (name %in% names(x)) {
.subset2(x, name)
} else {
function(...) {
.subset2(x, "do")(name, ...)
}
}
}
"If name is in the class, do that. If not, send name (and any arguments) to a function called do() defined in the class."
While I've marked this as the answer (because it solves the problem), jkd is still correct:
No there is no standard way of doing this from inside your class definition as you would do in python.
I created a package containing the following function:
make_LD <- function(a) {
setClass("LongitudinalData", slots = list(subject = "numeric", visit = "numeric", room = "character", timepoint = "numeric", value = "numeric"))
new("LongitudinalData", subject = a$id, visit = a$visit, room = a$room, timepoint = a$timepoint, value = a$value)
}
make_LD works fine in a workspace where it is explicitly defined, but not when I call the function elsewhere after loading the package. When I do this, I get the error:
Error in assignClassDef(Class, classDef, where) : class "LongitudinalData" has a locked definition in pkgname'
Could someone explain how I should have gone about defining an S4 class in the package such that data from elsewhere can be used to instantiate a new object of that class? Thanks in advance.
This happens because make_LD attempts to redefine the class "LongitudinalData" every time it is called (that's what setClass does.) Thus, when the user calls it, R refuses to redefine the class because it would conflict with the definition that already exists, if any.
Most likely, you want to have the setClass statement as a standalone somewhere in your package. Then, your function make_LD just calls new and returns the result.
In R, assume an object of type 'closure' called my_object that contains both a likelihood function and the associated parameters.
Assume further that I would now like to extract specifically the likelihood function from said object and pass it on to a different R command, which needs a likelihood function as its first argument. I can extract said function via command unenclose in library pryr.
> library(pryr)
> lik_func = unenclose(my_object)
> lik_func
function (pars, condition.surv = TRUE, root = ROOT.OBS, root.p = NULL,
intermediates = FALSE)
{ #function_specifics_here# }
<environment: namespace:diversitree>
However, what I apparently extract is just an object of class 'function', and not the likelihood function itself, as the next R command complains that it does not actually receive a function, but an object:
> asr.marginal(lik=lik_func, pars=my_pars)
Error in UseMethod("make.asr.marginal") :
no applicable method for 'make.asr.marginal' applied to an object of class "function"
How do I convert this object of class 'function' into a plain function, assuming such a distinction actually exists?
Note: I am uncertain if or why a distinction between an object of class 'function' and a plain function actually exists. Maybe someone answering this question could share some light on this too.
“Objects of class "function"” are, generally1, functions. In particular, they are objects of type "closure" and class "function".
Using pryr::unenclose doesn’t have any useful effect here. Its only effect is to take a function and replace all references to objects in an enclosing environment with their value. So if I have, say:
x = 1
f = function () x
… then unenclose(f) will yield:
function () 1
This doesn’t make f any more or less of a function.
Your error message seems to be fundamentally unrelated to that. Instead, asr.marginal specifically expects a likelihood function, which apparently needs to be created by one of the make.* functions in the package. A likelihood function in the context of ‹diversitree› is a function of class "dtlik".
1 The exception is if you are prone to shenanigans:
x = 42
class(x) = "function"
Now x has class function but obviously isn’t a function.
I present a short code to create a S4 class myclass and ensure that objects are created if they verify a condition given by a parameter param
setClass("myclass", slot = c(x = "numeric"))
#constructor
ValidmyClass<- function(object, param = 1)
{
if(object#x == param) return(TRUE)
else return("problem")
}
setValidity("myclass", ValidmyClass)
setMethod("initialize","myclass", function(.Object,...){
.Object <- callNextMethod()
validObject(.Object,...)
.Object
})
For which I get the following error message Error in substituteFunctionArgs(validity, "object", functionName = sprintf("validity method for class '%s'", :
trying to change the argument list of for validity method for class 'myclass' with 2 arguments to have arguments (object)
I understand the issue with the arguments but I cannot find a way to solve this. The document about setValidity mentions that the argument method should be "validity method; that is, either NULL or a function of one argument (object)". Hence from my understanding excluding more than one argument.
Nevertheless, the idea behind this example is that I want to be able to test the construction of a myclass object based on the value of an external given parameter. If more conditions were to be added, I would like enough flexibility so only the function ValidmyClass needs to be updated, without necessarily adding more slots.
The validity function has to have one argument named object. When I need to create one argument functions but really have more arguments or data to pass in I often fall back to using closures. Here the implementation of your ValidmyClass changes in that it will now return the actual validity function. The argument of the enclosing function is then the set of additional arguments you are interested in.
setClass("myclass", slot = c(x = "numeric"))
#constructor
ValidmyClass <- function(param) {
force(param)
function(object) {
if (object#x == param) TRUE
else "problem"
}
}
setValidity("myclass", ValidmyClass(1))
Also the validity function is called automatically on init; however not when the slot x is changed after the object is created.
setMethod("initialize", "myclass", function(.Object,...) {
.Object <- callNextMethod()
.Object
})
new("myclass", x = 2)
new("myclass", x = 1)
For more information on closures see adv-R. Although I think this answers your question, I do not see how this implementation is actually helpful. When you define your class, you basically also fix the additional parameters which the validity function knows about. If you have several classes for which you can abstract the validity function then I would use the closure. If you have one class with changing parameters at runtime, I would consider to add a slot to the class. If you do not want to alter the class definition you can add a slot of class list where you the can pass in an arbitrary number of values to test against.
Suppose we have the following dummy class
Foo <- setClass(Class = "Foo",slots = c(foo = "numeric"),
prototype = list(foo = numeric())
I thought, generics are used to overload different functions. So assume we want to implement an accessor:
setMethod(f = "getFoo", signature = "Foo",
definition = function(Foo)
{
return(Foo#foo)
}
)
Is this valid? Or do I have to define a generic first:
setGeneric(name="getFoo",
def=function(Foo)
{
standardGeneric("getFoo")
}
)
If there is just one particular "instance" of this function type, there is no reason to define a generic, correct?
In order to define an S4 method, there must be an existing S4 generic (either from base, imported from another package, or defined yourself). My understand of this design is to provide the flexibility to add on additional methods in the future, even if you can't even conceive of another one ATM.
That said, if you are just trying to be more concise you could just provide the default function directly to the generic function.
setClass(Class = "Foo",slots = c(foo = "numeric"),
prototype = list(foo = numeric()))
setGeneric(name="getFoo",
def=function(Foo)
{
standardGeneric("getFoo")
}, useAsDefault=function(Foo){return(Foo#foo)}
)
# test the function
testFoo <- new("Foo", foo=3)
getFoo(testFoo)
[1] 3
So, now you have your generic including the only functionality you really wanted anyway. You also have the option to add on the the generic in the future depending upon how your application develops.