[R] How to create a proper S4 class?
Martin Morgan
mtmorg@n@b|oc @end|ng |rom gm@||@com
Thu Nov 18 16:24:03 CET 2021
From my example,
as(employees, "People")
more general coercion is possible; see the documentation ?setAs.
From your problem description I would have opted for the solution that you now have, with two slots rather than inheritance. Inheritance has a kind of weird contract when using another package, where you're agreeing to inherit whatever generics and methods are / will be defined on the object, by any package loaded by the user; a usual benefit of object-oriented programming is better control over object type, and this contract seems to total undermine that.
I actually don't know how to navigate the mysterious error; the secret is somewhere in the cbind / cbind2 documentation, but this seems quite opaque to me.
Martin
On 11/17/21, 8:00 PM, "Leonard Mada" <leo.mada using syonic.eu> wrote:
Dear Martin,
thank you very much for the guidance.
Ultimately, I got it running. But, for mysterious reasons, it was
challenging:
- I skipped for now the inheritance (and used 2 explicit non-inherited
slots): this is still unresolved; [*]
- the code is definitely cleaner;
[*] Mysterious errors, like:
"Error in cbind(deparse.level, ...) :
cbind for agentMatrix is only defined for 2 agentMatrices"
One last question pops up:
If B inherits from A, how can I down-cast back to A?
b = new("B", someA);
??? as.A(b) ???
Is there a direct method?
I could not explore this, as I am still struggling with the inheritance.
The information may be useful, though: it helps in deciding the design
of the data-structures. [Actually, all base-methods should work natively
as well - but to have a solution in any case.]
Sincerely,
Leonard
On 11/17/2021 5:48 PM, Martin Morgan wrote:
> Hi Leonard --
>
> Remember that a class can have 'has a' and 'is a' relationships. For instance, a People class might HAVE slots name and age
>
> .People <- setClass(
> "People",
> slots = c(name = "character", age = "numeric")
> )
>
> while an Employees class might be described as an 'is a' relationship -- all employeeds are people -- while also having slots like years_of_employment and job_title
>
> .Employees <- setClass(
> "Employees",
> contains = "People",
> slots = c(years_of_employment = "numeric", job_title = "character")
> )
>
> I've used .People and .Employees to capture the return value of setClass(), and these can be used as constructors
>
> people <- .People(
> name = c("Simon", "Andre"),
> age = c(30, 60)
> )
>
> employees = .Employees(
> people, # unnamed arguments are class(es) contained in 'Employees'
> years_of_employment = c(3, 30),
> job_title = c("hard worker", "manager")
> )
>
> I would not suggest using attributes in addition to slots. Rather, embrace the paradigm and represent attributes as additional slots. In practice it is often helpful to write a constructor function that might transform between formats useful for users to formats useful for programming, and that can be easily documented.
>
> Employees <-
> function(name, age, years_of_employment, job_title)
> {
> ## implement sanity checks here, or in validity methods
> people <- .People(name = name, age = age)
> .Employees(people, years_of_employment = years_of_employment, job_title = job_title)
> }
>
> plot() and lines() are both S3 generics, and the rules for S3 generics using S4 objects are described in the help page ?Methods_for_S3. Likely you will want to implement a show() method; show() is an S4 method, so see ?Methods_Details. Typically this should use accessors rather than relying on direct slot access, e.g.,
>
> person_names <- function(x) x using name
> employee_names <- person_names
>
> The next method implemented is often the [ (single bracket subset) function; this is relatively complicated to get right, but worth exploring.
>
> I hope that gets you a little further along the road.
>
> Martin Morgan
>
> On 11/16/21, 11:34 PM, "R-help on behalf of Leonard Mada via R-help" <r-help-bounces using r-project.org on behalf of r-help using r-project.org> wrote:
>
> Dear List-Members,
>
>
> I want to create an S4 class with 2 data slots, as well as a plot and a
> line method.
>
>
> Unfortunately I lack any experience with S4 classes. I have put together
> some working code - but I presume that it is not the best way to do it.
> The actual code is also available on Github (see below).
>
>
> 1.) S4 class
> - should contain 2 data slots:
> Slot 1: the agents:
> = agentMatrix class (defined externally, NetlogoR S4 class);
> Slot 2: the path traveled by the agents:
> = a data frame: (x, y, id);
> - my current code: defines only the agents ("t");
> setClass("agentsWithPath", contains = c(t="agentMatrix"));
>
> 1.b.) Attribute with colors specific for each agent
> - should be probably an attribute attached to the agentMatrix and not a
> proper data slot;
> Note:
> - it is currently an attribute on the path data.frame, but I will
> probably change this once I get the S4 class properly implemented;
> - the agentMatrix does NOT store the colors (which are stored in another
> class - but it is useful to have this information available with the
> agents);
>
> 2.) plot & line methods for this class
> plot.agentsWithPath;
> lines.agentsWithPath;
>
>
> I somehow got stuck with the S4 class definition. Though it may be a
> good opportunity to learn about S4 classes (and it is probably better
> suited as an S4 class than polynomials).
>
>
> The GitHub code draws the agents, but was somehow hacked together. For
> anyone interested:
>
> https://github.com/discoleo/R/blob/master/Stat/ABM.Models.Particles.R
>
>
> Many thanks,
>
>
> Leonard
>
> ______________________________________________
> R-help using r-project.org mailing list -- To UNSUBSCRIBE and more, see
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.
More information about the R-help
mailing list