Looks like a bug in the methods package to me.

The extends() relationship looks transitive, as it should be:

> extends("SingleCellExperiment", "SummarizedExperiment")
[1] TRUE
> is(new("SingleCellExperiment"), "ExpData")
[1] TRUE
> extends("SingleCellExperiment", "ExpData")
[1] TRUE

Also is() is doing the right thing as Helena pointed out:

> is(new("SingleCellExperiment"), "ExpData")
[1] TRUE

However, when using the single-arg form of extends() to list all the ancestor classes, ExpData is no longer seen as an ancestor of SingleCellExperiment:

> extends("SummarizedExperiment")
[1] "SummarizedExperiment" "RectangularData" "Vector"
[4] "ExpData"              "Annotated" "vector_OR_Vector"

> extends("SingleCellExperiment")
[1] "SingleCellExperiment"       "RangedSummarizedExperiment"
[3] "SummarizedExperiment"       "RectangularData"
[5] "Vector"                     "Annotated"
[7] "vector_OR_Vector"

Same problem with selectSuperClasses(), which is an other way to list all the ancestors of a given class:

> selectSuperClasses("SummarizedExperiment", directOnly=FALSE)
[1] "RectangularData"  "Vector"           "ExpData" "Annotated"
[5] "vector_OR_Vector"

> selectSuperClasses("SingleCellExperiment", directOnly=FALSE)
[1] "RangedSummarizedExperiment" "SummarizedExperiment"
[3] "RectangularData"            "Vector"
[5] "Annotated"                  "vector_OR_Vector"

And same problem when trying to retrieve this list directly from the 'contains' slot of the class definitions:

> names(getClass("SummarizedExperiment")@contains)
[1] "RectangularData"  "Vector"           "ExpData" "Annotated"
[5] "vector_OR_Vector"

> names(getClass("RangedSummarizedExperiment")@contains)
[1] "SummarizedExperiment" "RectangularData" "Vector"
[4] "ExpData"              "Annotated" "vector_OR_Vector"

> names(getClass("SingleCellExperiment")@contains)
[1] "RangedSummarizedExperiment" "SummarizedExperiment"
[3] "RectangularData"            "Vector"
[5] "Annotated"                  "vector_OR_Vector"

Any volunteer to report this to the methods maintainer? (via the R bug tracker)

Anyways, not sure exactly what methods you're planning to implement for ExpData derivatives. Have you considered doing something like this instead:

.check_expdata <- function(expdata)
{
    ok <- is.matrix(expdata) ||
          is(expdata, "dgCMatrix") ||
          is(expdata, "ExpressionSet") ||
          is(expdata, "SummarizedExperiment")
    if (!ok) stop("'expdata' must be a matrix or dgCMatrix or ",
                  "ExpressionSet or SummarizedExperiment derivative")
}

foo <- function(expdata)
{
    .check_expdata(expdata)
    ...
    ...
}

Personally I find that using a union and method dispatch for this is a little bit overkill.

Best,

H.


On 1/8/25 05:35, Helena L. Crowell wrote:
Ui, my bad, messed up my environment… Indeed, I think what you had in mind 
doesn’t work. My guess is that the class union is “strict” in that method 
dispatch doesn’t just propagate to classes that inherit from what’s in the 
union.

Anything against something like … which should definitely work (I think):

