My unit test fails on comparing reference and expected SummarizedExperiment objects. Error message:
> expect_identical(target, current)
Error: `target` not identical to `current`.
Attributes: < Component “assays”: Class definitions are not identical >
MWE:
LINK TO DATASET
Code:
load("se-comparison.Rdata")
library(SummarizedExperiment)
library(testthat)
expect_identical(target, current)
# expect_identical() uses attr.all.equal() to compare S4 objects so check this
attr.all.equal(target, current)
# ok, check the attributes
cur <- attributes(current)
tar <- attributes(target)
class(cur$assays)
class(tar$assays)
expect_identical(
cur$assays,
tar$assays
)
expect_identical(
class(cur$assays),
class(tar$assays)
)
Output:
> library(SummarizedExperiment)
> library(testthat)
> expect_identical(target, current)
Error: `target` not identical to `current`.
Attributes: < Component “assays”: Class definitions are not identical >
> # expect_identical() uses attr.all.equal() to compare S4 objects so check this
> attr.all.equal(target, current)
[1] "Attributes: < Component “assays”: Class definitions are not identical >"
> # ok, check the attributes
> cur <- attributes(current)
> tar <- attributes(target)
> class(cur$assays)
[1] "ShallowSimpleListAssays"
attr(,"package")
[1] "SummarizedExperiment"
> class(tar$assays)
[1] "ShallowSimpleListAssays"
attr(,"package")
[1] "SummarizedExperiment"
> cur$assays
Reference class object of class "ShallowSimpleListAssays"
Field "data":
List of length 1
names(1): counts
> tar$assays
Reference class object of class "ShallowSimpleListAssays"
Field "data":
List of length 1
names(1): counts
> expect_identical(
+ cur$assays,
+ tar$assays
+ )
Error: cur$assays not identical to tar$assays.
Class definitions are not identical
> expect_identical(
+ class(cur$assays),
+ class(tar$assays)
+ )
>
Any ideas why comparison fails?
Not sure why it fails in general, but attr.all.equal compares class definitions returned by getClass method (if available). In this case, getClass for each $assays object returns class definition that differs in fieldPrototypes and refMethods (environment strings differ). This causes identical to fail.
Compare:
str(class(cur$assays))
str(cur$assays$getClass())
A workaround would be to skip checking attributes: expect_equal(target, current, check.attributes = FALSE).
As a side note, this also works fine (note that I use here a getter method for SummarizedExperiment class instead of $assays):
expect_equal(
assays(current),
assays(target)
)
Related
Is it possible to change default argument(s) of S3 Methods in R?
It's easy enough to change arguments using formals ...
# return default arguments of table
> args(table)
function (..., exclude = if (useNA == "no") c(NA, NaN), useNA = c("no",
"ifany", "always"), dnn = list.names(...), deparse.level = 1)
# Update an argument
> formals(table)$useNA <- "always"
# Check change
> args(table)
function (..., exclude = if (useNA == "no") c(NA, NaN), useNA = "always",
dnn = list.names(...), deparse.level = 1)
But not S3 methods ...
# View default argument of S3 method
> formals(utils:::str.default)$list.len
[1] 99
# Attempt to change
> formals(utils:::str.default)$list.len <- 99
Error in formals(utils:::str.default)$list.len <- 99 :
object 'utils' not found
At #nicola's generous prompting here is an answer-version of the comments:
You can edit S3 methods and other non-exported functions using assignInNamespace(). This lets you replace a function in a given namespace with a new user-defined function (fixInNamespace() will open the target function in an editor to let you make a change).
# Take a look at what we are going to change
formals(utils:::str.default)$list.len
#> [1] 99
# extract the whole function from utils namespace
f_to_edit <- utils:::str.default
# make the necessary alterations
formals(f_to_edit)$list.len<-900
# Now we substitute our new improved version of str.default inside
# the utils namespace
assignInNamespace("str.default", f_to_edit, ns = "utils")
# and check the result
formals(utils:::str.default)$list.len
#> [1] 900
If you restart your R session you'll recover the defaults (or you can put them back manually in the current session).
Trying to build a new class on the base of an 'ordered' class, using R.version 3.3.1 tried also on new R-devel
R Under development (unstable) (2016-10-26 r71594) -- "Unsuffered Consequences"
I get an error:
> setClass('newFactor', representation = c(tempValue = 'character'), contains = c('ordered'))
Error in validObject(.Object) :
invalid class “classRepresentation” object: invalid object for slot "slots" in class "classRepresentation": got class "character", should be or extend class "list"
>
The same thing works on the stable Version 3.2.5
EDIT
OK thanks #hrbrmstr I understood that R 3.3 does not support using S3 objects inside S4 objects but I am still puzzled. This:
numWithId <- setClass("numWithId", slots = c(id = "character"),contains = "numeric")
should work but this :
numWithId <- setClass("numWithId", slots = c(id = "character"),contains = "factor")
should not, except it does!
> numWithId <- setClass("numWithId", slots = c(id = "character"),contains = 'factor')
> new('numWithId')
An object of class "numWithId"
integer(0)
Slot "id":
character(0)
Slot "levels":
character(0)
Slot ".S3Class":
[1] "factor"
On the other hand this does not work:
numWithId <- setClass("numWithId", slots = c(id = "character"),contains = 'ordered')
Error in makePrototypeFromClassDef(properties, ClassDef, immediate, where) :
in constructing the prototype for class “numWithId”: prototype has class “S4”, but the data part specifies class “integer”
When I add list to the contains = it works {I have a feeling that this is not the right way of doing this}
> setClass("numWithId", slots = c(id = "character"),contains = c('list', 'ordered'))
> new("numWithId")
An object of class "numWithId"
list()
Slot "id":
character(0)
Slot ".S3Class":
[1] "ordered" "factor"
Slot "levels":
character(0)
>
What is the right way of inheriting an S3 object in to an S4 object?
I'm following this tutorial about MALDIquant package but i'm getting an error, before executing this line
peaks <- binPeaks(peaks, tolerance=0.002)
The Error is :
Error: binPeaks(peaks, tolerance = 0.002) : ‘l’ is no list of MALDIquant::MassPeaks objects!
When i do class(peaks) :
> class(peaks)
[1] "MassPeaks"
attr(,"package")
[1] "MALDIquant"
binPeaks just works on list of MassPeaks objects. Your class output shows that you have just a single MassPeaks object.
## load package
library("MALDIquant")
## create two MassPeaks objects
p <- list(createMassPeaks(mass=seq(100, 500, 100), intensity=1:5),
createMassPeaks(mass=c(seq(100.2, 300.2, 100), 395), intensity=1:4))
binnedPeaks <- binPeaks(p, tolerance=0.002)
## but not:
binPeaks(p[[1]])
# Error: binPeaks(peaks, tolerance = 0.002) : ‘l’ is no list of MALDIquant::MassPeaks objects!
Please look at ?binPeaks for details or write me an email (I am the maintainer, you will find my mail address at CRAN.
I am kind of a newbie to R. I am trying to build a function to retrieve and output all the dimensions. I am using the twitteR package which has an object 'user'. I use the getUser() which outputs a 'user' class type of object. As a part of the twitteR packages documentation, this object is not subsettable but does have fields such as names, screenNames, description, etc., e.g.:
> g <- getUser("CNN")
> g$name
[1] "CNN"
> g$screenName
[1] "CNN"
> g$description
[1] "Bringing you breaking news and the most talked about stories. Join the conversation and let’s connect!"
> g$statusesCount
[1] 35605
> g$followersCount
[1] 10542191
This is my function that I am trying to create and am struggling with:
userInfo <- function(user) {
userDims<- c("name", "screenName", "id", "lastStatus", "description", "statusesCount", "followersCount", "favoritesCount", "friendsCount")
for(i in seq_along(userDims)){
userObj <- getUser(user)
userObj$userDims[i]
}
}
My question is: How do I concatenate userObj$ with each entry in userDim without getting this error:
Error in envRefInferField(x, what, getClass(class(x)), selfEnv) :
‘userDims’ is not a valid field or method name for reference class “user”**
Reference Classes only seem to accept the basic/standard object types are permitted. For instance, I want a chron object but this does not allow me to define it:
> newclass <- setRefClass("newclass",fields=list(time="chron"))
Error in refClassInformation(Class, contains, fields, methods, where) :
class "chron" for field 'time' is not defined
Is this a limitation or there is a better way? I tried maybe setting it in the initialize method but apparently this is not the way to go either:
> newclass <- setRefClass("newclass",
+ fields=list(time="numeric"),
+ methods=list(initialize=function() time <<- as.chron(time)))
library(chron)
> x <- newclass(time=as.chron("2011-01-01"))
Error in .Object$initialize(...) : unused argument (time = 14975)
I think that you need to register your non standard class using setOldclass first.
require(chron)
dts <- dates(c("05/20/13", "06/10/13"))
tms <- times(c("19:30:00", "22:30:05"))
setOldClass("chron")
newclass <- setRefClass("newclass",
fields = list(time = "chron"))
mydate <- newclass(time = chron(dates = dts, times = tms))
mydate$time
## [1] (05/20/13 19:30:00) (06/10/13 22:30:05)