Bioconductor is famous for hosting excellent bioinformatics software, but if you look under the hood, you’ll find it relies heavily on a specific R framework called the S4 system.
S4 is used because it has strict rules that ensure complex biological
data is formatted correctly, helping to prevent silent errors in your
analysis. However, for a biologist transitioning into R development, S4
comes with a notoriously steep learning curve. Compared to the
flexibility of standard R data.frames or
lists, S4 can feel confusing and overly rigid.
A major goal of Bioconductor is making sure different packages can
easily share data. Because of this, the official guidelines strongly
recommend reusing existing data structures (called “classes”), like
SummarizedExperiment or GRanges, rather than
inventing your own.
But there is a practical catch: it is incredibly hard to figure out how these classes are related. S4 classes often build on top of several other “parent” classes, creating a deeply tangled family tree. When you try to use standard R functions to figure out what a class inherits from, you are usually hit with a dense, confusing wall of text in your console. This leaves you manually reading through lines of text just to understand how the data is structured.
S4Cartographer is designed to solve this exact problem.
It bridges the gap between Bioconductor’s recommendation to reuse
existing classes and the reality of how hard they are to navigate. By
providing intuitive tools, this package helps you easily untangle and
understand S4 family trees, so you can focus on the biology instead of
the programming syntax.
Let’s start by exploring S4Vectors, the most basic
package in Bioconductor which defines several fundamental S4-classes
that are used across hundreds of Bioconductor packages:
Here we see how the key classes of Rle,
Pairs, Hits and Factor all
inherit from the same virtual Vector and
Annotated classes. A separate branch is formed by various
types of List, that all inherit from the same
List virtual class.
Let’s now look further and added classes defined in
IRanges and GRanges, which build directly on
top of each other:
Now the graph has grown substantially! IRanges defines a
large number of specialized sub-classes from the building blocks in
S4Vectors. We can also see how GenomicRanges
depends on IRanges first and then
S4Vectors.
Now let’s go wild and plot the entirety of “core” Bioconductor packages:
plotS4ClassGraph(c("S4Vectors", "IRanges", "GenomicRanges",
"SummarizedExperiment", "XVector", "Biostrings",
"GenomicFeatures", "GenomicAlignments"))This of course generates a massive graph too large to fit in a vignette, but try it out if you have access to a very large monitor!
DelayedArray sub-graphDelayedArray is a powerful framework for working with
larger-than-memory data. The package forms the core of a network of
dependent packages, each implementing their own specialized
sub-classes:
plotS4ClassGraph(c("DelayedArray", "HDF5Array", "TileDBArray", "VCFArray",
"ScaledMatrix", "ResidualMatrix", "BiocSingular"))
#> Warning in plotS4ClassGraph(c("DelayedArray", "HDF5Array", "TileDBArray", :
#> Duplicated S4Class definitions: Removing these according to input ordering of
#> packagesSummarizedExperiment sub-graphSummarizedExperiment is a another popular package for
organizing various types of expression matrices. It has a large number
of that builds upon this functionality:
plotS4ClassGraph(c("SummarizedExperiment", "SingleCellExperiment",
"SpatialExperiment", "clusterExperiment", "InteractionSet",
"MultiAssayExperiment", "GenomicFiles", "DESeq2"))If you want to inspect the underlying data for the graphs, you can make the function return just that:
plotS4ClassGraph(c("SummarizedExperiment", "SingleCellExperiment"), plot=FALSE)
#> $nodes
#> S4Class Package Virtual Union Superclasses
#> 1 RangedSummarizedExperiment SummarizedExperiment FALSE FALSE 5
#> 2 SimpleAssays SummarizedExperiment FALSE FALSE 3
#> 3 Assays_OR_NULL SummarizedExperiment TRUE TRUE 0
#> 4 AssaysInEnv SummarizedExperiment FALSE FALSE 3
#> 5 SummarizedExperiment SummarizedExperiment FALSE FALSE 4
#> 6 ShallowSimpleListAssays SummarizedExperiment FALSE FALSE 10
#> 7 Assays SummarizedExperiment TRUE FALSE 2
#> 8 ShallowData SummarizedExperiment FALSE FALSE 6
#> 9 SummarizedExperimentByColumn SingleCellExperiment FALSE FALSE 0
#> 10 DualSubset SingleCellExperiment FALSE FALSE 0
#> 11 reduced.dim.matrix SingleCellExperiment TRUE FALSE 7
#> 12 LinearEmbeddingMatrix SingleCellExperiment FALSE FALSE 1
#> 13 SingleCellExperiment SingleCellExperiment FALSE FALSE 6
#> 14 RectangularData <NA> NA NA NA
#> 15 Vector <NA> NA NA NA
#> 16 envRefClass <NA> NA NA NA
#> 17 matrix <NA> NA NA NA
#> 18 Annotated <NA> NA NA NA
#> Subclasses
#> 1 0
#> 2 0
#> 3 6
#> 4 0
#> 5 1
#> 6 0
#> 7 3
#> 8 1
#> 9 0
#> 10 0
#> 11 0
#> 12 0
#> 13 0
#> 14 NA
#> 15 NA
#> 16 NA
#> 17 NA
#> 18 NA
#>
#> $edges
#> Superclass Subclass
#> 1 RangedSummarizedExperiment SummarizedExperiment
#> 2 SimpleAssays Assays
#> 3 AssaysInEnv Assays
#> 4 SummarizedExperiment RectangularData
#> 5 SummarizedExperiment Vector
#> 6 ShallowSimpleListAssays ShallowData
#> 7 ShallowSimpleListAssays Assays
#> 8 Assays RectangularData
#> 9 Assays Assays_OR_NULL
#> 10 ShallowData envRefClass
#> 11 reduced.dim.matrix matrix
#> 12 LinearEmbeddingMatrix Annotated
#> 13 SingleCellExperiment RangedSummarizedExperimentOr use the internal helpers, see ?getS4Classes and
?getS4Inheritance.
sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] Biobase_2.73.1 BiocGenerics_0.59.10 generics_0.1.4
#> [4] S4Cartographer_0.99.3 BiocStyle_2.41.0
#>
#> loaded via a namespace (and not attached):
#> [1] splines_4.6.1 BiocIO_1.23.3
#> [3] bitops_1.0-9 tibble_3.3.1
#> [5] polyclip_1.10-7 XML_3.99-0.23
#> [7] lifecycle_1.0.5 edgeR_4.11.4
#> [9] doParallel_1.0.17 lattice_0.22-9
#> [11] MASS_7.3-66 MultiAssayExperiment_1.39.0
#> [13] GenomicFiles_1.49.0 backports_1.5.1
#> [15] magrittr_2.0.5 limma_3.69.2
#> [17] sass_0.4.10 rmarkdown_2.31
#> [19] jquerylib_0.1.4 yaml_2.3.12
#> [21] NMF_0.28 zinbwave_1.35.0
#> [23] DBI_1.3.0 buildtools_1.0.0
#> [25] RColorBrewer_1.1-3 ade4_1.7-24
#> [27] ResidualMatrix_1.23.0 abind_1.4-8
#> [29] GenomicRanges_1.65.1 purrr_1.2.2
#> [31] ggraph_2.2.2 RCurl_1.98-1.19
#> [33] VariantAnnotation_1.59.2 tweenr_2.0.3
#> [35] IRanges_2.47.2 S4Vectors_0.51.5
#> [37] ggrepel_0.9.8 irlba_2.3.7
#> [39] maketools_1.3.2 genefilter_1.95.0
#> [41] annotate_1.91.0 codetools_0.2-20
#> [43] DelayedArray_0.39.3 xml2_1.6.0
#> [45] ggforce_0.5.0 tidyselect_1.2.1
#> [47] RNeXML_2.4.11 locfdr_1.1-8
#> [49] UCSC.utils_1.9.0 farver_2.1.2
#> [51] ScaledMatrix_1.21.0 viridis_0.6.5
#> [53] matrixStats_1.5.0 stats4_4.6.1
#> [55] Seqinfo_1.3.0 GenomicAlignments_1.49.1
#> [57] jsonlite_2.0.0 tidygraph_1.3.1
#> [59] phylobase_0.8.12 survival_3.8-9
#> [61] iterators_1.0.14 TileDBArray_1.23.0
#> [63] foreach_1.5.2 tools_4.6.1
#> [65] progress_1.2.3 tiledb_0.33.0
#> [67] clusterExperiment_2.33.0 Rcpp_1.1.2
#> [69] glue_1.8.1 spdl_0.0.5
#> [71] gridExtra_2.3.1 SparseArray_1.13.2
#> [73] BiocBaseUtils_1.15.1 DESeq2_1.53.2
#> [75] xfun_0.60 MatrixGenerics_1.25.0
#> [77] ggthemes_5.2.0 GenomeInfoDb_1.49.1
#> [79] dplyr_1.2.1 HDF5Array_1.41.0
#> [81] withr_3.0.3 BiocManager_1.30.27
#> [83] fastmap_1.2.0 VCFArray_1.29.0
#> [85] rhdf5filters_1.25.0 digest_0.6.39
#> [87] rsvd_1.0.5 R6_2.6.1
#> [89] colorspace_2.1-3 RcppCCTZ_0.2.14
#> [91] RSQLite_3.53.3 cigarillo_1.3.1
#> [93] h5mread_1.5.0 tidyr_1.3.2
#> [95] RcppSpdlog_0.0.29 rtracklayer_1.73.0
#> [97] InteractionSet_1.41.0 prettyunits_1.2.0
#> [99] graphlayouts_1.2.4 httr_1.4.8
#> [101] S4Arrays_1.13.0 pkgconfig_2.0.3
#> [103] gtable_0.3.6 blob_1.3.0
#> [105] registry_0.5-1 S7_0.2.2
#> [107] SingleCellExperiment_1.35.2 XVector_0.53.0
#> [109] sys_3.4.3 htmltools_0.5.9
#> [111] scales_1.4.0 png_0.1-9
#> [113] SpatialExperiment_1.23.0 knitr_1.51
#> [115] reshape2_1.4.5 rncl_0.8.9
#> [117] rjson_0.2.23 uuid_1.2-2
#> [119] checkmate_2.3.4 nlme_3.1-170
#> [121] curl_7.1.0 cachem_1.1.0
#> [123] zoo_1.8-15 rhdf5_2.57.1
#> [125] stringr_1.6.0 parallel_4.6.1
#> [127] softImpute_1.4-3 AnnotationDbi_1.75.2
#> [129] nanotime_0.3.15 restfulr_0.0.17
#> [131] pillar_1.11.1 grid_4.6.1
#> [133] vctrs_0.7.3 nanoarrow_0.8.0-1
#> [135] BiocSingular_1.29.0 beachmat_2.29.0
#> [137] xtable_1.8-8 cluster_2.1.8.2
#> [139] evaluate_1.0.5 GenomicFeatures_1.65.0
#> [141] magick_2.9.1 locfit_1.5-9.12
#> [143] cli_3.6.6 compiler_4.6.1
#> [145] Rsamtools_2.29.0 rlang_1.3.0
#> [147] crayon_1.5.3 rngtools_1.5.2
#> [149] labeling_0.4.3 plyr_1.8.9
#> [151] stringi_1.8.7 viridisLite_0.4.3
#> [153] gridBase_0.4-7 BiocParallel_1.47.0
#> [155] Biostrings_2.81.5 Matrix_1.7-5
#> [157] BSgenome_1.81.0 hms_1.1.4
#> [159] bit64_4.8.2 ggplot2_4.0.3
#> [161] Rhdf5lib_2.1.0 KEGGREST_1.53.5
#> [163] statmod_1.5.2 SummarizedExperiment_1.43.0
#> [165] kernlab_0.9-33 igraph_2.3.3
#> [167] memoise_2.0.1 bslib_0.11.0
#> [169] bit_4.6.0 ape_5.8-1