setClassUnion("ExpData", c(
     "matrix", "dgCMatrix", "ExpressionSet",
     "SingleCellExperiment", "SummarizedExperiment”))

What’s really weird imo is that is(se, "ExpData”) and is(sce, "ExpData”) both 
return TRUE, but still the SCE method can’t be found?
Maybe someone else has more insights, sorry!!

On Jan 8, 2025, at 14:11, Axel Klenk <axel.kl...@gmail.com> wrote:

Hi Helena,

thanks for your reply.  Unfortunately it doesn't work for me -- when
copy+paste'ing your code
the error now occurs for SummarizedExperiment (transcript below,
including session info
that I had forgotten yesterday).

In the current version of GSVA the class union contains all of SE, SCE
and SPE and we
still need to define all methods for all of them which doesn't feel
right... but I must be doing
something wrong.

Any ideas?

Cheers,

- axel


library("Matrix")
library("Biobase")
Loading required package: BiocGenerics

Attaching package: 'BiocGenerics'

The following objects are masked from 'package:stats':

    IQR, mad, sd, var, xtabs

The following objects are masked from 'package:base':

    anyDuplicated, aperm, append, as.data.frame, basename, cbind,
    colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find,
    get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply,
    match, mget, order, paste, pmax, pmax.int, pmin, pmin.int,
    Position, rank, rbind, Reduce, rownames, sapply, saveRDS, setdiff,
    table, tapply, union, unique, unsplit, which.max, which.min

Welcome to Bioconductor

    Vignettes contain introductory material; view with
    'browseVignettes()'. To cite Bioconductor, see
    'citation("Biobase")', and for packages 'citation("pkgname")'.

library("SummarizedExperiment")
Loading required package: MatrixGenerics
Loading required package: matrixStats

Attaching package: 'matrixStats'

The following objects are masked from 'package:Biobase':

    anyMissing, rowMedians


Attaching package: 'MatrixGenerics'

The following objects are masked from 'package:matrixStats':

    colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse,
    colCounts, colCummaxs, colCummins, colCumprods, colCumsums,
    colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs,
    colMads, colMaxs, colMeans2, colMedians, colMins, colOrderStats,
    colProds, colQuantiles, colRanges, colRanks, colSdDiffs, colSds,
    colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads,
    colWeightedMeans, colWeightedMedians, colWeightedSds,
    colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet,
    rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods,
    rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs, rowLogSumExps,
    rowMadDiffs, rowMads, rowMaxs, rowMeans2, rowMedians, rowMins,
    rowOrderStats, rowProds, rowQuantiles, rowRanges, rowRanks,
    rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs, rowVars,
    rowWeightedMads, rowWeightedMeans, rowWeightedMedians,
    rowWeightedSds, rowWeightedVars

The following object is masked from 'package:Biobase':

    rowMedians

Loading required package: GenomicRanges
Loading required package: stats4
Loading required package: S4Vectors

Attaching package: 'S4Vectors'

The following objects are masked from 'package:Matrix':

    expand, unname

The following object is masked from 'package:utils':

    findMatches

The following objects are masked from 'package:base':

    expand.grid, I, unname

Loading required package: IRanges
Loading required package: GenomeInfoDb
library("SingleCellExperiment")
setClassUnion("ExpData", c("matrix", "dgCMatrix",
+ "ExpressionSet", "SingleCellExperiment"))
setGeneric("expShow", \(.) standardGeneric("expShow"))
[1] "expShow"
setMethod("expShow", "ExpData", \(.) show(.))
p <- 10
n <- 30
y <- matrix(rnorm(n*p), nrow=p, ncol=n,
+     dimnames=list(
+         paste("g", 1:p, sep="") ,
+         paste("s", 1:n, sep="")))
se <- SummarizedExperiment(y)
sce <- SingleCellExperiment(y)
expShow(se)
Error: unable to find an inherited method for function 'expShow' for
signature '. = "SummarizedExperiment"'
expShow(sce)
class: SingleCellExperiment
dim: 10 30
metadata(0):
assays(1): ''
rownames(10): g1 g2 ... g9 g10
rowData names(0):
colnames(30): s1 s2 ... s29 s30
colData names(0):
reducedDimNames(0):
mainExpName: NULL
altExpNames(0):


sessionInfo()
R version 4.4.2 (2024-10-31)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 20.04.6 LTS

Matrix products: default
BLAS:   /home/axel/R/local/R-4.4.2/lib/libRblas.so
LAPACK: /home/axel/R/local/R-4.4.2/lib/libRlapack.so;  LAPACK version 3.12.0

locale:
[1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C
[3] LC_TIME=es_ES.UTF-8        LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=es_ES.UTF-8    LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=es_ES.UTF-8       LC_NAME=C
[9] LC_ADDRESS=C               LC_TELEPHONE=C
[11] LC_MEASUREMENT=es_ES.UTF-8 LC_IDENTIFICATION=C

time zone: Europe/Madrid
tzcode source: system (glibc)

attached base packages:
[1] stats4    stats     graphics  grDevices utils     datasets  methods
[8] base

other attached packages:
[1] SingleCellExperiment_1.28.1 SummarizedExperiment_1.36.0
[3] GenomicRanges_1.58.0        GenomeInfoDb_1.42.1
[5] IRanges_2.40.1              S4Vectors_0.44.0
[7] MatrixGenerics_1.18.0       matrixStats_1.5.0
[9] Biobase_2.66.0              BiocGenerics_0.52.0
[11] Matrix_1.7-1

loaded via a namespace (and not attached):
[1] R6_2.5.1                SparseArray_1.6.0       zlibbioc_1.52.0
[4] lattice_0.22-6          abind_1.4-8             GenomeInfoDbData_1.2.13
[7] S4Arrays_1.6.0          XVector_0.46.0          UCSC.utils_1.2.0
[10] fortunes_1.5-4          grid_4.4.2              DelayedArray_0.32.0
[13] compiler_4.4.2          httr_1.4.7              tools_4.4.2
[16] crayon_1.5.3            jsonlite_1.8.9

Am Mi., 8. Jan. 2025 um 13:41 Uhr schrieb Helena L. Crowell <hel...@crowell.eu>:
SCE inherits from SE, but not vice versa. So setting the class union on SCE 
(not SE) will do the trick. Briefly, Anything defined on an SCE will work 
upstream (SE), but anything defined on SE will not work downstream (SPE, SCE).

***

This works:

library("Matrix")
library("Biobase")
library("SummarizedExperiment")
library("SingleCellExperiment")

setClassUnion("ExpData", c("matrix", "dgCMatrix",
    "ExpressionSet", "SingleCellExperiment"))

setGeneric("expShow", \(.) standardGeneric("expShow"))
setMethod("expShow", "ExpData", \(.) show(.))

p <- 10
n <- 30
y <- matrix(rnorm(n*p), nrow=p, ncol=n,
    dimnames=list(
        paste("g", 1:p, sep="") ,
        paste("s", 1:n, sep="")))

se <- SummarizedExperiment(y)
sce <- SingleCellExperiment(y)

expShow(se)
expShow(sce)

On Jan 7, 2025, at 21:33, Axel Klenk <axel.kl...@gmail.com> wrote:

Dear Community, dear S4 Experts,

in the GSVA package I want to use an S4 class union as a superclass
for all supported data containers and S4 methods
defined for this superclass, rather than for each subclass, where a
class-specific implementation is not necessary.  In particular
I want to avoid having to implement individual methods for all current
(and possibly, future) subclasses of SummarizedExperiment
for common operations like accessing assay names and dimensions, assay
data, etc.

As you can see from the example code and output below, this works as
expected for SummarizedExperiment objects but not
for its subclasses such as SingleCellExperiment or SpatialExperiment
(if SummarizedExperiment is part of the class union and
the others are not).  In the latter case the result is "Error: unable
to find an inherited method for function ..." (please see below).

I'd be very grateful if someone with more S4 expertise than myself
could please let me know if and how this can be solved -- or
if the whole thing is not a good idea at all. ;-)

Thanks a lot,

- axel



### define a class union as a common superclass
library("Matrix")
library("Biobase")
library("SummarizedExperiment")
library("SingleCellExperiment")
## [package startup messages omitted]

setClassUnion("ExpData",
             c("matrix", "dgCMatrix", "ExpressionSet", "SummarizedExperiment"))

### ... and an example method for the superclass
setGeneric("expShow", function(object) standardGeneric("expShow"))
setMethod("expShow",
         signature=signature(object="ExpData"),
         function(object) {
             show(object)
         })

### generate some example data and test the method:
p <- 10
n <- 30
y <- matrix(rnorm(n*p), nrow=p, ncol=n,
           dimnames=list(paste("g", 1:p, sep="") , paste("s", 1:n, sep="")))

se <- SummarizedExperiment(y)
show(se)
## class: SummarizedExperiment
## dim: 10 30
## metadata(0):
## assays(1): ''
## rownames(10): g1 g2 ... g9 g10
## rowData names(0):
## colnames(30): s1 s2 ... s29 s30
## colData names(0):

expShow(se)
## class: SummarizedExperiment
## dim: 10 30
## metadata(0):
## assays(1): ''
## rownames(10): g1 g2 ... g9 g10
## rowData names(0):
## colnames(30): s1 s2 ... s29 s30
## colData names(0):

sce <- SingleCellExperiment(y)
show(sce)
## class: SingleCellExperiment
## dim: 10 30
## metadata(0):
## assays(1): ''
## rownames(10): g1 g2 ... g9 g10
## rowData names(0):
## colnames(30): s1 s2 ... s29 s30
## colData names(0):
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):

expShow(sce)
## Error: unable to find an inherited method for function 'expShow'
for signature 'object = "SingleCellExperiment"'

### ### ###

## we can define a new subclass of SummarizedExperiment in the global
environment that works -- as
## long as it is not coerced to SingleCellExperiment

setClass("expA",
        contains="RangedSummarizedExperiment")

ea <- new("expA")
show(ea)
## An object of class "expA"
## Slot "rowRanges":
## GRanges object with 0 ranges and 0 metadata columns:
##    seqnames    ranges strand
##       <Rle> <IRanges>  <Rle>
##   -------
##   seqinfo: no sequences
##
## Slot "colData":
## DataFrame with 0 rows and 0 columns
##
## Slot "assays":
## NULL
##
## Slot "NAMES":
## NULL
##
## Slot "elementMetadata":
## DataFrame with 0 rows and 0 columns
##
## Slot "metadata":
## list()

expShow(ea)
## class: SummarizedExperiment
## dim: 0 0
## metadata(0):
## assays(0):
## rownames: NULL
## rowData names(0):
## colnames: NULL
## colData names(0):

scea <- as(ea, "SingleCellExperiment")
show(scea)
## class: SingleCellExperiment
## dim: 0 0
## metadata(0):
## assays(0):
## rownames: NULL
## rowData names(0):
## colnames: NULL
## colData names(0):
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):

