My aim is to define one of the slots of class Security as another class Quote.
First I define class Quote:
Quote <- setClass("Quote", slots = c(Last = "numeric", Settle = "numeric"))
Then I am trying to define class Security as following:
Security <- setClass("Security", slots = c(Name = "character", Price = "Quote"))
Finally I am trying to create constructor for class Security:
Security <- function(Name = character(), Last = numeric(), Settle = numeric())
new("Security", Name = Name, Price#Last = Last, Price#Settle = Settle)
Unfortunately, this code doesn't work...
Thanks in advance.
If offering the user a constructor named Security, make sure the default constructor is named differently
.Security <- setClass("Security", slots = c(Name = "character", Price = "Quote"))
In your own constructor, create the slot instance as an argument to default constructor; use ... to allow for class inheritance
Security <-
function(Name = character(), Last = numeric(), Settle = numeric(), ...)
{
.Security(Name=Name, Price=Quote(Last=Last, Settle=Settle), ...)
}
I'm still trying to learn S4, and I see that a recognized expert has already given an answer, so I'm mostly posting this as an example for critique:
.Quote <- setClass("Quote", slots = c(Last = "numeric", Settle = "numeric"))
.Security <- setClass("Security", slots = c(Name = "character", Price = "Quote"))
aNewSecurity <- .Security(Name = "newSec",
Price = .Quote(Last =20, Settle = 40) )
aNewSecurity
An object of class "Security"
Slot "Name":
[1] "newSec"
Slot "Price":
An object of class "Quote"
Slot "Last":
[1] 20
Slot "Settle":
[1] 40
I'm not sufficiently knowledgeable to know if separating Quote items from Security items is needed in this domain.
Related
I want to create class in R, let's say it is an S4 class for a person. E.g.
setClass("Person", slots = list(name = "character", mood = "myMoodType"))
Now I want to create myMoodType to be an abstract type that can only take the three values "Happy", "Sad" and "Unknown".
I know that I could do this using the validity for S4 classes and have the mood as a character type and check the validity by verifying that the character string provided is one of the three options I list. But I would like to know if I can define an abstract type, like in julia, e.g.
abstract myMoodType
type Happy <: myMoodType end
type Sad <: myMoodType end
type Unknown <: myMoodType end
What would be the correct way to approach this in R?
This might not be one of R's strongest and most smooth feature, but you could solve it in the following way. For more information see the documentation or the Advanced R chapter on S4.
First set up the Person class with the mood represented as a factor, and link it to a validation function that checks its levels.
check_person <- function(object) {
if(identical(levels(object#mood), c("Happy", "Sad", "Unknown"))){
return(TRUE)
} else {
return("Invalid mood.")
}
}
setClass("Person",
representation(name = "character", mood = "factor"),
prototype = list(name = NA_character_,
mood = factor(NA, c("Happy", "Sad", "Unknown"))),
validity = check_person)
Creating new instances with new is however a bit messy since we have to write out all the levels each time:
john <- new("Person", name="John", mood=factor("Happy", levels=c("Happy", "Sad", "Unknown")))
lucy <- new("Person", name="Lucy", mood=factor("Sad", levels=c("Happy", "Sad", "Unknown")))
Otherwise we'll get an error:
new("Person", name="Eve", mood="Unknown")
Error in validObject(.Object) :
invalid class “Person” object: invalid object for slot "mood" in class "Person":
got class "character", should be or extend class "factor"
To get around that you could make your own constructor:
new_person <- function(name, mood){
new("Person", name = name, mood = factor(mood, levels = c("Happy", "Sad", "Unknown")))
}
new_person("Eve", "Unknown")
An object of class "Person"
Slot "name":
[1] "Eve"
Slot "mood":
[1] Unknown
Levels: Happy Sad Unknown
I am using R refClass example below.
Person = setRefClass("Person",fields = list(name = "character", age = "numeric")
) ## Person = setRefClass("Person",
Person$methods = list(
increaseAge <- function(howMuch){
age = age + howMuch
}
)
When I store this program in a file called Person.R and source it, it does not show any errors. Now I instantiate a new object.
p = new("Person",name="sachin",age=40)
And I try to invoke the method increaseAge, using p$increaseAge(40), and it shows the following error
Error in envRefInferField(x, what, getClass(class(x)), selfEnv) :
"increaseAge" is not a valid field or method name for reference class "Person"
I cannot figure out why it says that the method increaseAge is not a valid method name when I have defined it.
To specify a method independent of class definition, invoke the methods() function on the generator. Also, use either <<- or .self$age = for the assignment.
Person$methods(increaseAge=function(howMuch) {
age <<- age + howMuch
## alterenatively, .self$age = age + howMuch or .self$age <- age + howMuch
})
Remember that R works best on vectors, so think of a Persons class (modeling columns) representing all the individuals in your study, rather than a collection of Person instances (modeling rows).
I get an error using your code. I would do something like this:
Person = setRefClass("Person",
fields = list(name = "character", age = "numeric"),
methods = list(
increaseAge = function(howMuch) age <<- age + howMuch
))
> p = new("Person",name="sachin",age=40)
> p$increaseAge(5)
> p$age
[1] 45
Okay, it took me a while to create a snippet of code that replicates my problem. Here it is. Notice that if you run the command new("FirstSet", id = "Input", multiplier = 2)
you will get the correct answer. However, if you try to create a class that contains both you will get the following: Error in .local(.Object, ...) : argument "id" is missing, with no default. This is literally the best I can do to explain/show the problem.
What in the world am I doing wrong?
setClass("Details",
representation(
ID = "character",
Anumber = "numeric"))
Input <- new("Details",
ID = "Input",
Anumber = 2)
setClass("FirstSet",
representation(
Anothernumber = "numeric"))
setGeneric(
name = "FirstSet",
def = function(object){standardGeneric("FirstSet")}
)
setMethod("initialize",
signature(.Object = "FirstSet"),
function (.Object, id, multiplier)
{ x = id#Anumber
y = x * multiplier
.Object#Anothernumber = y
return(.Object)
}
)
setClass("Super", contains = c("Details", "FirstSet"))
Corrected Code now gives a new error. I followed the instruction in the post and solved my problem. I also created a generic and a method for "Super", see code below,. Now, I get a new error. Error in .local(.Object, ...) : trying to get slot "Anumber" from an object of a basic class ("character") with no slots. Man, this is exhausting, I thought I had it.
The goal for details is, there will be many files that are serialized and methods are called depending on characteristics of the data in the file. Is this even possible in R or am I trying to do something that R cannot do?
New Code
setClass("Details",
representation(
ID = "character",
Anumber = "numeric"))
setGeneric("Details",
def = function(object){standardGeneric("Details")})
setMethod("initialize",
signature(.Object = "Details"),
function(.Object, ID = character(), Anumber = numeric()){
.Object#ID = ID
.Object#Anumber = 2
return(.Object)
})
setClass("FirstSet",
representation(
Anothernumber = "numeric"))
setGeneric(
name = "FirstSet",
def = function(object){standardGeneric("FirstSet")}
)
setMethod("initialize",
signature(.Object = "FirstSet"),
function (.Object, id = character(), multiplier = numeric())
{ x = id#Anumber
y = x * multiplier
.Object#Anothernumber = y
return(.Object)
}
)
setClass("Super", contains = c("Details", "FirstSet"))
setGeneric("Super",
def = function(object){standardGeneric("Super")})
setMethod("initialize",
signature(.Object = "Super"),
function(.Object, id = character(), Anumber = numeric()){
Details <- new("Details", ID = id, Anumber = Anumber)
FirstSet <- new("FirstSet", Anothernumber = Anothernumber)
Super <- new("Super", Details, FirstSet)
return(.Object)
})
The basic rule is that new("FirstSet") (or any non-virtual class) needs to work. Yours doesn't (because the intiailize arguments don't have default values). See this answer for some more guidelines.
How do I create an S4 class which has arrays as slots? Below, I have an example class. I'd like to be able to construct in such a way that I get two "person" elements, each of which has appropriate array members.
The code below gives me the following error: "Error in validObject(.Object) :
invalid class “person” object: invalid object for slot "children" in class "person": got class "character", should be or extend class "array"
setClass("person", representation(name="character", age="numeric", children = "array"))
setMethod(
f = "[",
signature="person",
definition=function(x,i,j,...,drop=TRUE){
initialize(x, name=x#name[i], age = x#age[i], children = x#children[i])
}
)
setMethod(f = "length", signature = "person", definition = function(x){
length(x#name)
})
setMethod(f = "dim", signature = "person", definition = function(x){
length(x#name)
})
kids1 = as.array(c("Bob", "Joe", "Mary"))
person = new("person", name="John", age=40, children = kids1)
person#children[2]
kids2 = as.array(c("Steve", "Beth", "Kim"))
people = new("person", name=c("John", "Fred"), age=c(40, 20), children = as.array(c(kids1, kids2), dim = 2))
people[1]#age
people[2]#children[1]
You probably don't want #children to be an array. With a dim attribute of length 1, it's essentially the same as a vector, and you lose the ability to distinguish different people's children. Consider making this slot a list instead.
setClass("person",
representation(name="character", age="numeric", children = "list"))
person = new("person", name="John", age=40, children = list(kids1))
person#children
people = new("person", name=c("John", "Fred"), age=c(40, 20),
children = list(kids1, kids2))
people[1]
Add drop=FALSE to your subset of the children slot; this is a consequence of standard R rules for array subsetting
setMethod(
f = "[",
signature="person",
definition=function(x,i,j,...,drop=TRUE){
initialize(x, name=x#name[i], age = x#age[i],
children = x#children[i,drop=FALSE])
}
)
Also I'm not sure that your as.array() is doing what you think? The dim argument is being ignored.
Hong's answer works. I did need to add a list wrapper in the "[" subset function. Once that's done, everything works fine.
kids1 = as.array(c("Bob", "Joe"))
kids2 = as.array(c("Steve", "Beth", "Kim"))
setClass("person", representation(name="character", age="numeric", children = "list"))
setMethod(
f = "[",
signature="person",
definition=function(x,i,j,...,drop=TRUE){
initialize(x, name=x#name[i], age = x#age[i], children = list(x#children[i]))
}
)
people = new("person", name=c("John", "Fred"), age=c(40, 20), children = list(kids1, kids2))
people[1]#name
people[1]#age
people[1]#children
people[2]#name
people[2]#age
people[2]#children
I am getting the message
Error in parent.frame() : node stack overflow
Error during wrapup: node stack overflow
when I try to construct an object using the S4 command "as", but only when a superclass is declared "VIRTUAL".
The class hierarchy is as follows:
PivotBasic contains Pivot contains Model
The setClass commands for Pivot and Pivot Basic and the constructor for PivotBasic are below. Class Pivot does not have a constructor. The Model constructor is too big to insert here.
This is really not a big deal (I think) because everything works fine if the "VIRTUAL" keyword is removed from the representation argument of setClass. But I am curious about the reason for the problem. Would anyone have insights on it?
Thanks,
Fernando Saldanha
setClass(Class = "Pivot",
representation = representation(
pivotName = "character",
pivotNames = "character",
pivotData = "data.frame",
"VIRTUAL"
),
contains = "Model"
)
setClass(Class = "PivotBasic",
representation = representation(),
contains = "Pivot"
)
pivotBasic <- function(
portfolio,
assets,
controlVariableList,
pivotData = NULL, # pivotName is ignored if pivotData is not null
pivotName = "N_WEEKDAY_3_6",
firstPredictionDate = as.Date(integer(), origin = "1970-01-01"),
name = NULL,
tags = "Event"
) {
if (missing(portfolio)) stop("[PivotBasic: pivotBasic] - Missing portfolio argument")
if (missing(assets)) stop("[PivotBasic: pivotBasic] - Missing assets argument")
if (missing(controlVariableList)) stop("[PivotBasic: pivotBasic] - Missing controlVariableList argument")
object <- model(
portfolio,
assets,
controlVariableList,
firstPredictionDate,
name,
tags)
# The error message happens when this command is executed
mdl <- as(object, "PivotBasic")
# Other code
mdl
} # end pivotBasic
Is this a minimal example that illustrates your problem
.Model <- setClass(Class = "Model",
representation=representation(x="integer")
)
setClass(Class = "Pivot",
representation = representation("VIRTUAL"),
contains = "Model"
)
.PivotBasic <- setClass(Class = "PivotBasic",
contains = "Pivot"
)
This generates an error
> as(.Model(), "PivotBasic")
Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
> R.version.string
[1] "R version 3.0.0 Patched (2013-04-15 r62590)"
but might generate an error like you see under an earlier version of R. This thread on the R-devel mailing list is relevant, where a solution is to define a setIs method such as
setIs("PivotBasic", "Model",
coerce = function(from) .PivotBasic(x = from#x),
replace = function(from, value) {
from#x = value#x
from
}
)
I think of setIs as part of the class definition. If there are many slots needing copying, then a further work-around might be, in the replace function,
nms <- intersect(slotNames(value), slotNames(from))
for (nm in nms)
slot(from, nm) <- slot(value, nm)
from
but the underlying issue is really in S4's implementation. A cost to removing the "VIRTUAL" specification is that it compromises your class design, and presumably the formalism of the S4 system is what motivated your choice in the first place; maybe that's not such a bad cost when faced with the alternatives.