expShow(scea)
## Error: unable to find an inherited method for function 'expShow'
for signature 'object = "SingleCellExperiment"'

### ### ### ### ###

## as shown below, SummarizedExperiment and ExpData "know" about their
inheritance relation but
## SingleCellExperiment does not...

getClass("SummarizedExperiment")
## Class "SummarizedExperiment" [package "SummarizedExperiment"]
##
## Slots:
##
## Name:            colData            assays             NAMES
elementMetadata          metadata
## Class:         DataFrame    Assays_OR_NULL character_OR_NULL
DataFrame              list
##
## Extends:
## Class "RectangularData", directly
## Class "Vector", directly
## Class "ExpData", directly
## Class "Annotated", by class "Vector", distance 2
## Class "vector_OR_Vector", by class "Vector", distance 2
##
## Known Subclasses:
## Class "RangedSummarizedExperiment", directly, with explicit coerce

getClass("ExpData")
## Extended class definition ( "ClassUnionRepresentation" )
## Virtual Class "ExpData" [in ".GlobalEnv"]
##
## No Slots, prototype of class "matrix"
##
## Known Subclasses:
## Class "matrix", directly
## Class "dgCMatrix", directly
## Class "ExpressionSet", directly
## Class "SummarizedExperiment", directly
## Class "mts", by class "matrix", distance 2
## Class "RangedSummarizedExperiment", by class
"SummarizedExperiment", distance 2, with explicit coerce

getClass("SingleCellExperiment")
## Class "SingleCellExperiment" [package "SingleCellExperiment"]
##
## Slots:
##
## Name:           int_elementMetadata                  int_colData
            int_metadata
## Class:                    DataFrame                    DataFrame
                    list
##
## Name:                     rowRanges                      colData
                  assays
## Class: GenomicRanges_OR_GRangesList                    DataFrame
          Assays_OR_NULL
##
## Name:                         NAMES              elementMetadata
                metadata
## Class:            character_OR_NULL                    DataFrame
                    list
##
## Extends:
## Class "RangedSummarizedExperiment", directly
## Class "SummarizedExperiment", by class
"RangedSummarizedExperiment", distance 2
## Class "RectangularData", by class "RangedSummarizedExperiment", distance 3
## Class "Vector", by class "RangedSummarizedExperiment", distance 3
## Class "Annotated", by class "RangedSummarizedExperiment", distance 4
## Class "vector_OR_Vector", by class "RangedSummarizedExperiment", distance 4

### ### ### ### ###

getClass("SummarizedExperiment")
## Class "SummarizedExperiment" [package "SummarizedExperiment"]
##
## Slots:
##
## Name:            colData            assays             NAMES
elementMetadata          metadata
## Class:         DataFrame    Assays_OR_NULL character_OR_NULL
DataFrame              list
##
## Extends:
## Class "RectangularData", directly
## Class "Vector", directly
## Class "ExpData", directly
## Class "Annotated", by class "Vector", distance 2
## Class "vector_OR_Vector", by class "Vector", distance 2
##
## Known Subclasses:
## Class "RangedSummarizedExperiment", directly, with explicit coerce
## Class "expA", by class "RangedSummarizedExperiment", distance 2,
with explicit coerce

getClass("expA")
## Class "expA" [in ".GlobalEnv"]
##
## Slots:
##
## Name:                     rowRanges                      colData
                  assays
## Class: GenomicRanges_OR_GRangesList                    DataFrame
          Assays_OR_NULL
##
## Name:                         NAMES              elementMetadata
                metadata
## Class:            character_OR_NULL                    DataFrame
                    list
##
## Extends:
## Class "RangedSummarizedExperiment", directly
## Class "SummarizedExperiment", by class
"RangedSummarizedExperiment", distance 2, with explicit coerce
## Class "RectangularData", by class "RangedSummarizedExperiment", distance 3
## Class "Vector", by class "RangedSummarizedExperiment", distance 3
## Class "ExpData", by class "RangedSummarizedExperiment", distance 3,
with explicit coerce
## Class "Annotated", by class "RangedSummarizedExperiment", distance 4
## Class "vector_OR_Vector", by class "RangedSummarizedExperiment", distance 4

getClass("SingleCellExperiment")
## Class "SingleCellExperiment" [package "SingleCellExperiment"]
##
## Slots:
##
## Name:           int_elementMetadata                  int_colData
            int_metadata
## Class:                    DataFrame                    DataFrame
                    list
##
## Name:                     rowRanges                      colData
                  assays
## Class: GenomicRanges_OR_GRangesList                    DataFrame
          Assays_OR_NULL
##
## Name:                         NAMES              elementMetadata
                metadata
## Class:            character_OR_NULL                    DataFrame
                    list
##
## Extends:
## Class "RangedSummarizedExperiment", directly
## Class "SummarizedExperiment", by class
"RangedSummarizedExperiment", distance 2
## Class "RectangularData", by class "RangedSummarizedExperiment", distance 3
## Class "Vector", by class "RangedSummarizedExperiment", distance 3
## Class "Annotated", by class "RangedSummarizedExperiment", distance 4
## Class "vector_OR_Vector", by class "RangedSummarizedExperiment", distance 4

_______________________________________________
Bioc-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/bioc-devel

_______________________________________________
Bioc-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/bioc-devel

--
Hervé Pagès

Bioconductor Core Team
hpages.on.git...@gmail.com

_______________________________________________
Bioc-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/bioc-devel

Reply via email to