diff --git a/.Rbuildignore b/.Rbuildignore index bb40367f..36d6f51a 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -1,3 +1,5 @@ +^renv$ +^renv\.lock$ ^.*\.Rproj$ ^\.Rproj\.user$ ^\.git$ @@ -20,3 +22,4 @@ ^inst$ ^tests$ ^LICENSE\.md$ +^vignettes/articles$ diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 14e5e3ac..f8b2682e 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -25,6 +25,7 @@ jobs: - uses: r-lib/actions/setup-r@v2 with: use-public-rspm: true + extra-repositories: https://bnprks.r-universe.dev - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: local::. diff --git a/DESCRIPTION b/DESCRIPTION index fbd53e00..d2d40a65 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,17 +1,23 @@ Package: SeuratObject Type: Package Title: Data Structures for Single Cell Data -Version: 4.1.4 +Version: 5.0.0 Authors@R: c( person(given = 'Rahul', family = 'Satija', email = 'rsatija@nygenome.org', role = 'aut', comment = c(ORCID = '0000-0001-9448-8833')), - person(given = 'Andrew', family = 'Butler', email = 'abutler@nygenome.org', role = 'aut', comment = c(ORCID = '0000-0003-3608-0463')), person(given = 'Paul', family = 'Hoffman', email = 'seurat@nygenome.org', role = c('aut', 'cre'), comment = c(ORCID = '0000-0002-7693-8957')), + person(given = "Yuhan", family = "Hao", email = 'yhao@nygenome.org', role = 'aut', comment = c(ORCID = '0000-0002-1810-0822')), + person(given = "Austin", family = "Hartman", email = 'ahartman@nygenome.org', role = 'aut', comment = c(ORCID = '0000-0001-7278-1852')), + person(given = "Gesmira", family = "Molla", email = 'gmolla@nygenome.org', role = 'aut', comment = c(ORCID = '0000-0002-8628-5056')), + person(given = 'Andrew', family = 'Butler', email = 'abutler@nygenome.org', role = 'aut', comment = c(ORCID = '0000-0003-3608-0463')), person(given = 'Tim', family = 'Stuart', email = 'tstuart@nygenome.org', role = 'aut', comment = c(ORCID = '0000-0002-3044-0897')), + person(given = "Madeline", family = "Kowalski", email = "mkowalski@nygenome.org", role = "ctb", comment = c(ORCID = "0000-0002-5655-7620")), + person(given = "Saket", family = "Choudhary", email = "schoudhary@nygenome.org", role = "ctb", comment = c(ORCID = "0000-0001-5202-7633")), + person(given = "Skylar", family = "Li", email = "sli@nygenome.org", role = "ctb"), + person(given = "Longda", family = "Jiang", email = "ljiang@nygenome.org", role = "ctb", comment = c(ORCID = "0000-0003-4964-6497")), person(given = 'Jeff', family = 'Farrell', email = 'jfarrell@g.harvard.edu', role = 'ctb'), person(given = 'Shiwei', family = 'Zheng', email = 'szheng@nygenome.org', role = 'ctb', comment = c(ORCID = '0000-0001-6682-6743')), person(given = 'Christoph', family = 'Hafemeister', email = 'chafemeister@nygenome.org', role = 'ctb', comment = c(ORCID = '0000-0001-6365-8254')), - person(given = 'Patrick', family = 'Roelli', email = 'proelli@nygenome.org', role = 'ctb'), - person(given = "Yuhan", family = "Hao", email = 'yhao@nygenome.org', role = 'ctb', comment = c(ORCID = '0000-0002-1810-0822')) + person(given = 'Patrick', family = 'Roelli', email = 'proelli@nygenome.org', role = 'ctb') ) Description: Defines S4 classes for single-cell genomic data and associated information, such as dimensionality reduction embeddings, nearest-neighbor @@ -19,16 +25,19 @@ Description: Defines S4 classes for single-cell genomic data and associated R-native hooks to ensure the Seurat object is familiar to other R users. See Satija R, Farrell J, Gennert D, et al (2015) , Macosko E, Basu A, Satija R, et al (2015) , - and Stuart T, Butler A, et al (2019) for + and Stuart T, Butler A, et al (2019) , + Hao Y, Hao S, et al (2021) and + Hao Y, et al (2023) for more details. URL: https://mojaveazure.github.io/seurat-object/, https://github.com/mojaveazure/seurat-object -BugReports: - https://github.com/mojaveazure/seurat-object/issues +BugReports: https://github.com/mojaveazure/seurat-object/issues License: MIT + file LICENSE Encoding: UTF-8 LazyData: true RoxygenNote: 7.2.3 +Additional_repositories: + https://bnprks.r-universe.dev Depends: R (>= 4.0.0), sp (>= 1.5.0) @@ -44,32 +53,46 @@ Imports: rlang (>= 0.4.7), stats, tools, - utils + utils, + spam, + lifecycle, + generics Suggests: + DelayedArray, + fs (>= 1.5.2), ggplot2, - sf, - testthat + HDF5Array, + rmarkdown, + testthat, + BPCells, + sf (>= 1.0.0) Collate: 'RcppExports.R' 'zzz.R' 'generics.R' + 'keymixin.R' 'graph.R' + 'default.R' 'assay.R' + 'logmap.R' + 'layers.R' + 'assay5.R' 'centroids.R' 'command.R' + 'compliance.R' 'data.R' - 'default.R' 'jackstraw.R' 'dimreduc.R' 'segmentation.R' - 'keymixin.R' 'molecules.R' 'spatial.R' 'fov.R' - 'logmap.R' 'neighbor.R' 'seurat.R' + 'sparse.R' 'utils.R' LinkingTo: Rcpp, RcppEigen +Enhances: + Seurat Config/Needs/website: pkgdown diff --git a/NAMESPACE b/NAMESPACE index 6cc01e68..74b49243 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,57 +1,134 @@ # Generated by roxygen2: do not edit by hand +S3method("$",Assay) +S3method("$",Assay5) S3method("$",FOV) S3method("$",JackStrawData) S3method("$",Seurat) S3method("$",SeuratCommand) +S3method("$",StdAssay) +S3method("$<-",Assay) +S3method("$<-",Assay5) S3method("$<-",Seurat) +S3method("$<-",StdAssay) S3method("DefaultAssay<-",Assay) +S3method("DefaultAssay<-",Assay5) S3method("DefaultAssay<-",DimReduc) S3method("DefaultAssay<-",Graph) S3method("DefaultAssay<-",Seurat) S3method("DefaultAssay<-",SpatialImage) +S3method("DefaultAssay<-",StdAssay) S3method("DefaultBoundary<-",FOV) S3method("DefaultFOV<-",Seurat) +S3method("DefaultLayer<-",Assay5) +S3method("DefaultLayer<-",StdAssay) S3method("Idents<-",Seurat) S3method("Index<-",Neighbor) S3method("JS<-",DimReduc) S3method("JS<-",JackStrawData) S3method("Key<-",Assay) +S3method("Key<-",Assay5) S3method("Key<-",DimReduc) S3method("Key<-",KeyMixin) S3method("Key<-",SpatialImage) +S3method("LayerData<-",Assay) +S3method("LayerData<-",Assay5) +S3method("LayerData<-",Seurat) +S3method("LayerData<-",StdAssay) S3method("Loadings<-",DimReduc) S3method("Misc<-",Assay) +S3method("Misc<-",Assay5) S3method("Misc<-",DimReduc) S3method("Misc<-",Seurat) +S3method("Misc<-",StdAssay) S3method("Project<-",Seurat) S3method("Tool<-",Seurat) S3method("VariableFeatures<-",Assay) +S3method("VariableFeatures<-",Assay5) S3method("VariableFeatures<-",Seurat) +S3method("VariableFeatures<-",StdAssay) S3method("[",Assay) +S3method("[",Assay5) S3method("[",DimReduc) S3method("[",FOV) S3method("[",Seurat) S3method("[",SeuratCommand) S3method("[",SpatialImage) +S3method("[",StdAssay) S3method("[[",Assay) +S3method("[[",Assay5) S3method("[[",DimReduc) S3method("[[",FOV) S3method("[[",Seurat) +S3method("[[",StdAssay) +S3method("dimnames<-",Assay) +S3method("dimnames<-",Assay5) +S3method("dimnames<-",Seurat) +S3method("dimnames<-",StdAssay) S3method("levels<-",Seurat) +S3method(.AssayClass,Assay5T) +S3method(.AssayClass,StdAssay) +S3method(.AssayClass,default) +S3method(.CalcN,Assay) +S3method(.CalcN,StdAssay) +S3method(.CalcN,default) +S3method(.ClassPkg,DelayedArray) +S3method(.ClassPkg,R6) +S3method(.ClassPkg,R6ClassGenerator) +S3method(.ClassPkg,default) +S3method(.CreateStdAssay,Matrix) +S3method(.CreateStdAssay,default) +S3method(.CreateStdAssay,list) +S3method(.CreateStdAssay,matrix) +S3method(.DiskLoad,"10xMatrixH5") +S3method(.DiskLoad,AnnDataMatrixH5) +S3method(.DiskLoad,DelayedMatrix) +S3method(.DiskLoad,H5ADMatrix) +S3method(.DiskLoad,HDF5Matrix) +S3method(.DiskLoad,IterableMatrix) +S3method(.DiskLoad,MatrixDir) +S3method(.DiskLoad,MatrixH5) +S3method(.DiskLoad,TileDBMatrix) +S3method(.DiskLoad,default) +S3method(.DollarNames,Assay) +S3method(.DollarNames,Assay5) S3method(.DollarNames,FOV) S3method(.DollarNames,JackStrawData) S3method(.DollarNames,Seurat) S3method(.DollarNames,SeuratCommand) +S3method(.DollarNames,StdAssay) +S3method(.FilePath,DelayedMatrix) +S3method(.FilePath,IterableMatrix) +S3method(.FilePath,default) +S3method(.MARGIN,Assay5T) +S3method(.MARGIN,CsparseMatrix) +S3method(.MARGIN,RsparseMatrix) +S3method(.MARGIN,default) +S3method(.MARGIN,spam) +S3method(.SelectFeatures,StdAssay) +S3method(.SelectFeatures,list) +S3method(.SparseSlots,CsparseMatrix) +S3method(.SparseSlots,RsparseMatrix) +S3method(.SparseSlots,spam) S3method(AddMetaData,Assay) +S3method(AddMetaData,Assay5) S3method(AddMetaData,Seurat) +S3method(AddMetaData,StdAssay) +S3method(Assays,Seurat) S3method(Boundaries,FOV) +S3method(CastAssay,Assay5) +S3method(CastAssay,Seurat) +S3method(CastAssay,StdAssay) +S3method(Cells,Assay5) S3method(Cells,Centroids) S3method(Cells,DimReduc) S3method(Cells,FOV) +S3method(Cells,Graph) S3method(Cells,Neighbor) S3method(Cells,Segmentation) +S3method(Cells,Seurat) S3method(Cells,SpatialImage) +S3method(Cells,StdAssay) S3method(Cells,default) S3method(CheckMatrix,dMatrix) S3method(CheckMatrix,default) @@ -69,30 +146,46 @@ S3method(CreateMolecules,data.frame) S3method(CreateSegmentation,Segmentation) S3method(CreateSegmentation,data.frame) S3method(CreateSeuratObject,Assay) +S3method(CreateSeuratObject,Assay5) +S3method(CreateSeuratObject,StdAssay) S3method(CreateSeuratObject,default) S3method(Crop,Centroids) S3method(Crop,FOV) S3method(Crop,Molecules) S3method(Crop,Segmentation) S3method(DefaultAssay,Assay) +S3method(DefaultAssay,Assay5) S3method(DefaultAssay,DimReduc) S3method(DefaultAssay,Graph) S3method(DefaultAssay,Seurat) S3method(DefaultAssay,SeuratCommand) S3method(DefaultAssay,SpatialImage) +S3method(DefaultAssay,StdAssay) S3method(DefaultBoundary,FOV) S3method(DefaultFOV,Seurat) +S3method(DefaultLayer,Assay) +S3method(DefaultLayer,Assay5) +S3method(DefaultLayer,StdAssay) S3method(Distances,Neighbor) S3method(Embeddings,DimReduc) S3method(Embeddings,Seurat) +S3method(Features,Assay) +S3method(Features,Assay5) +S3method(Features,DimReduc) S3method(Features,FOV) S3method(Features,Molecules) +S3method(Features,Seurat) +S3method(Features,StdAssay) +S3method(FetchData,Assay) +S3method(FetchData,Assay5) S3method(FetchData,DimReduc) S3method(FetchData,FOV) S3method(FetchData,Molecules) S3method(FetchData,Seurat) +S3method(FetchData,StdAssay) S3method(GetAssayData,Assay) S3method(GetAssayData,Seurat) +S3method(GetAssayData,StdAssay) S3method(GetImage,Seurat) S3method(GetImage,SpatialImage) S3method(GetTissueCoordinates,Centroids) @@ -102,16 +195,24 @@ S3method(GetTissueCoordinates,Segmentation) S3method(GetTissueCoordinates,Seurat) S3method(GetTissueCoordinates,SpatialImage) S3method(HVFInfo,Assay) +S3method(HVFInfo,Assay5) S3method(HVFInfo,Seurat) +S3method(HVFInfo,StdAssay) S3method(Idents,Seurat) S3method(Index,Neighbor) S3method(Indices,Neighbor) S3method(IsGlobal,DimReduc) S3method(IsGlobal,SpatialImage) S3method(IsGlobal,default) +S3method(IsMatrixEmpty,default) S3method(JS,DimReduc) S3method(JS,JackStrawData) +S3method(JoinLayers,Assay5) +S3method(JoinLayers,Seurat) +S3method(JoinLayers,StdAssay) +S3method(Key,"NULL") S3method(Key,Assay) +S3method(Key,Assay5) S3method(Key,DimReduc) S3method(Key,KeyMixin) S3method(Key,Seurat) @@ -119,19 +220,30 @@ S3method(Key,SpatialImage) S3method(Key,character) S3method(Keys,FOV) S3method(Keys,Seurat) +S3method(LayerData,Assay) +S3method(LayerData,Assay5) +S3method(LayerData,Seurat) +S3method(LayerData,StdAssay) +S3method(Layers,Assay) +S3method(Layers,Assay5) +S3method(Layers,Seurat) +S3method(Layers,StdAssay) S3method(Loadings,DimReduc) S3method(Loadings,Seurat) S3method(MatchCells,"NULL") S3method(MatchCells,character) S3method(MatchCells,numeric) S3method(Misc,Assay) +S3method(Misc,Assay5) S3method(Misc,DimReduc) S3method(Misc,Seurat) +S3method(Misc,StdAssay) S3method(Molecules,FOV) S3method(Project,Seurat) S3method(Radius,Centroids) S3method(Radius,SpatialImage) S3method(RenameCells,Assay) +S3method(RenameCells,Assay5) S3method(RenameCells,Centroids) S3method(RenameCells,DimReduc) S3method(RenameCells,FOV) @@ -139,6 +251,7 @@ S3method(RenameCells,Neighbor) S3method(RenameCells,Segmentation) S3method(RenameCells,Seurat) S3method(RenameCells,SpatialImage) +S3method(RenameCells,StdAssay) S3method(RenameIdents,Seurat) S3method(ReorderIdent,Seurat) S3method(S4ToList,default) @@ -147,6 +260,7 @@ S3method(SVFInfo,Assay) S3method(SVFInfo,Seurat) S3method(SetAssayData,Assay) S3method(SetAssayData,Seurat) +S3method(SetAssayData,StdAssay) S3method(SetIdent,Seurat) S3method(Simplify,Molecules) S3method(Simplify,Spatial) @@ -155,13 +269,21 @@ S3method(SpatiallyVariableFeatures,Seurat) S3method(StashIdent,Seurat) S3method(Stdev,DimReduc) S3method(Stdev,Seurat) +S3method(StitchMatrix,IterableMatrix) +S3method(StitchMatrix,default) +S3method(StitchMatrix,dgCMatrix) +S3method(StitchMatrix,matrix) S3method(Theta,Centroids) S3method(Tool,Seurat) S3method(VariableFeatures,Assay) +S3method(VariableFeatures,Assay5) S3method(VariableFeatures,Seurat) +S3method(VariableFeatures,StdAssay) S3method(Version,Seurat) S3method(WhichCells,Assay) +S3method(WhichCells,Assay5) S3method(WhichCells,Seurat) +S3method(WhichCells,StdAssay) S3method(aggregate,FOV) S3method(aggregate,Molecules) S3method(as.Centroids,Segmentation) @@ -172,24 +294,34 @@ S3method(as.Neighbor,Graph) S3method(as.Segmentation,Centroids) S3method(as.list,SeuratCommand) S3method(as.logical,JackStrawData) +S3method(as.matrix,LogMap) S3method(as.sparse,Matrix) S3method(as.sparse,data.frame) S3method(as.sparse,matrix) S3method(as.sparse,ngCMatrix) S3method(dim,Assay) +S3method(dim,Assay5) S3method(dim,DimReduc) S3method(dim,FOV) S3method(dim,Neighbor) S3method(dim,Seurat) S3method(dim,SpatialImage) +S3method(dim,StdAssay) S3method(dimnames,Assay) +S3method(dimnames,Assay5) S3method(dimnames,DimReduc) S3method(dimnames,Seurat) +S3method(dimnames,StdAssay) +S3method(droplevels,LogMap) S3method(droplevels,Seurat) S3method(head,Assay) +S3method(head,Assay5) S3method(head,Seurat) +S3method(head,StdAssay) +S3method(intersect,LogMap) S3method(is.finite,Centroids) S3method(is.infinite,Centroids) +S3method(labels,LogMap) S3method(length,Centroids) S3method(length,DimReduc) S3method(length,FOV) @@ -197,13 +329,20 @@ S3method(lengths,Centroids) S3method(lengths,Segmentation) S3method(levels,Seurat) S3method(merge,Assay) +S3method(merge,Assay5) S3method(merge,DimReduc) S3method(merge,Seurat) +S3method(merge,StdAssay) S3method(names,DimReduc) S3method(names,FOV) S3method(names,Seurat) S3method(print,DimReduc) +S3method(split,Assay) +S3method(split,Assay5) +S3method(split,Seurat) +S3method(split,StdAssay) S3method(subset,Assay) +S3method(subset,Assay5) S3method(subset,Centroids) S3method(subset,DimReduc) S3method(subset,FOV) @@ -211,8 +350,12 @@ S3method(subset,Molecules) S3method(subset,Segmentation) S3method(subset,Seurat) S3method(subset,SpatialImage) +S3method(subset,StdAssay) S3method(tail,Assay) +S3method(tail,Assay5) S3method(tail,Seurat) +S3method(tail,StdAssay) +S3method(upgrade,seurat) export("%!NA%") export("%!na%") export("%NA%") @@ -222,27 +365,56 @@ export("%||%") export("DefaultAssay<-") export("DefaultBoundary<-") export("DefaultFOV<-") +export("DefaultLayer<-") export("Idents<-") export("Index<-") export("JS<-") export("Key<-") +export("LayerData<-") export("Loadings<-") export("Misc<-") export("Project<-") export("Tool<-") export("VariableFeatures<-") +export(.AssayClass) +export(.BPMatrixMode) +export(.CalcN) +export(.CheckFmargin) +export(.ClassPkg) +export(.Collections) export(.Contains) +export(.CreateStdAssay) export(.DefaultFOV) +export(.Deprecate) +export(.DiskLoad) +export(.FileMove) +export(.FilePath) +export(.FilterObjects) +export(.FindObject) +export(.GetMethod) +export(.IsFutureSeurat) +export(.KeyPattern) +export(.MARGIN) +export(.PropagateList) +export(.RandomKey) +export(.SelectFeatures) +export(.SparseSlots) +export(.Subobjects) export(AddMetaData) export(Assays) export(AttachDeps) export(Boundaries) +export(CastAssay) export(Cells) export(CellsByIdentities) export(CheckDots) +export(CheckFeaturesNames) export(CheckGC) +export(CheckLayersName) export(CheckMatrix) +export(ClassKey) export(Command) +export(CreateAssay5Object) export(CreateAssayObject) export(CreateCentroids) export(CreateDimReducObject) @@ -255,10 +427,12 @@ export(DefaultAssay) export(DefaultBoundary) export(DefaultDimReduc) export(DefaultFOV) +export(DefaultLayer) export(Degrees) export(Distances) export(Embeddings) export(EmptyDF) +export(ExtractField) export(Features) export(FetchData) export(FilterObjects) @@ -275,10 +449,15 @@ export(IsGlobal) export(IsMatrixEmpty) export(IsNamedList) export(IsS4List) +export(IsSparse) export(JS) +export(JoinLayers) export(Key) export(Keys) +export(LayerData) +export(Layers) export(ListToS4) +export(LoadSeuratRds) export(Loadings) export(LogMap) export(LogSeuratCommand) @@ -294,6 +473,7 @@ export(Radians) export(Radius) export(RandomName) export(Reductions) +export(RegisterSparseMatrix) export(RenameAssays) export(RenameCells) export(RenameIdents) @@ -301,12 +481,15 @@ export(ReorderIdent) export(RowMergeSparseMatrices) export(S4ToList) export(SVFInfo) +export(SaveSeuratRds) export(SetAssayData) export(SetIdent) export(Simplify) +export(SparseEmptyMatrix) export(SpatiallyVariableFeatures) export(StashIdent) export(Stdev) +export(StitchMatrix) export(Theta) export(Tool) export(UpdateSeuratObject) @@ -320,14 +503,12 @@ export(as.Neighbor) export(as.Segmentation) export(as.Seurat) export(as.sparse) -export(colMeans) -export(colSums) export(handlers) +export(intersect) export(plan) -export(rowMeans) -export(rowSums) export(with_progress) exportClasses(Assay) +exportClasses(Assay5) exportClasses(DimReduc) exportClasses(FOV) exportClasses(Graph) @@ -338,6 +519,7 @@ exportClasses(Neighbor) exportClasses(Seurat) exportClasses(SeuratCommand) exportClasses(SpatialImage) +exportClasses(StdAssay) exportMethods("[[") exportMethods("[[<-") exportMethods(Overlay) @@ -346,34 +528,53 @@ exportMethods(colSums) exportMethods(rowMeans) exportMethods(rowSums) exportMethods(show) +importClassesFrom(Matrix,CsparseMatrix) +importClassesFrom(Matrix,Matrix) +importClassesFrom(Matrix,RsparseMatrix) importClassesFrom(Matrix,dgCMatrix) importClassesFrom(sp,CRS) importClassesFrom(sp,Spatial) importClassesFrom(sp,SpatialPoints) importClassesFrom(sp,SpatialPolygons) +importClassesFrom(spam,spam) importFrom(Matrix,Matrix) importFrom(Matrix,colMeans) importFrom(Matrix,colSums) +importFrom(Matrix,nnzero) importFrom(Matrix,rowMeans) importFrom(Matrix,rowSums) +importFrom(Matrix,t) importFrom(Rcpp,evalCpp) importFrom(future,plan) importFrom(future.apply,future_lapply) importFrom(future.apply,future_mapply) +importFrom(generics,intersect) importFrom(grDevices,as.raster) importFrom(grid,nullGrob) +importFrom(lifecycle,deprecate_soft) +importFrom(lifecycle,deprecate_stop) +importFrom(lifecycle,deprecate_warn) +importFrom(lifecycle,deprecated) +importFrom(lifecycle,is_present) importFrom(methods,"slot<-") importFrom(methods,.hasSlot) importFrom(methods,as) importFrom(methods,callNextMethod) importFrom(methods,getClass) importFrom(methods,getClassDef) +importFrom(methods,getClasses) importFrom(methods,initialize) importFrom(methods,is) +importFrom(methods,isClass) +importFrom(methods,isClassUnion) +importFrom(methods,isGeneric) +importFrom(methods,isXS3Class) importFrom(methods,new) +importFrom(methods,selectMethod) importFrom(methods,setAs) importFrom(methods,setClass) importFrom(methods,setClassUnion) +importFrom(methods,setGeneric) importFrom(methods,setMethod) importFrom(methods,setOldClass) importFrom(methods,setValidity) @@ -385,12 +586,30 @@ importFrom(progressr,handlers) importFrom(progressr,progressor) importFrom(progressr,with_progress) importFrom(rlang,"%||%") +importFrom(rlang,abort) +importFrom(rlang,arg_match) +importFrom(rlang,arg_match0) +importFrom(rlang,caller_env) +importFrom(rlang,check_installed) importFrom(rlang,enquo) importFrom(rlang,eval_tidy) +importFrom(rlang,have_name) +importFrom(rlang,inform) +importFrom(rlang,is_bare_character) +importFrom(rlang,is_bare_integerish) importFrom(rlang,is_bare_list) +importFrom(rlang,is_bare_numeric) +importFrom(rlang,is_missing) importFrom(rlang,is_na) +importFrom(rlang,is_named) importFrom(rlang,is_null) importFrom(rlang,is_quosure) +importFrom(rlang,is_scalar_character) +importFrom(rlang,missing_arg) +importFrom(rlang,ns_env_name) +importFrom(rlang,quo_get_env) +importFrom(rlang,quo_get_expr) +importFrom(rlang,warn) importFrom(sp,Polygon) importFrom(sp,Polygons) importFrom(sp,SpatialPoints) @@ -398,15 +617,22 @@ importFrom(sp,SpatialPolygons) importFrom(sp,bbox) importFrom(sp,coordinates) importFrom(sp,over) +importFrom(spam,t) importFrom(stats,aggregate) +importFrom(stats,median) importFrom(stats,na.omit) +importFrom(stats,setNames) importFrom(tools,file_path_sans_ext) importFrom(utils,.DollarNames) +importFrom(utils,adist) importFrom(utils,argsAnywhere) +importFrom(utils,getS3method) importFrom(utils,head) importFrom(utils,isS3method) importFrom(utils,isS3stdGeneric) importFrom(utils,methods) +importFrom(utils,modifyList) importFrom(utils,packageVersion) importFrom(utils,tail) +importFrom(utils,upgrade) useDynLib(SeuratObject) diff --git a/NEWS.md b/NEWS.md index c6c9b6c0..64e728bf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,30 @@ +# SeuratObject 5.0.0 +## Added +- New `Assay5` class with support for layers; layers provide support for: + - arbitrary expression matrix names and number + - arbitrary expression matrix shape + - disk-backed expression matrices +- New `$` method for `Assay` and `Assay5` objects to pull expression matrices, replacing informal usage of `@` +- New `LayerData()` and `LayerData()<-` functions to replace `GetAssayData()` and `SetAssayData()`, respectively +- Support for renaming cells and features with `dimnames()<-` (changing feature names does not apply to v3 `Assay` objects) +- New `SaveSeuratRds()` and `LoadSeuratRds()` to save and load `Seurat` objects with disk-backed layers +- New `droplevels.LogMap()` to drop unused entries from a `LogMap` +- New ability to split (`split()`) and rejoin layers (`JoinLayers()`) within `Assay` and `Assay5` objects based on grouping factor + +## Changes +- `slot` argument deprecated in all contexts; where applicable, replaced with `layer` argument +- `[` for `Assay` and `Assay5` objects take a layer name to pull an expression matrix + - option `Seurat.object.assay.brackets` allows restoring v3/v4 behavior of subsetting the main expression matrix (eg. `data`) +- Stricter object validation routines at all levels +- `PackageCheck()` deprecated in favor of `rlang::check_installed()` +- `AttachDeps()` deprecated in favor of using the `Depends` field of `DESCRIPTION` +- Subobjects within a `Seurat` object may have subsets of cells present at the object level +- Begun replacement of `stop()` and `warning()` with `rlang::abort()` and `rlang::warn()` for easier debugging +- Expanded validation and utility of `KeyMixin` objects + +## Removed +- Unused object constructors (eg. `Assay()`, `Seurat()`) + # SeuratObject 4.1.4 ## Changes - Fixes for `CellsByIdentities` (#80) diff --git a/R/assay.R b/R/assay.R index 2a30172e..4152a052 100644 --- a/R/assay.R +++ b/R/assay.R @@ -1,7 +1,8 @@ #' @include zzz.R #' @include generics.R +#' @include default.R #' @include graph.R -#' @importFrom methods new slot slot<- +#' @include keymixin.R #' NULL @@ -21,29 +22,32 @@ setClassUnion(name = 'AnyMatrix', members = c("matrix", "dgCMatrix")) #' @slot counts Unnormalized data such as raw counts or TPMs #' @slot data Normalized expression data #' @slot scale.data Scaled expression data -#' @slot key Key for the Assay +# @slot key Key for the Assay #' @slot assay.orig Original assay that this assay is based off of. Used to #' track assay provenance #' @slot var.features Vector of features exhibiting high variance across #' single cells #' @slot meta.features Feature-level metadata -#' @slot misc Utility slot for storing additional data associated with the assay +# @slot misc Utility slot for storing additional data associated with the assay +#' @template slot-misc +#' @template slot-key #' #' @name Assay-class #' @rdname Assay-class #' @exportClass Assay #' -#' @concept assay +#' @family assay #' -#' @seealso \code{\link{Assay-methods}} +#' @aliases Assay #' -Assay <- setClass( +setClass( Class = 'Assay', + contains = 'KeyMixin', slots = c( counts = 'AnyMatrix', data = 'AnyMatrix', scale.data = 'matrix', - key = 'character', + # key = 'character', assay.orig = 'OptionalCharacter', var.features = 'vector', meta.features = 'data.frame', @@ -67,10 +71,12 @@ Assay <- setClass( #' @param data Prenormalized data; if provided, do not pass \code{counts} #' @param min.cells Include features detected in at least this many cells. Will #' subset the counts matrix as well. To reintroduce excluded features, create a -#' new object with a lower cutoff. +#' new object with a lower cutoff #' @param min.features Include cells where at least this many features are -#' detected. -#' @param check.matrix Check counts matrix for NA, NaN, Inf, and non-integer values +#' detected +#' @param key Optional key to initialize assay with +#' @param check.matrix Check counts matrix for NA, NaN, Inf, and +#' non-integer values #' @param ... Arguments passed to \code{\link{as.sparse}} #' #' @return A \code{\link{Assay}} object @@ -80,7 +86,7 @@ Assay <- setClass( #' #' @export #' -#' @concept assay +#' @family assay #' #' @examples #' \dontrun{ @@ -97,39 +103,36 @@ CreateAssayObject <- function( data, min.cells = 0, min.features = 0, + key = NULL, check.matrix = FALSE, ... ) { if (missing(x = counts) && missing(x = data)) { - stop("Must provide either 'counts' or 'data'") + abort(message = "Must provide either 'counts' or 'data'") } else if (!missing(x = counts) && !missing(x = data)) { - stop("Either 'counts' or 'data' must be missing; both cannot be provided") + abort(message = "Either 'counts' or 'data' must be missing; both cannot be provided") } else if (!missing(x = counts)) { # check that dimnames of input counts are unique if (anyDuplicated(x = rownames(x = counts))) { - warning( - "Non-unique features (rownames) present in the input matrix, making unique", - call. = FALSE, - immediate. = TRUE + warn( + message = "Non-unique features (rownames) present in the input matrix, making unique" ) rownames(x = counts) <- make.unique(names = rownames(x = counts)) } if (anyDuplicated(x = colnames(x = counts))) { - warning( - "Non-unique cell names (colnames) present in the input matrix, making unique", - call. = FALSE, - immediate. = TRUE + warn( + message = "Non-unique cell names (colnames) present in the input matrix, making unique" ) colnames(x = counts) <- make.unique(names = colnames(x = counts)) } if (is.null(x = colnames(x = counts))) { - stop("No cell names (colnames) names present in the input matrix") + abort(message = "No cell names (colnames) names present in the input matrix") } if (any(rownames(x = counts) == '')) { - stop("Feature names of counts matrix cannot be empty", call. = FALSE) + abort(message = "Feature names of counts matrix cannot be empty") } if (nrow(x = counts) > 0 && is.null(x = rownames(x = counts))) { - stop("No feature names (rownames) names present in the input matrix") + abort(message = "No feature names (rownames) names present in the input matrix") } if (!inherits(x = counts, what = 'dgCMatrix')) { if (inherits(x = counts, what = "data.frame")) { @@ -155,35 +158,29 @@ CreateAssayObject <- function( } else if (!missing(x = data)) { # check that dimnames of input data are unique if (anyDuplicated(x = rownames(x = data))) { - warning( - "Non-unique features (rownames) present in the input matrix, making unique", - call. = FALSE, - immediate. = TRUE + warn( + message = "Non-unique features (rownames) present in the input matrix, making unique" ) rownames(x = data) <- make.unique(names = rownames(x = data)) } if (anyDuplicated(x = colnames(x = data))) { - warning( - "Non-unique cell names (colnames) present in the input matrix, making unique", - call. = FALSE, - immediate. = TRUE + warn( + message = "Non-unique cell names (colnames) present in the input matrix, making unique" ) colnames(x = data) <- make.unique(names = colnames(x = data)) } if (is.null(x = colnames(x = data))) { - stop("No cell names (colnames) names present in the input matrix") + abort(message = "No cell names (colnames) names present in the input matrix") } if (any(rownames(x = data) == '')) { - stop("Feature names of data matrix cannot be empty", call. = FALSE) + abort(message = "Feature names of data matrix cannot be empty", call. = FALSE) } if (nrow(x = data) > 0 && is.null(x = rownames(x = data))) { - stop("No feature names (rownames) names present in the input matrix") + abort(message = "No feature names (rownames) names present in the input matrix") } if (min.cells != 0 | min.features != 0) { - warning( - "No filtering performed if passing to data rather than counts", - call. = FALSE, - immediate. = TRUE + warn( + message = "No filtering performed if passing to data rather than counts" ) } counts <- new(Class = 'matrix') @@ -201,51 +198,27 @@ CreateAssayObject <- function( if (!is.vector(x = colnames(x = data))) { colnames(x = data) <- as.vector(x = colnames(x = data)) } - if (any(grepl(pattern = '_', x = rownames(x = counts))) || any(grepl(pattern = '_', x = rownames(x = data)))) { - warning( - "Feature names cannot have underscores ('_'), replacing with dashes ('-')", - call. = FALSE, - immediate. = TRUE - ) - rownames(x = counts) <- gsub( - pattern = '_', - replacement = '-', - x = rownames(x = counts) - ) - rownames(x = data) <- gsub( - pattern = '_', - replacement = '-', - x = rownames(x = data) - ) - } - if (any(grepl(pattern = '|', x = rownames(x = counts), fixed = TRUE)) || any(grepl(pattern = '|', x = rownames(x = data), fixed = TRUE))) { - warning( - "Feature names cannot have pipe characters ('|'), replacing with dashes ('-')", - call. = FALSE, - immediate. = TRUE - ) - rownames(x = counts) <- gsub( - pattern = '|', - replacement = '-', - x = rownames(x = counts), - fixed = TRUE - ) - rownames(x = data) <- gsub( - pattern = '|', - replacement = '-', - x = rownames(x = data), - fixed = TRUE - ) - } + counts <- CheckFeaturesNames(data = counts) + data <- CheckFeaturesNames(data = data) # Initialize meta.features init.meta.features <- data.frame(row.names = rownames(x = data)) + misc <- if (.GetSeuratCompat() < '5.0.0') { + list() + } else { + calcN_option <- getOption( + x = 'Seurat.object.assay.calcn', + default = Seurat.options$Seurat.object.assay.calcn + ) + list(calcN = calcN_option %||% TRUE) + } assay <- new( Class = 'Assay', counts = counts, data = data, scale.data = new(Class = 'matrix'), + key = Key(object = key)[1L] %||% '', meta.features = init.meta.features, - misc = list() + misc = misc ) return(assay) } @@ -254,11 +227,51 @@ CreateAssayObject <- function( # Methods for Seurat-defined generics #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#' @importFrom Matrix colSums +#' +#' @method .CalcN Assay +#' @export +#' +.CalcN.Assay <- function(object, layer = 'counts', ...) { + layer <- tryCatch( + expr = Layers(object = object, search = layer), + error = function(...) NULL + ) + if (is.null(x = layer)) { + return(NULL) + } + ldat <- LayerData(object = object, layer = layer) + if (IsMatrixEmpty(x = ldat) || !inherits(x = ldat, what = 'Matrix')) { + return(NULL) + } + cells_stat <- .CalcN.default(object = ldat) + return(cells_stat) +} + #' @rdname AddMetaData +#' +# @templateVar fname AddMetaData +# @templateVar version 4 +# @template name-oldv +#' #' @export #' @method AddMetaData Assay #' -AddMetaData.Assay <- .AddMetaData +AddMetaData.Assay <- function(object, metadata, col.name = NULL) { + if (is.null(x = col.name) && (is.atomic(x = metadata) && !is.matrix(x = metadata))) { + abort(message = "'col.name' must be provided for atomic meta data") + } + if (inherits(x = metadata, what = c('matrix', 'Matrix'))) { + metadata <- as.data.frame(x = metadata) + } + col.name <- col.name %||% names(x = metadata) %||% colnames(x = metadata) + if (is.null(x = col.name)) { + abort(message = "No metadata name provided and could not infer it from metadata object") + } + object[[col.name]] <- metadata + return(object) +} + #' @rdname DefaultAssay #' @export @@ -279,23 +292,138 @@ DefaultAssay.Assay <- function(object, ...) { return(object) } +#' @rdname DefaultLayer +#' @method DefaultLayer Assay +#' @export +#' +DefaultLayer.Assay <- function(object, ...) { + return('data') +} + +#' @method Features Assay +#' @export +#' +Features.Assay <- function( + x, + layer = c('data', 'scale.data', 'counts'), + slot = deprecated(), + ... +) { + if (is_present(arg = slot)) { + .Deprecate( + when = '5.0.0', + what = 'Features(slot = )', + with = 'Features(layer = )' + ) + layer <- slot + } + layer <- layer[1L] %||% 'data' + layer <- match.arg(arg = layer) + features <- rownames(x = GetAssayData(object = x, slot = layer)) + if (!length(x = features)) { + features <- NULL + } + return(features) +} + +#' @method FetchData Assay +#' @export +#' +FetchData.Assay <- function( + object, + vars, + cells = NULL, + layer = NULL, + slot = deprecated(), + ... +) { + if (is_present(arg = slot)) { + .Deprecate( + when = '5.0.0', + what = 'FetchData(slot = )', + with = 'FetchData(layer = )' + ) + layer <- layer %||% slot + } + # Identify slot to use + layer <- layer %||% 'data' + layer <- match.arg(arg = layer, choices = c('counts', 'data', 'scale.data')) + # Identify cells to use + cells <- cells %||% colnames(x = object) + if (is.numeric(x = cells)) { + cells <- colnames(x = object)[cells] + } + cells.orig <- cells + cells <- intersect(x = cells, y = colnames(x = object)) + if (length(x = cells) != length(x = cells.orig)) { + warn(message = paste( + "Removing", + length(x = cells.orig) - length(x = cells), + "cells not present in the assay" + )) + } + # Check vars + orig <- vars + vars <- gsub( + pattern = paste0('^', Key(object = object)), + replacement = '', + x = vars + ) + # Pull expression information + mat <- GetAssayData(object = object, slot = layer) + if (IsMatrixEmpty(x = mat)) { + abort(message = paste("Layer", sQuote(x = layer), "is empty in this assay")) + } + vars <- intersect(x = vars, y = rownames(x = mat)) + tf <- .GetMethod(fxn = 't', cls = class(x = mat)) + data.fetched <- as.data.frame(x = as.matrix( + x = tf(x = mat[vars, cells, drop = FALSE]) + )) + # Add keys to keyed vars + keyed.features <- paste0(Key(object = object), names(x = data.fetched)) + keyed.idx <- which(x = keyed.features %in% orig) + if (length(x = keyed.idx)) { + names(x = data.fetched)[keyed.idx] <- keyed.features[keyed.idx] + } + # Check the final list of features + missing <- setdiff(x = orig, y = names(x = data.fetched)) + if (length(x = missing) == length(x = orig)) { + abort(message = "None of the requested features found") + } else if (length(x = missing)) { + warn(message = paste( + "The following features could not be found", + paste(missing, collapse = ', ') + )) + } + return(data.fetched) +} + #' @rdname AssayData #' @export #' @method GetAssayData Assay #' #' @examples #' # Get the data directly from an Assay object -#' GetAssayData(pbmc_small[["RNA"]], slot = "data")[1:5,1:5] +#' GetAssayData(pbmc_small[["RNA"]], layer = "data")[1:5,1:5] #' GetAssayData.Assay <- function( object, - slot = c('data', 'scale.data', 'counts'), + layer = c('data', 'scale.data', 'counts'), + slot = deprecated(), ... ) { CheckDots(...) - slot <- slot[1] - slot <- match.arg(arg = slot) - return(slot(object = object, name = slot)) + if (is_present(arg = slot)) { + .Deprecate( + when = '5.0.0', + what = 'GetAssayData(slot = )', + with = 'GetAssayData(layer = )' + ) + layer <- slot + } + layer <- layer[1L] %||% 'data' + layer <- match.arg(arg = layer) + return(methods::slot(object = object, name = layer)) } #' @rdname VariableFeatures @@ -304,32 +432,46 @@ GetAssayData.Assay <- function( #' #' @examples #' # Get the HVF info directly from an Assay object -#' HVFInfo(pbmc_small[["RNA"]], selection.method = 'vst')[1:5, ] +#' HVFInfo(pbmc_small[["RNA"]], method = 'vst')[1:5, ] #' -HVFInfo.Assay <- function(object, selection.method, status = FALSE, ...) { +HVFInfo.Assay <- function( + object, + method, + status = FALSE, + selection.method = deprecated(), + ... +) { CheckDots(...) + if (is_present(arg = selection.method)) { + .Deprecate( + when = '5.0.0', + what = 'HVFInfo(selection.method = )', + with = 'HVFInfo(method = )' + ) + method <- selection.method + } disp.methods <- c('mean.var.plot', 'dispersion', 'disp') - if (tolower(x = selection.method) %in% disp.methods) { - selection.method <- 'mvp' + if (tolower(x = method) %in% disp.methods) { + method <- 'mvp' } - selection.method <- switch( - EXPR = tolower(x = selection.method), - 'sctransform' = 'sct', - selection.method + method <- switch( + EXPR = tolower(x = method), + sctransform = 'sct', + method ) vars <- switch( - EXPR = selection.method, - 'vst' = c('mean', 'variance', 'variance.standardized'), - 'mvp' = c('mean', 'dispersion', 'dispersion.scaled'), - 'sct' = c('gmean', 'variance', 'residual_variance'), - stop("Unknown method: '", selection.method, "'", call. = FALSE) + EXPR = method, + vst = c('mean', 'variance', 'variance.standardized'), + mvp = c('mean', 'dispersion', 'dispersion.scaled'), + sct = c('gmean', 'variance', 'residual_variance'), + abort(message = paste("Unknown method:", sQuote(x = method))) ) tryCatch( - expr = hvf.info <- object[[paste(selection.method, vars, sep = '.')]], + expr = hvf.info <- object[[paste(method, vars, sep = '.')]], error = function(e) { stop( "Unable to find highly variable feature information for method '", - selection.method, + method, "'", call. = FALSE ) @@ -337,7 +479,7 @@ HVFInfo.Assay <- function(object, selection.method, status = FALSE, ...) { ) colnames(x = hvf.info) <- vars if (status) { - hvf.info$variable <- object[[paste0(selection.method, '.variable')]] + hvf.info$variable <- object[[paste0(method, '.variable')]] } return(hvf.info) } @@ -370,6 +512,197 @@ Key.Assay <- function(object, ...) { return(object) } +#' @rdname Layers +#' @method LayerData Assay +#' @export +#' +LayerData.Assay <- function( + object, + layer = NULL, + cells = NULL, + features = NULL, + slot = deprecated(), + ... +) { + if (is_present(arg = slot)) { + deprecate_stop( + when = "5.0.0", + what = "LayerData(slot = )", + with = "LayerData(layer = )" + ) + } + # Figure out which matrix we're pulling + layer <- layer[1L] %||% "data" + + # layer <- match.arg( + # arg = layer, + # choices = Layers(object = object, search = FALSE) + # ) + # Handle empty layers + if (IsMatrixEmpty(x = methods::slot(object = object, name = layer))) { + msg <- paste("Layer", sQuote(x = layer), "is empty") + opt <- getOption( + x = 'Seurat.object.assay.v3.missing_layer', + default = Seurat.options$Seurat.object.assay.v3.missing_layer + ) + opt <- tryCatch( + expr = arg_match0(arg = opt, values = c('matrix', 'null', 'error')), + error = function(...) { + return(Seurat.options$Seurat.object.assay.v3.missing_layer) + } + ) + if (opt == 'error') { + abort(message = msg) + } + warn(message = msg) + return(switch( + EXPR = opt, + matrix = switch( + EXPR = layer, + scale.data = new(Class = 'matrix'), + new(Class = 'dgCMatrix') + ), + NULL + )) + } + # Allow cell/feature subsets + cells <- cells %||% colnames(x = object) + features <- features %||% Features(x = object, layer = layer) + if (is_bare_integerish(x = cells, finite = TRUE)) { + cells <- colnames(x = object)[cells] + } + cells <- arg_match( + arg = cells, + values = colnames(x = object), + multiple = TRUE + ) + if (is_bare_integerish(x = features, finite = TRUE)) { + features <- Features(x = object, layer = layer)[features] + } + features <- arg_match( + arg = features, + values = Features(x = object, layer = layer), + multiple = TRUE + ) + if (length(x = features) == 0) { + stop('features are not found') + } + # Pull the matrix for the cells/features requested + return(methods::slot(object = object, name = layer)[features, cells]) +} + +#' @rdname Layers +#' @method LayerData<- Assay +#' @export +#' +"LayerData<-.Assay" <- function(object, layer, ..., value) { + # Check the layer name + layer <- layer[1L] + layer <- match.arg( + arg = layer, + choices = Layers(object = object, search = FALSE) + ) + # Allow short-hand switch + if (rlang::is_scalar_character(x = value)) { + value <- arg_match0(arg = value, values = Layers(object = object)) + value <- LayerData(object = object, layer = value) + } + # Prepare an empty matrix if value is NULL + value <- value %||% switch( + EXPR = layer, + scale.data = new(Class = 'matrix'), + counts = new(Class = 'dgCMatrix'), + data = { + if (IsMatrixEmpty(x = suppressWarnings(expr = LayerData(object = object, layer = 'counts')))) { + abort(message = "Cannot remove the data layer") + } + warn(message = "Resetting the data matrix to the raw counts") + LayerData(object = object, layer = 'counts') + } + ) + # Check the class of the matrix + if (!inherits(x = value, what = c('matrix', 'dgCMatrix'))) { + abort(message = paste( + "'value' must be a 'matrix' or 'dgCMatrix' in v3 Assays, not a", + sQuote(x = class(x = value)[1L]) + )) + } + if (!IsMatrixEmpty(x = value)) { + vnames <- dimnames(x = value) + # Check presence of cell- and feature-names + if (is.null(x = vnames)) { + if (!all(dim(x = value) == dim(x = object))) { + abort(message = "New data must have feature and cell names") + } + dimnames(x = value) <- dimnames(x = object) + } else if (any(.IsNull(x = vnames)) || !all(unlist(x = lapply(X = vnames, FUN = nzchar)))) { + abort(message = "New data must have feature and cell names") + } + # Remove underscores from feature names + if (any(grepl(pattern = '_', x = rownames(x = value)))) { + warn( + message = "Feature names cannot have underscores ('_'), replacing with dashes ('-')" + ) + rownames(x = value) <- gsub( + pattern = '_', + replacement = '-', + x = rownames(x = value) + ) + } + # Check the the cells + if (ncol(x = value) != ncol(x = object)) { + abort(message = "The new data must have the same number of cells as the current data") + } else if (!all(colnames(x = value) %in% colnames(x = object))) { + abort(message = "The new data must have the same cells as the current data") + } + value <- value[, colnames(x = object), drop = FALSE] + # Check the features + if (!any(rownames(x = value) %in% rownames(x = object))) { + abort(message = "None of the features provided are present in the existing data") + } else if (!all(rownames(x = value) %in% rownames(x = object))) { + warn(message = "Extra features present in the the new data compared to the existing data") + } + features <- intersect(x = rownames(x = object), y = rownames(x = value)) + value <- value[features, , drop = FALSE] + if (layer %in% c('counts', 'data') && nrow(x = value) != nrow(x = object)) { + abort(message = "The new data must have the same number of features as the current data") + } + } + slot(object = object, name = layer) <- value + validObject(object = object) + return(object) +} + +#' @rdname Layers +#' @method Layers Assay +#' @export +#' +Layers.Assay <- function(object, search = NA, ...) { + layers <- c('counts', 'data', 'scale.data') + if (isFALSE(x = search)) { + return(layers) + } + layers <- Filter( + f = function(x) { + return(!IsMatrixEmpty(x = slot(object = object, name = x))) + }, + x = layers + ) + if (!length(x = layers)) { + abort(message = "All matrices are empty in this Assay") + } + if (is.null(x = search)) { + return(DefaultLayer(object = object)) + } + if (!is_na(x = search)) { + layers <- intersect(x = search, y = layers) + if (length(x = layers) == 0) { + return(NULL) + } + } + return(layers) +} + #' @param slot Name of specific bit of meta data to pull #' #' @rdname Misc @@ -401,6 +734,7 @@ Misc.Assay <- .Misc #' RenameCells.Assay <- function(object, new.names = NULL, ...) { CheckDots(...) + names(new.names) <- NULL for (data.slot in c("counts", "data", "scale.data")) { old.data <- GetAssayData(object = object, slot = data.slot) if (ncol(x = old.data) <= 1) { @@ -418,20 +752,29 @@ RenameCells.Assay <- function(object, new.names = NULL, ...) { #' @method SetAssayData Assay #' #' @examples -#' # Set an Assay slot directly -#' count.data <- GetAssayData(pbmc_small[["RNA"]], slot = "counts") +#' # Set an Assay layer directly +#' count.data <- GetAssayData(pbmc_small[["RNA"]], layer = "counts") #' count.data <- as.matrix(x = count.data + 1) -#' new.assay <- SetAssayData(pbmc_small[["RNA"]], slot = "counts", new.data = count.data) +#' new.assay <- SetAssayData(pbmc_small[["RNA"]], layer = "counts", new.data = count.data) #' SetAssayData.Assay <- function( object, - slot = c('data', 'scale.data', 'counts'), + layer = c('data', 'scale.data', 'counts'), new.data, + slot = deprecated(), ... ) { + if (is_present(arg = slot)) { + .Deprecate( + when = '5.0.0', + what = 'SetAssayData(slot = )', + with = 'SetAssayData(layer = )' + ) + layer <- slot + } CheckDots(...) - slot <- slot[1] - slot <- match.arg(arg = slot) + layer <- layer[1] + layer <- match.arg(arg = layer) if (!IsMatrixEmpty(x = new.data)) { if (any(grepl(pattern = '_', x = rownames(x = new.data)))) { warning( @@ -453,13 +796,13 @@ SetAssayData.Assay <- function( } num.counts <- nrow(x = object) counts.names <- rownames(x = object) - if (slot == 'scale.data' && nrow(x = new.data) > num.counts) { + if (layer == 'scale.data' && nrow(x = new.data) > num.counts) { warning( "Adding more features than present in current data", call. = FALSE, immediate. = TRUE ) - } else if (slot %in% c('counts', 'data') && nrow(x = new.data) != num.counts) { + } else if (layer %in% c('counts', 'data') && nrow(x = new.data) != num.counts) { warning( "The new data doesn't have the same number of features as the current data", call. = FALSE, @@ -485,7 +828,7 @@ SetAssayData.Assay <- function( ) } new.data <- new.data[new.features, colnames(x = object), drop = FALSE] - if (slot %in% c('counts', 'data') && !all(dim(x = new.data) == dim(x = object))) { + if (layer %in% c('counts', 'data') && !all(dim(x = new.data) == dim(x = object))) { stop( "Attempting to add a different number of cells and/or features", call. = FALSE @@ -498,7 +841,7 @@ SetAssayData.Assay <- function( if (!is.vector(x = colnames(x = new.data))) { colnames(x = new.data) <- as.vector(x = colnames(x = new.data)) } - slot(object = object, name = slot) <- new.data + slot(object = object, name = layer) <- new.data return(object) } @@ -511,12 +854,21 @@ SetAssayData.Assay <- function( #' SpatiallyVariableFeatures.Assay <- function( object, - selection.method = "markvariogram", + method = "moransi", decreasing = TRUE, + selection.method = deprecated(), ... ) { CheckDots(...) - vf <- SVFInfo(object = object, selection.method = selection.method, status = TRUE) + if (is_present(arg = selection.method)) { + .Deprecate( + when = '5.0.0', + what = 'SpatiallyVariableFeatures(selection.method = )', + with = 'SpatiallyVariableFeatures(method = )' + ) + method <- selection.method + } + vf <- SVFInfo(object = object, method = method, status = TRUE) vf <- vf[rownames(x = vf)[which(x = vf[, "variable"][, 1])], ] if (!is.null(x = decreasing)) { vf <- vf[order(x = vf[, "rank"], decreasing = !decreasing), ] @@ -530,33 +882,42 @@ SpatiallyVariableFeatures.Assay <- function( #' SVFInfo.Assay <- function( object, - selection.method = c("markvariogram", "moransi"), + method = c("markvariogram", "moransi"), status = FALSE, + selection.method = deprecated(), ... ) { CheckDots(...) - selection.method <- selection.method[1] - selection.method <- match.arg(arg = selection.method) + if (is_present(arg = selection.method)) { + .Deprecate( + when = '5.0.0', + what = 'SVFInfo(selection.method = )', + with = 'SVFInfo(method = )' + ) + method <- selection.method + } + method <- method[1] + method <- match.arg(arg = method) vars <- switch( - EXPR = selection.method, - 'markvariogram' = grep( + EXPR = method, + markvariogram = grep( pattern = "r.metric", - x = colnames(x = object[[]]), + x = colnames(x = object[]), value = TRUE ), - 'moransi' = grep( + moransi = grep( pattern = 'moransi', - x = colnames(x = object[[]]), + x = colnames(x = object[]), value = TRUE ), - stop("Unknown method: '", selection.method, "'", call. = FALSE) + abort(message = paste("Unknown method:", sQuote(x = method))) ) tryCatch( expr = svf.info <- object[[vars]], error = function(e) { stop( "Unable to find highly variable feature information for method '", - selection.method, + method, "'", call. = FALSE ) @@ -564,8 +925,8 @@ SVFInfo.Assay <- function( ) colnames(x = svf.info) <- vars if (status) { - svf.info$variable <- object[[paste0(selection.method, '.spatially.variable')]] - svf.info$rank <- object[[paste0(selection.method, '.spatially.variable.rank')]] + svf.info$variable <- object[[paste0(method, '.spatially.variable')]] + svf.info$rank <- object[[paste0(method, '.spatially.variable.rank')]] } return(svf.info) } @@ -574,12 +935,25 @@ SVFInfo.Assay <- function( #' @export #' @method VariableFeatures Assay #' -VariableFeatures.Assay <- function(object, selection.method = NULL, ...) { - CheckDots(...) - if (!is.null(x = selection.method)) { +VariableFeatures.Assay <- function( + object, + method = NULL, + selection.method = deprecated(), + ... +) { + suppressWarnings(CheckDots(...)) + if (is_present(arg = selection.method)) { + .Deprecate( + when = '5.0.0', + what = 'VariableFeatures(selection.method = )', + with = 'VariableFeatures(method = )' + ) + method <- selection.method + } + if (!is.null(x = method)) { vf <- HVFInfo( object = object, - selection.method = selection.method, + method = method, status = TRUE ) return(rownames(x = vf)[which(x = vf[, "variable"][, 1])]) @@ -593,7 +967,7 @@ VariableFeatures.Assay <- function(object, selection.method = NULL, ...) { #' "VariableFeatures<-.Assay" <- function(object, ..., value) { CheckDots(...) - if (length(x = value) == 0) { + if (!length(x = value)) { slot(object = object, name = 'var.features') <- character(length = 0) return(object) } @@ -608,10 +982,7 @@ VariableFeatures.Assay <- function(object, selection.method = NULL, ...) { value <- split(x = value, f = value %in% rownames(x = object)) if (length(x = value[['FALSE']]) > 0) { if (length(x = value[['TRUE']]) == 0) { - stop( - "None of the features provided are in this Assay object", - call. = FALSE - ) + abort(message = "None of the features provided are in this Assay object") } else { warning( "Not all features provided are in this Assay object, removing the following feature(s): ", @@ -633,7 +1004,6 @@ VariableFeatures.Assay <- function(object, selection.method = NULL, ...) { #' @param invert Invert the selection of cells #' #' @importFrom stats na.omit -#' @importFrom rlang is_quosure enquo eval_tidy #' #' @rdname WhichCells #' @export @@ -678,8 +1048,7 @@ WhichCells.Assay <- function( ) vars.use <- which(x = expr.char %in% rownames(x = object)) expr.char <- expr.char[vars.use] - data.subset <- as.data.frame(x = t(x = as.matrix(x = object[expr.char, ]))) - colnames(x = data.subset) <- expr.char + data.subset <- FetchData(object = object, vars = expr.char) cells <- rownames(x = data.subset)[eval_tidy(expr = expr, data = data.subset)] } if (invert) { @@ -693,50 +1062,130 @@ WhichCells.Assay <- function( # Methods for R-defined generics #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -#' \code{Assay} Methods +#' @inherit .DollarNames.Assay5 return title details sections seealso +#' +#' @description Autocompletion for \code{$} access on an +#' \code{\link{Assay}} object +#' +#' @inheritParams utils::.DollarNames +#' @param x An \code{\link{Assay}} object #' -#' Methods for \code{\link{Assay}} objects for generics defined in -#' other packages +#' @importFrom utils .DollarNames #' -#' @param x,object An \code{\link{Assay}} object -#' @param i,features For \code{[[}: metadata names; for all other methods, -#' feature names or indices -#' @param j,cells Cell names or indices -#' @param ... Arguments passed to other methods +#' @keywords internal #' -#' @name Assay-methods -#' @rdname Assay-methods +#' @method .DollarNames Assay +#' @export #' #' @concept assay #' -NULL +.DollarNames.Assay <- function(x, pattern = '') { + slots.avial <- Layers(x) + slots.avial <- as.list(slots.avial) + names(slots.avial) <- unlist(slots.avial) + return(.DollarNames(x = slots.avial, pattern = pattern)) +} -#' @describeIn Assay-methods Get expression data from an \code{Assay} +#' @inherit $.Assay5 return title description details sections params #' -#' @return \code{[}: The \code{data} slot for features \code{i} and cells -#' \code{j} +#' @param x An \code{\link{Assay}} object #' +#' @method $ Assay #' @export +#' +#' @family assay +#' +#' @examples +#' rna <- pbmc_small[["RNA"]] +#' +#' # Fetch a layer with `$` +#' rna$data[1:10, 1:4] +#' +"$.Assay" <- function(x, i) { + return(LayerData(object = x, layer = i)) +} + +#' @rdname cash-.Assay +#' +#' @method $<- Assay +#' @export +#' +#' @examples +#' # Add a layer with `$` +#' rna$data <- rna$counts +#' rna$data[1:10, 1:4] +#' +"$<-.Assay" <- function(x, i, value) { + LayerData(object = x, layer = i) <- value + return(x) +} + +#' @inherit [.Assay5 return title description details sections +#' +#' @inheritParams [.Assay5 +#' @param x An \code{\link{Assay}} object +#' @param j Ignored +#' #' @method [ Assay +#' @export #' -"[.Assay" <- function(x, i, j, ...) { - if (missing(x = i)) { - i <- seq_len(length.out = nrow(x = x)) +#' @order 1 +#' +#' @seealso \code{\link{LayerData}} +#' +#' @family assay +#' +#' @examples +#' rna <- pbmc_small[["RNA"]] +#' +#' # Get a vector of layer names in this assay +#' rna[] +#' +#' # Fetch layer data +#' rna["data"][1:10, 1:4] +#' +"[.Assay" <- function(x, i = missing_arg(), j = missing_arg(), ...) { + if (getOption(x = 'Seurat.object.assay.brackets', default = 'v5') == 'v3') { + if (is_missing(x = i)) { + i <- seq_len(length.out = nrow(x = x)) + } + if (is_missing(x = j)) { + j <- seq_len(length.out = ncol(x = x)) + } + return(LayerData( + object = x, + layer = DefaultLayer(object = x)[1L], + cells = j, + features = i + )) } - if (missing(x = j)) { - j <- seq_len(length.out = ncol(x = x)) + if (is_missing(x = i)) { + return(Layers(object = x)) } - return(GetAssayData(object = x)[i, j, ..., drop = FALSE]) + return(LayerData(object = x, layer = i, ...)) } -#' @describeIn Assay-methods Get feature-level metadata +#' @inherit [[.Assay5 return title description details sections #' -#' @param drop See \code{\link[base]{drop}} +#' @inheritParams [[.Assay5 +#' @param x An \code{\link{Assay}} object #' -#' @return \code{[[}: The feature-level metadata for \code{i} -#' -#' @export #' @method [[ Assay +#' @export +#' +#' @family assay +#' +#' @order 1 +#' +#' @examples +#' rna <- pbmc_small[["RNA"]] +#' +#' # Pull the entire feature-level meta data data frame +#' head(rna[[]]) +#' +#' # Pull a specific column of feature-level meta data +#' head(rna[["vst.mean"]]) +#' head(rna[["vst.mean", drop = TRUE]]) #' "[[.Assay" <- function(x, i, ..., drop = FALSE) { if (missing(x = i)) { @@ -750,63 +1199,144 @@ NULL return(data.return) } -#' @describeIn Assay-methods Number of cells and features for an \code{Assay} +#' @inherit dim.Assay5 return title description details sections #' -#' @return \code{dim}: The number of features (\code{nrow}) and cells -#' (\code{ncol}) +#' @inheritParams [.Assay #' -#' @export #' @method dim Assay +#' @export +#' +#' @family assay +#' +#' @examples +#' rna <- pbmc_small[["RNA"]] +#' dim(rna) #' dim.Assay <- function(x) { return(dim(x = GetAssayData(object = x))) } -#' @describeIn Assay-methods Cell- and feature-names for an \code{Assay} +#' @inherit dimnames.Assay5 title description details sections #' -#' @return \code{dimnames}: Feature (row) and cell (column) names +#' @inheritParams [.Assay +#' +#' @return \code{dimnames}: A two-length list with the following values: +#' \itemize{ +#' \item A character vector will all features in \code{x} +#' \item A character vector will all cells in \code{x} +#' } #' -#' @export #' @method dimnames Assay +#' @export +#' +#' @family assay +#' @family dimnames +#' +#' @examples +#' rna <- pbmc_small[["RNA"]] +#' +#' # Feature and cell names can be acquired with `rownames` and `colnames` +#' head(rownames(rna)) +#' head(colnames(rna)) #' dimnames.Assay <- function(x) { return(dimnames(x = GetAssayData(object = x))) } -#' @describeIn Assay-methods Get the first rows of feature-level metadata +#' @param value A two-length list where the first entry is the existing feature +#' names for \code{x} and the second entry is the \emph{updated} cell names +#' for \code{x} #' -#' @inheritParams utils::head +#' @return \code{dimnames<-}: \code{x} with the cell names updated to those +#' in \code{value[[2L]]} #' -#' @return \code{head}: The first \code{n} rows of feature-level metadata +#' @rdname dimnames.Assay #' +#' @method dimnames<- Assay #' @export +#' +#' @examples +#' # Cell names can be updated with `colnames<-` +#' colnames(rna)[1] <- "newcell" +#' head(colnames(rna)) +#' +"dimnames<-.Assay" <- function(x, value) { + op <- options(Seurat.object.validate = FALSE) + on.exit(expr = options(op), add = TRUE) + # Check the provided dimnames + msg <- "Invalid 'dimnames' given for a Seurat object" + if (!is_bare_list(x = value, n = 2L)) { + abort(message = msg) + } else if (!all(sapply(X = value, FUN = length) == dim(x = x))) { + abort(message = msg) + } + value <- lapply(X = value, FUN = as.character) + # Warn about changing features + if (!all(value[[1L]] == rownames(x = slot(object = x, name = 'data')))) { + warn(message = "Changing feature names in v3 Assays is not supported") + } + # Set cell names + for (lyr in c('counts', 'data', 'scale.data')) { + if (!IsMatrixEmpty(x = slot(object = x, name = lyr))) { + suppressWarnings(expr = colnames(x = slot(object = x, name = lyr)) <- value[[2L]]) + } + } + # Validate and return the Seurat object + options(op) + validObject(object = x) + return(x) +} + +#' @rdname sub-sub-.Assay +#' #' @method head Assay +#' @export #' -head.Assay <- .head +#' @examples +#' # `head` and `tail` can be used to quickly view feature-level meta data +#' head(rna) +#' +head.Assay <- function(x, n = 10L, ...) { + return(head(x[[]], n = 10L, ...)) +} -#' @describeIn Assay-methods Merge \code{Assay} objects +#' Merge Assays #' -#' @param y A vector or list of one or more objects to merge +#' Merge one or more v3 assays together +#' +#' @inheritParams [[.Assay +#' @param y One or more \code{\link{Assay}} objects #' @param add.cell.ids A character vector of \code{length(x = c(x, y))}; #' appends the corresponding values to the start of each objects' cell names #' @param merge.data Merge the data slots instead of just merging the counts #' (which requires renormalization); this is recommended if the same #' normalization approach was applied to all objects +#' @param labels,collapse Currently unused #' -#' @return \code{merge}: Merged object +#' @return A new assay with data merged from \code{c(x, y)} #' -#' @export #' @method merge Assay +#' @export +#' +#' @family assay #' merge.Assay <- function( x = NULL, y = NULL, add.cell.ids = NULL, merge.data = TRUE, + labels = NULL, + collapse = TRUE, ... ) { CheckDots(...) assays <- c(x, y) + if (any(sapply( + X = assays, + FUN = function(assay.i) inherits(x = assay.i, what = "Assay5") + ))) { + return(merge(x = as(x, "Assay5"), y, ...)) + } if (!is.null(x = add.cell.ids)) { for (i in seq_along(along.with = assays)) { assays[[i]] <- RenameCells( @@ -817,7 +1347,7 @@ merge.Assay <- function( } # Merge the counts (if present) counts.mats <- lapply(X = assays, FUN = ValidateDataForMerge, slot = "counts") - keys <- sapply(X = assays, FUN = Key) + keys <- unlist(sapply(X = assays, FUN = Key)) merged.counts <- RowMergeSparseMatrices( mat1 = counts.mats[[1]], mat2 = counts.mats[2:length(x = counts.mats)] @@ -827,9 +1357,7 @@ merge.Assay <- function( min.cells = -1, min.features = -1 ) - if (length(x = unique(x = keys)) == 1) { - Key(object = combined.assay) <- keys[1] - } + Key(object = combined.assay) <- keys[1] if (merge.data) { data.mats <- lapply(X = assays, FUN = ValidateDataForMerge, slot = "data") merged.data <- RowMergeSparseMatrices( @@ -849,14 +1377,61 @@ merge.Assay <- function( return(combined.assay) } -#' @describeIn Assay-methods Subset an \code{Assay} +#' @inherit split.Assay5 title description details #' -#' @return \code{subset}: A subsetted \code{Assay} +#' @inheritParams split.Assay5 +#' @param x An \code{\link{Assay}} object #' -#' @importFrom stats na.omit +#' @return Returns a v5 assay with splitted layers #' +#' @method split Assay #' @export +#' +#' @family assay +#' +split.Assay <- function( + x, + f, + drop = FALSE, + layers = NA, + ... +) { + warn(message = paste( + strwrap(x = paste( + "Input is a v3 assay and `split()` only works for v5 assays;", + "converting to a v5 assay" + )) + )) + x <- as(object = x, Class = 'Assay5') + split.x <- split( + x = x, + f = f, + drop = drop, + layers = layers, + ... + ) + return(split.x) +} + +#' @inherit subset.Assay5 title description details sections +#' +#' @inheritParams subset.Assay5 +#' @param x An \code{\link{Assay}} object +#' +#' @return \code{x} with just the cells and features specified by +#' \code{cells} and \code{features} +#' +#' @importFrom stats na.omit +#' #' @method subset Assay +#' @export +#' +#' @family assay +#' +#' @examples +#' rna <- pbmc_small[["RNA"]] +#' rna2 <- subset(rna, features = VariableFeatures(rna)) +#' rna2 #' subset.Assay <- function(x, cells = NULL, features = NULL, ...) { CheckDots(...) @@ -864,14 +1439,15 @@ subset.Assay <- function(x, cells = NULL, features = NULL, ...) { if (all(is.na(x = cells))) { cells <- colnames(x = x) } else if (any(is.na(x = cells))) { - warning("NAs passed in cells vector, removing NAs") + warn(message = "NAs passed in cells vector, removing NAs") cells <- na.omit(object = cells) } + cells <- intersect(x = colnames(x), y = cells) features <- features %||% rownames(x = x) if (all(is.na(x = features))) { features <- rownames(x = x) } else if (any(is.na(x = features))) { - warning("NAs passed in the features vector, removing NAs") + warn(message = "NAs passed in the features vector, removing NAs") features <- na.omit(object = features) } if (all(sapply(X = list(features, cells), FUN = length) == dim(x = x))) { @@ -887,7 +1463,7 @@ subset.Assay <- function(x, cells = NULL, features = NULL, ...) { ) features <- intersect(x = features, y = rownames(x = x)) if (length(x = features) == 0) { - stop("Cannot find features provided") + abort(message = "Cannot find features provided") } if (ncol(x = GetAssayData(object = x, slot = 'counts')) == ncol(x = x)) { slot(object = x, name = "counts") <- GetAssayData(object = x, slot = "counts")[features, cells, drop = FALSE] @@ -897,7 +1473,7 @@ subset.Assay <- function(x, cells = NULL, features = NULL, ...) { cells.scaled <- cells.scaled[cells.scaled %in% cells] cells.scaled <- cells.scaled[na.omit(object = match(x = colnames(x = x), table = cells.scaled))] features.scaled <- rownames(x = GetAssayData(object = x, slot = 'scale.data')) - features.scaled <- features.scaled[features.scaled %in% features] + features.scaled <- intersect(x = features, y = features.scaled) slot(object = x, name = "scale.data") <- if (length(x = cells.scaled) > 0 && length(x = features.scaled) > 0) { GetAssayData(object = x, slot = "scale.data")[features.scaled, cells.scaled, drop = FALSE] } else { @@ -905,35 +1481,49 @@ subset.Assay <- function(x, cells = NULL, features = NULL, ...) { } VariableFeatures(object = x) <- VariableFeatures(object = x)[VariableFeatures(object = x) %in% features] slot(object = x, name = 'meta.features') <- x[[]][features, , drop = FALSE] + validObject(object = x) return(x) } -#' @describeIn Assay-methods Get the last rows of feature-level metadata -#' -#' @return \code{tail}: The last \code{n} rows of feature-level metadata -#' -#' @importFrom utils tail +#' @rdname sub-sub-.Assay #' -#' @export #' @method tail Assay +#' @export +#' +#' @examples +#' tail(rna) #' -tail.Assay <- .tail +tail.Assay <- function(x, n = 10L, ...) { + return(tail(x[[]], n = n, ...)) +} #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # S4 methods #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -#' @describeIn Assay-methods Add feature-level metadata +#' @rdname sub-.Assay #' -#' @param value Additional metadata to add +#' @examples +#' # Set layer data +#' rna["data"] <- rna["counts"] +#' rna["data"][1:10, 1:4] #' -#' @return \code{[[<-}: \code{x} with metadata \code{value} added as \code{i} +setMethod( + f = '[<-', + signature = c(x = 'Assay', i = 'character'), + definition = function(x, i, ..., value) { + LayerData(object = x, layer = i, ...) <- value + return(x) + } +) + +#' @rdname sub-sub-.Assay #' -#' @export +#' @order 2 #' setMethod( f = '[[<-', - signature = c('x' = 'Assay'), + signature = c(x = 'Assay'), definition = function(x, i, ..., value) { meta.data <- x[[]] feature.names <- rownames(x = meta.data) @@ -977,22 +1567,62 @@ setMethod( } ) -#' @describeIn Assay-methods Calculate \code{\link[base]{colMeans}} on an -#' \code{Assay} +#' @rdname sub-sub-.Assay +#' +setMethod( + f = '[[<-', + signature = c( + x = 'Assay', + i = 'missing', + j = 'missing', + value = 'data.frame' + ), + definition = function(x, ..., value) { + # Allow removing all meta data + if (IsMatrixEmpty(x = value)) { + x[[names(x = x[[]])]] <- NULL + return(x) + } + if (is.null(names(x = value))) { + warn(message = 'colnames of input cannot be NULL') + } else { + # If no `i` provided, use the column names from value + x[[names(x = value)]] <- value + } + return(x) + } +) + +#' Row and Column Sums and Means +#' +#' Calculate \code{\link{rowSums}}, \code{\link{colSums}}, +#' \code{\link{rowMeans}}, and \code{\link{colMeans}} on \code{Assay} objects #' +#' @inheritParams [[.Assay +#' @inheritParams Matrix::colMeans #' @param slot Name of assay expression matrix to calculate column/row #' means/sums on -#' @inheritParams Matrix::colMeans #' #' @return \code{colMeans}: The column (cell-wise) means of \code{slot} #' #' @importFrom Matrix colMeans #' +#' @keywords internal +#' #' @export #' +#' @concept assay +#' +#' @seealso \code{\link{Assay}} +#' +#' @examples +#' rna <- pbmc_small[["RNA"]] +#' +#' colMeans(rna) +#' setMethod( f = 'colMeans', - signature = c('x' = 'Assay'), + signature = c(x = 'Assay'), definition = function(x, na.rm = FALSE, dims = 1, ..., slot = 'data') { return(Matrix::colMeans( x = GetAssayData(object = x, slot = slot), @@ -1003,18 +1633,20 @@ setMethod( } ) -#' @describeIn Assay-methods Calculate \code{\link[base]{colSums}} on an -#' \code{Assay} -#' #' @return \code{colSums}: The column (cell-wise) sums of \code{slot} #' +#' @rdname colMeans-Assay-method +#' #' @importFrom Matrix colSums #' #' @export #' +#' @examples +#' colSums(rna) +#' setMethod( f = 'colSums', - signature = c('x' = 'Assay'), + signature = c(x = 'Assay'), definition = function(x, na.rm = FALSE, dims = 1, ..., slot = 'data') { return(Matrix::colSums( x = GetAssayData(object = x, slot = slot), @@ -1025,18 +1657,20 @@ setMethod( } ) -#' @describeIn Assay-methods Calculate \code{\link[base]{rowMeans}} on an -#' \code{Assay} -#' #' @return \code{rowMeans}: The row (feature-wise) means of \code{slot} #' +#' @rdname colMeans-Assay-method +#' #' @importFrom Matrix rowMeans #' #' @export #' +#' @examples +#' rowMeans(rna) +#' setMethod( f = 'rowMeans', - signature = c('x' = 'Assay'), + signature = c(x = 'Assay'), definition = function(x, na.rm = FALSE, dims = 1, ..., slot = 'data') { return(Matrix::rowMeans( x = GetAssayData(object = x, slot = slot), @@ -1047,18 +1681,20 @@ setMethod( } ) -#' @describeIn Assay-methods Calculate \code{\link[base]{rowSums}} on an -#' \code{Assay} -#' #' @return \code{rowSums}: The row (feature-wise) sums of \code{slot} #' +#' @rdname colMeans-Assay-method +#' #' @importFrom Matrix rowSums #' #' @export #' +#' @examples +#' rowSums(rna) +#' setMethod( f = 'rowSums', - signature = c('x' = 'Assay'), + signature = c(x = 'Assay'), definition = function(x, na.rm = FALSE, dims = 1, ..., slot = 'data') { return(Matrix::rowSums( x = GetAssayData(object = x, slot = slot), @@ -1069,22 +1705,29 @@ setMethod( } ) -#' @describeIn Assay-methods Overview of an \code{Assay} object +#' V3 Assay Overview #' -#' @return \code{show}: Prints summary to \code{\link[base]{stdout}} and -#' invisibly returns \code{NULL} +#' Overview of an \code{\link{Assay}} object #' -#' @importFrom utils head -#' @importFrom methods show +#' @template return-show #' -#' @export +#' @keywords internal +#' +#' @concept assay +#' +#' @seealso \code{\link{Assay}} +#' +#' @examples +#' rna <- pbmc_small[["RNA"]] +#' rna #' setMethod( f = 'show', signature = 'Assay', definition = function(object) { cat( - 'Assay data with', + class(x = object)[1], + 'data with', nrow(x = object), 'features for', ncol(x = object), 'cells\n' @@ -1118,6 +1761,111 @@ setMethod( } ) +#' V3 Assay Validity +#' +#' @templateVar cls Assay +#' @template desc-validity +#' +#' @section \code{data} Validation: +#' blah +#' +#' @section \code{counts} Validation: +#' blah +#' +#' @section \code{scale.data} Validation: +#' blah +#' +#' @section Feature-Level Meta Data Validation: +#' blah +#' +#' @section Variable Feature Validation: +#' blah +#' +#' @inheritSection Key-validity Key Validation +#' +#' @name Assay-validity +#' +#' @family assay +#' @seealso \code{\link[methods]{validObject}} +#' +#' @examples +#' rna <- pbmc_small[["RNA"]] +#' validObject(rna) +#' +setValidity( + Class = 'Assay', + method = function(object) { + if (.GetSeuratCompat() < '5.0.0') { + return(TRUE) + } + if (isFALSE(x = getOption(x = "Seurat.object.validate", default = TRUE))) { + warn( + message = paste("Not validating", class(x = object)[1L], "objects"), + class = 'validationWarning' + ) + return(TRUE) + } + valid <- NULL + # Check matrices + features <- rownames(x = slot(object = object, name = 'data')) + if (anyDuplicated(x = features)) { + valid <- c(valid, "duplicate feature names are not allowed") + } + cells <- colnames(x = slot(object = object, name = 'data')) + if (anyDuplicated(x = cells)) { + valid <- c(valid, "duplicate cell names are not allowed") + } + for (lyr in c('counts', 'scale.data')) { + ldat <- slot(object = object, name = lyr) + if (IsMatrixEmpty(x = ldat)) { + next + } + if (!all(colnames(x = ldat) == cells)) { + valid <- c( + valid, + paste0("'", lyr, "' must have the same cells as 'data'") + ) + } + if (lyr == 'counts' && !all(rownames(x = ldat) == features)) { + valid <- c( + valid, + paste0("'", lyr, "' must have the same features as 'data'") + ) + } else if (lyr == 'scale.data') { + scaled <- rownames(x = ldat) + if (!all(scaled %in% features)) { + valid <- c( + valid, + "all features in 'scale.data' must be present in 'data'" + ) + } else if (is.unsorted(x = MatchCells(new = scaled, orig = features, ordered = TRUE))) { + valid <- c( + valid, + "features in 'scale.data' must be in the same order as in 'data'" + ) + } + } + } + # Check meta.features + mf <- slot(object = object, name = 'meta.features') + if (nrow(x = mf) != length(x = features)) { + valid <- c( + valid, + "'meta.features' must have the same number of rows as 'data'" + ) + } else if (!all(row.names(x = mf) == features)) { + valid <- c(valid, "meta.features' must have the same features as 'data'") + } + # Check variable features + vf <- slot(object = object, name = 'var.features') + if (length(x = vf) && !all(vf %in% features)) { + valid <- c(valid, "all 'var.features' must be present in") + } + # TODO: Check assay.orig + return(valid %||% TRUE) + } +) + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Internal #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1128,8 +1876,6 @@ setMethod( #' #' @return A named list with nCount and nFeature #' -#' @importFrom Matrix colSums -#' #' @keywords internal #' #' @noRd @@ -1140,15 +1886,7 @@ setMethod( #' head(as.data.frame(calcn)) #' } #' -CalcN <- function(object) { - if (IsMatrixEmpty(x = GetAssayData(object = object, slot = "counts"))) { - return(NULL) - } - return(list( - nCount = Matrix::colSums(x = object, slot = 'counts'), - nFeature = Matrix::colSums(x = GetAssayData(object = object, slot = 'counts') > 0) - )) -} +CalcN <- .CalcN #' Subset cells in vst data #' diff --git a/R/assay5.R b/R/assay5.R new file mode 100644 index 00000000..d4270289 --- /dev/null +++ b/R/assay5.R @@ -0,0 +1,3305 @@ +#' @include zzz.R +#' @include assay.R +#' @include layers.R +#' @include logmap.R +#' @include keymixin.R +#' @importFrom methods callNextMethod setAs +#' +NULL + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Class definitions +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#' Core Assay Infrastructure +#' +#' The \code{StdAssay} class is a virtual class that provides core +#' infrastructure for assay data in \pkg{Seurat}. Assays contain expression +#' data (layers) and associated feature-level meta data. Derived classes +#' (eg. \link[=Assay5]{the v5 Assay}) may optionally +#' define additional functionality +#' +#' @template slot-stdassay +#' @template slot-misc +#' @template slot-key +#' +#' @keywords internal +#' +#' @exportClass StdAssay +#' +#' @aliases StdAssay +#' +#' @family stdassay +#' +#' @seealso \code{\link{Assay5-class}} \code{\link{Assay5T-class}} +#' +setClass( + Class = 'StdAssay', + contains = c('VIRTUAL', 'KeyMixin'), + slots = c( + layers = 'list', + cells = 'LogMap', + features = 'LogMap', + default = 'integer', + assay.orig = 'character', + meta.data = 'data.frame', + misc = 'list' + ) +) + +#' The v5 \code{Assay} Object +#' +#' The v5 \code{Assay} is the typical \code{Assay} class used in \pkg{Seurat} +#' v5; ... +#' +#' @template slot-stdassay +#' @template slot-misc +#' @template slot-key +#' +#' @exportClass Assay5 +#' +#' @aliases Assay5 +#' +#' @family assay5 +#' +setClass( + Class = 'Assay5', + contains = 'StdAssay' +) + +#' The Transposed v5 \code{Assay} Object +#' +#' @template slot-stdassay +#' @template slot-misc +#' @template slot-key +#' +#' @template lifecycle-experimental +#' +#' @keywords internal +#' +#' @aliases Assay5T +#' +setClass( + Class = 'Assay5T', + contains = 'StdAssay' +) + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Functions +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#' Create a v5 Assay object +#' +#' Create an \code{\link{Assay5}} object from a feature expression matrix; +#' the expected format of the matrix is features x cells +#' +#' @inheritParams .CreateStdAssay +#' @param data Optional prenormalized data matrix +#' @template param-dots-method +# @param transpose Create a transposed assay +# @param ... Extra parameters passed to \code{\link{.CreateStdAssay}} +#' +#' @return An \code{\link{Assay5}} object +#' +#' @export +#' +#' @concept assay +#' +CreateAssay5Object <- function( + counts = NULL, + data = NULL, + min.cells = 0, + min.features = 0, + csum = NULL, + fsum = NULL, + ... +) { + transpose <- FALSE + colsums <- Matrix::colSums + rowsums <- Matrix::rowSums + type <- 'Assay5' + csum <- csum %||% colsums + fsum <- fsum %||% rowsums + counts <- CheckLayersName(matrix.list = counts, layers.type = 'counts') + data <- CheckLayersName(matrix.list = data, layers.type = 'data') + if (!is.null(x = counts) & !is.null(data)) { + counts.cells <- unlist( + x = lapply( + X = counts, + FUN = function(x) colnames(x = x) + ) + ) + data.cells <- unlist( + x = lapply( + X = data, + FUN = function(x) colnames(x) + ) + ) + if (!all(counts.cells == data.cells)) { + abort(message = 'counts and data input should have the same cells') + } + } + counts <- c(counts, data) + data <- NULL + CheckGC() + return(.CreateStdAssay( + counts = counts, + min.cells = min.cells, + min.features = min.features, + transpose = transpose, + type = type, + csum = csum, + fsum = fsum, + ... + )) +} + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Methods for Seurat-defined generics +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#' @method .AssayClass Assay5T +#' @export +#' +.AssayClass.Assay5T <- function(object) { + return('Transposed Assay (v5)') +} + +#' @method .CalcN StdAssay +#' @export +#' +.CalcN.StdAssay <- function(object, layer = 'counts', simplify = TRUE, ...) { + layer <- tryCatch( + expr = Layers(object = object, search = layer), + error = \(...) NULL + ) # %||% DefaultLayer(object = object) + if (is.null(x = layer)) { + warn( + message = "Cannot find the layer(s) specified", + class = 'missingLayerWarning' + ) + return(NULL) + } + calcn <- vector(mode = 'list', length = length(x = layer)) + names(x = calcn) <- layer + for (lyr in layer) { + ldat <- LayerData(object = object, layer = lyr) + if (IsMatrixEmpty(x = ldat)) { + next + } + calcn[[lyr]] <- .CalcN(object = ldat) + } + calcn <- Filter(f = length, x = calcn) + # If every layer is empty, return `NULL` + if (!length(x = calcn)) { + return(NULL) + } else if (isFALSE(x = simplify)) { + # If we're not simplifying, return the list as-is + return(calcn) + } else if (length(x = calcn) == 1L) { + # If we're only calculating N for one layer, return those results + return(calcn[[1L]]) + } + # Simplify the calcn list for all cells + all.cells <- Cells(x = object, layer = layer, simplify = TRUE) + ncells <- length(x = all.cells) + ncalc <- list( + nCount = vector(mode = 'numeric', length = ncells), + nFeature = vector(mode = 'numeric', length = ncells) + ) + names(x = ncalc$nCount) <- names(x = ncalc$nFeature) <- all.cells + # For every layer, add the nCount and nFeature counts to existing cells + for (i in seq_along(along.with = calcn)) { + lcells <- names(x = calcn[[i]][['nCount']]) + ncalc[['nCount']][lcells] <- calcn[[i]][['nCount']] + ncalc[['nCount']][lcells] + ncalc[['nFeature']][lcells] <- calcn[[i]][['nFeature']] + ncalc[['nFeature']][lcells] + } + return(ncalc) +} + +#' @method .CalcN default +#' @export +#' +.CalcN.default <- function(object, ...) { + return(list( + nCount = Matrix::colSums(x = object), + nFeature = Matrix::colSums(x = object > 0) + )) +} + +#' @param layer Name of layer to store \code{counts} as +#' +#' @rdname dot-CreateStdAssay +#' @method .CreateStdAssay default +#' @export +#' +.CreateStdAssay.default <- function( + counts, + min.cells = 0, + min.features = 0, + cells = NULL, + features = NULL, + transpose = FALSE, + type = 'Assay5', + layer = 'counts', + ... +) { + if (!is_bare_integerish(x = dim(x = counts), n = 2L, finite = TRUE)) { + abort(message = "'counts' must be a two-dimensional object") + } + dnames <- dimnames(x = counts) + cls <- class(x = counts) + + if (isTRUE(x = transpose)) { + csum <- .GetMethod(fxn = 'rowSums', cls = cls) + cells <- cells %||% dnames[[1L]] + fsum <- .GetMethod(fxn = 'colSums', cls = cls) + features <- features %||% dnames[[2L]] + } else { + csum <- .GetMethod(fxn = 'colSums', cls = cls) + cells <- cells %||% dnames[[2L]] + fsum <- .GetMethod(fxn = 'rowSums', cls = cls) + features <- features %||% dnames[[1L]] + } + counts <- list(counts) + names(x = counts) <- layer + return(.CreateStdAssay( + counts = counts, + min.cells = min.cells, + layer = layer, + min.features = min.features, + cells = cells, + features = features, + transpose = transpose, + type = type, + fsum = fsum, + csum = csum, + ... + )) +} + +#' @param csum Function for calculating cell sums +#' @param fsum Function for calculating feature sums +#' +#' @importFrom methods getClass +#' @importFrom utils getS3method methods +#' +#' @rdname dot-CreateStdAssay +#' @method .CreateStdAssay list +#' @export +#' +.CreateStdAssay.list <- function( + counts, + min.cells = 0, + min.features = 0, + cells = NULL, + features = NULL, + transpose = FALSE, + type = 'Assay5', + csum = Matrix::colSums, + fsum = Matrix::rowSums, + ... +) { + # Figure out feature/cell MARGINs + cdef <- getClass(Class = type) + contains <- names(x = slot(object = cdef, name = 'contains')) + if (!'StdAssay' %in% contains) { + stop("Class '", type, "' does not inherit from StdAssay") + } + for (i in c(type, contains, 'default')) { + fmargin <- getS3method(f = '.MARGIN', class = i, optional = TRUE) + if (is.function(x = fmargin)) { + break + } + } + cdim <- fmargin(object = type, type = 'cells') + fdim <- fmargin(object = type, type = 'features') + counts <- lapply(X = counts, FUN = function(x) { + x <- CheckFeaturesNames(data = x) + return(x) + }) + # Check cell/feature names for all layers + if (is.atomic(x = cells) || is.null(x = cells)) { + cells <- rep_len(x = list(cells), length.out = length(x = counts)) + } + if (!is_bare_list(x = cells) || length(x = cells) != length(x = counts)) { + stop("Not enough cells for the counts matrices provided", call. = FALSE) + } + cells <- .CheckNames(x = cells, n = names(x = counts)) + if (is.atomic(x = features) || is.null(x = features)) { + features <- rep_len(x = list(features), length.out = length(x = counts)) + } + if (!is_bare_list(x = features) || length(x = features) != length(x = counts)) { + stop("Not enough features for the counts matrices provided", call. = FALSE) + } + features <- .CheckNames(x = features, n = names(x = counts)) + for (layer in names(x = counts)) { + cells[[layer]] <- cells[[layer]] %||% + dimnames(x = counts[[layer]])[[cdim]] %||% + paste0('Cell_', seq_len(length.out = dim(x = counts[[layer]])[cdim])) + features[[layer]] <- features[[layer]] %||% + dimnames(x = counts[[layer]])[[fdim]] %||% + paste0('Feature', seq_len(length.out = dim(x = counts[[layer]])[fdim])) + } + # Filter based on min.features + if (min.features > 0) { + for (layer in names(x = counts)) { + if (inherits(x = counts[[layer]], what = "IterableMatrix")) { + check_installed(pkg = 'BPCells', reason = 'for working with BPCells') + col_stat <- BPCells::matrix_stats(matrix = counts[[layer]], col_stats = 'nonzero')$col_stats + cells.use <- which(x = col_stat >= min.features) + } else { + cells.use <- which(x = csum(counts[[layer]] > 0) >= min.features) + } + counts[[layer]] <- if (cdim == 1L) { + counts[[layer]][cells.use, ] + } else { + counts[[layer]][, cells.use] + } + cells[[layer]] <- cells[[layer]][cells.use] + } + } + # For now, coerce to dgCMatrix if not dgCMatrix, IterableMatrix, or DelayedArray + if (!inherits(x = counts[[layer]], what = c('dgCMatrix', 'IterableMatrix', 'DelayedArray'))) { + warning('Data is of class ', class(counts[[layer]])[1], ". Coercing to dgCMatrix.", + call. = FALSE, immediate. = TRUE) + if (inherits(x = counts[[layer]], what = "data.frame")) { + counts[[layer]] <- as.sparse(x = counts[[layer]], ...) + } else { + counts[[layer]] <- as.sparse(x = counts[[layer]]) + } + } + # Filter based on min.cells + if (min.cells > 0) { + for (layer in names(x = counts)) { + if (inherits(x = counts[[layer]], what = "IterableMatrix")) { + check_installed(pkg = 'BPCells', reason = 'for working with BPCells') + row_stat <- BPCells::matrix_stats(matrix = counts[[layer]], row_stats = 'nonzero')$row_stats + features.use <- which(x = row_stat >= min.cells) + } else { + features.use <- which(x = fsum(counts[[layer]] > 0) >= min.cells) + } + counts[[layer]] <- if (fdim == 1L) { + counts[[layer]][features.use, ] + } else { + counts[[layer]][, features.use] + } + features[[layer]] <- features[[layer]][features.use] + } + } + features.all <- Reduce(f = union, x = features) + cells.all <- Reduce(f = union, x = cells) + calcN_option <- getOption( + x = 'Seurat.object.assay.calcn', + default = Seurat.options$Seurat.object.assay.calcn + ) + # Create the object + object <- new( + Class = type, + layers = list(), + default = 0L, + features = LogMap(y = features.all), + cells = LogMap(y = cells.all), + meta.data = EmptyDF(n = length(x = features.all)), + misc = list(calcN = calcN_option %||% TRUE), + ... + ) + for (layer in names(x = counts)) { + LayerData( + object = object, + layer = layer, + features = features[[layer]], + cells = cells[[layer]], + transpose = transpose + ) <- counts[[layer]] + } + DefaultLayer(object = object) <- Layers(object = object)[1L] + validObject(object = object) + return(object) +} + +#' @rdname dot-CreateStdAssay +#' @method .CreateStdAssay Matrix +#' @export +#' +.CreateStdAssay.Matrix <- function( + counts, + min.cells = 0, + min.features = 0, + cells = NULL, + features = NULL, + transpose = FALSE, + type = 'Assay5', + layer = 'counts', + ... +) { + counts <- list(counts) + names(x = counts) <- layer + if (isTRUE(x = transpose)) { + csum <- Matrix::rowSums + fsum <- Matrix::colSums + } else { + csum <- Matrix::colSums + fsum <- Matrix::rowSums + } + return(.CreateStdAssay( + counts = counts, + layer = layer, + min.cells = min.cells, + min.features = min.features, + cells = cells, + features = features, + transpose = transpose, + type = type, + csum = csum, + fsum = fsum, + ... + )) +} + +#' @rdname dot-CreateStdAssay +#' @method .CreateStdAssay matrix +#' @export +#' +.CreateStdAssay.matrix <- .CreateStdAssay.Matrix + +#' @method .MARGIN Assay5T +#' @export +#' +.MARGIN.Assay5T <- function(x, type = c('features', 'cells'), ...) { + type <- type[1] + type <- match.arg(arg = type) + return(unname(obj = c(features = 2L, cells = 1L)[type])) +} + +#' @method .SelectFeatures StdAssay +#' @export +#' +.SelectFeatures.StdAssay <- function(object, ...) { + .NotYetImplemented() +} + +#' @templateVar fxn AddMetaData +#' @template method-stdassay +#' +#' @method AddMetaData StdAssay +#' @export +#' +AddMetaData.StdAssay <- AddMetaData.Assay + +#' @rdname AddMetaData +#' @method AddMetaData Assay5 +#' @export +#' +AddMetaData.Assay5 <- AddMetaData.StdAssay + +#' @templateVar fxn CastAssay +#' @template method-stdassay +#' +#' @importFrom methods as +#' @importFrom rlang quo_get_env quo_get_expr +#' +#' @method CastAssay StdAssay +#' @export +#' +CastAssay.StdAssay <- function(object, to, layers = NA, verbose = TRUE, ...) { + layers <- Layers(object = object, search = layers) + if (is_quosure(x = to)) { + to <- eval( + expr = quo_get_expr(quo = to), + envir = quo_get_env(quo = to) + ) + } + stopifnot(is.character(x = to) || is.function(x = to)) + for (lyr in layers) { + if (isTRUE(x = verbose)) { + msg <- paste("Attempting to cast layer", lyr) + if (is.character(x = to)) { + msg <- paste(msg, "to", to) + } + message(msg) + } + clyr <- Cells(x = object, layer = lyr) + flyr <- Features(x = object, layer = lyr) + w <- function(e) { + warn(message = paste0( + "Unable to cast layer ", + sQuote(x = lyr), + ": ", + e$message + )) + return(invisible(x = NULL)) + } + if (is.function(x = to)) { + tryCatch( + expr = LayerData( + object = object, + layer = lyr, + cells = clyr, + features = flyr + ) <- to(LayerData(object = object, layer = lyr, fast = TRUE), ...), + error = w + ) + } else { + check <- is( + object = LayerData(object = object, layer = lyr, fast = TRUE), + class2 = to + ) + if (isTRUE(x = check)) { + next + } + tryCatch( + expr = LayerData( + object = object, + layer = lyr, + cells = clyr, + features = flyr + ) <- as( + object = LayerData(object = object, layer = lyr, fast = TRUE), + Class = to + ), + error = w + ) + } + } + return(object) +} + +#' @template param-verbose +#' @param layers A vector of layers to cast; defaults to all layers +#' +#' @rdname CastAssay +#' @method CastAssay Assay5 +#' @export +#' +CastAssay.Assay5 <- CastAssay.StdAssay + +#' @templateVar fxn Cells +#' @template method-stdassay +#' +#' @method Cells StdAssay +#' @export +#' +Cells.StdAssay <- function(x, layer = NULL, simplify = TRUE, ...) { + if (any(is.na(x = layer)) || is.null(x = layer)) { + return(rownames(x = slot(object = x, name = 'cells'))) + } + layer <- Layers(object = x, search = layer) + cells <- sapply( + X = layer, + FUN = function(lyr) { + return(slot(object = x, name = 'cells')[[lyr]]) + }, + simplify = FALSE, + USE.NAMES = TRUE + ) + if (isFALSE(x = simplify)) { + return(cells) + } + return(Reduce(f = union, x = cells)) +} + +#' @param layer Layer to pull cells/features for; defaults to default layer; +#' if \code{NA}, returns all cells for the assay +#' @param simplify Simplify the cell/feature names into a single vector; if +#' \code{FALSE}, separates each cell/feature names by layer +#' +#' @rdname Cells +#' @method Cells Assay5 +#' @export +#' +Cells.Assay5 <- Cells.StdAssay + +#' @templateVar fxn DefaultAssay +#' @template method-stdassay +#' +#' @method DefaultAssay StdAssay +#' @export +#' +DefaultAssay.StdAssay <- function(object, ...) { + return(slot(object = object, name = 'assay.orig')) +} + +#' @rdname DefaultAssay +#' @method DefaultAssay Assay5 +#' @export +#' +DefaultAssay.Assay5 <- DefaultAssay.StdAssay + +#' @rdname DefaultAssay-StdAssay +#' @method DefaultAssay<- StdAssay +#' @export +#' +"DefaultAssay<-.StdAssay" <- function(object, ..., value) { + slot(object = object, name = 'assay.orig') <- value + return(object) +} + +#' @rdname DefaultAssay +#' @method DefaultAssay<- Assay5 +#' @export +#' +"DefaultAssay<-.Assay5" <- `DefaultAssay<-.StdAssay` + +#' @templateVar fxn DefaultLayer +#' @template method-stdassay +#' +#' @method DefaultLayer StdAssay +#' @export +#' +DefaultLayer.StdAssay <- function(object, ...) { + idx <- slot(object = object, name = 'default') + if (!length(x = idx) || idx == 0L) { + idx <- 1L + } + return(Layers(object = object)[seq_len(length.out = idx)]) +} + +#' @rdname DefaultLayer +#' @method DefaultLayer Assay5 +#' @export +#' +DefaultLayer.Assay5 <- DefaultLayer.StdAssay + +#' @rdname DefaultLayer-StdAssay +#' @method DefaultLayer<- StdAssay +#' @export +#' +"DefaultLayer<-.StdAssay" <- function(object, ..., value) { + layers <- Layers(object = object) + value <- Layers(object = object, search = value) + idx <- MatchCells(new = layers, orig = value, ordered = TRUE) + slot(object = object, name = 'layers') <- c( + slot(object = object, name = 'layers')[idx], + slot(object = object, name = 'layers')[-idx] + ) + slot(object = object, name = 'default') <- length(x = value) + validObject(object = object) + return(object) +} + +#' @rdname DefaultLayer +#' @method DefaultLayer<- Assay5 +#' @export +#' +"DefaultLayer<-.Assay5" <- `DefaultLayer<-.StdAssay` + +#' @rdname Cells-StdAssay +#' @method Features StdAssay +#' @export +#' +Features.StdAssay <- function(x, layer = NULL, simplify = TRUE, ...) { + if (any(is.na(x = layer)) || is.null(x = layer)) { + return(rownames(x = slot(object = x, name = 'features'))) + } + layer <- Layers(object = x, search = layer) + features <- sapply( + X = layer, + FUN = function(lyr) { + return(slot(object = x, name = 'features')[[lyr]]) + }, + simplify = FALSE, + USE.NAMES = TRUE + ) + if (isFALSE(x = simplify)) { + return(features) + } + return(Reduce(f = union, x = features)) +} + +#' @rdname Cells +#' @method Features Assay5 +#' @export +#' +Features.Assay5 <- Features.StdAssay + +#' @method FetchData StdAssay +#' @export +#' +FetchData.StdAssay <- function( + object, + vars, + cells = NULL, + layer = NULL, + clean = TRUE, + ... +) { + # Identify layer(s) to use + layer.set <- rev(x = Layers( + object = object, + search = layer %||% 'data' + )) + if (is.null(layer) && length(layer.set) == 1 && layer.set == 'scale.data'){ + warning('Default search for "data" layer yielded no results; utilizing "scale.data" layer instead.') + } + if (is.null(layer.set) & is.null(layer) ) { + warning('data layer is not found and counts layer is used') + layer.set <- rev(x = Layers( + object = object, + search = 'counts' + )) + } + if (is.null(layer.set)) { + stop('layer "', layer,'" is not found in the object') + } else { + layer <- layer.set + } + + # Identify cells to use + cells <- cells %||% colnames(x = object) + if (is.numeric(x = cells)) { + cells <- colnames(x = object)[cells] + } + cells <- intersect(x = cells, y = colnames(x = object)) + if (!length(x = cells)) { + abort(message = "None of the cells requested found in this assay") + } + # Check vars + orig <- vars + vars <- gsub( + pattern = paste0('^', Key(object = object)), + replacement = '', + x = vars + ) + # Pull expression information + features <- sapply( + X = layer, + FUN = Features, + x = object, + simplify = FALSE, + USE.NAMES = TRUE + ) + vars <- intersect(x = vars, y = Reduce(f = union, x = features)) + data.fetched <- as.data.frame(x = matrix( + data = NA_real_, + nrow = length(x = cells), + ncol = length(x = vars), + dimnames = list(cells, vars) + )) + for (lyr in layer) { + lcells <- intersect(x = cells, y = Cells(x = object, layer = lyr)) + lvars <- intersect(x = vars, y = Features(x = object, layer = lyr)) + if (!length(x = lcells) || !length(x = lvars)) { + next + } + data.fetched[lcells, lvars] <- as(t(x = LayerData( + object = object, + layer = lyr, + cells = lcells, + features = lvars + )[lvars, lcells, drop = FALSE]), + "matrix") + } + # Clean out missing cells from the expression matrix + if (isTRUE(x = clean)) { + no.data <- which(x = apply( + X = data.fetched, + MARGIN = 1L, + FUN = function(x) { + return(all(is.na(x = x))) + } + )) + if (length(x = no.data)) { + warn(message = paste( + "Removing", + length(x = no.data), + "cells missing data for features requested" + )) + data.fetched <- data.fetched[-no.data, , drop = FALSE] + } + } + # Add keys to keyed vars + keyed.features <- paste0(Key(object = object), colnames(x = data.fetched)) + keyed.idx <- which(x = keyed.features %in% orig) + if (length(x = keyed.idx)) { + colnames(x = data.fetched)[keyed.idx] <- keyed.features[keyed.idx] + } + # Check final list of features + fetched <- setdiff(x = unlist(x = dimnames(x = data.fetched)), y = cells) + missing <- setdiff(x = orig, y = fetched) + if (length(x = missing) == length(x = orig)) { + abort(message = "None of the requested variables found", class = 'varsNotFoundError') + } else if (length(x = missing)) { + warn(message = paste( + "The following variables could not be found:", + paste(missing, collapse = ', ') + )) + } + return(data.fetched) + # # Pull feature-level metadata + # meta.fetch <- c( + # grep(pattern = '^md_', x = vars, value = TRUE), + # vars[vars %in% colnames(x = object[[]])] + # ) + # meta.fetch <- setdiff(x = meta.fetch, y = colnames(x = data.fetched)) + # meta.keyed <- which(x = grepl(pattern = '^md', x = meta.fetch)) + # meta.fetch <- gsub(pattern = '^md_', replacement = '', x = meta.fetch) + # meta.data <- lapply( + # X = meta.fetch, + # FUN = function(x, f) { + # df <- as.data.frame(x = matrix( + # data = NA, + # nrow = 1L, + # ncol = length(x = f), + # dimnames = list(x, f) + # )) + # df[x, ] <- object[[x]][f, , drop = TRUE] + # return(df) + # }, + # f = colnames(x = data.fetched) + # ) + # meta.data <- do.call(what = 'rbind', args = meta.data) + # if (length(x = meta.keyed)) { + # rownames(x = meta.data)[meta.keyed] <- paste0( + # 'md_', + # rownames(x = meta.data)[meta.keyed] + # ) + # } + # keyed.meta <- paste0(Key(object = object), rownames(x = meta.data)) + # keyed.meta.idx <- which(x = keyed.meta %in% orig) + # if (length(x = keyed.meta.idx)) { + # rownames(x = meta.data)[keyed.meta.idx] <- keyed.meta[keyed.meta.idx] + # } + # if (nrow(x = data.fetched) && (nrow(x = meta.data) %||% 0)) { + # warning( + # "Returning both expression and meta data; data types might be different than expected", + # call. = FALSE, + # immediate. = TRUE + # ) + # } + # data.fetched <- rbind(data.fetched, meta.data) + # # Add keys to keyed vars + # keyed.features <- paste0(Key(object = object), colnames(x = data.fetched)) + # keyed.idx <- which(x = keyed.features %in% orig) + # if (length(x = keyed.idx)) { + # colnames(x = data.fetched)[keyed.idx] <- keyed.features[keyed.idx] + # } + # # Check final list of features + # fetched <- setdiff(x = unlist(x = dimnames(x = data.fetched)), y = cells) + # missing <- setdiff(x = orig, y = fetched) + # if (length(x = missing) == length(x = orig)) { + # stop("None of the requested variables found", call. = FALSE) + # } else if (length(x = missing)) { + # warning( + # "The following variables could not be found: ", + # paste(missing, collapse = ', '), + # call. = FALSE, + # immediate. = TRUE + # ) + # } + # return(data.fetched) +} + +#' @method FetchData Assay5 +#' @export +#' +FetchData.Assay5 <- FetchData.StdAssay + +#' @templateVar fxn AssayData +#' @template method-stdassay +#' +#' @method GetAssayData StdAssay +#' @export +#' +GetAssayData.StdAssay <- function( + object, + layer = NULL, + slot = deprecated(), + ... +) { + CheckDots(..., fxns = LayerData) + if (is_present(arg = slot)) { + .Deprecate( + when = '5.0.0', + what = 'GetAssayData(slot = )', + with = 'GetAssayData(layer = )' + ) + layer <- slot + } + layer_name <- layer[1L] %||% DefaultLayer(object = object)[1L] + layer.set <- suppressWarnings(expr = Layers( + object = object, + search = layer %||% 'data' + )) + if (is.null(layer.set) & is.null(layer)) { + warning('data layer is not found and counts layer is used') + layer <- rev(x = Layers( + object = object, + search = 'counts' + )) + } else { + layer <- layer.set + } + if (length(x = layer) > 1) { + abort("GetAssayData doesn't work for multiple layers in v5 assay.", + " You can run 'object <- JoinLayers(object = object, layers = layer)'.") + } + if (is.null(x = layer)) { + msg <- paste("Layer", sQuote(x = layer_name), "is empty") + opt <- getOption(x = "Seurat.object.assay.v3.missing_layer", + default = Seurat.options$Seurat.object.assay.v3.missing_layer) + opt <- tryCatch(expr = arg_match0( + arg = opt, + values = c("matrix","null", "error")), + error = function(...) { + return(Seurat.options$Seurat.object.assay.v3.missing_layer) + } + ) + if (opt == "error") { + abort(message = msg) + } + warn(message = msg) + return(switch( + EXPR = opt, + matrix = switch( + EXPR = layer_name, + scale.data = new(Class = "matrix"), new(Class = "dgCMatrix") + ), + NULL)) + } + return(LayerData(object = object, layer = layer, ...)) +} + +#' @templateVar fxn VariableFeatures +#' @template method-stdassay +#' +#' @importFrom utils adist +#' +#' @method HVFInfo StdAssay +#' @export +#' +HVFInfo.StdAssay <- function( + object, + method = NULL, + status = FALSE, + layer = NULL, + strip = TRUE, + ... +) { + # Find available HVF methods and layers + vf.methods.layers <- .VFMethodsLayers(object = object, type = 'hvf') + #vf.methods <- .VFMethods(object = object, type = 'hvf') + #vf.layers <- .VFLayers(object = object, type = 'hvf') + # Determine which method and layer to use + method <- method[length(methods)] %||% names(vf.methods.layers[length(vf.methods.layers)]) + method <- switch( + EXPR = tolower(x = method), + mean.var.plot = 'mvp', + dispersion = 'disp', + method + ) + method <- tryCatch( + expr = match.arg(arg = method, choices = names(vf.methods.layers)), + error = function(...) { + return(NULL) + } + ) + # If no methods found, return NULL + if (is.null(x = method)) { + return(method) + } + vf.methods.layers <- unlist(vf.methods.layers, use.names = FALSE) + layer <- Layers(object = object, search = layer) + layer <- vf.methods.layers[which.min(x = adist(x = layer, y = vf.methods.layers))] + # Find the columns for the specified method and layer + cols <- grep( + pattern = paste0(paste('^vf', method, layer, sep = '_'), '_'), + x = colnames(x = object[[]]), + value = TRUE + ) + if (!isTRUE(x = status)) { + cols <- setdiff( + x = cols, + y = paste('vf', method, layer, c('variable', 'rank'), sep = '_') + ) + } + hvf.info <- object[[cols]] + colnames(x = hvf.info) <- gsub( + pattern = '^vf_', + replacement = '', + x = colnames(x = hvf.info) + ) + if (isTRUE(x = strip)) { + colnames(x = hvf.info) <- gsub( + pattern = paste0(paste(method, layer, sep = '_'), '_'), + replacement = '', + x = colnames(x = hvf.info) + ) + } + return(hvf.info) +} + +#' @param layer Layer to pull variable features for +#' @param strip Remove method/layer identifiers from highly variable data frame +#' +#' @rdname VariableFeatures +#' @method HVFInfo Assay5 +#' @export +#' +HVFInfo.Assay5 <- HVFInfo.StdAssay + +#' @method JoinLayers StdAssay +#' @export +#' +JoinLayers.StdAssay <- function( + object, + layers = NULL, + new = NULL, + ... +) { + layers <- layers %||% c('counts', 'data', 'scale.data') + new <- new %||% layers + if (length(x = layers) != length(x = new)) { + stop('Number of layers and new should be the same') + } + var.features <- VariableFeatures(object = object) + for (i in seq_along(layers)) { + num.layers <- suppressWarnings( + expr = length(x = Layers(object = object, search = layers[i])) + ) + if (num.layers > 0L) { + object <- JoinSingleLayers( + object = object, + layers = layers[i], + new = new[i], + default = TRUE, + ... + ) + } + } + VariableFeatures(object = object) <- var.features + return(object) +} + +#' @param layers Names of layers to split or join +#' @param new Name of new layers +#' +#' @rdname SplitLayers +#' +#' @method JoinLayers Assay5 +#' @export +#' +JoinLayers.Assay5 <- JoinLayers.StdAssay + +#' @rdname Key +#' @method Key Assay5 +#' @export +#' +Key.Assay5 <- .Key + +#' @rdname Key +#' @method Key<- Assay5 +#' @export +#' +"Key<-.Assay5" <- `.Key<-` + +#' @templateVar fxn Layers +#' @template method-stdassay +#' +#' @method LayerData StdAssay +#' @export +#' +LayerData.StdAssay <- function( + object, + layer = NULL, + cells = NULL, + features = NULL, + fast = FALSE, + slot = deprecated(), + ... +) { + if (is_present(arg = slot)) { + deprecate_stop( + when = '5.0.0', + what = 'LayerData(slot = )', + with = 'LayerData(layer = )"' + ) + } + layer_name <- layer[1L] %||% DefaultLayer(object = object)[1L] + # Identify layer(s) to use + layer.set <- suppressWarnings(expr = Layers( + object = object, + search = layer %||% 'data' + )) + # If layer.set doesnt return anything and layer is not defined + if (is.null(layer.set) & is.null(layer) ) { + warning( + 'data layer is not found and counts layer is used', + call. = F, + immediate. = T + ) + layer <- Layers( + object = object, + search = 'counts' + ) + } else { + layer <- layer.set + } + + if (length(x = layer) > 1) { + warning("multiple layers are identified by ", + paste0(layer, collapse = ' '), + "\n only the first layer is used") + layer <- layer[1L] + } + # layer <- match.arg(arg = layer, choices = Layers(object = object)) + if (is.null(x = layer) || any(is.na(x = layer))) { + msg <- paste("Layer", sQuote(x = layer_name), "is empty") + opt <- getOption(x = "Seurat.object.assay.v3.missing_layer", + default = Seurat.options$Seurat.object.assay.v3.missing_layer) + opt <- tryCatch(expr = arg_match0( + arg = opt, + values = c("matrix","null", "error")), + error = function(...) { + return(Seurat.options$Seurat.object.assay.v3.missing_layer) + } + ) + if (opt == "error") { + abort(message = msg) + } + warn(message = msg) + return(switch( + EXPR = opt, + matrix = switch( + EXPR = layer_name, + scale.data = new(Class = "matrix"), new(Class = "dgCMatrix") + ), + NULL)) + } + # Allow cell/feature subsets + dnames <- list( + Features(x = object, layer = layer), + Cells(x = object, layer = layer) + ) + cells <- cells %||% dnames[[2L]] + if (is.numeric(x = cells)) { + cells <- dnames[[2L]][cells] + } + cells <- sort(x = MatchCells( + new = dnames[[2L]], + orig = cells, + ordered = TRUE + )) + dnames[[2L]] <- dnames[[2L]][cells] + features <- features %||% dnames[[1L]] + if (is.numeric(x = features)) { + features <- dnames[[1L]][features] + } + features <- sort(x = MatchCells( + new = dnames[[1L]], + orig = features, + ordered = TRUE + )) + dnames[[1L]] <- dnames[[1L]][features] + if(length(x = dnames[[1L]]) == 0) { + stop('features are not found') + } + # Pull the layer data + ldat <- if (.MARGIN(x = object) == 1L) { + methods::slot(object = object, name = 'layers')[[layer]][features, cells, drop = FALSE] + } else { + methods::slot(object = object, name = 'layers')[[layer]][cells, features, drop = FALSE] + } + # Add dimnames and transpose if requested + ldat <- if (isTRUE(x = fast)) { + ldat + } else if (is_na(x = fast)) { + .GetLayerData2(x = ldat, dnames = dnames, fmargin = 1L) + # .GetLayerData( + # x = ldat, + # dnames = dnames, + # fmargin = 1L, + # ... + # ) + } else { + .GetLayerData2( + x = ldat, + dnames = dnames, + fmargin = .MARGIN(x = object, type = 'features') + ) + # .GetLayerData( + # x = ldat, + # dnames = dnames, + # fmargin = .MARGIN(x = object, type = 'features'), + # ... + # ) + } + return(ldat) +} + +#' @param features,cells Vectors of features/cells to include +#' @param fast Determine how to return the layer data; choose from: +#' \describe{ +#' \item{\code{FALSE}}{Apply any transpositions and attempt to add +#' feature/cell names (if supported) back to the layer data} +#' \item{\code{NA}}{Attempt to add feature/cell names back to the layer data, +#' skip any transpositions} +#' \item{\code{TRUE}}{Do not apply any transpositions or add feature/cell +#' names to the layer data} +#' } +#' +#' @rdname Layers +#' @method LayerData Assay5 +#' @export +#' +LayerData.Assay5 <- LayerData.StdAssay + +#' +#' @rdname Layers-StdAssay +#' @method LayerData<- StdAssay +#' @export +#' +"LayerData<-.StdAssay" <- function( + object, + layer, + features = NULL, + cells = NULL, + ..., + value +) { + if (!is_scalar_character(x = layer) || !nzchar(x = layer)) { + abort(message = "'layer' must be a single non-empty string") + } + # Remove a layer + if (is.null(x = value)) { + if (length(x = Layers(object = object)) == 1L) { + stop("Cannot remove only layer") + } else if (layer %in% DefaultLayer(object = object)) { + msg <- 'Removing default layer' + if (length(x = DefaultLayer(object = object)) == 1L) { + DefaultLayer(object = object) <- Layers(object = object)[2] + msg <- paste0( + msg, + ', setting default to ', DefaultLayer(object = object) + ) + } else { + didx <- slot(object = object, name = 'default') - 1L + slot(object = object, name = 'default') <- didx + } + warning(msg, call. = FALSE, immediate. = TRUE) + } + slot(object = object, name = 'layers')[[layer]] <- NULL + if (slot(object = object, name = 'default') > length(x = Layers(object = object)) || + !length(x = slot(object = object, name = 'default'))) { + slot(object = object, name = 'default') <- length(x = Layers(object = object)) + } + maps <- c( + 'cells', + 'features' + ) + for (i in maps) { + slot(object = object, name = i)[[layer]] <- NULL + } + validObject(object = object) + return(object) + } + # Add a layer + fdim <- .MARGIN(x = object, type = 'features') + cdim <- .MARGIN(x = object, type = 'cells') + # Assume input matrix is features x cells + dnames <- list( + features %||% dimnames(x = value)[[1L]], + cells %||% dimnames(x = value)[[2L]] + ) + if (length(x = unique(x = dim(x = value))) > 1L) { + didx <- match( + x = vapply( + X = dnames, + FUN = length, + FUN.VALUE = numeric(length = 1L), + USE.NAMES = FALSE + ), + table = dim(x = value) + ) + dnames <- dnames[didx] + } + value <- .PrepLayerData2( + x = value, + target = dim(x = object), + dnames = dnames, + fmargin = fdim, + ... + ) + # value <- .PrepLayerData( + # x = value, + # target = dim(x = object), + # dnames = dnames, + # fmargin = fdim, + # ... + # ) + # Check features and cells + features <- attr(x = value, which = 'features') %||% seq_len(length.out = dim(x = value)[fdim]) + cells <- attr(x = value, which = 'cells') %||% seq_len(length.out = dim(x = value)[cdim]) + fmatch <- MatchCells( + new = features, + orig = rownames(x = slot(object = object, name = 'features')), + ordered = TRUE + ) + cmatch <- MatchCells( + new = cells, + orig = rownames(x = slot(object = object, name = 'cells')), + ordered = TRUE + ) + if (is.null(x = fmatch)) { + stop( + "No feature overlap between existing object and new layer data", + call. = FALSE + ) + } else if (is.null(x = cmatch)) { + stop( + "No cell overlap between existing object and new layer data", + call. = FALSE + ) + } + features <- features[fmatch] + cells <- cells[cmatch] + # Check for existing layer data + if (layer %in% Layers(object = object)) { + fcheck <- if (is.numeric(x = features)) { + Features(x = object, layer = layer)[features] + } else { + features + } + ccheck <- if (is.numeric(x = cells)) { + Cells(x = object, layer = layer)[cells] + } else { + cells + } + if (!identical(x = fcheck, y = Features(x = object, layer = layer))) { + warning( + "Different features in new layer data than already exists for ", + layer, + call. = FALSE, + immediate. = TRUE + ) + } + if (!identical(x = ccheck, y = Cells(x = object, layer = layer))) { + warning( + "Different cells in new layer data than already exists for ", + layer, + call. = FALSE, + immediate. = TRUE + ) + } + } + # Reorder the layer data + value <- if (fdim == 1L) { + value[fmatch, cmatch] + } else { + value[cmatch, fmatch] + } + # Add the layer + slot(object = object, name = 'layers')[[layer]] <- value + # Update the maps + slot(object = object, name = 'features')[[layer]] <- features + slot(object = object, name = 'cells')[[layer]] <- cells + validObject(object = object) + return(object) +} + +#' @rdname Layers +#' @method LayerData<- Assay5 +#' @export +#' +"LayerData<-.Assay5" <- `LayerData<-.StdAssay` + +#' @rdname Layers-StdAssay +#' @method Layers StdAssay +#' @export +#' +Layers.StdAssay <- function(object, search = NA, ...) { + if (is.null(x = search)) { + return(DefaultLayer(object = object)) + } + layers <- names(x = slot(object = object, name = 'layers')) + if (!is_na(x = search)) { + layers <- unique(x = unlist(x = lapply( + X = search, + FUN = function(lyr) { + if (lyr %in% layers) { + return(lyr) + } + patterns <- c(paste0('^', lyr), paste0(lyr, '$'), lyr) + res <- vector(mode = 'character') + for (p in patterns) { + res <- grep(pattern = p, x = layers, value = TRUE, ...) + if (length(x = res)) { + break + } + } + return(res) + } + ))) + if (!length(x = layers)) { + warning(message = "No layers found matching search pattern provided", + call. = FALSE, + immediate. = TRUE) + return(NULL) + } + } + return(layers) +} + +#' @param search A pattern to search layer names for; pass one of: +#' \itemize{ +#' \item \dQuote{\code{NA}} to pull all layers +#' \item \dQuote{\code{NULL}} to pull the default layer(s) +#' \item a \link[base:grep]{regular expression} that matches layer names +#' } +#' +#' @rdname Layers +#' @method Layers Assay5 +#' @export +#' +Layers.Assay5 <- Layers.StdAssay + +#' @templateVar fxn Misc +#' @template method-stdassay +#' +#' @method Misc StdAssay +#' @export +#' +Misc.StdAssay <- .Misc + +#' @rdname Misc +#' @method Misc Assay5 +#' @export +#' +Misc.Assay5 <- .Misc + +#' @templateVar fxn Misc +#' @template method-stdassay +#' +#' @method Misc<- StdAssay +#' @export +#' +"Misc<-.StdAssay" <- `.Misc<-` + +#' @rdname Misc +#' @method Misc<- Assay5 +#' @export +#' +"Misc<-.Assay5" <- `.Misc<-` + +#' @templateVar fxn RenameCells +#' @template method-stdassay +#' +#' @method RenameCells StdAssay +#' @export +#' +RenameCells.StdAssay <- function(object, new.names = NULL, ...) { + CheckDots(...) + colnames(object) <- new.names[colnames(object)] + return(object) +} + +#' @rdname RenameCells +#' @method RenameCells Assay5 +#' @export +#' +RenameCells.Assay5 <- RenameCells.StdAssay + +#' @rdname AssayData-StdAssay +#' @method SetAssayData StdAssay +#' @export +#' +SetAssayData.StdAssay <- function( + object, + layer, + new.data, + slot = deprecated(), + ... +) { + if (is_present(arg = slot)) { + .Deprecate( + when = '5.0.0', + what = 'SetAssayData(slot = )', + with = 'SetAssayData(layer = )' + ) + layer <- slot + } + LayerData(object = object, layer = slot) <- new.data + return(object) +} + +#' @rdname VariableFeatures-StdAssay +#' @method VariableFeatures StdAssay +#' @export +#' +VariableFeatures.StdAssay <- function( + object, + method = NULL, + layer = NA, + simplify = TRUE, + nfeatures = Inf, + selection.method = deprecated(), + ... +) { + if (is_present(arg = selection.method)) { + .Deprecate( + when = '5.0.0', + what = 'VariableFeatures(selection.method = )', + with = 'VariableFeatures(method = )' + ) + method <- selection.method + } + nfeatures <- nfeatures %||% Inf + if ("var.features" %in% colnames(object[[]])) { + if ("var.features.rank" %in% colnames(object[[]])) { + var.features <- row.names(x = object[[]])[which(!is.na(object[[]]$var.features.rank))] + var.features <- var.features[order(object[[]][["var.features.rank"]][which(!is.na(object[[]]$var.features))])] + } + else { + var.features <- as.vector(object["var.features", drop = TRUE]) + var.features <- var.features[!is.na(var.features)] + } + if (isTRUE(x = simplify) & (is.null(x = layer) || any(is.na(x = layer))) & + (is.infinite(x = nfeatures) || length(x = var.features) == + nfeatures)) { + return(var.features) + } + } + msg <- 'No variable features found' + layer.orig <- layer + methods <- .VFMethodsLayers(object = object, type = 'hvf', layers = layer) + layer <- Layers(object = object, search = layer) + method <- method %||% names(x = methods)[length(x = methods)] + method <- match.arg(arg = method, choices = names(x = methods)) + if (is_na(x = layer.orig) || is.null(x = layer.orig)) { + layer <- unlist(methods[method], use.names = FALSE) + } + vf <- sapply( + X = layer, + FUN = function(lyr) { + hvf.info <- HVFInfo( + object = object, + method = method, + layer = lyr, + status = TRUE, + strip = TRUE + ) + if (is.null(x = hvf.info)) { + return(NULL) + } else if (!'variable' %in% names(x = hvf.info)) { + return(NA) + } + vf <- row.names(x = hvf.info)[which(x = hvf.info$variable)] + if ('rank' %in% names(x = hvf.info)) { + vf <- vf[order(hvf.info$rank[which(x = hvf.info$variable)])] + } else { + warn(message = paste0( + "No variable feature rank found for ", + sQuote(x = lyr), + ", returning features in assay order" + )) + } + }, + simplify = FALSE, + USE.NAMES = TRUE + ) + if (is.null(x = unlist(x = vf))) { + return(NULL) + } else if (all(is.na(x = unlist(x = vf)))) { + abort(message = msg) + } + if (isTRUE(x = simplify)) { + vf <- .SelectFeatures( + object = vf, + all.features = intersect( + x = slot(object = object, name = 'features')[,layer] + ), + nfeatures = nfeatures + ) + } + return(vf) + # hvf.info <- HVFInfo( + # object = object, + # method = method, + # layer = layer, + # status = TRUE, + # strip = TRUE + # ) + # if (is.null(x = hvf.info)) { + # warning(msg, call. = FALSE, immediate. = TRUE) + # return(NULL) + # } + # if (!'variable' %in% names(x = hvf.info)) { + # stop(msg, call. = FALSE) + # } + # vf <- rownames(x = hvf.info)[which(x = hvf.info$variable)] + # if ('rank' %in% names(x = hvf.info)) { + # vf <- vf[order(hvf.info$rank[which(x = hvf.info$variable)])] + # } else { + # warning( + # "No variable feature rank found, returning features in assay order", + # call. = FALSE, + # immediate. = TRUE + # ) + # } + # return(vf) +} + +#' @param simplify When pulling for multiple layers, combine into a single +#' vector and select a common set of variable features for all layers +#' @param nfeatures Maximum number of features to select when simplifying +#' +#' @rdname VariableFeatures +#' @method VariableFeatures Assay5 +#' @export +#' +VariableFeatures.Assay5 <- VariableFeatures.StdAssay + +#' @rdname VariableFeatures-StdAssay +#' @method VariableFeatures<- StdAssay +#' @export +#' +"VariableFeatures<-.StdAssay" <- function( + object, + method = 'custom', + layer = NULL, + ..., + value +) { + if (!length(x = value)) { + return(object) + } + value <- intersect(x = value, y = rownames(x = object)) + if (!length(x = value)) { + stop("None of the features specified are present in this assay", call. = FALSE) + } + object[['var.features']] <- value + # add rank + object[['var.features.rank']] <- NA + object[[]][row.names(object[[]]) %in% value,]$var.features.rank <- match(row.names(object[[]])[row.names(object[[]]) %in% value], value) + + # layer <- Layers(object = object, search = layer) + # df <- data.frame(TRUE, seq_along(along.with = value), row.names = value) + # for (lyr in layer) { + # names(x = df) <- paste('vf', method, lyr, c('variable', 'rank'), sep = '_') + # object[] <- df + # } + return(object) +} + +#' @rdname VariableFeatures +#' @method VariableFeatures<- Assay5 +#' @export +#' +"VariableFeatures<-.Assay5" <- `VariableFeatures<-.StdAssay` + +#' @method WhichCells StdAssay +#' @export +#' +WhichCells.StdAssay <- WhichCells.Assay + +# WhichCells.StdAssay <- function( +# object, +# cells = NULL, +# expression = missing_arg(), +# invert = FALSE, +# ... +# ) { +# cells <- cells %||% colnames(x = object) +# if (!is_missing(x = expression) && !is.null(x = substitute(expr = expression))) { +# key.pattern <- paste0('^', Key(object = object)) +# expr <- if (tryCatch(expr = is_quosure(x = expression), error = \(...) FALSE)) { +# expression +# } else if (is.call(x = enquo(arg = expression))) { +# enquo(arg = expression) +# } else { +# parse(text = expression) +# } +# expr.char <- suppressWarnings(expr = as.character(x = expr)) +# expr.char <- unlist(x = lapply(X = expr.char, FUN = strsplit, split = ' ')) +# expr.char <- gsub( +# pattern = key.pattern, +# replacement = '', +# x = expr.char, +# perl = TRUE +# ) +# expr.char <- gsub( +# pattern = '(', +# replacement = '', +# x = expr.char, +# fixed = TRUE +# ) +# expr.char <- gsub( +# pattern = '`', +# replacement = '', +# x = expr.char +# ) +# } +# if (isTRUE(x = invert)) { +# cells <- setdiff(x = colnames(x = object), y = cells) +# } +# cells <- '' +# return(as.character(x = cells)) +# } + +#' @method WhichCells Assay5 +#' @export +#' +WhichCells.Assay5 <- WhichCells.StdAssay + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Methods for R-defined generics +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#' @inherit .DollarNames.Assay5 params return title description details sections +#' +#' @importFrom utils .DollarNames +#' +#' @keywords internal +#' @method .DollarNames StdAssay +#' @export +#' +#' @family stdassay +#' +.DollarNames.StdAssay <- function(x, pattern = '') { + layers <- as.list(x = Layers(object = x)) + names(x = layers) <- unlist(x = layers) + return(.DollarNames(x = layers, pattern = pattern)) +} + +#' Dollar-sign Autocompletion +#' +#' Autocompletion for \code{$} access on an \code{\link{Assay5}} object +#' +#' @inheritParams [.Assay5 +#' @inheritParams utils::.DollarNames +#' +#' @return The layer name matches for \code{pattern} +#' +#' @importFrom utils .DollarNames +#' +#' @keywords internal +#' +#' @method .DollarNames Assay5 +#' @export +#' +#' @concept assay5 +#' +#' @seealso \code{\link[utils:.DollarNames]{utils::.DollarNames}} +#' +.DollarNames.Assay5 <- .DollarNames.StdAssay + +#' @inherit $.Assay5 params return title description details sections +#' +#' @keywords internal +#' @method $ StdAssay +#' @export +#' +#' @family stdassay +#' +"$.StdAssay" <- function(x, i) { + return(LayerData(object = x, layer = i)) +} + +#' Layer Data +#' +#' Get and set layer data +#' +#' @inheritParams [.Assay5 +#' +#' @return {$}: Layer data for layer \code{i} +#' +#' @method $ Assay5 +#' @export +#' +#' @family assay5 +#' +"$.Assay5" <- `$.StdAssay` + + +#' @rdname cash-.StdAssay +#' +#' @method $<- StdAssay +#' @export +#' +"$<-.StdAssay" <- `$<-.Assay` + +#' @return \code{$<-}: \code{x} with layer data \code{value} saved as \code{i} +#' +#' @rdname cash-.Assay5 +#' +#' @method $<- Assay5 +#' @export +#' +"$<-.Assay5" <- `$<-.StdAssay` + +#' @inherit [.Assay5 params return title description details sections +#' +#' @keywords internal +#' @method [ StdAssay +#' @export +#' +#' @family stdassay +#' +"[.StdAssay" <- `[.Assay` + +#' Layer Data +#' +#' Get and set layer data +#' +#' @inheritParams [[.Assay5 +#' @param i Name of layer data to get or set +#' @param ... Arguments passed to \code{\link{LayerData}} +#' +#' @return \code{[}: The layer data for layer \code{i} +#' +#' @method [ Assay5 +#' @export +#' +#' @family assay5 +#' +#' @seealso \code{\link{LayerData}} +#' +#' @order 1 +#' +"[.Assay5" <- `[.StdAssay` + +#' @inherit [[.Assay5 params return title description details sections +#' +#' @keywords internal +#' @method [[ StdAssay +#' @export +#' +#' @family stdassay +#' +"[[.StdAssay" <- function(x, i, j, ..., drop = FALSE) { + if (missing(x = i)) { + i <- colnames(x = slot(object = x, name = 'meta.data')) + } + data.return <- slot(object = x, name = 'meta.data')[, i, drop = FALSE, ...] + if (nrow(x = data.return) == 0) { + return(data.return) + } + row.names(x = data.return) <- rownames(x = x) + if (isTRUE(x = drop)) { + data.return <- unlist(x = data.return, use.names = FALSE) + names(x = data.return) <- rep.int( + x = rownames(x = x), + times = length(x = i) + ) + } + return(data.return) +} + +#' Feature-Level Meta Data +#' +#' Get and set feature-level meta data +#' +#' @param x An \code{\link{Assay5}} object +#' @param i Name of feature-level meta data to fetch or add +#' @param j Ignored +#' @param drop See \code{\link{drop}} +#' @template param-dots-ignored +#' +#' @return \code{[[}: The feature-level meta data for \code{i} +#' +#' @method [[ Assay5 +#' @export +#' +#' @family assay5 +#' +#' @order 1 +#' +"[[.Assay5" <- `[[.StdAssay` + +#' @inherit dim.Assay5 params return title description details sections +#' +#' @keywords internal +#' @method dim StdAssay +#' @export +#' +#' @family stdassay +#' +dim.StdAssay <- function(x) { + return(vapply( + X = c('features', 'cells'), + FUN = function(s) { + return(nrow(x = slot(object = x, name = s))) + }, + FUN.VALUE = numeric(length = 1L), + USE.NAMES = FALSE + )) +} + +#' Feature and Cell Numbers +#' +#' @inheritParams [[.Assay5 +#' +#' @return A two-length numeric vector with the total number of +#' features and cells in \code{x} +#' +#' @method dim Assay5 +#' @export +#' +#' @family assay5 +#' +dim.Assay5 <- dim.StdAssay + +#' @inherit dimnames.Assay5 params return title description details sections +#' +#' @keywords internal +#' @method dimnames StdAssay +#' @export +#' +#' @seealso \code{\link{Cells}} \code{\link{Features}} +#' @family stdassay +#' +dimnames.StdAssay <- function(x) { + return(list(Features(x = x, layer = NA), Cells(x = x, layer = NA))) +} + +#' Assay-Level Feature and Cell Names +#' +#' Get and set feature and cell names in v5 Assays +#' +#' @inheritParams [[.Assay5 +#' +#' @return \code{dimnames}: A two-length list with the following values: +#' \itemize{ +#' \item A character vector with all features in \code{x} +#' \item A character vector with all cells in \code{x} +#' } +#' +#' @method dimnames Assay5 +#' @export +#' +#' @family assay5 +#' @family dimnames +#' +dimnames.Assay5 <- dimnames.StdAssay + +#' @rdname dimnames.StdAssay +#' +#' @method dimnames<- StdAssay +#' @export +#' +"dimnames<-.StdAssay" <- function(x, value) { + msg <- "Invalid 'dimnames' given for an assay" + if (!is_bare_list(x = value, n = 2L)) { + stop(msg, call. = FALSE) + } else if (!all(sapply(X = value, FUN = length) == dim(x = x))) { + stop(msg, call. = FALSE) + } + value <- lapply(X = value, FUN = as.character) + rownames(x = slot(object = x, name = 'features')) <- value[[1L]] + rownames(x = slot(object = x, name = 'cells')) <- value[[2L]] + validObject(object = x) + return(x) +} + +#' @param value A two-length list with updated feature and/or cells names +#' +#' @return \code{dimnames<-}: \code{x} with the feature and/or cell +#' names updated to \code{value} +#' +#' @rdname dimnames.Assay5 +#' +#' @method dimnames<- Assay5 +#' @export +#' +"dimnames<-.Assay5" <- `dimnames<-.StdAssay` + +#' @rdname sub-sub-.StdAssay +#' +#' @method head StdAssay +#' @export +#' +head.StdAssay <- head.Assay + +#' @param n Number of meta data rows to show +#' +#' @return \code{head}: The first \code{n} rows of feature-level meta data +#' +#' @rdname sub-sub-.Assay5 +#' +#' @method head Assay5 +#' @export +#' +head.Assay5 <- head.StdAssay + +#' @inherit merge.Assay5 params return title description details sections +#' +#' @note All assays must be of the same type; merging different v5 assays (eg. +#' \code{\link{Assay5}} and \code{\link{Assay5T}}) is currently unsupported +#' +#' @keywords internal +#' @method merge StdAssay +#' @export +#' +merge.StdAssay <- function( + x, + y, + labels = NULL, + add.cell.ids = NULL, + collapse = FALSE, + ... +) { + assays <- c(x, y) + for (i in seq_along(assays)) { + if (inherits(x = assays[[i]], what = 'Assay')) { + assays[[i]] <- as(object = assays[[i]], Class = "Assay5") # TODO: support Assay5T + } + } + labels <- labels %||% as.character(x = seq_along(along.with = assays)) + # add.cell.ids <- add.cell.ids %||% labels + # TODO: Support collapsing layers + if (isTRUE(x = collapse)) { + abort(message = "Collapsing layers is not yet supported") + } + for (i in seq_along(along.with = assays)) { + if (is_na(x = labels[i])) { + labels[i] <- as.character(x = i) + } + if (is_na(x = add.cell.ids[i])) { + add.cell.ids[i] <- as.character(x = i) + } + if (!is.null(x = add.cell.ids[i])) { + colnames(x = assays[[i]]) <- paste( + colnames(x = assays[[i]]), + add.cell.ids[i], sep = '.' + ) + } + } + features.all <- LogMap(y = Reduce( + f = union, + x = lapply(X = assays, FUN = rownames) + )) + combined <- new( + Class = class(x = x), + layers = list(), + cells = LogMap(y = Reduce( + f = union, + x = lapply(X = assays, FUN = colnames) + )), + features = features.all, + meta.data = EmptyDF(n = nrow(x = features.all)), + misc = list(), + key = Key(object = x) %||% character(length = 0L) + ) + + # Add layers + # TODO: Support collapsing layers + if (isTRUE(x = collapse)) { + abort(message = "Collapsing layers is not yet supported") + } else { + # Get default layer as default of first assay + default <- DefaultLayer(assays[[1]]) + for (i in seq_along(along.with = assays)) { + for (lyr in Layers(object = assays[[i]])) { + LayerData( + object = combined, + layer = paste(lyr, labels[i], sep = '.'), + features = Features(x = assays[[i]], layer = lyr), + cells = Cells(x = assays[[i]], layer = lyr) + ) <- LayerData(object = assays[[i]], layer = lyr, fast = TRUE) + } + } + } + + # Add feature-level metadata + for (i in seq_along(along.with = assays)) { + # Rename HVF columns + mf <- assays[[i]][[]] + if (!ncol(x = mf)) { + next + } + for (type in c('vf')) { + vf.idx <- grep(pattern = paste0('^', type, '_'), x = names(x = mf)) + if (length(x = vf.idx)) { + names(x = mf)[vf.idx] <- vapply( + X = names(x = mf)[vf.idx], + FUN = function(vf) { + vf <- unlist(x = strsplit(x = vf, split = '_')) + vf <- paste( + paste(vf[1:2], collapse = '_'), + paste( + paste(vf[3:(length(x = vf) - 1L)], collapse = '_'), + labels[i], + sep = '.' + ), + vf[length(x = vf)], + sep = '_' + ) + }, + FUN.VALUE = character(length = 1L) + ) + } + } + combined[[]] <- mf + } + # TODO: Add misc + DefaultLayer(combined) <- Layers(object = combined, search = default) + validObject(object = combined) + return(combined) +} + +#' Merge Assays +#' +#' Merge one or more v5 assays together +#' +#' \strong{Note}: collapsing layers is currently not supported +#' +#' @inheritParams [.Assay5 +#' @template param-dots-ignored +#' @param y One or more \code{\link{Assay5}} objects +#' @param labels A character vector equal to the number of objects; defaults to +#' \code{as.character(seq_along(c(x, y)))} +#' @param add.cell.ids A character vector equal to the number of objects +#' provided to append to all cell names; if \code{TRUE}, uses \code{labels} as +#' \code{add.cell.ids} +#' @param collapse If \code{TRUE}, merge layers of the same name together; if +#' \code{FALSE}, appends \code{labels} to the layer name +#' +#' @return A new v5 assay with data merged from \code{c(x, y)} +#' +#' @method merge Assay5 +#' @export +#' +#' @family assay5 +#' +merge.Assay5 <- merge.StdAssay + +#' @inherit split.Assay5 params return title description details sections +#' +#' @keywords internal +#' @method split StdAssay +#' @export +#' +#' @family stdassay +#' +split.StdAssay <- function( + x, + f, + drop = FALSE, + layers = c("counts", "data"), + ret = c('assay', 'multiassays', 'layers'), + ... +) { + op <- options(Seurat.object.assay.brackets = 'v5') + on.exit(expr = options(op)) + ret <- ret[1L] + ret <- match.arg(arg = ret) + layers.to.split <- Layers(object = x, search = layers) + if (!identical(Layers(object = x), layers.to.split)) { + message( + 'Splitting ', + paste(sQuote(x = layers.to.split), collapse = ', '), + ' layers. Not splitting ', + paste( + sQuote(x = setdiff(Layers(object = x), layers.to.split)), + collapse = ', ' + ), + '. If you would like to split other layers, set in `layers` argument.' + ) + } + layers <- Layers(object = x, search = layers) + layers.split <- list() + for (i in seq_along(along.with = layers)) { + if (length(x = colnames(x = x[layers[i]])) != length(x = colnames(x = x))) { + layers.split[[i]] <- layers[i] + } + } + layers.split <- unlist(x = layers.split) + if (length(x = layers.split)) { + abort(message = paste( + strwrap(x = paste( + "The following layers are already split:", + paste(sQuote(x = layers.split), collapse = ', '), + "\nPlease join before splitting" + )) + )) + } + default <- ifelse( + test = DefaultLayer(object = x) %in% layers, + yes = DefaultLayer(object = x), + no = layers[1L] + ) + cells <- Cells(x = x, layer = layers) + if (is_named(x = f)) { + f <- f[cells] + } + if (length(x = f) != length(x = cells)) { + abort(message = "Not enough splits for this assay") + } + if (any(is.na(x = f))) { + f <- factor(x = f, levels = c(unique(as.character(f)), 'na')) + f[is.na(x = f)] <- 'na' + } else { + f <- factor(x = f, levels = unique(x = as.character(x = f))) + } + splits <- split(x = cells, f = f, drop = drop) + names(x = splits) <- .MakeNames(x = names(x = splits)) + return(switch( + EXPR = ret, + assay = { + for (lyr in layers) { + p <- progressor(steps = length(x = splits)) + p( + message = paste( + 'Splitting layer', + sQuote(x = lyr), + 'into', + length(x = splits), + 'splits' + ), + class = 'sticky', + amount = 0 + ) + lcells <- Cells(x = x, layer = lyr) + for (i in seq_along(along.with = splits)) { + p( + message = paste( + 'Creating split for', + sQuote(x = names(x = splits)[i]) + ), + class = 'sticky', + amount = 0 + ) + group <- paste(lyr, names(x = splits)[i], sep = '.') + xcells <- intersect(x = splits[[i]], y = lcells) + LayerData(object = x, layer = group, cells = xcells) <- LayerData( + object = x, + layer = lyr, + cells = xcells + ) + p() + } + p(type = 'finish') + suppressWarnings(expr = LayerData(object = x, layer = lyr) <- NULL) + DefaultLayer(object = x) <- default + } + x + }, + multiassays = { + value <- vector(mode = 'list', length = length(x = splits)) + names(x = value) <- names(x = splits) + for (group in names(x = splits)) { + value[[group]] <- subset( + x = x, + cells = splits[[group]], + layers = layers + ) + Key(object = value[[group]]) <- Key(object = group, quiet = TRUE) + } + value + }, + layers = { + groups <- apply( + X = expand.grid(layers, names(x = splits)), + MARGIN = 1L, + FUN = paste, + collapse = '.' + ) + value <- vector(mode = 'list', length = length(x = groups)) + names(x = value) <- groups + for (lyr in layers) { + lcells <- Cells(x = x, layer = lyr) + for (i in seq_along(along.with = splits)) { + group <- paste(lyr, names(x = splits)[i], sep = '.') + xcells <- intersect(x = splits[[i]], y = lcells) + value[[group]] <- LayerData(object = x, layer = lyr, cells = xcells) + } + } + value + }, + abort(message = paste("Unknown split return type", sQuote(x = ret))) + )) +} + +#' Split an Assay +#' +#' @inheritParams [.Assay5 +#' @inheritParams base::split +#' @param layers Names of layers to include in the split; pass \code{NA} for +#' all layers; pass \code{NULL} for the \link[=DefaultLayer]{default layer} +#' @param ret Type of return value; choose from: +#' \itemize{ +#' \item \dQuote{\code{assay}}: a single \code{\link{Assay5}} object +#' \item \dQuote{\code{multiassay}}: a list of \code{\link{Assay5}} objects +#' \item \dQuote{\code{layers}}: a list of layer matrices +#' } +#' @template param-dots-ignored +#' +#' @return Depends on the value of \code{ret}: +#' \itemize{ +#' \item \dQuote{\code{assay}}: \code{x} with the layers requested in +#' \code{layers} split based on \code{f}; all other layers are left as-is +#' \item \dQuote{\code{multiassay}}: a list of \code{\link{Assay5}} objects; +#' the list contains one value per split and each assay contains only the +#' layers requested in \code{layers} with the \link[=Key]{key} set to the split +#' \item \dQuote{\code{layers}}: a list of matrices of length +#' \code{length(assays) * length(unique(f))}; the list is named as +#' \dQuote{\code{layer.split}} +#' } +#' +#' @method split Assay5 +#' @export +#' +#' @family assay5 +#' +#' @template section-progressr +#' +split.Assay5 <- split.StdAssay + +#' @inherit subset.Assay5 params return title description details sections +#' +#' @keywords internal +#' @method subset StdAssay +#' @export +#' +#' @family stdassay +#' +subset.StdAssay <- function( + x, + cells = NULL, + features = NULL, + layers = NULL, + ... +) { + if (is.null(x = cells) && is.null(x = features)) { + return(x) + } + # Check the cells vector + if (all(is.na(x = cells))) { + cells <- Cells(x = x, layer = NA) + } else if (any(is.na(x = cells))) { + warning( + "NAs passed in cells vector, removing NAs", + call. = FALSE, + immediate. = TRUE + ) + cells <- cells[!is.na(x = cells)] + } + if (is.numeric(x = cells)) { + cells <- Cells(x = x, layer = NA)[cells] + } + cells <- intersect(x = Cells(x = x, layer = NA), y = cells) + if (!length(x = cells)) { + stop("None of the cells provided found in this assay", call. = FALSE) + } + # Check the features vector + if (all(is.na(x = features))) { + features <- Features(x = x, layer = NA) + } else if (any(is.na(x = features))) { + warning( + "NAs passed in features vector, removing NAs", + call. = FALSE, + immediate. = TRUE + ) + features <- features[!is.na(x = features)] + } + if (is.numeric(x = features)) { + features <- Features(x = x, layer = NA)[features] + } + features <- intersect(x = features, y = Features(x = x, layer = NA)) + if (!length(x = features)) { + stop("None of the features provided found in this assay", call. = FALSE) + } + # Check the layers + layers.all <- Layers(object = x) + layers <- layers %||% layers.all + layers <- match.arg( + arg = layers, + choices = layers.all, + several.ok = TRUE + ) + # Remove unused layers + for (lyr in setdiff(x = layers.all, y = layers)) { + LayerData(object = x, layer = lyr) <- NULL + } + # Perform the subsets + for (l in layers) { + lcells <- MatchCells( + new = Cells(x = x, layer = l), + orig = cells, + ordered = TRUE + ) + lfeatures <- MatchCells( + new = Features(x = x, layer = l), + orig = features, + ordered = TRUE + ) + if (is.null(x = lcells) || is.null(x = features)) { + LayerData(object = x, layer = l) <- NULL + } else { + LayerData(object = x, layer = l) <- LayerData( + object = x, + layer = l, + cells = lcells, + features = lfeatures + ) + } + } + slot(object = x, name = 'cells') <- droplevels(x = slot( + object = x, + name = 'cells' + )) + # Update the cell/feature maps + for (i in c('cells', 'features')) { + slot(object = x, name = i) <- droplevels(x = slot(object = x, name = i)) + } + # Subset feature-level metadata + mfeatures <- MatchCells( + new = Features(x = x, layer = NA), + orig = features, + ordered = TRUE + ) + slot(object = x, name = 'meta.data') <- slot( + object = x, + name = 'meta.data' + )[mfeatures, , drop = FALSE] + validObject(object = x) + return(x) +} + +#' Subset an Assay +#' +#' @inheritParams [[.Assay5 +#' @param cells Cell names +#' @param features Feature names +#' @param layers Layer to keep; defaults to all layers +#' +#' @return \code{x} with just the cells and features specified by +#' \code{cells} and \code{features} for the layers specified by \code{layers} +#' +#' @method subset Assay5 +#' @export +#' +#' @family assay5 +#' +subset.Assay5 <- subset.StdAssay + +#' @rdname sub-sub-.StdAssay +#' +#' @method tail StdAssay +#' @export +#' +tail.StdAssay <- tail.Assay + +#' @return \code{tail}: the last \code{n} rows of feature-level meta data +#' +#' @rdname sub-sub-.Assay5 +#' +#' @method tail Assay5 +#' @export +#' +tail.Assay5 <- tail.StdAssay + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Internal +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +.VFLayers <- function( + object, + type = c('hvf', 'svf'), + layers = NA, + missing = FALSE +) { + type <- type[1L] + type <- match.arg(arg = type) + pattern <- switch( + EXPR = type, + 'hvf' = '^vf_', + stop("Unknown type: '", type, "'", call. = FALSE) + ) + vf.cols <- grep( + pattern = paste0(pattern, '[[:alnum:]]+_'), + x = colnames(x = object[[]]), + value = TRUE + ) + vf.layers <- unique(x = unlist(x = lapply( + X = strsplit(x = vf.cols, split = '_'), + FUN = function(x) { + return(paste(x[3L:(length(x = x) - 1L)], collapse = '_')) + } + ))) + + if (!isTRUE(x = missing)) { + vf.layers <- intersect( + x = vf.layers, + y = Layers(object = object, search = layers) + ) + } + if (!length(x = vf.layers)) { + vf.layers <- NULL + } + return(vf.layers) +} + +#' @param object A \code{\link{StdAssay}} object +#' @param type Type of variable feature method to pull; choose from: +#' \itemize{ +#' \item \dQuote{\code{hvf}}: highly variable features +#' \item \dQuote{\code{svf}}: spatially variable features +#' } +#' @param layers Vector of layers to restrict methods for, or a search pattern +#' for multiple layers +#' +#' @return A vector of variable feature methods found in \code{object} +#' +#' @noRd +#' +.VFMethods <- function( + object, + type = c('hvf', 'svf'), + layers = NA, + missing = FALSE +) { + type <- type[1L] + type <- match.arg(arg = type) + pattern <- switch( + EXPR = type, + 'hvf' = '^vf_', + abort(message = paste("Unknown type:", sQuote(x = type))) + ) + vf.cols <- grep( + pattern = paste0(pattern, '[[:alnum:]]+_'), + x = colnames(x = object[[]]), + value = TRUE + ) + # layers <- Layers(object = object, search = layers) + layers <- .VFLayers( + object = object, + type = type, + layers = layers, + missing = missing + ) + vf.cols <- Filter( + f = function(x) { + x <- unlist(x = strsplit(x = x, split = '_')) + x <- paste(x[3:(length(x = x) - 1L)], collapse = '_') + return(x %in% layers) + }, + x = vf.cols + ) + vf.methods <- unique(x = unlist(x = lapply( + X = strsplit(x = vf.cols, split = '_'), + FUN = '[[', + 2L + ))) + if (!length(x = vf.methods)) { + vf.methods <- NULL + } + return(vf.methods) +} + +#' @param object A \code{\link{StdAssay}} object +#' @param type Type of variable feature method to pull; choose from: +#' \itemize{ +#' \item \dQuote{\code{hvf}}: highly variable features +#' \item \dQuote{\code{svf}}: spatially variable features +#' } +#' @param layers Vector of layers to restrict methods for, or a search pattern +#' for multiple layers +#' +#' @return A vector of variable feature methods and corresponding layers found in \code{object} +#' +#' @importFrom stats setNames +#' @importFrom utils modifyList +#' +#' @noRd +#' +.VFMethodsLayers <- function( + object, + type = c('hvf', 'svf'), + layers = NA, + missing = FALSE +) { + type <- type[1L] + type <- match.arg(arg = type) + pattern <- switch( + EXPR = type, + 'hvf' = '^vf_', + abort(message = paste("Unknown type:", sQuote(x = type))) + ) + vf.cols <- grep( + pattern = paste0(pattern, '[[:alnum:]]+_'), + x = colnames(x = object[[]]), + value = TRUE + ) + # layers <- Layers(object = object, search = layers) + layers <- .VFLayers( + object = object, + type = type, + layers = layers, + missing = missing + ) + vf.cols <- Filter( + f = function(x) { + x <- unlist(x = strsplit(x = x, split = '_')) + x <- paste(x[3:(length(x = x) - 1L)], collapse = '_') + return(x %in% layers) + }, + x = vf.cols + ) + # Extract methods and layers + vf.methods.layers <- lapply(vf.cols, function(col) { + components <- strsplit(col, split = "_")[[1]] + method <- components[2] + layer <- paste(components[3:(length(components) - 1)], collapse = "_") + return(c(method = method, layer = layer)) + }) + + # Combine into a list + vf.list <- lapply(unique(unlist(lapply(vf.methods.layers, `[[`, "method"))), function(method) { + layers <- unique(unlist(lapply(vf.methods.layers, function(x) { + if (x['method'] == method) + return(x['layer']) + }))) + return(setNames(list(layers), method)) + }) + vf.list <- Reduce(modifyList, vf.list) + if (!length(x = vf.list)) { + vf.list <- NULL + } + return(vf.list) +} + +CalcN5 <- function(object) { + if (IsMatrixEmpty(x = LayerData(object = object))) { + return(NULL) + } + return(list( + nCount = colSums(x = object), + nFeature = colSums(x = LayerData(object = object) > 0) + )) +} + +# Join single layers +# +JoinSingleLayers <- function( + object, + layers = NULL, + new = NULL, + default = TRUE, + nfeatures = Inf, + ... +) { + if (is.null(x = layers)) { + stop('Layers cannot be NULL') + } + if (length(x = layers) > 1L) { + stop('The length of input layers should be 1') + } + layers <- Layers(object = object, search = layers) + new <- new %||% 'newlayer' + if (length(x = layers) == 1L) { + LayerData(object = object, layer = new) <- LayerData(object = object, layer = layers) + return(object) + } + if (length(x = layers) == 0L) { + return(object) + } + # Stitch the layers together + ldat <- StitchMatrix( + x = LayerData(object = object, layer = layers[1L]), + y = lapply(X = layers[2:length(x = layers)], FUN = LayerData, object = object), + rowmap = slot(object = object, name = 'features')[, layers], + colmap = slot(object = object, name = 'cells')[, layers] + ) + LayerData(object = object, layer = new) <- ldat + # Set the new layer as default + if (isTRUE(x = default)) { + DefaultLayer(object = object) <- new + } + # Remove the old layers + for (lyr in layers) { + object[lyr] <- NULL + } + return(object) +} + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# S4 methods +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +setAs( + from = 'Assay', + to = 'Assay5', + def = function(from) { + # Initialize the new object + to <- new( + Class = 'Assay5', + cells = LogMap(y = colnames(x = from)), + features = LogMap(y = rownames(x = from)), + assay.orig = DefaultAssay(object = from) %||% character(length = 0L), + meta.data = EmptyDF(n = nrow(x = from)), + key = Key(object = from) + ) + # browser() + # Add the expression matrices + for (i in c('counts', 'data', 'scale.data')) { + adata <- GetAssayData(object = from, slot = i) + if (IsMatrixEmpty(x = adata)) { + next + } + LayerData(object = to, layer = i) <- adata + } + # Set the default layer + DefaultLayer(object = to) <- ifelse( + test = 'counts' %in% Layers(object = to) && !'scale.data' %in% Layers(object = to), + yes = 'counts', + no = 'data' + ) + # Add feature-level meta data + to[[]] <- from[[]] + # Set Variable features + VariableFeatures(object = to) <- VariableFeatures(object = from) + # Add miscellaneous data + mdata <- Misc(object = from) + for (i in names(x = mdata)) { + Misc(object = to, slot = i) <- mdata[[i]] + } + + return(to) + } +) + +setAs( + from = 'Assay5', + to = 'Assay', + def = function(from) { + data.list <- c() + original.layers <- Layers(object = from) + layers.saved <- c() + for (i in c('counts', 'data', 'scale.data')) { + layers.saved <- c(layers.saved, Layers(object = from, search = i)) + if (length(Layers(object = from, search = i)) > 1) { + warning("Joining '", i, "' layers. If you have the same cells in multiple layers, ", + "the expression value for the cell in the '", + i, "' slot will be the value from the '", + Layers(object = from, search = i)[1], "' layer.", + call. = FALSE, + immediate. = TRUE) + from <- JoinLayers(object = from, + layers = i, + new = i) + } + if (i == "data") { + if (isTRUE(Layers(object = from, search = i) == "scale.data")) { + warning("No counts or data slot in object. Setting 'data' slot using", + " data from 'scale.data' slot. To recreate 'data' slot, you", + " must set and normalize data from a 'counts' slot.", + call. = FALSE) + } + } + adata <- LayerData(object = from, layer = i) + if(inherits(x = adata, what = "IterableMatrix")) { + warning("Converting IterableMatrix to sparse dgCMatrix", + call. = FALSE) + adata <- as(object = adata, Class = "dgCMatrix") + } + data.list[[i]] <- adata + } + if (IsMatrixEmpty(x = data.list[["data"]])){ + data.list[["data"]] <- data.list[["counts"]] + } + if (any(!(original.layers %in% layers.saved))){ + layers.remove <- original.layers[!(original.layers %in% layers.saved)] + warning("Layers ", paste0(layers.remove, collapse = ', '), + " will be removed from the object as v3 assays only support", + " 'counts', 'data', or 'scale.data' slots.", + call. = FALSE, + immediate. = TRUE) + } + to <- new( + Class = 'Assay', + counts = data.list[["counts"]], + data = data.list[["data"]], + scale.data = data.list[["scale.data"]], + assay.orig = DefaultAssay(object = from) %||% character(length = 0L), + meta.features = data.frame(row.names = rownames(x = data.list[["data"]])), + key = Key(object = from) + ) + # Add feature-level meta data + suppressWarnings(to[[]] <- from[[]]) + # set variable features + VariableFeatures(object = to) <- VariableFeatures(object = from) + mdata <- Misc(object = from) + for (i in names(x = mdata)) { + Misc(object = to, slot = i) <- mdata[[i]] + } + return(to) + } +) + +#' @rdname sub-.StdAssay +#' +setMethod( + f = '[<-', + signature = c(x = 'StdAssay', i = 'character'), + definition = function(x, i, ..., value) { + LayerData(object = x, layer = i, ...) <- value + return(x) + } +) + +#' @param value A matrix-like object to add as a new layer +#' +#' @return \code{[<-}: \code{x} with layer data \code{value} saved as \code{i} +#' +#' @rdname sub-.Assay5 +#' +setMethod( + f = '[<-', + signature = c(x = 'Assay5', i = 'character'), + definition = function(x, i, ..., value) { + return(callNextMethod(x = x, i = i, ..., value = value)) + } +) + +#' @rdname sub-sub-.StdAssay +#' +setMethod( + f = '[[<-', + signature = c( + x = 'StdAssay', + i = 'character', + j = 'missing', + value = 'data.frame' + ), + definition = function(x, i, ..., value) { + if (!length(x = i) && !ncol(x = value)) { + return(x) + } + i <- match.arg(arg = i, choices = colnames(x = value), several.ok = TRUE) + names.intersect <- intersect( + x = row.names(x = value), + y = Features(x = x, layer = NA) + ) + if (length(x = names.intersect)) { + value <- value[names.intersect, , drop = FALSE] + } else if (nrow(x = value) == nrow(x = x)) { + row.names(x = value) <- Features(x = x, layer = NA) + } else { + abort(message = "Cannot add more or less meta data without feature names") + } + for (n in i) { + v <- value[[n]] + names(x = v) <- row.names(value) + x[[n]] <- v + } + return(x) + } +) + +#' @rdname sub-sub-.StdAssay +#' +setMethod( + f = '[[<-', + signature = c( + x = 'StdAssay', + i = 'missing', + j = 'missing', + value = 'data.frame' + ), + definition = function(x, ..., value) { + # Allow removing all meta data + if (IsMatrixEmpty(x = value)) { + x[[names(x = x[[]])]] <- NULL + return(x) + } + if (is.null(names(x = value))) { + warn(message = 'colnames of input cannot be NULL') + } else { + # If no `i` provided, use the column names from value + x[[names(x = value)]] <- value + } + return(x) + } +) + +#' @importFrom methods selectMethod +#' +#' @rdname sub-sub-.StdAssay +#' +setMethod( + f = '[[<-', + signature = c(x = 'StdAssay', i = 'character', j = 'missing', value = 'factor'), + definition = function(x, i, ..., value) { + f <- slot( + object = selectMethod( + f = '[[<-', + signature = c( + x = 'StdAssay', + i = 'character', + j = 'missing', + value = 'vector' + ) + ), + name = '.Data' + ) + return(f(x = x, i = i, value = value)) + } +) + +#' @rdname sub-sub-.StdAssay +#' +setMethod( + f = '[[<-', + signature = c(x = 'StdAssay', i = 'character', j = 'missing', value = 'NULL'), + definition = function(x, i, ..., value) { + for (name in i) { + slot(object = x, name = 'meta.data')[[name]] <- NULL + } + return(x) + } +) + +#' @rdname sub-sub-.StdAssay +#' +setMethod( + f = '[[<-', + signature = c(x = 'StdAssay', i = 'character', j = 'missing', value = 'vector'), + definition = function(x, i, ..., value) { + # Add multiple bits of metadata + if (length(x = i) > 1L) { + value <- rep_len(x = value, length.out = length(x = i)) + for (idx in seq_along(along.with = i)) { + x[[i[idx]]] <- value[[idx]] + } + } else { + # Add a single column of metadata + if (is.null(x = names(x = value))) { + if (length(x = unique(x = value)) == 1) { + value <- rep_len(x = value, length.out = nrow(x = x)) + names(x = value) <- Features(x = x, layer = NA) + } else { + names(x = value) <- value + } + } + names.intersect <- intersect( + x = names(x = value), + y = Features(x = x, layer = NA) + ) + if (!length(x = names.intersect)) { + abort(message = "No feature overlap between new meta data and assay") + } + value <- value[names.intersect] + df <- EmptyDF(n = nrow(x = x)) + rownames(x = df) <- Features(x = x, layer = NA) + # df[[i]] <- if (i %in% names(x = x[[]])) { + # x[[i]] + # } else { + # NA + # } + df[names(x = value), i] <- value + if (nrow(x = slot(object = x, name = 'meta.data')) == 0) { + slot(object = x, name = 'meta.data') <- EmptyDF(n = nrow(x = x)) + } + slot(object = x, name = 'meta.data')[, i] <- df[[i]] + } + validObject(object = x) + return(x) + } +) + +#' @rdname sub-sub-.StdAssay +#' +setMethod( + f = '[[<-', + signature = c(x = 'StdAssay', i = 'numeric', j = 'missing', value = 'ANY'), + definition = function(x, i, ..., value) { + if (ncol(x = x[[]])) { + i <- colnames(x = x[[]])[as.integer(x = i)] + i <- i[!is.na(x = i)] + if (length(x = i)) { + x[[i]] <- value + } + } + return(x) + } +) + +#' @rdname sub-sub-.StdAssay +#' +setMethod( + f = '[[<-', + signature = c(x = 'StdAssay', i = 'missing', j = 'missing', value = 'NULL'), + definition = function(x, ..., value) { + slot(object = x, name = 'meta.data') <- EmptyDF(n = nrow(x = x)) + return(x) + } +) + +#' @param value Feature-level meta data to add +#' +#' @return \code{[[<-}: \code{x} with \code{value} added as \code{i} +#' in feature-level meta data +#' +#' @rdname sub-sub-.Assay5 +#' +#' @order 2 +#' +setMethod( + f = '[[<-', + signature = c(x = 'Assay5'), + definition = function(x, i, ..., value) { + return(callNextMethod(x = x, i = i, ..., value = value)) + } +) + +#' V5 Assay Summaries +#' +#' Summary maths for \code{\link{StdAssay}} Objects +#' +#' @inheritParams base::colSums +#' @param layer Name of layer to run function on +#' @template param-dots-ignored +#' +#' @return The results of the summary math function for the layer specified +#' +#' @name v5-assay-summaries +#' @rdname v5-assay-summaries +#' +#' @keywords internal +#' +NULL + +#' @rdname v5-assay-summaries +#' +setMethod( + f = 'colMeans', + signature = c(x = 'StdAssay'), + definition = function(x, na.rm = FALSE, dims = 1, layer = NULL, ...) { + return(Matrix::colMeans( + x = LayerData(object = x, layer = layer), + na.rm = na.rm, + dims = dims + )) + } +) + +#' @rdname v5-assay-summaries +#' +setMethod( + f = 'colSums', + signature = c(x = 'StdAssay'), + definition = function(x, na.rm = FALSE, dims = 1, layer = NULL, ...) { + return(Matrix::colSums( + x = LayerData(object = x, layer = layer), + na.rm = na.rm, + dims = dims + )) + } +) + +#' @rdname v5-assay-summaries +#' +setMethod( + f = 'rowMeans', + signature = c(x = 'StdAssay'), + definition = function(x, na.rm = FALSE, dims = 1, layer = NULL, ...) { + return(Matrix::rowMeans( + x = LayerData(object = x, layer = layer), + na.rm = na.rm, + dims = dims + )) + } +) + +#' @rdname v5-assay-summaries +#' +setMethod( + f = 'rowSums', + signature = c(x = 'StdAssay'), + definition = function(x, na.rm = FALSE, dims = 1, layer = NULL, ...) { + return(Matrix::rowSums( + x = LayerData(object = x, layer = layer), + na.rm = na.rm, + dims = dims + )) + } +) + +#' V5 Assay Overview +#' +#' Overview of a \code{\link{StdAssay}} object +#' +#' @param object A v5 Assay +#' +#' @template return-show +#' +#' @keywords internal +#' +#' @family stdassay +#' +setMethod( + f = 'show', + signature = 'StdAssay', + definition = function(object) { + # Basic assay info + cat( + .AssayClass(object = object), + 'data with', + nrow(x = object), + 'features for', + ncol(x = object), + 'cells\n' + ) + # Feature information + if (length(x = VariableFeatures(object = object))) { + top.ten <- head(x = VariableFeatures(object = object), n = 10L) + top <- 'Top' + variable <- 'variable' + } else { + top.ten <- head(x = Features(x = object), n = 10L) + top <- 'First' + variable <- '' + } + features <- paste( + variable, + paste0( + ifelse(test = length(x = top.ten) == 1L, yes = 'feature', no = 'features'), + ":\n" + ) + ) + features <- gsub(pattern = '^\\s+', replacement = '', x = features) + cat( + top, + length(x = top.ten), + features, + paste(strwrap(x = paste(top.ten, collapse = ', ')), collapse = '\n'), + '\n' + ) + cat( + "Layers:\n", + paste(strwrap(x = paste(Layers(object = object), collapse = ', ')), collapse = '\n'), + "\n" + ) + return(invisible(x = NULL)) + } +) + +#' @rdname split.StdAssay +#' +setMethod( # Because R is stupid + f = 'split', + signature = c(x = 'StdAssay'), + definition = split.StdAssay +) + +#' V5 Assay Validity +#' +#' @templateVar cls StdAssay +#' @template desc-validity +#' +#' @section Layer Validation: +#' blah +#' +#' @inheritSection Key-validity Key Validation +#' +#' @keywords internal +#' +#' @name StdAssay-validity +#' +#' @family stdassay +#' +#' @seealso \code{\link[methods]{validObject}} +#' +setValidity( + Class = 'StdAssay', + method = function(object) { + if (isFALSE(x = getOption(x = "Seurat.object.validate", default = TRUE))) { + warn( + message = paste("Not validating", class(x = object)[1L], "objects"), + class = 'validationWarning' + ) + return(TRUE) + } + valid <- NULL + # Check layers + dorder <- c( + features = .MARGIN(x = object, type = 'features'), + cells = .MARGIN(x = object, type = 'cells') + ) + adims <- dim(x = object) # c(features, cells) + if (!IsNamedList(x = slot(object = object, name = 'layers'), pass.zero = TRUE)) { + valid <- c(valid, "'layers' must be a named list") + } + for (layer in Layers(object = object)) { + # Reorder dimensions of layer to c(features, cells) + ldims <- dim(x = slot(object = object, name = 'layers')[[layer]])[dorder] + if (length(x = ldims) != 2L) { + valid <- c(valid, "Layers must be two-dimensional objects") + break + } + # Check that we have the correct features and cells + for (i in seq.int(from = 1L, to = 2L)) { + if (ldims[i] > adims[i]) { + valid <- c( + valid, + paste0( + "Layers may not have more ", + names(x = dorder)[i], + " than present in the assay (offending layer", + layer, + ")" + ) + ) + } + } + # Check that we've recorded the cells and features in the maps + for (i in c('cells', 'features')) { + didx <- c(features = 1L, cells = 2L)[i] + if (!layer %in% colnames(x = slot(object = object, name = i))) { + valid <- c( + valid, + paste0( + "All layers must have a record in the ", + i, + " map (offending layer: ", + layer, + ")" + ) + ) + } else { + nmap <- length(x = slot(object = object, name = i)[[layer]]) + if (nmap != ldims[didx]) { + valid <- c( + valid, + paste0( + "Layers must have the same ", + i, + " as present in the map (offending layer: ", + layer, + ")" + ) + ) + } + } + } + } + didx <- slot(object = object, name = 'default') + if (length(x = didx)) { + if (didx < 0 || didx > length(x = Layers(object = object))) { + valid <- c( + valid, + "'default' must be between 0 and the number of layers present" + ) + } + } + # TODO: Check variable features + # TODO: Check meta features + # TODO: Check key + # TODO: Check misc + return(valid %||% TRUE) + } +) + +#' @inherit StdAssay-validity title details sections +#' +#' @templateVar cls Assay5 +#' @template desc-validity +#' +#' @name Assay5-validity +#' +#' @family assay5 +#' +#' @seealso \code{\link[methods]{validObject}} +#' +NULL diff --git a/R/centroids.R b/R/centroids.R index 39967a8c..4aab525c 100644 --- a/R/centroids.R +++ b/R/centroids.R @@ -61,6 +61,8 @@ setClass( #' #' @seealso \code{\link{Centroids-class}} #' +#' @family segmentation +#' NULL #' @@ -354,9 +356,15 @@ setMethod( f = 'over', signature = c(x = 'Centroids', y = 'SpatialPolygons'), definition = function(x, y, returnList = FALSE, fn = NULL, ...) { + deprecate_stop( + when = '5.0.0', + what = 'over()', + details = "Future integration with `sf` is on the roadmap with no current ETA" + ) + check_installed(pkg = 'sf') return(over( - x = as(object = x, Class = 'SpatialPoints'), - y = as(object = y, Class = 'SpatialPolygons'), + x = as(object = x, Class = 'sf'), + y = as(object = y, Class = 'sf'), returnList = returnList, fn = fn, ... @@ -371,10 +379,24 @@ setMethod( f = 'Overlay', signature = c(x = 'Centroids', y = 'SpatialPolygons'), definition = function(x, y, invert = FALSE, ...) { - idx <- over(x = x, y = y) - idx <- idx[!is.na(x = idx)] - if (!length(idx)) { - warning("The selected region does not contain any cell centroids") + check_installed(pkg = 'sf', reason = 'to overlay spatial information') + idx <- sf::st_intersects( + x = as(object = x, Class = 'sf'), + y = as(object = y, Class = 'sf'), + sparse = FALSE + ) + idx <- which(idx) + names_in_sf_object1 <- if (!is.null(x = row.names(x = x))) { + row.names(x = x)[idx] + } else { + x$id[idx] + } + idx <- setNames( + object = rep(x = TRUE, length(x = idx)), + nm = names_in_sf_object1 + ) + if (!length(x = idx)) { + warn(message = "The selected region does not contain any cell centroids") return(NULL) } idx <- sort(x = as.integer(x = names(x = idx))) @@ -412,6 +434,13 @@ setMethod( setValidity( Class = 'Centroids', method = function(object) { + if (isFALSE(x = getOption(x = "Seurat.object.validate", default = TRUE))) { + warn( + message = paste("Not validating", class(x = object)[1L], "objects"), + class = 'validationWarning' + ) + return(TRUE) + } valid <- NULL # Check cell names cells <- Cells(x = object) diff --git a/R/command.R b/R/command.R index 500482e8..12bbee06 100644 --- a/R/command.R +++ b/R/command.R @@ -8,10 +8,10 @@ NULL # Class definitions #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -#' The SeuratCommand Class +#' The \code{SeuratCommand} Class #' -#' The SeuratCommand is used for logging commands that are run on a -#' \code{Seurat} object; it stores parameters and timestamps +#' The \code{SeuratCommand} is used for logging commands that are run +#' on a \code{Seurat} object; it stores parameters and timestamps #' #' @slot name Command name #' @slot time.stamp Timestamp of when command was tun @@ -24,7 +24,11 @@ NULL #' @rdname SeuratCommand-class #' @exportClass SeuratCommand #' -SeuratCommand <- setClass( +#' @family command +#' +#' @aliases SeuratCommand +#' +setClass( Class = 'SeuratCommand', slots = c( name = 'character', @@ -45,14 +49,14 @@ SeuratCommand <- setClass( #' the Seurat object #' #' @param object Name of Seurat object -#' @param return.command Return a \link{SeuratCommand} object instead +#' @param return.command Return a \code{\link{SeuratCommand}} object instead #' -#' @return If \code{return.command}, returns a SeuratCommand object. Otherwise, -#' returns the Seurat object with command stored +#' @return If \code{return.command}, returns a \code{\link{SeuratCommand}} +#' object; otherwise, returns the Seurat object with command stored #' #' @export #' -#' @concept command +#' @family command #' #' @seealso \code{\link{Command}} #' @@ -145,7 +149,7 @@ LogSeuratCommand <- function(object, return.command = FALSE) { call.string = call.string, assay.used = cmd.assay ) - if (return.command) { + if (isTRUE(x = return.command)) { return(seurat.command) } object[[command.name]] <- seurat.command @@ -169,89 +173,102 @@ DefaultAssay.SeuratCommand <- function(object, ...) { # Methods for R-defined generics #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -#' \code{SeuratCommand} Methods -#' -#' Methods for \code{\link{SeuratCommand}} objects for generics defined in -#' other packages -#' -#' @param x,object A \code{\link{SeuratCommand}} object -#' @param ... Arguments passed to other methods +#' @inherit .DollarNames.Seurat title #' -#' @name SeuratCommand-methods -#' @rdname SeuratCommand-methods -#' -#' @concept command -#' -NULL - -#' @describeIn SeuratCommand-methods Autocompletion for \code{$} access on a -#' \code{SeuratCommand} object +#' @description Autocompletion for \code{$} access on a +#' \code{\link{SeuratCommand}} object #' #' @inheritParams utils::.DollarNames +#' @param x A \code{\link{SeuratCommand}} object +#' +#' @return The parameter name matches for \code{pattern} #' #' @importFrom utils .DollarNames -#' @export +#' #' @method .DollarNames SeuratCommand +#' @export +#' +#' @family command #' ".DollarNames.SeuratCommand" <- function(x, pattern = '') { return(.DollarNames(x = slot(object = x, name = "params"), pattern = pattern)) } -#' @describeIn SeuratCommand-methods Access a parameter from a -#' \code{SeuratCommand} object +#' Command Log Parameter Access +#' +#' Pull parameter values from a \code{\link{SeuratCommand}} object #' -#' @param i For a \code{$}, a parameter name; for \code{[}, a -#' \code{SeuratCommand} slot name +#' @inheritParams .DollarNames.SeuratCommand +#' @param i A parameter name #' -#' @return \code{$}: The value for parameter \code{i} +#' @return The value for parameter \code{i} #' +#' @method $ SeuratCommand #' @export #' -"$.SeuratCommand" <- function(x, i, ...) { +#' @family command +#' +#' @examples +#' cmd <- pbmc_small[["NormalizeData.RNA"]] +#' cmd$normalization.method +#' +"$.SeuratCommand" <- function(x, i) { params <- slot(object = x, name = "params") return(params[[i]]) } -#' @describeIn SeuratCommand-methods Access data from a \code{SeuratCommand} -#' object +#' Command Log Data Access +#' +#' Access data from a \code{SeuratCommand} object +#' +#' @inheritParams .DollarNames.SeuratCommand +#' @param i The name of a command log slot +#' @template param-dots-ignored #' #' @return \code{[}: Slot \code{i} from \code{x} #' -#' @export #' @method [ SeuratCommand +#' @export +#' +#' @family command +#' +#' @examples +#' cmd <- pbmc_small[["NormalizeData.RNA"]] +#' cmd["call.string"] #' "[.SeuratCommand" <- function(x, i, ...) { - slot.use <- c("name", "timestamp", "call_string", "params") - if (!i %in% slot.use) { - stop("Invalid slot") - } + i <- arg_match(arg = i, values = slotNames(x = x)) return(slot(object = x, name = i)) } -#' @describeIn SeuratCommand-methods Coerce a \code{SeuratCommand} to a list +#' Coerce a \code{SeuratCommand} to a list #' +#' @inheritParams .DollarNames.SeuratCommand #' @param complete Include slots besides just parameters #' (eg. call string, name, timestamp) +#' @template param-dots-ignored #' -#' @return \code{as.list}: A list with the parameters and, if -#' \code{complete = TRUE}, the call string, name, and timestamp +#' @return A list with the parameters and, if \code{complete = TRUE}, +#' the call string, name, and timestamp #' -#' @export #' @method as.list SeuratCommand +#' @export +#' +#' @family command +#' +#' @examples +#' cmd <- pbmc_small[["NormalizeData.RNA"]] +#' as.list(cmd) +#' as.list(cmd, complete = TRUE) #' as.list.SeuratCommand <- function(x, complete = FALSE, ...) { CheckDots(...) cmd <- slot(object = x, name = 'params') - if (complete) { + if (isTRUE(x = complete)) { cmd <- append( x = cmd, values = sapply( - X = grep( - pattern = 'params', - x = slotNames(x = x), - invert = TRUE, - value = TRUE - ), + X = setdiff(x = slotNames(x = x), y = 'params'), FUN = slot, object = x, simplify = FALSE, @@ -260,7 +277,7 @@ as.list.SeuratCommand <- function(x, complete = FALSE, ...) { after = 0 ) } - for (i in 1:length(x = cmd)) { + for (i in seq_along(along.with = cmd)) { if (is.character(x = cmd[[i]])) { cmd[[i]] <- paste(trimws(x = cmd[[i]]), collapse = ' ') } @@ -272,14 +289,19 @@ as.list.SeuratCommand <- function(x, complete = FALSE, ...) { # S4 methods #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -#' @describeIn SeuratCommand-methods Overview of a \code{SeuratCommand} object +#' Command Log Overview #' -#' @return \code{show}: Prints summary to \code{\link[base]{stdout}} and -#' invisibly returns \code{NULL} +#' Overview of a \code{\link{SeuratCommand}} object #' -#' @importFrom methods show +#' @template return-show #' -#' @export +#' @keywords internal +#' +#' @concept command +#' +#' @examples +#' cmd <- pbmc_small[["NormalizeData.RNA"]] +#' cmd #' setMethod( f = 'show', diff --git a/R/compliance.R b/R/compliance.R new file mode 100644 index 00000000..fd4b21b3 --- /dev/null +++ b/R/compliance.R @@ -0,0 +1,63 @@ +.SetSeuratCompat <- local({ + seurat.version <- NULL + function(pkgname, pkgpath) { + current <- .RoundVersion(current = packageVersion(pkg = pkgname)) + if (pkgname == 'Signac') { + if (is.null(x = seurat.version)) { + seurat.version <<- ifelse( + test = paste(current, collapse = '.') >= '1.12.9000', + yes = '5.0.0', + no = '4.4.0' + ) + } + return(invisible(x = NULL)) + } + seurat.version <<- paste(current, collapse = '.') + if (!is.null(x = seurat.version) && seurat.version < '5.0.0') { + options( + Seurat.object.assay.brackets = 'v3', + Seurat.object.assay.version = 'v3' + ) + } + return(invisible(x = NULL)) + } +}) + +.GetSeuratCompat <- local( + envir = environment(fun = .SetSeuratCompat), + function() { + if (is.null(x = seurat.version) && isNamespaceLoaded(name = 'Seurat')) { + .SetSeuratCompat() + } + return(seurat.version %||% '5.0.0') + } +) + +.SeuratCompatMessage <- local( + envir = environment(fun = .SetSeuratCompat), + function(pkgname, pkgpath) { + seurat <- .GetSeuratCompat() + if (!is.null(x = seurat) && seurat < '5.0.0') { + options( + Seurat.object.assay.brackets = 'v3', + Seurat.object.assay.version = 'v3' + ) + version <- paste0('v', substr(x = seurat, start = 1L, stop = 1L)) + packageStartupMessage(paste( + strwrap(x = paste( + pkgname, + switch( + EXPR = pkgname, + Seurat = version, + "built for for SeuratObject v4" + ), + "was just loaded with SeuratObject v5;", + "disabling v5 assays and validation routines,", + "and ensuring assays work in strict v3/v4 compatibility mode" + )), + collapse = '\n' + )) + } + return(invisible(x = NULL)) + } +) diff --git a/R/default.R b/R/default.R index 56c87c03..2e51e8b0 100644 --- a/R/default.R +++ b/R/default.R @@ -1,3 +1,4 @@ +#' @include zzz.R #' @include generics.R #' NULL @@ -6,7 +7,30 @@ NULL # Methods for Seurat-defined generics #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#' @rdname dot-AssayClass +#' @method .AssayClass StdAssay +#' @export +#' +.AssayClass.StdAssay <- function(object) { + cls <- gsub( + pattern = '5$|v5$', + replacement = '', + x = class(x = object)[1L], + ignore.case = TRUE + ) + return(paste(cls, '(v5)')) +} + +#' @method .MARGIN default +#' @export +#' +.MARGIN.default <- function(x, type = c('features', 'cells'), ...) { + type <- arg_match(arg = type) + return(unname(obj = c(features = 1L, cells = 2L)[type])) +} + #' @rdname Cells +#' @method Cells default #' @export #' Cells.default <- function(x, ...) { @@ -14,8 +38,8 @@ Cells.default <- function(x, ...) { } #' @rdname IsGlobal -#' @export #' @method IsGlobal default +#' @export #' IsGlobal.default <- function(object, ...) { return(FALSE) @@ -60,3 +84,223 @@ MatchCells.numeric <- function(new, orig, ordered = FALSE) { } return(new) } + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Methods for R-defined generics +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Internal +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#' Add Object Metadata +#' +#' Internal \code{\link{AddMetaData}} definition +#' +#' @param object An object +#' @param metadata A vector, list, or data.frame with metadata to add +#' @param col.name A name for meta data if not a named list or data.frame +#' +#' @return object with metadata added +#' +#' @keywords internal +#' +#' @noRd +#' +.AddMetaData <- function(object, metadata, col.name = NULL) { + if (is.null(x = col.name) && (is.atomic(x = metadata) && !is.matrix(x = metadata))) { + abort(message = "'col.name' must be provided for atomic meta data") + } + if (inherits(x = metadata, what = c('matrix', 'Matrix'))) { + metadata <- as.data.frame(x = metadata) + } + col.name <- col.name %||% names(x = metadata) %||% colnames(x = metadata) + if (is.null(x = col.name)) { + abort(message = "No metadata name provided and could not infer it from metadata object") + } + object[[col.name]] <- metadata + return(object) +} + +#' Internal Cropping Function +#' +#' @inheritParams Crop +#' +#' @return ... +#' +#' @keywords internal +#' +#' @noRd +#' +.Crop <- function(object, x = NULL, y = NULL, coords = c('plot','tissue'), ...) { + if (is.null(x = x) && is.null(x = y)) { + return(object) + } + coords <- coords[1L] + coords <- match.arg(arg = coords) + switch( + EXPR = coords, + 'plot' = { + cx <- 'y' + cy <- 'x' + }, + 'tissue' = { + cx <- 'x' + cy <- 'y' + } + ) + x <- range(x %||% bbox(obj = object)[cx, , drop = TRUE]) + y <- range(y %||% bbox(obj = object)[cy, , drop = TRUE]) + idx <- c(max = 1L, min = 2L)[[getOption( + x = 'Seurat.coords.short_range', + default = Seurat.options$Seurat.coords.short_range + )]] + if (x[1L] == x[2L]) { + x[idx] <- bbox(obj = object)[cx, idx] + } + if (y[1L] == y[2L]) { + y[idx] <- bbox(obj = object)[cy, idx] + } + args <- list(x, y) + names(x = args) <- switch( + EXPR = coords, + 'plot' = c('y', 'x'), + 'tissue' = c('x', 'y') + ) + args <- args[c('x', 'y')] + df <- do.call(what = expand.grid, args = args) + df <- df[c(1, 3, 4, 2), ] + df$cell <- 'cell' + return(Overlay(x = object, y = CreateSegmentation(coords = df))) +} + +#' Test Finiteness of Centroids +#' +#' Determines if a \code{\link{Centroids}} object should be finite; for +#' \code{Centroids}, this means if their \code{nsides} slot is an integer >= 3 +#' +#' @param x A \code{\link{Centroids}} object +#' +#' @return \code{TRUE} if the \code{Centroids} are finite; otherwise +#' \code{FALSE} +#' +#' @keywords internal +#' +#' @noRd +#' +.FiniteCentroids <- function(x) { + return(as.logical(x = length(x = x))) +} + +#' Head and Tail Object Metadata +#' +#' Internal \code{\link[utils]{head}} and \code{\link[utils]{tail}} definitions +#' +#' @param x An object +#' @param n Number of rows to return +#' @inheritDotParams utils::head +#' +#' @return The first or last \code{n} rows of object metadata +#' +#' @keywords internal +#' +#' @noRd +#' +.head <- function(x, n = 10L, ...) { + return(head(x = x[[]], n = n, ...)) +} + +.tail <- function(x, n = 10L, ...) { + return(tail(x = x[[]], n = n, ...)) +} + +#' Miscellaneous Data +#' +#' Internal functions for getting and setting miscellaneous data +#' +#' @param object An object +#' @param slot Name of miscellaneous data to get or set +#' @param ... Arguments passed to other methods +#' +#' @return \code{.Misc}: If \code{slot} is \code{NULL}, all miscellaneous +#' data, otherwise the miscellaneous data for \code{slot} +#' +#' @keywords internal +#' +#' @noRd +#' +.Misc <- function(object, slot = NULL, ...) { + CheckDots(...) + if (is.null(x = slot)) { + return(slot(object = object, name = 'misc')) + } + return(slot(object = object, name = 'misc')[[slot]]) +} + +#' @param value Data to add +#' +#' @return \code{.Misc<-}: \code{object} with \code{value} added to the +#' miscellaneous data slot \code{slot} +#' +#' @rdname dot-Misc +#' +#' @noRd +#' +".Misc<-" <- function(object, slot, ..., value) { + CheckDots(...) + if (slot %in% names(x = Misc(object = object))) { + warning( + "Overwriting miscellanous data for ", + slot, + call. = FALSE, + immediate. = TRUE + ) + } + if (is.list(x = value)) { + slot(object = object, name = 'misc')[[slot]] <- c(value) + } else { + slot(object = object, name = 'misc')[[slot]] <- value + } + return(object) +} + +.OverBbox <- function(x, y, invert = FALSE, ...) { + df <- .BboxDF(x = bbox(obj = y)) + df$cell <- 'cell' + return(Overlay( + x = x, + y = CreateSegmentation(coords = df), + invert = invert, + ... + )) +} + +#' Internal Overlay Method +#' +#' @param x Query spatial object +#' @param y Target spatial object +#' @param ... Ignored +#' +#' @return \code{x} with only the components that fall within +#' the bounds of \code{y} +#' +#' @keywords internal +#' +#' @noRd +#' +.Overlay <- function(x, y, ...) { + idx <- over(x = x, y = y) + idx <- idx[!is.na(x = idx)] + names(x = idx) <- vapply( + X = strsplit(x = names(x = idx), split = '\\.'), + FUN = '[[', + FUN.VALUE = character(length = 1L), + 1L, + USE.NAMES = FALSE + ) + return(x[names(x = idx)]) +} + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# S4 methods +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/R/dimreduc.R b/R/dimreduc.R index 9fa33897..9885e589 100644 --- a/R/dimreduc.R +++ b/R/dimreduc.R @@ -1,6 +1,7 @@ #' @include zzz.R #' @include generics.R #' @include jackstraw.R +#' @include keymixin.R #' @importFrom methods new slot slot<- slotNames #' NULL @@ -22,19 +23,20 @@ NULL #' @slot global Is this \code{DimReduc} global/persistent? If so, it will not be #' removed when removing its associated assay #' @slot stdev A vector of standard deviations -#' @slot key Key for the \code{DimReduc}, must be alphanumeric characters -#' followed by an underscore #' @slot jackstraw A \code{\link{JackStrawData-class}} object associated with #' this \code{DimReduc} -#' @slot misc Utility slot for storing additional data associated with the -#' \code{DimReduc} (e.g. the total variance of the PCA) +#' @template slot-misc +#' @template slot-key #' -#' @name DimReduc-class -#' @rdname DimReduc-class #' @exportClass DimReduc #' -DimReduc <- setClass( +#' @aliases DimReduc +#' +#' @family dimreduc +#' +setClass( Class = 'DimReduc', + contains = 'KeyMixin', slots = c( cell.embeddings = 'matrix', feature.loadings = 'matrix', @@ -42,7 +44,7 @@ DimReduc <- setClass( assay.used = 'character', global = 'logical', stdev = 'numeric', - key = 'character', + # key = 'character', jackstraw = 'JackStrawData', misc = 'list' ) @@ -72,7 +74,7 @@ DimReduc <- setClass( #' #' @export #' -#' @concept dimreduc +#' @family dimreduc #' #' @examples #' data <- GetAssayData(pbmc_small[["RNA"]], slot = "scale.data") @@ -97,16 +99,12 @@ CreateDimReducObject <- function( misc = list() ) { if (is.null(x = assay)) { - warning( - "No assay specified, setting assay as RNA by default.", - call. = FALSE, - immediate. = TRUE - ) - assay <- "RNA" + warn(message = "No assay specified, setting assay as RNA by default.") + assay <- 'RNA' } # Try to infer key from column names if (is.null(x = key) && is.null(x = colnames(x = embeddings))) { - stop("Please specify a key for the DimReduc object") + abort(message = "Please specify a key for the DimReduc object") } else if (is.null(x = key)) { key <- regmatches( x = colnames(x = embeddings), @@ -115,33 +113,25 @@ CreateDimReducObject <- function( key <- unique(x = unlist(x = key, use.names = FALSE)) } if (length(x = key) != 1) { - stop("Please specify a key for the DimReduc object") - } else if (!grepl(pattern = '^[[:alnum:]]+_$', x = key)) { + abort(message = "Please specify a key for the DimReduc object") + } else if (!grepl(pattern = .KeyPattern(), x = key)) { old.key <- key - key <- UpdateKey(key = old.key) + key <- Key(object = key) colnames(x = embeddings) <- gsub( x = colnames(x = embeddings), pattern = old.key, replacement = key ) - warning( - "All keys should be one or more alphanumeric characters followed by an underscore '_', setting key to ", - key, - call. = FALSE, - immediate. = TRUE - ) } # ensure colnames of the embeddings are the key followed by a numeric if (is.null(x = colnames(x = embeddings))) { - warning( + warn(message = paste0( "No columnames present in cell embeddings, setting to '", key, "1:", ncol(x = embeddings), - "'", - call. = FALSE, - immediate. = TRUE - ) + "'" + )) colnames(x = embeddings) <- paste0(key, 1:ncol(x = embeddings)) } else if (!all(grepl(pattern = paste0('^', key, "[[:digit:]]+$"), x = colnames(x = embeddings)))) { digits <- unlist(x = regmatches( @@ -155,13 +145,13 @@ CreateDimReducObject <- function( } if (!IsMatrixEmpty(x = loadings)) { if (any(rownames(x = loadings) == '')) { - stop("Feature names of loadings matrix cannot be empty", call. = FALSE) + abort(message = "Feature names of loadings matrix cannot be empty") } colnames(x = loadings) <- colnames(x = embeddings) } if (!IsMatrixEmpty(x = projected)) { if (any(rownames(x = loadings) == '')) { - stop("Feature names of projected loadings matrix cannot be empty", call. = FALSE) + abort(message = "Feature names of projected loadings matrix cannot be empty") } colnames(x = projected) <- colnames(x = embeddings) } @@ -212,6 +202,18 @@ DefaultAssay.DimReduc <- function(object, ...) { return(object) } +#' @method Features DimReduc +#' @export +#' +Features.DimReduc <- function(x, projected = NULL, ...) { + projected <- isTRUE(x = projected %||% Projected(object = x)) + features <- rownames(x = Loadings(object = x, projected = projected)) + if (!length(x = features)) { + features <- NULL + } + return(features) +} + #' @rdname FetchData #' @export #' @method FetchData DimReduc @@ -269,6 +271,64 @@ Embeddings.DimReduc <- function(object, ...) { return(slot(object = object, name = 'cell.embeddings')) } +#' @method FetchData DimReduc +#' @export +#' +FetchData.DimReduc <- function( + object, + vars, + cells = NULL, + # layer = c('embeddings', 'loadings', 'projected'), + # layer = 'embeddings', + ... +) { + layer <- 'embeddings' + layer <- arg_match0(arg = layer, values = 'embeddings') + cells <- cells %||% Cells(x = object) + if (is.numeric(x = cells)) { + cells <- Cells(x = object)[cells] + } + cells <- intersect(x = cells, y = Cells(x = object)) + if (!length(x = cells)) { + abort(message = "None of the cells requested found in this dimensional reduction") + } + key <- Key(object = object) + ovars <- vars + vars <- grep( + pattern = paste0('^(', key, ')?[[:digit:]]+$'), + x = vars, + value = TRUE + ) + if (!length(x = vars)) { + abort(message = "None of the vars provided are valid for reduced dimensions") + } else if (length(x = vars) != length(x = ovars)) { + warn(message = paste( + "The following requested vars are not valid:", + paste(setdiff(x = ovars, y = vars), collapse = ', '), + )) + } + vars <- paste0( + key, + as.integer(x = gsub(pattern = key, replacement = '', x = vars)) + ) + data <- switch( + EXPR = layer, + 'embeddings' = Embeddings(object = object), + Loadings(object = object, projected = layer == 'projected') + ) + missing <- setdiff(x = vars, y = colnames(x = data)) + if (length(x = missing) == length(x = vars)) { + abort(message = "Cannot find any of the requested dimensions") + } else if (length(x = missing)) { + warn(message = paste( + "Cannot find the following dimensions:", + paste0(missing, collapse = ', ') + )) + vars <- setdiff(x = vars, y = missing) + } + return(as.data.frame(x = data)[cells, vars, drop = FALSE]) +} + #' @rdname IsGlobal #' @export #' @method IsGlobal DimReduc @@ -318,10 +378,7 @@ JS.DimReduc <- function(object, slot = NULL, ...) { #' # Get a DimReduc key #' Key(object = pbmc_small[["pca"]]) #' -Key.DimReduc <- function(object, ...) { - CheckDots(...) - return(slot(object = object, name = 'key')) -} +Key.DimReduc <- .Key #' @rdname Key #' @export @@ -333,27 +390,24 @@ Key.DimReduc <- function(object, ...) { #' Key(object = pbmc_small[["pca"]]) #' "Key<-.DimReduc" <- function(object, ..., value) { - CheckDots(...) - object <- UpdateSlots(object = object) - old.key <- Key(object = object) - slots <- Filter( - f = function(x) { - return(class(x = slot(object = object, name = x)) == 'matrix') - }, - x = slotNames(x = object) - ) - for (s in slots) { - mat <- slot(object = object, name = s) - if (!IsMatrixEmpty(x = mat)) { - colnames(x = mat) <- sub( - pattern = paste0('^', old.key), - replacement = value, - x = colnames(x = mat) - ) + op <- options(Seurat.object.validate = FALSE) + on.exit(expr = options(op), add = TRUE) + old <- Key(object = object) + suppressWarnings(expr = object <- NextMethod(), classes = 'validationWarning') + for (i in c("cell.embeddings", "feature.loadings", "feature.loadings.projected")) { + mat <- slot(object = object, name = i) + if (IsMatrixEmpty(x = mat)) { + next } - slot(object = object, name = s) <- mat + colnames(x = mat) <- gsub( + pattern = paste0('^', old), + replacement = Key(object = object), + x = colnames(x = mat) + ) + slot(object = object, name = i) <- mat } - slot(object = object, name = 'key') <- value + options(op) + validObject(object = object) return(object) } @@ -432,6 +486,7 @@ RenameCells.DimReduc <- function(object, new.names = NULL, ...) { old.data <- Embeddings(object = object) rownames(x = old.data) <- new.names slot(object = object, name = "cell.embeddings") <- old.data + validObject(object = object) return(object) } @@ -452,32 +507,33 @@ Stdev.DimReduc <- function(object, ...) { # Methods for R-defined generics #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -#' \code{DimReduc} Methods +#' Get Feature Loadings #' -#' Methods for \code{\link{DimReduc}} objects for generics defined in -#' other packages +#' Pull feature loadings from a \link[=DimReduc]{dimensional reduction} #' -#' @inheritParams Assay-methods -#' @param x,object A \code{\link{DimReduc}} object -#' @param i For \code{[}: feature names or indices; for \code{[[}: cell names -#' or indices -#' @param j Dimensions to pull for -#' @param ... Arguments passed to other methods +#' \code{[} does not distinguish between projected and unprojected feature +#' loadings; to select whether projected or unprojected loadings should be +#' pulled, please use \code{\link{Loadings}} #' -#' @name DimReduc-methods -#' @rdname DimReduc-methods +#' @param x A \code{\link{DimReduc}} object +#' @param i Feature identifiers or indices +#' @param j Dimension identifiers or indices +#' @param drop Coerce the result to the lowest possible dimension; see +#' \code{\link{drop}} for further details +#' @template param-dots-method #' -#' @concept dimreduc +#' @return Feature loadings for features \code{i} and dimensions \code{j} #' -NULL - -#' @describeIn DimReduc-methods Pull feature loadings +#' @method [ DimReduc +#' @export #' -#' @return \code{[}: Feature loadings for features \code{i} and dimensions -#' \code{j} +#' @family dimreduc #' -#' @export -#' @method [ DimReduc +#' @seealso \code{\link{Loadings}} +#' +#' @examples +#' pca <- pbmc_small[["pca"]] +#' pca[1:10, 1:5] #' "[.DimReduc" <- function(x, i, j, drop = FALSE, ...) { loadings <- Loadings(object = x) @@ -503,12 +559,25 @@ NULL return(Loadings(object = x)[i, j, drop = drop, ...]) } -#' @describeIn DimReduc-methods Pull cell embeddings +#' Get Cell Embeddings #' -#' @return \code{[[}: Cell embeddings for cells \code{i} and dimensions \code{j} +#' Pull cell embeddings from a \link[=DimReduc]{dimensional reduction} +#' +#' @inheritParams [.DimReduc +#' @param i Cell names or indices +#' +#' @return Cell embeddings for cells \code{i} and dimensions \code{j} #' -#' @export #' @method [[ DimReduc +#' @export +#' +#' @family dimreduc +#' +#' @seealso \code{\link{Embeddings}} +#' +#' @examples +#' pca <- pbmc_small[["pca"]] +#' pca[[1:10, 1:5]] #' "[[.DimReduc" <- function(x, i, j, drop = FALSE, ...) { if (missing(x = i)) { @@ -534,48 +603,86 @@ NULL return(embeddings[i, j, drop = drop, ...]) } -#' @describeIn DimReduc-methods The number of cells and dimensions for a -#' \code{DimReduc} +#' Dimensional Reduction Meta-Information +#' +#' Pull meta-information about cells and dimensions for a given +#' \link[=DimReduc]{dimensional reduction}; cell meta-information is stored +#' as row meta-information (eg. \code{nrow}, \code{rownames}) and dimension +#' meta-information is stored as column meta-information (eg. \code{ncol}, +#' \code{colnames}) +#' +#' @inheritParams [.DimReduc #' #' @return \code{dim}: The number of cells (\code{nrow}) and dimensions #' (\code{ncol}) #' -#' @export #' @method dim DimReduc +#' @export +#' +#' @family dimreduc +#' +#' @examples +#' pca <- pbmc_small[["pca"]] +#' pca +#' dim(pca) +#' +#' # nrow is number of cells +#' nrow(pca) +#' +#' # rownames pulls cell names +#' head(rownames(pca)) +#' +#' # ncol and length are number of dimensions +#' ncol(pca) +#' length(pca) +#' +#' # colnames and names pull dimension identifiers +#' head(colnames(pca)) +#' head(names(pca)) #' dim.DimReduc <- function(x) { return(dim(x = Embeddings(object = x))) } -#' @describeIn DimReduc-methods The cell and dimension names for a -#' \code{DimReduc} object -#' #' @return \code{dimnames}: The cell (row) and dimension (column) names #' -#' @export +#' @rdname dim.DimReduc +#' #' @method dimnames DimReduc +#' @export +#' +#' @seealso \code{Cells} #' dimnames.DimReduc <- function(x) { return(dimnames(x = Embeddings(object = x))) } -#' @describeIn DimReduc-methods The number of dimensions for a \code{DimReduc} -#' object -#' #' @return \code{length}: The number of dimensions #' +#' @rdname dim.DimReduc +#' #' @export #' @method length DimReduc #' length.DimReduc <- function(x) { - return(ncol(x = Embeddings(object = x))) + return(ncol(x = x)) } -#' @describeIn DimReduc-methods Merge two or more \code{DimReduc} objects -#' together +#' Merge Dimensional Reductions +#' +#' Merge two or more \link[=DimReduc]{dimensional reduction} together +#' +#' @inheritParams [.DimReduc +#' @inheritParams merge.Assay5 +#' @param y One or more \code{\link{DimReduc}} objects +#' @template param-dots-ignored +#' +#' @return A new \code{DimReduc} object with data merged from \code{c(x, y)} #' -#' @export #' @method merge DimReduc +#' @export +#' +#' @family dimreduc #' merge.DimReduc <- function( x = NULL, @@ -586,24 +693,45 @@ merge.DimReduc <- function( CheckDots(...) drs <- c(x, y) if (!is.null(x = add.cell.ids)) { - for (i in 1:length(x = drs)) { + add.cell.ids <- unique(x = add.cell.ids) + if (!is_bare_character(x = add.cell.ids, n = length(x = drs))) { + abort( + message = "'add.cell.ids' must be unique for every dimensional reduction" + ) + } + for (i in seq_along(along.with = drs)) { drs[[i]] <- RenameCells(object = drs[[i]], new.names = add.cell.ids[i]) } } - embeddings.mat <- list() - min.dim <- c() - for (i in 1:length(x = drs)) { - embeddings.mat[[i]] <- Embeddings(object = drs[[i]]) - min.dim <- c(min.dim, ncol(x = embeddings.mat[[i]])) + all.cells <- unlist(x = lapply(X = drs, FUN = Cells)) + if (anyDuplicated(x = all.cells)) { + abort(message = "Duplicate cells in provided dimensional reductions") } + embeddings.mat <- lapply(X = drs, FUN = Embeddings) + min.dim <- vapply( + X = embeddings.mat, + FUN = ncol, + FUN.VALUE = integer(length = 1L), + USE.NAMES = FALSE + ) + # embeddings.mat <- list() + # min.dim <- c() + # for (i in 1:length(x = drs)) { + # embeddings.mat[[i]] <- Embeddings(object = drs[[i]]) + # min.dim <- c(min.dim, ncol(x = embeddings.mat[[i]])) + # } if (length(x = unique(x = min.dim)) > 1) { min.dim <- min(min.dim) - warning( - "Reductions contain differing numbers of dimensions, merging first ", - min.dim, - call. = FALSE, - immediate. = TRUE - ) + warn(message = paste( + "Reductions contain differing numbers of dimensions, merging first", + min.dim + )) + # warning( + # "Reductions contain differing numbers of dimensions, merging first ", + # min.dim, + # call. = FALSE, + # immediate. = TRUE + # ) embeddings.mat <- lapply( X = embeddings.mat, FUN = function(x) { @@ -623,34 +751,44 @@ merge.DimReduc <- function( return(merged.dr) } -#' @describeIn DimReduc-methods The dimension names for a \code{DimReduc} object +#' @return \code{names}: The dimension identifiers #' -#' @return \code{names}: The names for the dimensions (eg. \dQuote{PC_1}) +#' @rdname dim.DimReduc #' -#' @export #' @method names DimReduc +#' @export #' names.DimReduc <- function(x) { - return(colnames(x = Embeddings(object = x))) + # return(colnames(x = Embeddings(object = x))) + return(colnames(x = x)) } -#' @describeIn DimReduc-methods Prints a set of features that most strongly -#' define a set of components; \strong{note}: requires feature loadings to be -#' present in order to work +#' Print Top Feature Loadings +#' +#' Prints a set of features that most strongly define a set of components; +#' \strong{note}: requires feature loadings to be present in order to work #' +#' @inheritParams [.DimReduc #' @param dims Number of dimensions to display #' @param nfeatures Number of genes to display #' @param projected Use projected slot -#' @param ... Arguments passed to other methods +#' @template param-dots-ignored #' -#' @return \code{print}: Displays set of features defining the components and +#' @return Displays set of features defining the components and #' invisibly returns \code{x} #' +#' @method print DimReduc +#' @export +#' #' @aliases print +#' +#' @family dimreduc +#' #' @seealso \code{\link[base]{cat}} #' -#' @export -#' @method print DimReduc +#' @examples +#' pca <- pbmc_small[["pca"]] +#' print(pca) #' print.DimReduc <- function( x, @@ -676,13 +814,6 @@ print.DimReduc <- function( dims <- intersect(x = dims, y = seq_len(length.out = ncol(x = loadings))) } for (dim in dims) { - # features <- TopFeatures( - # object = x, - # dim = dim, - # nfeatures = nfeatures * 2, - # projected = projected, - # balanced = TRUE - # ) features <- Top( data = loadings[, dim, drop = FALSE], num = nfeatures * 2, @@ -716,15 +847,20 @@ print.DimReduc <- function( return(invisible(x = x)) } -#' @describeIn DimReduc-methods Subset a \code{DimReduc} object +#' Subset a Dimensional Reduction +#' +#' Subset a \code{\link{DimReduc}} object #' +#' @inheritParams [.DimReduc #' @param cells,features Cells and features to keep during the subset +#' @template param-dots-ignored #' -#' @return \code{subset}: \code{x} for cells \code{cells} and features -#' \code{features} +#' @return \code{x} for cells \code{cells} and features \code{features} #' -#' @export #' @method subset DimReduc +#' @export +#' +#' @family dimreduc #' subset.DimReduc <- function(x, cells = NULL, features = NULL, ...) { CheckDots(...) @@ -732,7 +868,7 @@ subset.DimReduc <- function(x, cells = NULL, features = NULL, ...) { if (all(is.na(x = cells))) { cells <- Cells(x = x) } else if (any(is.na(x = cells))) { - warning("NAs passed in cells vector, removing NAs") + warn(message = "NAs passed in cells vector, removing NAs") cells <- na.omit(object = cells) } # features <- rownames(x = x) %iff% features %||% rownames(x = x) @@ -746,7 +882,7 @@ subset.DimReduc <- function(x, cells = NULL, features = NULL, ...) { if (is.numeric(x = cells)) { cells <- Cells(x = x)[cells] } - cells <- intersect(x = cells, y = Cells(x = x)) + cells <- intersect(x = Cells(x = x), y = cells) if (length(x = cells) == 0) { stop("Cannot find cell provided", call. = FALSE) } @@ -783,18 +919,74 @@ subset.DimReduc <- function(x, cells = NULL, features = NULL, ...) { return(x) } +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Internal +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +.RenameFeatures <- function(object, old.names = NULL, new.names = NULL) { + if (is.null(x = old.names) && is.null(x = new.names)) { + return(object) + } + # Checks + op <- options(Seurat.object.validate = FALSE) + on.exit(expr = options(op)) + stopifnot(length(x = old.names) == length(x = new.names)) + stopifnot(all(nzchar(x = old.names))) + stopifnot(all(nzchar(x = new.names))) + if (is.null(x = Features(x = object)) && length(x = new.names)) { + warning("No features present in this DimReduc", call. = FALSE, immediate. = TRUE) + } + # Rename features + names(x = new.names) <- old.names + features <- Features(x = object, projected = FALSE) + ldat <- Loadings(object = object, projected = FALSE) + rownames(x = ldat) <- unname(obj = new.names[features]) + Loadings(object = object, projected = FALSE) <- ldat + if (isTRUE(x = Projected(object = object))) { + pdat <- Loadings(object = object, projected = TRUE) + pfeatures <- Features(x = object, projected = TRUE) + rownames(x = pdat) <- unname(obj = new.names[pfeatures]) + Loadings(object = object, projected = TRUE) <- pdat + } + # Validate and return + options(op) + validObject(object = object) + return(object) +} + +#' Check to see if projected loadings have been set +#' +#' @param object a DimReduc object +#' +#' @return TRUE if projected loadings have been set, else FALSE +#' +#' @keywords internal +#' +#' @noRd +#' +Projected <- function(object) { + return(!IsMatrixEmpty(x = Loadings(object = object, projected = TRUE))) +} + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # S4 methods #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -#' @describeIn DimReduc-methods Show basic summary of a \code{DimReduc} object +#' Dimensional Reduction Overview #' -#' @return \code{show}: Prints summary to \code{\link[base]{stdout}} and -#' invisibly returns \code{NULL} +#' Overview of a \code{\link{DimReduc}} object #' -#' @importFrom methods show +#' @param object A dimensional reduction #' -#' @export +#' @template return-show +#' +#' @keywords internal +#' +#' @seealso \code{\link{DimReduc}} +#' +#' @examples +#' pca <- pbmc_small[["pca"]] +#' pca #' setMethod( f = 'show', @@ -803,6 +995,7 @@ setMethod( cat( "A dimensional reduction object with key", Key(object = object), '\n', 'Number of dimensions:', length(x = object), '\n', + 'Number of cells:', length(x = Cells(x = object)), '\n', 'Projected dimensional reduction calculated: ', Projected(object = object), '\n', 'Jackstraw run:', as.logical(x = JS(object = object)), '\n', 'Computed using assay:', DefaultAssay(object = object), '\n' @@ -811,20 +1004,161 @@ setMethod( } ) -#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -# Internal -#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -#' Check to see if projected loadings have been set +#' Dimensional Reduction Validity #' -#' @param object a DimReduc object +#' @templateVar cls DimReduc +#' @template desc-validity #' -#' @return TRUE if projected loadings have been set, else FALSE +#' @section Cell Embeddings Validation: +#' The cell embeddings matrix must be a numeric matrix of dimensions +#' \eqn{n_{cells}} by \eqn{d_{dimensions}}; row names must be the cell names +#' and column names must be the dimension identifier. The dimension identifier +#' must be \dQuote{\code{key_dimension}} (eg. \dQuote{\code{PC_1}}). Dimension +#' identifiers must be in order and cannot be skipped #' -#' @keywords internal +#' @section Feature and Projected Feature Loadings Validation: +#' blah #' -#' @noRd +#' @inheritSection Key-validity Key Validation #' -Projected <- function(object) { - return(!IsMatrixEmpty(x = Loadings(object = object, projected = TRUE))) -} +#' @section Standard Deviations Validation: +#' blah +#' +#' @name DimReduc-validity +#' +#' @family dimreduc +#' +setValidity( + Class = 'DimReduc', + method = function(object) { + if (.GetSeuratCompat() < '5.0.0') { + return(TRUE) + } + if (isFALSE(x = getOption(x = "Seurat.object.validate", default = TRUE))) { + warn( + message = paste("Not validating", class(x = object)[1L], "objects"), + class = 'validationWarning' + ) + return(TRUE) + } + valid <- NULL + ValidColnames <- function( + mat, + ref = NULL, + type = c('embeddings', 'loadings', 'projected') + ) { + ret <- NULL + if (IsMatrixEmpty(x = mat)) { + return(ret) + } + type <- match.arg(arg = type) + type <- switch( + EXPR = type, + embeddings = 'cell.embeddings', + loadings = 'feature.loadings', + projected = 'feature.loadings.projected' + ) + mat.names <- colnames(x = mat) + key <- paste0('^', Key(object = object)) + if (is.null(x = mat.names)) { + ret <- c(ret, paste("colnames must be present in", sQuote(x = type))) + } else if (!all(grepl(pattern = key, x = mat.names))) { + ret <- c( + ret, + paste( + "colnames for", + sQuote(x = type), + "must start with reduction key", + paste0("(", Key(object = object), ")") + ) + ) + } else { + dims <- as.numeric(x = gsub(pattern = key, replacement = '', x = mat.names)) + if (!is_bare_integerish(x = dims, n = ncol(x = mat), finite = TRUE) || any(dims < 1L)) { + ret <- c( + ret, + paste( + "dimension names for", + sQuote(x = type), + "must be positive integers" + ) + ) + } else if (is.unsorted(x = dims)) { + ret <- c( + ret, + paste("dimensions for", sQuote(x = type), "must be in order") + ) + } + } + if (!is.null(x = ref)) { + if (length(x = mat.names) != length(x = ref)) { + ret <- c( + ret, + paste(sQuote(x = type), "must have", length(x = ref), "dimensions") + ) + } else if (!all(mat.names == ref)) { + ret <- c( + ret, + paste( + "dimensions in", + sQuote(x = type), + "do not match dimensions in reduction" + ) + ) + } + } + return(ret) + } + # Validate cell embeddings + emb <- Embeddings(object = object) + if (!is.numeric(x = emb)) { + valid <- c(valid, "'cell.embeddings' must be a numeric matrix") + } + if (is.null(x = rownames(x = emb)) || !all(nzchar(x = rownames(x = emb)))) { + valid <- c(valid, "rownames must be present in 'cell.embeddings'") + } + valid <- c(valid, ValidColnames(mat = emb, type = 'embeddings')) + if (!is.null(x = valid)) { + return(valid) + } + dims <- colnames(x = emb) + # if (is.null(x = colnames(x = emb))) { + # valid <- c(valid, "colnames must be present in 'cell.embeddings'") + # } else { + # emb.names <- colnames(x = emb) + # if (!all(grepl(pattern = paste0('^', Key(object = object)), x = emb.names))) { + # valid <- c( + # valid, + # paste0( + # "colnames for 'cell.embeddings' must start with reduction key (", + # Key(object = object), + # ")" + # ) + # ) + # } + # } + # if (!is.null(x = valid)) { + # return(valid) + # } + # TODO: Validate feature loadings + lds <- Loadings(object = object, projected = FALSE) + valid <- c(valid, ValidColnames(mat = lds, type = 'loadings')) + # TODO: Validate projected loadings + prj <- Loadings(object = object, projected = TRUE) + valid <- c(valid, ValidColnames(mat = prj, type = 'projected')) + # TODO: Validate assay used + if (!rlang::is_scalar_character(x = DefaultAssay(object = object))) { + valid <- c(valid, "'assay.orig' must be a 1-length character") + } + # Validate globalness + if (!rlang::is_scalar_logical(x = IsGlobal(object = object))) { + valid <- c(valid, "'global' must be a 1-length logical") + } else if (is_na(x = IsGlobal(object = object))) { + valid <- c(valid, "'global' must be TRUE or FALSE") + } + # TODO: Validate standard deviations + # TODO: Validate JackStraw data + # TODO: Validate misc + return(valid %||% TRUE) + } +) diff --git a/R/fov.R b/R/fov.R index b1b0ec0f..9a64190f 100644 --- a/R/fov.R +++ b/R/fov.R @@ -18,24 +18,23 @@ NULL #' Supports coordinates for spatially-resolved molecule (FISH) data. #' Compatible with \code{\link{SpatialImage}} #' -#' @slot molecules (\code{\link[base]{list}}) A named list of +#' @slot molecules A named list of #' \code{\link[SeuratObject:Molecules-class]{Molecules}} objects defining #' spatially-resolved molecular coordinates -#' @slot boundaries (\code{[named]\link[base]{list}} -#' \{\code{\link[SeuratObject:Segmentation-class]{Segmentation}}, -#' \code{\link[SeuratObject:Centroids-class]{Centroids}}\}) A named list of +#' @slot boundaries A named list of #' \code{\link[SeuratObject:Segmentation-class]{Segmentation}} and #' \code{\link[SeuratObject:Centroids-class]{Centroids}} objects defining #' spatially-resolved boundaries -#' @slot assay (\code{\link[base:character]{character [1L]}}) A character -#' naming the associated assay of the spatial coordinates -#' @slot key (\code{\link[base:character]{character [1L]}}) The key -#' for the spatial coordinates +#' @slot assay A character naming the associated assay +#' of the spatial coordinates +#' @template slot-key #' #' @exportClass FOV #' #' @aliases FOV #' +#' @concept fov +#' #' @seealso \code{\link{FOV-methods}} #' setClass( @@ -81,6 +80,8 @@ setClass( #' @name FOV-methods #' @rdname FOV-methods #' +#' @concept fov +#' #' @seealso \code{\link{FOV-class}} #' NULL @@ -320,6 +321,8 @@ FetchData.FOV <- function( vars.orig <- vars if (is.numeric(x = cells)) { cells <- Cells(x = object)[cells] + } else if (is.null(cells)) { + cells <- Cells(x = object) } # Find keyed molecules object.keys <- Keys(object = object) @@ -574,6 +577,8 @@ RenameCells.FOV <- function(object, new.names = NULL, ...) { #' @name aggregate #' @rdname aggregate #' +#' @keywords internal +#' #' @method aggregate FOV #' @export #' @@ -995,9 +1000,33 @@ setMethod( } ) +#' FOV Validity +#' +#' @templateVar cls FOV +#' @template desc-validity +#' +#' @section Boundary Validation: +#' blah +#' +#' @section Molecule Validation: +#' blah +#' +#' @name FOV-validity +#' +#' @family fov +#' +#' @seealso \code{\link[methods]{validObject}} +#' setValidity( Class = 'FOV', method = function(object) { + if (isFALSE(x = getOption(x = "Seurat.object.validate", default = TRUE))) { + warn( + message = paste("Not validating", class(x = object)[1L], "objects"), + class = 'validationWarning' + ) + return(TRUE) + } valid <- NULL # Check boundaries nlist <- IsNamedList( @@ -1029,12 +1058,6 @@ setValidity( "All segmentation boundaries must have cells" ) break - } else if (is.unsorted(x = matched.cells)) { - valid <- c( - valid, - "All segmentation boundaries must be ordered" - ) - break } } else { valid <- c( diff --git a/R/generics.R b/R/generics.R index 325cd299..f810b901 100644 --- a/R/generics.R +++ b/R/generics.R @@ -2,6 +2,160 @@ #' NULL +#' Assay Class Label +#' +#' @param object A \code{\link{StdAssay}} object +#' +#' @return The assay class label for \code{object} +#' +#' @keywords internal +#' +#' @export .AssayClass +#' +.AssayClass <- function(object) { + UseMethod(generic = '.AssayClass', object = object) +} + +#' Calculate nCount and nFeature +#' +#' @template param-dots-method +#' @param object An assay-like object +#' +#' @return A named list with ... +#' +#' @keywords internal +#' +#' @export .CalcN +#' +#' @examples +#' calcn <- .CalcN(pbmc_small[["RNA"]]) +#' head(as.data.frame(calcn)) +#' +.CalcN <- function(object, ...) { + UseMethod(generic = '.CalcN', object = object) +} + +#' Get the Package that Defines a Class +#' +#' @param object An object +#' +#' @return The package that defines the class of \code{object} +#' +#' @keywords internal +#' +#' @export .ClassPkg +#' +#' @examples +#' .ClassPkg(pbmc_small) +#' +.ClassPkg <- function(object) { + UseMethod(generic = '.ClassPkg', object = object) +} + +#' Generic Assay Creation +#' +#' Create an assay object; runs a standardized filtering scheme that +#' works regardless of the direction of the data (eg. cells as columns +#' and features as rows or vice versa) and creates an assay object based +#' on the initialization scheme defined for \code{\link{StdAssay}}-derived +#' class \code{type} +#' +#' @param counts A two-dimensional expression matrix +#' @param min.cells Include features detected in at least this many cells; +#' will subset the counts matrix as well. To reintroduce excluded features, +#' create a new object with a lower cutoff +#' @param min.features Include cells where at least this many features +#' are detected +#' @param cells Vector of cell names +#' @param features Vector of feature names +#' @param type Type of assay object to create; must be the name of a class +#' that's derived from \code{\link{StdAssay}} +#' @param ... Extra parameters passed to \code{\link[methods]{new}} for +#' assay creation; used to set slots not defined by \code{\link{StdAssay}} +#' +#' @return An object of class \code{type} with a layer named \code{layer} +#' containing the data found in \code{counts} +#' +#' @keywords internal +#' +#' @export .CreateStdAssay +#' +#' @concept assay +#' +.CreateStdAssay <- function( + counts, + min.cells = 0, + min.features = 0, + cells = NULL, + features = NULL, + transpose = FALSE, + type = 'Assay5', + ... +) { + UseMethod(generic = '.CreateStdAssay', object = counts) +} + +#' Disk Loading Function +#' +#' Generate a function to load a matrix from an on-disk file +#' +#' @inheritParams .FilePath +#' +#' @return A one-length character that defines a function to load a matrix from +#' a file +#' +#' @keywords internal +#' +#' @export .DiskLoad +#' +.DiskLoad <- function(x) { + UseMethod(generic = '.DiskLoad', object = x) +} + +#' Find a File Path +#' +#' @param x A file-backed object +#' +#' @return The path to the file that backs \code{x}; if \code{x} is not a +#' file-backed object, returns \code{NULL} +#' +#' @keywords internal +#' +#' @export .FilePath +#' +.FilePath <- function(x) { + UseMethod(generic = '.FilePath', object = x) +} + +#' Get the Margin of an Object +#' +#' @param x An object +#' +#' @return The margin, eg. \code{1} for rows or \code{2} for columns +#' +#' @keywords internal +#' +#' @export .MARGIN +#' +.MARGIN <- function(x, ...) { + UseMethod(generic = '.MARGIN', object = x) +} + +#' Combine and Select Features +#' +#' @template param-dots-method +#' @param object An object +#' +#' @return A vector of features selected +#' +#' @keywords internal +#' +#' @export .SelectFeatures +#' +.SelectFeatures <- function(object, ...) { + UseMethod(generic = '.SelectFeatures', object = object) +} + #' Add in metadata associated with either cells or features. #' #' Adds additional data to the object. Can be any piece of information @@ -42,15 +196,15 @@ AddMetaData <- function(object, metadata, col.name = NULL) { #' Convert a \code{\link[base]{matrix}} (or \code{\link[Matrix]{Matrix}}) to #' a \code{\link{Graph}} object #' +#' @template param-dots-ignored #' @param x The matrix to convert -#' @param ... Arguments passed to other methods (ignored for now) #' #' @return A \code{\link{Graph}} object #' #' @rdname as.Graph #' @export as.Graph #' -#' @concept graph +#' @family graph #' as.Graph <- function(x, ...) { UseMethod(generic = "as.Graph", object = x) @@ -59,14 +213,16 @@ as.Graph <- function(x, ...) { #' Convert Segmentation Layers #' #' @inheritParams CreateCentroids +#' @template param-dots-method #' @param x An object -#' @param ... Arguments passed to other methods #' #' @return \code{as.Centroids}: A #' \code{\link[SeuratObject:Centroids-class]{Centroids}} object #' #' @export #' +#' @concept spatial +#' as.Centroids <- function(x, nsides = NULL, radius = NULL, theta = NULL, ...) { UseMethod(generic = "as.Centroids", object = x) } @@ -75,8 +231,8 @@ as.Centroids <- function(x, nsides = NULL, radius = NULL, theta = NULL, ...) { #' #' Convert objects to \code{\link{Neighbor}} objects #' +#' @template param-dots-method #' @param x An object to convert to \code{\link{Neighbor}} -#' @param ... Arguments passed to other methods #' #' @return A \code{\link{Neighbor}} object #' @@ -103,8 +259,8 @@ as.Segmentation <- function(x, ...) { #' #' Convert objects to Seurat objects #' +#' @template param-dots-method #' @param x An object to convert to class \code{Seurat} -#' @param ... Arguments passed to other methods #' #' @return A \code{\link{Seurat}} object generated from \code{x} #' @@ -121,8 +277,8 @@ as.Seurat <- function(x, ...) { #' #' Convert dense objects to sparse representations #' +#' @template param-dots-method #' @param x An object -#' @param ... Arguments passed to other methods #' #' @return A sparse representation of the input data #' @@ -135,10 +291,35 @@ as.sparse <- function(x, ...) { UseMethod(generic = 'as.sparse', object = x) } +#' Query Specific Object Types +#' +#' List the names of \code{\link{Assay}}, \code{\link{DimReduc}}, +#' \code{\link{Graph}}, \code{\link{Neighbor}} objects +#' +#' @template param-dots-ignored +#' @param object A \code{\link{Seurat}} object +#' @param slot Name of component object to return +#' +#' @return If \code{slot} is \code{NULL}, the names of all component objects +#' in this \code{Seurat} object. Otherwise, the specific object specified +#' +#' @rdname ObjectAccess +#' +#' @export +#' +#' @concept data-access +#' +#' @examples +#' Assays(pbmc_small) +#' +Assays <- function(object, ...) { + UseMethod(generic = "Assays", object = object) +} + #' Get, Set, and Query Segmentation Boundaries #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @name Boundaries #' @return \code{Boundaries}: The names of all segmentation boundaries present @@ -147,16 +328,37 @@ as.sparse <- function(x, ...) { #' @rdname Boundaries #' @export #' +#' @concept spatial +#' Boundaries <- function(object, ...) { UseMethod(generic = 'Boundaries', object = object) } +#' Cast Assay Layers +#' +#' Cast layers in v5 assays to other classes +#' +#' @param object An object +#' @param to Either a class name or a function that takes a layer and returns +#' the same layer as a new class +#' @param ... If \code{to} is a function, arguments passed to \code{to} +#' +#' @return \code{object} with the layers cast to class specified by \code{to} +#' +#' @export +#' +#' @concept assay5 +#' +CastAssay <- function(object, to, ...) { + UseMethod(generic = 'CastAssay', object = object) +} + #' Cell and Feature Names #' #' Get the cell and feature names of an object #' +#' @template param-dots-method #' @param x An object -#' @param ... Arguments passed to other methods #' #' @return \code{Cell}: A vector of cell names #' @@ -164,6 +366,7 @@ Boundaries <- function(object, ...) { #' @export Cells #' #' @concept data-access +#' @family dimnames #' #' @examples #' Cells(x = pbmc_small) @@ -174,6 +377,7 @@ Cells <- function(x, ...) { #' Check Matrix Validity #' +#' @template param-dots-method #' @param object A matrix #' @param checks Type of checks to perform, choose one or more from: #' \itemize{ @@ -184,7 +388,6 @@ Cells <- function(x, ...) { #' \item \dQuote{\code{na}}: Emit a warning if any value is an \code{NA} #' or \code{NaN} #' } -#' @param ... Arguments passed to other methods #' #' @return Emits warnings for each test and invisibly returns \code{NULL} #' @@ -195,6 +398,8 @@ Cells <- function(x, ...) { #' #' @export #' +#' @concept utils +#' CheckMatrix <- function(object, checks, ...) { UseMethod(generic = 'CheckMatrix', object = object) } @@ -203,8 +408,8 @@ CheckMatrix <- function(object, checks, ...) { #' #' Pull information on previously run commands in the Seurat object. #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return Either a SeuratCommand object or the requested parameter value #' @@ -229,12 +434,32 @@ Command <- function(object, ...) { #' #' @export #' +#' @concept spatial +#' CreateCentroids <- function(coords, nsides, radius, theta) { UseMethod(generic = 'CreateCentroids', object = coords) } +#' Create Spatial Coordinates +#' +#' @template param-dots-method +#' @param coords Spatial coordinates +#' +#' @return A \code{\link{FOV}} object +#' +#' @export +#' +#' @concept spatial +#' +#' @seealso \code{\link{FOV-class}} +#' +CreateFOV <- function(coords, ...) { + UseMethod(generic = 'CreateFOV', object = coords) +} + #' Create a \code{\link{Molecules}} Object #' +#' @template param-dots-method #' @param coords Spatial coordinates for molecules; should be a data frame #' with three columns: #' \itemize{ @@ -242,12 +467,13 @@ CreateCentroids <- function(coords, nsides, radius, theta) { #' \item \dQuote{\code{y}}: y-coordinates for each molecule #' \item \dQuote{\code{gene}}: gene name for each molecule #' } -#' @param ... Arguments passed to other methods #' #' @return A \code{\link{Molecules}} object #' #' @export #' +#' @concept spatial +#' CreateMolecules <- function(coords, ...) { UseMethod(generic = 'CreateMolecules', object = coords) } @@ -260,6 +486,8 @@ CreateMolecules <- function(coords, ...) { #' #' @export #' +#' @concept spatial +#' CreateSegmentation <- function(coords) { UseMethod(generic = 'CreateSegmentation', object = coords) } @@ -269,6 +497,7 @@ CreateSegmentation <- function(coords) { #' Create a \code{Seurat} object from raw data #' #' @inheritParams CreateAssayObject +#' @template param-dots-method #' @param counts Either a \code{\link[base]{matrix}}-like object with #' unnormalized data with cells as columns and features as rows or an #' \code{\link{Assay}}-derived object @@ -286,7 +515,6 @@ CreateSegmentation <- function(coords) { #' Should be a \code{\link[base]{data.frame}} where the rows are cell names and #' the columns are additional metadata fields. Row names in the metadata need #' to match the column names of the counts matrix. -#' @param ... Arguments passed to other methods #' #' @note In previous versions (<3.0), this function also accepted a parameter to #' set the expression threshold for a \sQuote{detected} feature (gene). This @@ -314,33 +542,19 @@ CreateSegmentation <- function(coords) { #' CreateSeuratObject <- function( counts, - project = 'CreateSeuratObject', assay = 'RNA', names.field = 1, names.delim = '_', meta.data = NULL, + project = 'CreateSeuratObject', ... ) { UseMethod(generic = 'CreateSeuratObject', object = counts) } -#' Create Spatial Coordinates -#' -#' @param coords Spatial coordinates -#' @param ... Arguments passed to other methods -#' -#' @return A \code{\link{FOV}} object -#' -#' @export -#' -#' @seealso \code{\link{FOV-class}} -#' -CreateFOV <- function(coords, ...) { - UseMethod(generic = 'CreateFOV', object = coords) -} - #' Crop Coordinates #' +#' @template param-dots-method #' @param object An object #' @param x,y Range to crop x/y limits to; if \code{NULL}, uses full range of #' \code{x}/\code{y} @@ -350,13 +564,14 @@ CreateFOV <- function(coords, ...) { #' \item \dQuote{\code{tissue}}: Coordinates from #' \code{\link{GetTissueCoordinates}} #' } -#' @param ... ... #' #' @return \code{object} cropped to the region specified by \code{x} #' and \code{y} #' #' @export #' +#' @concept spatial +#' Crop <- function(object, x = NULL, y = NULL, coords = c('plot', 'tissue'), ...) { UseMethod(generic = 'Crop', object = object) } @@ -365,8 +580,8 @@ Crop <- function(object, x = NULL, y = NULL, coords = c('plot', 'tissue'), ...) #' #' Get and set the default assay #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return \code{DefaultAssay}: The name of the default assay #' @@ -416,8 +631,8 @@ DefaultBoundary <- function(object) { #' Get and Set the Default FOV #' +#' @template param-dots-method #' @param object A \code{\link{Seurat}} Object -#' @param ... Arguments passed to other methods #' #' @return \code{DefaultFOV}: The name of the default \code{\link{FOV}} #' @@ -426,6 +641,8 @@ DefaultBoundary <- function(object) { #' #' @export #' +#' @concept spatial +#' DefaultFOV <- function(object, ...) { UseMethod(generic = 'DefaultFOV', object = object) } @@ -443,10 +660,39 @@ DefaultFOV <- function(object, ...) { UseMethod(generic = 'DefaultFOV<-', object = object) } +#' Default Layer +#' +#' Get and set the default layer +#' +#' @template param-dots-method +#' @param object An object +#' +#' @return \code{DefaultLayer}: The name of the default layer +#' +#' @rdname DefaultLayer +#' @export DefaultLayer +#' +#' @concept assay5 +#' +DefaultLayer <- function(object, ...) { + UseMethod(generic = 'DefaultLayer', object = object) +} + +#' @param value Name of layer to set as default +#' +#' @return \code{DefaultLayer<-}: An object with the default layer updated +#' +#' @rdname DefaultLayer +#' @export DefaultLayer<- +#' +"DefaultLayer<-" <- function(object, ..., value) { + UseMethod(generic = 'DefaultLayer<-', object = object) +} + #' Get the Neighbor nearest neighbors distance matrix #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return The distance matrix #' @@ -461,8 +707,8 @@ Distances <- function(object, ...) { #' Get Cell Embeddings #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return The embeddings matrix #' @@ -480,14 +726,13 @@ Embeddings <- function(object, ...) { #' Retrieves data (feature expression, PCA scores, metrics, etc.) for a set #' of cells in a Seurat object #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @export FetchData #' #' @concept data-access #' -#' FetchData <- function(object, ...) { UseMethod(generic = 'FetchData', object = object) } @@ -509,12 +754,21 @@ Features <- function(x, ...) { #' \dQuote{scale.data}). \code{SetAssayData} can be used to replace one of these #' expression matrices #' +#' @template param-dots-method #' @param object An object -#' @param slot Specific assay data to get or set -#' @param ... Arguments passed to other methods +#' @param layer Name of layer to get or set +#' @param slot \Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")} Specific assay data to get or set #' #' @return \code{GetAssayData}: returns the specified assay data #' +#' @template lifecycle-superseded +#' +#' @section Lifecycle: +#' +#' \code{GetAssayData} and \code{SetAssayData} have been superseded. To fetch +#' expression matrices, use \code{\link{LayerData}}; to set expression data, +#' use \code{\link{LayerData<-}} +#' #' @name AssayData #' @rdname AssayData #' @export GetAssayData @@ -523,16 +777,16 @@ Features <- function(x, ...) { #' #' @concept data-access #' -GetAssayData <- function(object, slot, ...) { +GetAssayData <- function(object, ...) { UseMethod(generic = 'GetAssayData', object = object) } #' Get image data #' +#' @template param-dots-method #' @param object An object #' @param mode How to return the image; should accept one of \dQuote{grob}, #' \dQuote{raster}, \dQuote{plotly}, or \dQuote{raw} -#' @param ... Arguments passed to other methods #' #' @return Image data, varying depending on the value of \code{mode}: #' \describe{ @@ -563,8 +817,8 @@ GetImage <- function(object, mode = c('grob', 'raster', 'plotly', 'raw'), ...) { #' Get tissue coordinates #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return A data frame with tissue coordinates #' @@ -584,8 +838,9 @@ GetTissueCoordinates <- function(object, ...) { #' features, while \code{SVFInfo} and \code{SpatiallyVariableFeatures} are #' restricted to spatially variable features #' +#' @template param-dots-method #' @param object An object -#' @param selection.method Which method to pull. For \code{HVFInfo} and +#' @param method Which method to pull. For \code{HVFInfo} and #' \code{VariableFeatures}, choose one from one of the #' following: #' \itemize{ @@ -600,8 +855,7 @@ GetTissueCoordinates <- function(object, ...) { #' \item \dQuote{moransi} #' } #' @param status Add variable status to the resulting data frame -#' -#' @param ... Arguments passed to other methods +#' @param selection.method \Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")} #' #' @return \code{HVFInfo}: A data frame with feature means, dispersion, and #' scaled dispersion @@ -613,7 +867,7 @@ GetTissueCoordinates <- function(object, ...) { #' #' @concept data-access #' -HVFInfo <- function(object, selection.method, status = FALSE, ...) { +HVFInfo <- function(object, method, status = FALSE, ...) { UseMethod(generic = 'HVFInfo', object = object) } @@ -664,8 +918,8 @@ Idents <- function(object, ... ) { #' Get Neighbor algorithm index #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods; #' #' @return Returns the value in the alg.idx slot of the Neighbor object #' @@ -691,8 +945,8 @@ Index <- function(object, ...) { #' Get Neighbor nearest neighbor index matrices #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods; #' #' @return A matrix with the nearest neighbor indices #' @@ -713,8 +967,8 @@ Indices <- function(object, ...) { #' are removed as well. If an associated object is marked as global/persistent, #' the associated object will remain even if its original assay was deleted #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return \code{TRUE} if the object is global/persistent otherwise \code{FALSE} #' @@ -730,10 +984,48 @@ IsGlobal <- function(object, ...) { UseMethod(generic = 'IsGlobal', object = object) } +#' Check if a matrix is empty +#' +#' Takes a matrix and asks if it's empty (either 0x0 or 1x1 with a value of NA) +#' +#' @param x A matrix +#' +#' @return Whether or not \code{x} is empty +#' +#' @rdname IsMatrixEmpty +#' @export IsMatrixEmpty +#' +#' @concept utils +#' +#' @examples +#' IsMatrixEmpty(new("matrix")) +#' IsMatrixEmpty(matrix()) +#' IsMatrixEmpty(matrix(1:3)) +#' +IsMatrixEmpty <- function(x) { + UseMethod(generic = 'IsMatrixEmpty', object = x) +} + +#' Split and Join Layers Together +#' +#' @param object An object +#' @template param-dots-method +#' +#' @return \code{object} with the layers specified joined +#' +#' @rdname SplitLayers +#' @export JoinLayers +#' +#' @concept assay5 +#' +JoinLayers <- function(object, ...) { + UseMethod(generic = 'JoinLayers', object = object) +} + #' Get and set JackStraw information #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return \code{JS}: either a \code{\link{JackStrawData}} object or the #' specified jackstraw data @@ -760,8 +1052,8 @@ JS <- function(object, ...) { #' Get and set object keys #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return \code{Key}: the object key #' @@ -774,6 +1066,15 @@ Key <- function(object, ...) { UseMethod(generic = 'Key', object = object) } +#' @return \code{Keys}: a named vector of keys of sub-objects +#' +#' @rdname Key +#' @export +#' +Keys <- function(object, ...) { + UseMethod(generic = 'Keys', object = object) +} + #' @param value Key value #' #' @return \code{Key<-}: \code{object} with an updated key @@ -787,19 +1088,49 @@ Key <- function(object, ...) { UseMethod(generic = 'Key<-', object = object) } -#' @return \code{Keys}: a named vector of keys of sub-objects +#' Query and Manipulate Assay Layers #' -#' @rdname Key -#' @export +#' @template param-dots-method +#' @param object An object +#' @param layer Name of layer to fetch or set +#' @param slot \Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")} #' -Keys <- function(object, ...) { - UseMethod(generic = 'Keys', object = object) +#' @return \code{LayerData}: the layer data for \code{layer} from \code{object} +#' +#' @rdname Layers +#' @export LayerData +#' +LayerData <- function(object, layer, ...) { + UseMethod(generic = 'LayerData', object = object) +} + +#' @param value New two-dimensional data to be added as a layer +#' +#' @return \code{Layer<-}: \code{object} with \code{value} added as a layer +#' named \code{layer} +#' +#' @rdname Layers +#' @export LayerData<- +#' +"LayerData<-" <- function(object, layer, ..., value) { + UseMethod(generic = 'LayerData<-', object = object) +} + +#' @return \code{Layers}: the names of the layers present in \code{object} +#' +#' @rdname Layers +#' @export Layers +#' +#' @concept data-access +#' +Layers <- function(object, ...) { + UseMethod(generic = 'Layers', object = object) } #' Get and set feature loadings #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return \code{Loadings}: the feature loadings for \code{object} #' @@ -832,8 +1163,12 @@ Loadings <- function(object, ...) { #' @return A numeric vector with new cells in order of the original cells; if #' no match can be found, returns \code{NULL} #' +#' @keywords internal +#' #' @export #' +#' @concept utils +#' MatchCells <- function(new, orig, ordered = FALSE) { if (!is.character(x = orig)) { stop("'orig' must be a character vector") @@ -884,17 +1219,22 @@ Molecules <- function(object, ...) { #' target object (\code{y}). Basically, find all components of a query that #' fall within the bounds of a target spatial region #' +#' @template param-dots-ignored #' @param x Query \code{Spatial} object #' @param y Target \code{Spatial} object #' @param invert Invert the overlay and return only the components of \code{x} #' that fall \emph{outside} the bounds of \code{y} -#' @param ... Ignored #' #' @return \code{x} with only the components that fall within the #' bounds of \code{y} #' +#' @templateVar pkg sf +#' @template note-reqdpkg +#' #' @export #' +#' @concept spatial +#' setGeneric( name = 'Overlay', def = function(x, y, invert = FALSE, ...) { @@ -905,8 +1245,8 @@ setGeneric( #' Get and set project information #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return Project information #' @@ -950,8 +1290,8 @@ Radius <- function(object) { #' Change the cell names in all the different parts of an object. Can be useful #' before combining multiple objects. #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return An object with new cell names #' @@ -1005,11 +1345,12 @@ ReorderIdent <- function(object, var, ...) { #' Convert S4 objects to lists and vice versa. Useful for declassing an S4 #' object while keeping track of it's class using attributes (see section #' \strong{S4 Class Definition Attributes} below for more details). Both -#' \code{ListToS4} and \code{S4ToList} are recursive functions, affecting all -#' lists/S4 objects contained as sub-lists/sub-objects. +#' \code{ListToS4} and \code{S4ToList} are recursive functions, affecting +#' all lists/S4 objects contained as sub-lists/sub-objects #' -#' @param x A list with an S4 class definition attribute #' @param object An S4 object +#' @param x A list with an S4 class definition +#' (\dQuote{\code{classDef}}) attribute #' #' @return \code{S4ToList}: A list with an S4 class definition attribute #' @@ -1017,18 +1358,30 @@ ReorderIdent <- function(object, var, ...) { #' S4 classes are scoped to the package and class name. In order to properly #' track which class a list is generated from in order to build a new one, #' these function use an \code{\link[base:attr]{attribute}} to denote the -#' class name and package of origin. This attribute is stored as -#' \dQuote{classDef} and takes the form of \dQuote{\code{package:class}}. +#' class name and package of origin. This attribute is stored as 1-length +#' character vector named \dQuote{\code{classDef}} and takes the form +#' of \dQuote{\code{package:class}} #' #' @name s4list #' @rdname s4list #' -#' @concept utils +#' @keywords internal #' #' @export #' +#' @concept utils +#' @family s4list +#' +#' @examples +#' # Turn an S4 object into a list +#' pbmc.list <- S4ToList(pbmc_small) +#' class(pbmc.list) +#' attributes(pbmc.list) +#' +#' str(pbmc.list$reductions) +#' S4ToList <- function(object) { - if (!(isS4(object) || inherits(x = object, what = 'list'))) { + if (!(isS4(object) || is_bare_list(x = object))) { return(object) } UseMethod(generic = 'S4ToList', object = object) @@ -1043,8 +1396,7 @@ S4ToList <- function(object) { #' #' @order 2 #' -#' -SetAssayData <- function(object, slot, new.data, ...) { +SetAssayData <- function(object, layer, new.data, slot = deprecated(), ...) { UseMethod(generic = 'SetAssayData', object = object) } @@ -1066,10 +1418,12 @@ SetIdent <- function(object, ...) { #' #' @param coords ... #' -#' @return ... +#' @return A simplified version of \code{coords} #' #' @export #' +#' @concept spatial +#' Simplify <- function(coords, tol, topologyPreserve = TRUE) { UseMethod(generic = 'Simplify', object = coords) } @@ -1082,10 +1436,19 @@ Simplify <- function(coords, tol, topologyPreserve = TRUE) { #' #' @order 5 #' -SpatiallyVariableFeatures <- function(object, selection.method, ...) { +SpatiallyVariableFeatures <- function(object, method, ...) { UseMethod(generic = 'SpatiallyVariableFeatures', object = object) } +# @rdname SplitLayers +# @export SplitLayers +# +# @order 1 +# +SplitLayers <- function(object, ...) { + UseMethod(generic = 'SplitLayers', object = object) +} + #' @return \code{StashIdent}: An object with the identities stashed #' #' @rdname Idents @@ -1102,8 +1465,8 @@ StashIdent <- function(object, save.name, ...) { #' Get the standard deviations for an object #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return The standard deviations #' @@ -1116,6 +1479,32 @@ Stdev <- function(object, ...) { UseMethod(generic = 'Stdev', object = object) } +#' Stitch Matrices Together +#' +#' @template param-dots-method +#' @param x A matrix +#' @param y One or more matrices of the same class or coercible to the +#' same class as \code{x} +#' @param rowmap,colmap \code{\link{LogMap}s} describing the row and cell +#' membership of each matrix; the \code{LogMap} entries are assumed to be in +#' the order of \code{c(x, y)} +#' +#' @return A single matrix of type \code{class(x)} consisting of all values +#' in component matrices +#' +#' @export +#' +#' @concept utils +#' +StitchMatrix <- function(x, y, rowmap, colmap, ...) { + if (!inherits(x = rowmap, what = 'LogMap')) { + abort(message = "'rowmap' must be a 'LogMap'") + } else if (!inherits(x = colmap, what = 'LogMap')) { + abort(message = "'colmap' must be a 'LogMap'") + } + UseMethod(generic = 'StitchMatrix', object = x) +} + #' @return \code{SVFInfo}: a data frame with the spatially variable features #' #' @rdname VariableFeatures @@ -1123,7 +1512,7 @@ Stdev <- function(object, ...) { #' #' @order 4 #' -SVFInfo <- function(object, selection.method, status, ...) { +SVFInfo <- function(object, method, status, ...) { UseMethod(generic = 'SVFInfo', object = object) } @@ -1134,27 +1523,29 @@ SVFInfo <- function(object, selection.method, status, ...) { #' @rdname Theta #' @export #' +#' @concept spatial +#' Theta <- function(object) { UseMethod(generic = 'Theta', object = object) } -#' Get and set additional tool data +#' Get and Set Additional Tool Data #' #' Use \code{Tool} to get tool data. If no additional arguments are provided, #' will return a vector with the names of tools in the object. #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return If no additional arguments, returns the names of the tools in the #' object; otherwise returns the data placed by the tool requested #' #'@note For developers: set tool data using \code{Tool<-}. \code{Tool<-} will #'automatically set the name of the tool to the function that called -#'\code{Tool<-},so each function gets one entry in the tools list and cannot +#'\code{Tool<-}, so each function gets one entry in the tools list and cannot #'overwrite another function's entry. The automatic naming will also remove any -#'method identifiers (eg. RunPCA.Seurat will become RunPCA); please -#'plan accordingly. +#'method identifiers (eg. \code{RunPCA.Seurat} will become \code{RunPCA}); +#'please plan accordingly #' #' @rdname Tool #' @export Tool @@ -1163,6 +1554,26 @@ Theta <- function(object) { #' #' @concept data-access #' +#' @examples +#' # Example function that adds unstructured data to tools +#' MyTool <- function(object) { +#' sample.tool.output <- matrix(rnorm(n = 16), nrow = 4) +#' # Note: `Tool<-` must be called from within a function +#' # and the name of the tool will be generated from the function name +#' Tool(object) <- sample.tool.output +#' return(object) +#' } +#' +#' # Run our tool +#' set.seed(42L) +#' pbmc_small <- MyTool(pbmc_small) +#' +#' # Get a list of tools run +#' Tool(pbmc_small) +#' +#' # Access specific tool data +#' Tool(pbmc_small, slot = "MyTool") +#' Tool <- function(object, ...) { UseMethod(generic = 'Tool', object = object) } @@ -1183,7 +1594,7 @@ Tool <- function(object, ...) { #' #' @order 2 #' -VariableFeatures <- function(object, selection.method = NULL, ...) { +VariableFeatures <- function(object, method = NULL, ...) { UseMethod(generic = 'VariableFeatures', object = object) } @@ -1200,8 +1611,8 @@ VariableFeatures <- function(object, selection.method = NULL, ...) { #' Get Version Information #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @rdname Version #' @export Version @@ -1220,8 +1631,8 @@ Version <- function(object, ...) { #' Returns a list of cells that match a particular set of criteria such as #' identity class, high/low values for particular PCs, etc. #' +#' @template param-dots-method #' @param object An object -#' @param ... Arguments passed to other methods #' #' @return A vector of cell names #' diff --git a/R/graph.R b/R/graph.R index 168e6ece..8c20cf19 100644 --- a/R/graph.R +++ b/R/graph.R @@ -16,15 +16,18 @@ NULL #' #' @name Graph-class #' @rdname Graph-class +#' #' @exportClass Graph #' #' @seealso \code{\link[Matrix]{dgCMatrix-class}} #' +#' @family graph +#' Graph <- setClass( Class = 'Graph', contains = "dgCMatrix", slots = list( - assay.used = 'OptionalCharacter' + assay.used = 'character' ) ) @@ -53,10 +56,10 @@ as.Graph.Matrix <- function(x, ...) { CheckDots(...) x <- as.sparse(x = x) if (is.null(x = rownames(x = x))) { - stop("Please provide rownames to the matrix before converting to a Graph.") + abort(message = "Please provide rownames to the matrix before converting to a Graph") } if (is.null(x = colnames(x = x))) { - stop("Please provide colnames to the matrix before converting to a Graph.") + abort(message = "Please provide colnames to the matrix before converting to a Graph") } return(as(object = x, Class = "Graph")) } @@ -102,13 +105,31 @@ as.Graph.Neighbor <- function(x, weighted = TRUE, ...) { return(graph) } +#' @method Cells Graph +#' @export +#' +Cells.Graph <- function(x, margin = 1L, ...) { + margin <- as.integer(x = margin) + if (is_na(x = margin)) { + return(Reduce(f = union, x = dimnames(x = x))) + } + if (!isTRUE(margin %in% c(1L, 2L))) { + stop("'margin' must be either 1 or 2", call. = FALSE) + } + return(dimnames(x = x)[[margin]]) +} + #' @rdname DefaultAssay #' @export #' @method DefaultAssay Graph #' DefaultAssay.Graph <- function(object, ...) { - object <- UpdateSlots(object = object) - return(slot(object = object, name = 'assay.used')) + # object <- UpdateSlots(object = object) + assay <- slot(object = object, name = 'assay.used') + if (!length(x = assay)) { + assay <- NULL + } + return(assay) } #' @rdname DefaultAssay @@ -116,8 +137,15 @@ DefaultAssay.Graph <- function(object, ...) { #' @method DefaultAssay<- Graph #' "DefaultAssay<-.Graph" <- function(object, ..., value) { - object <- UpdateSlots(object = object) + op <- options(Seurat.object.validate = FALSE) + on.exit(expr = options(op)) + object <- suppressWarnings(expr = UpdateSlots(object = object)) + if (!length(x = value) || !isTRUE(x = nzchar(x = value))) { + value <- character(length = 0L) + } slot(object = object, name = 'assay.used') <- value + options(op) + validObject(object = object) return(object) } @@ -129,6 +157,67 @@ DefaultAssay.Graph <- function(object, ...) { # S4 methods #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +setValidity( + Class = 'Graph', + method = function(object) { + if (.GetSeuratCompat() < '5.0.0') { + return(TRUE) + } + if (isFALSE(x = getOption(x = "Seurat.object.validate", default = TRUE))) { + warn( + message = paste("Not validating", class(x = object)[1L], "objects"), + class = 'validationWarning' + ) + return(TRUE) + } + valid <- NULL + # Check dimnames + dnames <- dimnames(x = object) + for (i in seq_along(along.with = dnames)) { + type <- c('row', 'column')[i] + if (is.null(x = dnames[[i]])) { + valid <- c(valid, paste(type, "names must be provided")) + } else if (any(!nzchar(x = dnames[[i]]))) { + valid <- c(valid, paste(type, "names must not be empty strings")) + } else if (anyDuplicated(x = dnames[[i]])) { + valid <- c(valid, paste(type, "names may not contain duplicates")) + } + } + # Check default assay + assay <- DefaultAssay(object = object) + if (length(x = assay) && !nzchar(x = assay)) { + valid <- c(valid, "'assay.used' may not be an empty character ('')") + } + return(valid %||% TRUE) + } +) + +#' Graph Object Overview +#' +#' Overview of a \code{\link{Graph}} Object +#' +#' @template return-show +#' +#' @keywords internal +#' +#' @concept graph +#' +#' @examples +#' pbmc_small[["RNA_snn"]] +#' +setMethod( + f = 'show', + signature = 'Graph', + definition = function(object) { + cat( + "A Graph object containing", + nrow(x = object), + "cells" + ) + } +) + + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Internal #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/R/keymixin.R b/R/keymixin.R index 790954e3..fb185520 100644 --- a/R/keymixin.R +++ b/R/keymixin.R @@ -1,4 +1,5 @@ #' @include zzz.R +#' @include generics.R #' NULL @@ -11,9 +12,7 @@ NULL #' A mixin (virtual class) for enabling keyed objects; provides consistent #' behavior for getting, setting, and validating keys #' -#' @slot key A one-length character vector with the object's key; keys must -#' be one or more alphanumeric characters followed by an underscore -#' \dQuote{\code{_}} (regex pattern \dQuote{\code{^[[:alnum:]]+_$}}) +#' @template slot-key #' #' @keywords internal #' @@ -21,6 +20,8 @@ NULL #' #' @aliases KeyMixin #' +#' @family key +#' setClass( Class = 'KeyMixin', contains = 'VIRTUAL', @@ -31,6 +32,48 @@ setClass( # Functions #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#' Regex Pattern for Keys +#' +#' @return Returns the regex pattern for keys +#' (\dQuote{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}) +#' +#' @keywords internal +#' +#' @export +#' +#' @family key +#' +.KeyPattern <- function() { + return('^[a-zA-Z][a-zA-Z0-9]*_$') +} + +#' Generate a Random Key +#' +#' @inheritParams RandomName +#' +#' @return Returns a valid key +#' +#' @keywords internal +#' +#' @export +#' +#' @family key +#' +#' @examples +#' set.seed(42L) +#' .RandomKey() +#' +.RandomKey <- function(length = 7L, ...) { + return(Key( + object = RandomName( + length = length, + chars = c(letters, LETTERS, seq.int(from = 0L, to = 9L)), + ... + ), + quiet = TRUE + )) +} + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Methods for Seurat-defined generics #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -55,14 +98,19 @@ Key.character <- function(object, quiet = FALSE, ...) { #' @details \code{Key.KeyMixin}: Get the key of a keyed object #' -#' @return \code{Key.KeyMixin}: The key from \code{object} +#' @return \code{Key.KeyMixin}: The key from \code{object}; if no key set, +#' returns \code{NULL} #' #' @rdname KeyMixin-class #' @method Key KeyMixin #' @export #' Key.KeyMixin <- function(object, ...) { - return(slot(object = object, name = 'key')) + key <- slot(object = object, name = 'key') + if (!length(x = key)) { + key <- NULL + } + return(key) } #' @details \code{Key<-}: Set the key of a keyed object @@ -79,6 +127,13 @@ Key.KeyMixin <- function(object, ...) { return(object) } +#' @method Key NULL +#' @export +#' +Key.NULL <- function(object, ...) { + return(NULL) +} + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Methods for R-defined generics #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -87,23 +142,162 @@ Key.KeyMixin <- function(object, ...) { # Internal #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#' Internal Key Methods +#' +#' Internal key methods for classes that inherit from \code{\link{KeyMixin}}; +#' these functions are designed to be used as the body for methods for +#' \code{Key()} and \code{Key<-()} when an immediate public method is required. +#' Generally speaking, classes that inherit from \code{KeyMixin} should use the +#' \code{KeyMixin} methods for \code{Key()} and \code{Key<-()} +#' +#' @inheritParams Key +#' +#' @inherit Key return +#' +#' @keywords internal +#' +#' @noRd +#' +.Key <- function(object, ...) { + CheckDots(...) + return(NextMethod()) +} + +#' @rdname dot-Key +#' +#' @noRd +#' +".Key<-" <- function(object, ..., value) { + CheckDots(...) + object <- UpdateSlots(object = object) + object <- NextMethod() + return(object) +} + +#' Update a Key +#' +#' @param key A character to become a Seurat Key +#' +#' @return An updated Key that's valid for Seurat +#' +#' @keywords internal +#' +#' @family key +#' +#' @noRd +#' +UpdateKey <- function(key) { + key.msg <- 'Keys should be one or more alphanumeric characters followed by an underscore' + if (isTRUE(x = grepl(pattern = .KeyPattern(), x = key))) { + return(key) + } + new.key <- regmatches( + x = key, + m = gregexpr(pattern = '[[:alnum:]]+', text = key) + ) + new.key <- paste0(paste(unlist(x = new.key), collapse = ''), '_') + if (new.key == '_') { + new.key <- paste0(RandomName(length = 3), '_') + } + warning( + key.msg, + ", setting key from ", + key, + " to ", + new.key, + call. = FALSE, + immediate. = TRUE + ) + return(new.key) +} + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # S4 methods #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#' Key Validity +#' +#' Validation of \code{\link{KeyMixin}} objects is handled by +#' \code{\link[methods]{validObject}} +#' +#' @section Key Validation: +#' Keys must be a one-length character vector; a key must be composed of one +#' of the following: +#' \itemize{ +#' \item An empty string (eg. \dQuote{\code{''}}) where \code{nzchar() == 0} +#' \item An string composed of one or more alphanumeric values +#' (both lower- and upper-case) that ends with an underscore +#' (\dQuote{\code{_}}); the first character must be a letter +#' } +#' Keys that are not empty strings are validated with the regex +#' \dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}} +#' +#' @importFrom rlang is_scalar_character +#' +#' @keywords internal +#' +#' @name Key-validity +#' +#' @family key +#' setValidity( Class = 'KeyMixin', method = function(object) { + if (isFALSE(x = getOption(x = "Seurat.object.validate", default = TRUE))) { + warn( + message = paste("Not validating", class(x = object)[1L], "objects"), + class = 'validationWarning' + ) + return(TRUE) + } valid <- NULL - pattern <- '^[[:alnum:]]+_$' key <- Key(object = object) # Ensure key has length of 1 - if (length(x = key) != 1L) { - valid <- c(valid, "Keys must be a one-length character vector") - } else if (nchar(x = key) && !grepl(pattern = pattern, x = key)) { - # Ensure proper key composition - valid <- c(valid, paste0("Keys must match the pattern '", pattern, "'")) + if (!is.null(x = key) && .GetSeuratCompat() >= '5.0.0') { + if (!is_scalar_character(x = key)) { + valid <- c(valid, "Keys must be a one-length character vector") + } else if (is_na(x = key)) { + valid <- c(valid, "Keys may not be 'NA'") + } else if (nzchar(x = key) && !grepl(pattern = .KeyPattern(), x = key)) { + # Ensure proper key composition + valid <- c( + valid, + paste0("Keys must match the pattern '", .KeyPattern(), "'") + ) + } } return(valid %||% TRUE) } ) + +.CheckKey <- function(key, existing = NULL, name = NULL) { + if (rlang::is_missing(x = key) || !length(x = key) || !nzchar(x = key)) { + key <- Key(object = tolower(name) %||% RandomName(), quiet = TRUE) + } + if (!is.null(x = names(x = existing)) && !is.null(x = name)) { + existing <- existing[setdiff(x = names(x = existing), y = name)] + } + if (key %in% existing) { + old <- key + key <- Key(object = tolower(x = name %||% RandomName()), quiet = TRUE) + i <- 1L + n <- 5L + while (key %in% existing) { + key <- Key(object = RandomName(length = n), quiet = TRUE) + i <- i + 1L + if (!i %% 7L) { + n <- n + 2L + } + } + warn( + message = paste( + "Key", + sQuote(x = old), + "taken, using", + sQuote(x = key), + "instead" + ) + ) + } + return(key) +} diff --git a/R/layers.R b/R/layers.R new file mode 100644 index 00000000..7cccf025 --- /dev/null +++ b/R/layers.R @@ -0,0 +1,379 @@ +#' @include zzz.R +#' @importFrom methods as callNextMethod +#' +NULL + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Generics +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +setGeneric( + name = '.GetLayerData', + def = function(x, ...) { + standardGeneric(f = '.GetLayerData') + }, + signature = c('x') +) + +setGeneric( + name = '.PrepLayerData', + def = function(x, target, transpose = NULL, ...) { + standardGeneric(f = '.PrepLayerData') + }, + signature = c('x', 'target') +) + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Functions +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Methods for Seurat-defined generics +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Methods for R-defined generics +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Internal +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +.CheckDimnames <- function(dnames, dims) { + if (is.null(x = dnames)) { + return(NULL) + } + if (!is.numeric(x = dims)) { + stop("'dims' must be a numeric vector", call. = FALSE) + } else if (!is.list(x = dnames) || length(x = dnames) != length(x = dims)) { + stop("'dnames' must be a list of length ", length(x = dims), call. = FALSE) + } + dims <- dims %/% 1L + didx <- match( + x = vapply( + X = dnames, + FUN = length, + FUN.VALUE = numeric(length = 1L), + USE.NAMES = FALSE + ), + table = dims + ) + didx[duplicated(x = didx)] <- NA + didx[is.na(x = didx)] <- setdiff( + x = seq_along(along.with = dims), + y = didx + ) + return(dnames[didx]) +} + +#' Check Feature Margin +#' +#' @param fmargin Either \code{1} or \code{2} +#' +#' @return \code{fmargin} +#' +#' @keywords internal +#' +#' @export +#' +#' @examples +#' .CheckFmargin(1L) +#' .CheckFmargin(2.3) +#' +#' # Error if `fmargin` is outside of [1:2] +#' if (FALSE) { +#' .CheckFmargin(3L) +#' } +#' +.CheckFmargin <- function(fmargin) { + fmargin <- fmargin %/% 1L + if (!fmargin %in% seq.int(from = 1L, to = 2L)) { + abort(message = paste(sQuote(x = fmargin), "must be either 1 or 2")) + } + return(fmargin) +} + +.Cmargin <- function(fmargin) { + fmargin <- .CheckFmargin(fmargin = fmargin) + return(setdiff(x = seq.int(from = 1L, to = 2L), y = fmargin)) +} + +#' Get and Prepare Layer Data +#' +#' Assemble layer data pulled from and prepare layer data for addition to +#' v5 assays; v5 assays allow layers to be in multiple formats and support +#' both regular and transposed orientations +#' +#' When transposition is required, \code{.GetLayerData2} and +#' \code{.PrepLayerData2} will attempt to +#' \link[.GetMethod]{determine the optimal method} of \code{\link[base:t]{t()}} +#' to use; if no optimal method is found, \code{base::t.default} will be used +#' for transposition, which may be slow +#' +#' @param x A matrix-like object +#' @param dnames An optional list with feature and cell names +#' (in order for \code{.GetLayerData2}) +#' @param fmargin Margin for features (1 or 2); for \code{.GetLayerData2}, if +#' \code{fmargin} is 2, \code{x} will be transposed (see details) +#' +#' @return \code{.GetLayerData2}: \code{x}, potentially transposed and +#' potentially with \code{dnames} set as the \code{\link{dimnames}} +#' +#' @keywords internal +#' +#' @noRd +#' +.GetLayerData2 <- function(x, dnames = NULL, fmargin = 1L) { + # Check dimnames + if (!is.null(x = dnames)) { + ndims <- length(x = dim(x = x)) + if (!is_bare_list(x = dnames, n = ndims)) { + abort(message = paste("'dnames' must be a list of length", ndims)) + } + didx <- match( + x = sapply(X = dnames, FUN = length, USE.NAMES = FALSE), + table = dim(x = x) + ) + didx[duplicated(x = didx)] <- NA + didx[is.na(x = didx)] <- setdiff(x = seq_len(length.out = ndims), y = didx) + dnames <- dnames[didx] + } + # Check fmargin + fmargin <- fmargin %/% 1L + if (!fmargin %in% c(1L, 2L)) { + abort(message = "'fmargin' must be either 1 or 2") + } + # Do we transpose + if (fmargin == 2L) { + tf <- .GetMethod(fxn = 't', cls = class(x = x)) + x <- tf(x) + dnames <- rev(x = dnames) + } + suppressWarnings(expr = suppressMessages(expr = dimnames(x = x) <- dnames)) + return(x) +} + +#' @param target An optional two-length integer vector with dimensions of +#' the v5 assay that \code{x} will be added to; used only if +#' \code{transpose} is \code{NULL} +#' @param transpose Transpose \code{x} before returning it; if \code{NULL} and +#' \code{target} is provided, will attempt to determine if transposition is +#' necessary (see details) +#' +#' @return \code{.PrepLayerData2}: \code{x} with \code{\link{dimnames}} removed +#' and \code{dnames} added as attributes \dQuote{\code{features}} and +#' \dQuote{\code{cells}} and potentially transposed +#' +#' @rdname dot-GetLayerData2 +#' +#' @keywords internal +#' +#' @noRd +#' +.PrepLayerData2 <- function( + x, + target = NULL, + transpose = FALSE, + dnames = NULL, + fmargin = 1L +) { + if (is.null(x = x)) { + return(x) + } + # Check fmargin + fmargin <- fmargin %/% 1L + if (!fmargin %in% c(1L, 2L)) { + abort(message = "'fmargin' must be either 1 or 2") + } + cmargin <- c(2L, 1L)[fmargin] + # Auto-check transposition + if (!is.null(x = target) && is.null(x = transpose)) { + if (!is_bare_integerish(x = target, n = 2L, finite = TRUE)) { + abort(message = "'target' must be a two-length integer vector") + } + xdim <- dim(x)[c(fmargin, cmargin)] + if (all(rev(xdim) == target)) { + transpose <- TRUE + } + } + # Check dimnames + if (is.null(x = dnames)) { + dnames <- dimnames(x = x) + } else if (!is_bare_list(x = dnames, n = 2L)) { + abort(message = "'dnames' must be a two-length list") + } + # Handle transposition + if (isTRUE(x = transpose)) { + tf <- .GetMethod(fxn = 't', cls = class(x = x)) + x <- tf(x) + dnames <- rev(x = dnames) + } + x <- suppressMessages(expr = unname(x)) + attr(x = x, which = 'features') <- dnames[[fmargin]] + attr(x = x, which = 'cells') <- dnames[[cmargin]] + return(x) +} + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# S4 methods +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +setMethod( + f = '.GetLayerData', + signature = c(x = 'ANY'), + definition = function(x, dnames = NULL, fmargin = 1L, ...) { + dnames <- .CheckDimnames(dnames = dnames, dims = dim(x = x)) + fmargin <- .CheckFmargin(fmargin = fmargin) + if (fmargin == 2L) { + x <- base::t(x = x) + dnames <- rev(x = dnames) + } + dimnames(x = x) <- dnames + return(x) + } +) + +setMethod( + f = '.GetLayerData', + signature = c(x = 'data.frame'), + definition = function(x, dnames = NULL, fmargin = 1L, ...) { + x <- callNextMethod() + return(as.data.frame(x = x)) + } +) + +#' @importFrom Matrix t +#' @importClassesFrom Matrix Matrix +#' +setMethod( + f = '.GetLayerData', + signature = c(x = 'Matrix'), + definition = function(x, dnames = NULL, fmargin = 1L, ...) { + dnames <- .CheckDimnames(dnames = dnames, dims = dim(x = x)) + fmargin <- .CheckFmargin(fmargin = fmargin) + if (fmargin == 2L) { + x <- Matrix::t(x = x) + dnames <- rev(x = dnames) + } + dimnames(x = x) <- dnames + return(x) + } +) + +#' @importFrom spam t +#' @importClassesFrom spam spam +#' +setMethod( + f = '.GetLayerData', + signature = c(x = 'spam'), + definition = function(x, fmargin = 1L, ...) { + fmargin <- .CheckFmargin(fmargin = fmargin) + if (fmargin == 2L) { + x <- spam::t(x = x) + } + return(x) + } +) + +setMethod( + f = '.PrepLayerData', + signature = c(x = 'ANY', target = 'numeric'), + definition = function( + x, + target, + transpose = NULL, + dnames = NULL, + fmargin = 1L, + ... + ) { + # Check the value of target + # target should be c(nfeatures, ncells) + if (length(x = target) != 2L) { + stop("'target' must be a two-length numeric vector", call. = FALSE) + } + # If transpose is NULL, try to determine if we should transpose + if (is.null(x = transpose)) { + fmargin <- .CheckFmargin(fmargin = fmargin) + cmargin <- .Cmargin(fmargin = fmargin) + xdim <- dim(x = x)[c(fmargin, cmargin)] + if (all(rev(x = xdim) == target)) { + transpose <- TRUE + } + } + return(.PrepLayerData( + x = x, + target = NULL, + transpose = transpose, + dnames = dnames, + fmargin = fmargin, + ... + )) + } +) + +setMethod( + f = '.PrepLayerData', + signature = c(x = 'ANY', target = 'NULL'), + definition = function( + x, + target, + transpose = NULL, + dnames = NULL, + fmargin = 1L, + tf = base::t, + ... + ) { + fmargin <- .CheckFmargin(fmargin = fmargin) + cmargin <- .Cmargin(fmargin = fmargin) + if (isTRUE(x = transpose)) { + x <- tf(x) + dnames <- rev(x = dnames) + } + x <- suppressMessages(expr = unname(x)) + attr(x = x, which = 'features') <- dnames[[fmargin]] + attr(x = x, which = 'cells') <- dnames[[cmargin]] + return(x) + } +) + +#' @importFrom Matrix t +#' @importClassesFrom Matrix Matrix +#' +setMethod( + f = '.PrepLayerData', + signature = c(x = 'Matrix', target = 'NULL'), + definition = function(x, target, transpose = NULL, ...) { + return(callNextMethod( + x = x, + target = target, + transpose = transpose, + tf = Matrix::t, + ... + )) + } +) + +setMethod( + f = '.PrepLayerData', + signature = c(x = 'NULL', target = 'ANY'), + definition = function(x, target, transpose = NULL, ...) { + return(x) + } +) + +#' @importFrom spam t +#' @importClassesFrom spam spam +#' +setMethod( + f = '.PrepLayerData', + signature = c(x = 'spam', target = 'NULL'), + definition = function(x, target, transpose = NULL, ...) { + return(callNextMethod( + x = x, + target = target, + transpose = transpose, + tf = spam::t, + ... + )) + } +) diff --git a/R/logmap.R b/R/logmap.R index e3177da2..9fdcded8 100644 --- a/R/logmap.R +++ b/R/logmap.R @@ -10,14 +10,16 @@ NULL #' #' A simple container for storing mappings of values using logical matrices. #' Keeps track of which values (rows) are present in which observations -#' (columns). \code{LogMap} objects can be created with \code{LogMap()}; queries -#' can be performed with \code{[[} and observations can be added or removed -#' with \code{[[<-} +#' (columns). \code{LogMap} objects can be created with \code{LogMap()}; +#' queries can be performed with \code{[[} and observations can be added +#' or removed with \code{[[<-} #' #' @slot .Data A logical matrix with at least one row #' #' @exportClass LogMap #' +#' @family logmap +#' setClass( Class = 'LogMap', contains = 'matrix' @@ -27,15 +29,17 @@ setClass( # Functions #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#' @rdname LogMap-class +#' #' @param y A character vector #' #' @return \code{LogMap}: A new \code{LogMap} object with zero columns and #' \code{length(x = x)} rows; rownames are set to \code{x} #' -#' @rdname LogMap-class -#' #' @export #' +#' @order 1 +#' #' @examples #' # Create a LogMap #' map <- LogMap(letters[1:10]) @@ -47,6 +51,7 @@ setClass( #' #' # Add an observation to the LogMap #' map[['obs']] <- c(1, 3, 7) +#' map[['entry']] <- c(2, 7, 10) #' map #' #' # Get the names of observations in the LogMap @@ -60,6 +65,7 @@ setClass( #' #' # Remove an observation from the LogMap #' map[['obs']] <- NULL +#' map[['entry']] <- NULL #' map #' LogMap <- function(y) { @@ -80,6 +86,200 @@ LogMap <- function(y) { # Methods for R-defined generics #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#' Coerce Logical Maps to Matrices +#' +#' Coerce a logical map to a matrix; this removes all +#' \link[=LogMap]{logical map} class capabilities from +#' the object and returns a base-R matrix object +#' +#' @param x A \code{\link{LogMap}} object +#' @template param-dots-ignored +#' +#' @return A base-R matrix created from \code{x} +#' +#' @method as.matrix LogMap +#' @export +#' +#' @family logmap +#' +#' @examples +#' map <- LogMap(letters[1:10]) +#' map[['obs']] <- c(1, 3, 7) +#' mat <- as.matrix(map) +#' mat +#' class(mat) +#' +as.matrix.LogMap <- function(x, ...) { + return(as(object = x, Class = 'matrix')) +} + +#' Drop Unused Logical Map Values +#' +#' Remove any unused values from a \link[=LogMap]{logical map} +#' +#' @template param-dots-ignored +#' @param x A \code{LogMap} object +#' +#' @return \code{x} with values not present in any +#' observation removed +#' +#' @method droplevels LogMap +#' @export +#' +#' @family logmap +#' +#' @examples +#' map <- LogMap(letters[1:10]) +#' map[['obs']] <- c(1, 3, 7) +#' map[['entry']] <- c(2, 7, 10) +#' +#' # Remove unused values +#' map <- droplevels(map) +#' map +#' map[[]] +#' +droplevels.LogMap <- function(x, ...) { + fidx <- which(x = apply( + X = x, + MARGIN = 1L, + FUN = function(row) { + return(all(vapply( + X = row, + FUN = isFALSE, + FUN.VALUE = logical(length = 1L) + ))) + } + )) + if (length(x = fidx)) { + x <- as(object = x[-fidx, , drop = FALSE], Class = 'LogMap') + } + validObject(object = x) + return(x) +} + +#' Find Common Logical Map Values +#' +#' Identify values in a \link[=LogMap]{logical map} that are +#' common to every observation +#' +#' @inheritParams droplevels.LogMap +#' @param y Ignored +#' +#' @return The values of \code{x} that are present in \strong{every} observation +#' +#' @method intersect LogMap +#' @export +#' +#' @family logmap +#' +#' @examples +#' map <- LogMap(letters[1:10]) +#' map[['obs']] <- c(1, 3, 7) +#' map[['entry']] <- c(2, 7, 10) +#' +#' # Identify values that are present in every observation +#' intersect(map) +#' +intersect.LogMap <- function(x, y = missing_arg(), ...) { + if (!is_missing(x = y)) { + abort(message = "'y' must not be provided") + } + idx <- which(x = apply(X = x, MARGIN = 1L, FUN = all)) + return(rownames(x = x)[idx]) +} + +#' Find Observations by Value +#' +#' Identify the observations that contain a specific +#' value in a \link[=LogMap]{logical map} +#' +#' @template param-dots-ignored +#' @param object A \code{\link{LogMap}} object +#' @param values A vector of values to find observations for +#' @param select Observation selection method; choose from: +#' \itemize{ +#' \item \dQuote{\code{first}}: the first observation the value is found in +#' \item \dQuote{\code{last}}: the last observation the value is found in +#' \item \dQuote{\code{common}}: the first most-common observation the value +#' is found in; most-common is determined by the observation that contains +#' the most of the values requested +#' \item \dQuote{\code{all}}: all observations the value is found in +#' } +#' @param simplify Simplify the resulting list to a vector +#' +#' @return \code{labels}: A list, or vector if \code{simplify} is \code{TRUE}, +#' of all values and the observations they're found in, according +#' to the value of \code{select} +#' +#' @method labels LogMap +#' @export +#' +#' @family logmap +#' +#' @examples +#' map <- LogMap(letters[1:10]) +#' map[['obs']] <- c(1, 3, 7) +#' map[['entry']] <- c(2, 7, 10) +#' +#' # Find observations for a set of values +#' labels(map, c('a', 'b', 'g')) +#' +labels.LogMap <- function( + object, + values, + select = c('first', 'last', 'common', 'all'), + simplify = TRUE, + ... +) { + select <- select[1L] + select <- match.arg(arg = select) + values <- intersect(x = values, y = rownames(x = object)) + p <- progressor(along = values) + obs <- sapply( + X = values, + FUN = function(x) { + vals <- colnames(x = object)[which(x = object[x, , drop = TRUE])] + p() + return(vals) + }, + simplify = FALSE, + USE.NAMES = TRUE + ) + obs <- Filter(f = length, x = obs) + obs <- switch( + EXPR = select, + 'first' = lapply(X = obs, FUN = '[[', 1L), + 'last' = lapply( + X = obs, + FUN = function(x) { + return(x[[length(x = x)]]) + } + ), + common = { + counts <- table(unlist(x = obs)) + tmp <- obs + obs <- vector(mode = 'character', length = length(x = tmp)) + names(x = obs) <- names(x = tmp) + for (i in seq_along(along.with = obs)) { + obs[i] <- names(x = which.max( + x = counts[names(x = counts) %in% tmp[[i]]] + )) + } + obs + }, + obs + ) + if (isTRUE(x = simplify)) { + tmp <- obs + obs <- unlist(x = tmp) + names(x = obs) <- make.unique(names = rep.int( + x = names(x = tmp), + times = vapply(X = tmp, FUN = length, FUN.VALUE = numeric(length = 1L)) + )) + } + return(obs) +} + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Internal #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -88,9 +288,140 @@ LogMap <- function(y) { # S4 methods #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#' Matrix-like Subsetting for \link[=LogMap]{Logical Maps} +#' +#' @inheritParams base::`[` +#' @inheritParams LogMap-class +#' @param i,j Vectors of values (\code{i}) and observations (\code{j}) to pull +#' from \code{x} +#' @template param-dots-method +#' +#' @note \code{i} is not reordable; passing a different order for \code{i} +#' will return a subset with rows in the same order as \code{x} +#' +#' @name [,LogMap +#' @rdname sub-LogMap-method +#' +#' @keywords internal +#' +#' @examples +#' map <- LogMap(letters[1:10]) +#' map[['obs']] <- c(1, 3, 7) +#' map[['entry']] <- c(2, 7, 10) +#' +#' map[] +#' map[1:5, 2L] +#' map[c("b", "c", "f"), "obs"] +#' +#' # Pass `drop = TRUE` to cast to `matrix` +#' map[1:3, , drop = TRUE] +#' +#' # Note that `i` is non-reordable +#' rownames(map)[1:3] +#' map[c("b", "c", "a"), , drop = TRUE] +#' +NULL + +#' @rdname sub-LogMap-method +#' +setMethod( + f = '[', + signature = c(x = 'LogMap', i = 'missing', j = 'missing'), + definition = function(x, i, j, ..., drop = FALSE) { + return(x) + } +) + +#' @rdname sub-LogMap-method +#' +setMethod( + f = '[', + signature = c(x = 'LogMap', i = 'character', j = 'character'), + definition = function(x, i, j, ..., drop = FALSE) { + i <- i[MatchCells(new = i, orig = rownames(x = x), ordered = TRUE)] + x <- as.matrix(x = x)[i, j, drop = drop] + if (!isTRUE(x = drop)) { + x <- as(object = x, Class = 'LogMap') + } + return(x) + } +) + +#' @rdname sub-LogMap-method +#' +setMethod( + f = '[', + signature = c(x = 'LogMap', i = 'character', j = 'missing'), + definition = function(x, i, j, ..., drop = FALSE) { + i <- i[MatchCells(new = i, orig = rownames(x = x), ordered = TRUE)] + x <- as.matrix(x = x)[i, , drop = drop] + if (!isTRUE(x = drop)) { + x <- as(object = x, Class = 'LogMap') + } + return(x) + } +) + +#' @rdname sub-LogMap-method +#' +setMethod( + f = '[', + signature = c(x = 'LogMap', i = 'missing', j = 'character'), + definition = function(x, i, j, ..., drop = FALSE) { + x <- as.matrix(x = x)[, j, drop = drop] + if (!isTRUE(x = drop)) { + x <- as(object = x, Class = 'LogMap') + } + return(x) + } +) + +#' @rdname sub-LogMap-method +#' +setMethod( + f = '[', + signature = c(x = 'LogMap', i = 'numeric', j = 'missing'), + definition = function(x, i, j, ..., drop = FALSE) { + stopifnot(is_bare_integerish(x = i)) + if (is.unsorted(x = i)) { + i <- sort(x = i) + } + i <- rownames(x = x)[i] + return(callNextMethod(x, i, ..., drop = drop)) + } +) + +#' @rdname sub-LogMap-method +#' +setMethod( + f = '[', + signature = c(x = 'LogMap', i = 'missing', j = 'numeric'), + definition = function(x, i, j, ..., drop = FALSE) { + stopifnot(is_bare_integerish(x = j)) + j <- colnames(x = x)[j] + return(callNextMethod(x, , j, ..., drop = drop)) + } +) + +#' @rdname sub-LogMap-method +#' +setMethod( + f = '[', + signature = c(x = 'LogMap', i = 'numeric', j = 'numeric'), + definition = function(x, i, j, ..., drop = FALSE) { + stopifnot(is_bare_integerish(x = i), is_bare_integerish(x = j)) + if (is.unsorted(x = i)) { + i <- sort(x = i) + } + i <- rownames(x = x)[i] + j <- colnames(x = x)[j] + return(callNextMethod(x, i, j, ..., drop = drop)) + } +) + #' @rdname LogMap-class #' -#' @param x,object A \code{LogMap} object +#' @param x A \code{LogMap} object #' @param i A character vector of length 1, or \code{NULL} #' @param j Not used #' @param ... Ignored @@ -100,6 +431,8 @@ LogMap <- function(y) { #' #' @export #' +#' @order 2 +#' setMethod( f = '[[', signature = c(x = 'LogMap', i = 'character', j = 'missing'), @@ -110,10 +443,33 @@ setMethod( } ) +#' \code{\link{LogMap}} Interaction Methods +#' +#' Additional methods for using \code{[[} with \code{\link{LogMap}} objects +#' +#' @inheritParams LogMap +#' @param i An integer or numeric vector of length 1 +#' +#' @return The rownames that are mapped to \code{i} +#' +#' @rdname sub-sub-LogMap-internal-method +#' +#' @keywords internal +#' +setMethod( + f = '[[', + signature = c(x = 'LogMap', i = 'integer', j = 'missing'), + definition = function(x, i, ...) { + return(x[[colnames(x = x)[i]]]) + } +) + #' @rdname LogMap-class #' #' @export #' +#' @order 3 +#' setMethod( f = '[[', signature = c(x = 'LogMap', i = 'missing', j = 'missing'), @@ -122,10 +478,22 @@ setMethod( } ) +#' @rdname sub-sub-LogMap-internal-method +#' +setMethod( + f = '[[', + signature = c(x = 'LogMap', i = 'numeric', j = 'missing'), + definition = function(x, i, ...) { + return(x[[as.integer(x = i)]]) + } +) + #' @rdname LogMap-class #' #' @export #' +#' @order 4 +#' setMethod( f = '[[', signature = c(x = 'LogMap', i = 'NULL', j = 'missing'), @@ -145,6 +513,8 @@ setMethod( #' #' @export #' +#' @order 5 +#' setMethod( f = '[[<-', signature = c( @@ -164,6 +534,8 @@ setMethod( #' #' @export #' +#' @order 6 +#' setMethod( f = '[[<-', signature = c( @@ -195,6 +567,8 @@ setMethod( #' #' @export #' +#' @order 7 +#' setMethod( f = '[[<-', signature = c(x = 'LogMap', i = 'character', j = 'missing', value = 'NULL'), @@ -216,6 +590,8 @@ setMethod( #' #' @export #' +#' @order 8 +#' setMethod( f = '[[<-', signature = c( @@ -231,9 +607,15 @@ setMethod( } ) -#' @rdname LogMap-class +#' \code{\link{LogMap}} Object Overview #' -#' @export +#' Overview of a \code{\link{LogMap}} object +#' +#' @param object A \code{\link{LogMap}} object +#' +#' @template return-show +#' +#' @concept logmap #' setMethod( f = 'show', @@ -250,29 +632,71 @@ setMethod( } ) +#' Logical Map Validity +#' +#' @templateVar cls LogMap +#' @template desc-validity +#' +#' @section Data Validation: +#' Logical maps must be a logical matrix containing only TRUE or FALSE values +#' +#' @section Value Validation: +#' All values must be named within the rownames of the object. Duplicate or +#' empty (\code{""}) values are not allowed +#' +#' @section Observation Validation: +#' All observations must be named within the column names of the object. +#' Duplicate or empty (\code{""}) observations are not allowed +#' +#' @name LogMap-validity +#' +#' @family logmap +#' @seealso \code{\link[methods]{validObject}} +#' +#' @examples +#' map <- LogMap(letters[1:10]) +#' map[['obs']] <- c(1, 3, 7) +#' map[['entry']] <- c(2, 7, 10) +#' validObject(map) +#' setValidity( Class = 'LogMap', method = function(object) { + if (isFALSE(x = getOption(x = "Seurat.object.validate", default = TRUE))) { + warn( + message = paste("Not validating", class(x = object)[1L], "objects"), + class = 'validationWarning' + ) + return(TRUE) + } valid <- NULL # Ensure we have a logical matrix if (!is.logical(x = object)) { valid <- c(valid, "The map must be a logical matrix") } + if (any(is.na(x = object))) { + valid <- c(valid, "The may may not contain NAs") + } # Check rownames if (is.null(x = rownames(x = object))) { valid <- c(valid, "Rownames must be supplied") } + if (any(!nzchar(x = rownames(x = object)))) { + valid <- c(valid, "Rownames cannot be empty strings") + } if (anyDuplicated(x = rownames(x = object))) { valid <- c(valid, "Duplicate rownames not allowed") } # Check colnames if (!is.null(x = colnames(x = object))) { - if (!all(nchar(x = colnames(x = object)))) { + if (any(!nzchar(x = colnames(x = object)))) { valid <- c(valid, "Columnn names cannot be empty strings") } if (anyDuplicated(x = colnames(x = object))) { valid <- c(valid, "Duplicate colnames not allowed") } + } else if (ncol(x = object)) { + valid <- c(valid, "Colnames must be supplied") } return(valid %||% TRUE) } diff --git a/R/molecules.R b/R/molecules.R index 7d1710f4..cf646629 100644 --- a/R/molecules.R +++ b/R/molecules.R @@ -1,10 +1,8 @@ #' @include zzz.R #' @include generics.R #' @include keymixin.R -#' @importFrom progressr progressor #' @importFrom future.apply future_lapply #' @importClassesFrom sp CRS SpatialPoints -#' NULL #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -47,6 +45,8 @@ setClass( #' #' @seealso \code{\link{Molecules-class}} #' +#' @family segmentation +#' NULL #' @param key A key to set for the molecules @@ -393,7 +393,13 @@ setMethod( setValidity( Class = 'Molecules', method = function(object) { - if (!length(x = object)) { + if (isFALSE(x = getOption(x = "Seurat.object.validate", default = TRUE))) { + warn( + message = paste("Not validating", class(x = object)[1L], "objects"), + class = 'validationWarning' + ) + return(TRUE) + } else if (!length(x = object)) { return(TRUE) } valid <- NULL diff --git a/R/segmentation.R b/R/segmentation.R index ec54b32e..c79e2708 100644 --- a/R/segmentation.R +++ b/R/segmentation.R @@ -3,7 +3,7 @@ #' @importFrom sp coordinates #' @importFrom methods as callNextMethod #' @importClassesFrom sp SpatialPolygons -#' + NULL #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -72,10 +72,11 @@ setClass( #' \href{https://future.futureverse.org/articles/future-1-overview.html}{\code{vignette("future-1-overview")}} #' #' @concept future - #' #' @seealso \code{\link{Segmentation-class}} #' +#' @family segmentation +#' NULL #' @template method-cells @@ -287,9 +288,16 @@ setMethod( f = 'over', signature = c(x = 'Segmentation', y = 'SpatialPolygons'), definition = function(x, y, returnList = FALSE, fn = NULL, ...) { + deprecate_stop( + when = '5.0.0', + what = 'over()', + details = "Future integration with `sf` is on the roadmap with no current ETA" + ) + check_installed(pkg = 'sf') return(over( - x = as(object = x, Class = 'SpatialPolygons'), - y = as(object = y, Class = 'SpatialPolygons'), + x = as(object = x, Class = 'sf'), + y = as(object = y, Class = 'sf'), + sparse = FALSE, returnList = returnList, fn = fn, ... @@ -304,10 +312,24 @@ setMethod( f = 'Overlay', signature = c(x = 'Segmentation', y = 'SpatialPolygons'), definition = function(x, y, invert = FALSE, ...) { - idx <- over(x = x, y = y) - idx <- idx[!is.na(x = idx)] - if (!length(idx)) { - warning("The selected region does not contain any cell segmentations") + check_installed(pkg = 'sf', reason = 'to overlay spatial information') + idx <- sf::st_intersects( + x = as(object = x, Class = 'sf'), + y = as(object = y, Class = 'sf'), + sparse = FALSE + ) + idx <- which(x = idx) + names_in_sf_object1 <- if (!is.null(x = row.names(x = x))) { + row.names(x = x)[idx] + } else { + x$id[idx] + } + idx <- setNames( + object = rep.int(x = TRUE, times = length(x = idx)), + nm = names_in_sf_object1 + ) + if (!length(x = idx)) { + warn("The selected region does not contain any cell segmentations") return(NULL) } names(x = idx) <- vapply( diff --git a/R/seurat.R b/R/seurat.R index 2bf91e43..b3e51648 100644 --- a/R/seurat.R +++ b/R/seurat.R @@ -49,7 +49,11 @@ NULL #' @rdname Seurat-class #' @exportClass Seurat #' -Seurat <- setClass( +#' @family seurat +#' +#' @aliases Seurat +#' +setClass( Class = 'Seurat', slots = c( assays = 'list', @@ -112,13 +116,14 @@ Seurat <- setClass( #' #' @name seurat-class #' @rdname oldseurat-class -#' @aliases seurat-class +#' @aliases seurat-class oldseurat #' #' @concept unsorted +#' @concept v2 #' #' @keywords internal #' -seurat <- setClass( +setClass( Class = "seurat", slots = c( raw.data = "ANY", @@ -148,50 +153,13 @@ seurat <- setClass( # Functions #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -#' Query Specific Object Types -#' -#' List the names of \code{\link{Assay}}, \code{\link{DimReduc}}, -#' \code{\link{Graph}}, \code{\link{Neighbor}} objects -#' -#' @param object A \code{\link{Seurat}} object -#' @param slot Name of component object to return -#' -#' @return If \code{slot} is \code{NULL}, the names of all component objects -#' in this \code{Seurat} object. Otherwise, the specific object specified -#' -#' @rdname ObjectAccess -#' -#' @export -#' -#' @concept data-access -#' -#' @examples -#' Assays(object = pbmc_small) -#' -Assays <- function(object, slot = NULL) { - assays <- FilterObjects(object = object, classes.keep = 'Assay') - if (is.null(x = slot)) { - return(assays) - } - if (!slot %in% assays) { - warning( - "Cannot find an assay of name ", - slot, - " in this Seurat object", - call. = FALSE, - immediate. = TRUE - ) - } - return(slot(object = object, name = 'assays')[[slot]]) -} - #' Get cell names grouped by identity class #' #' @param object A Seurat object #' @param idents A vector of identity class levels to limit resulting list to; #' defaults to all identity class levels #' @param cells A vector of cells to grouping to -#' @param return.null If no cells are request, return a \code{NULL}; +#' @param return.null If no cells are requested, return a \code{NULL}; #' by default, throws an error #' #' @return A named list where names are identity classes and values are vectors @@ -284,10 +252,19 @@ CellsByImage <- function(object, images = NULL, unlist = FALSE) { #' #' @concept utils #' +#' @templateVar fxn FilterObjects +#' @templateVar ver 5.0.0 +#' @templateVar repl .FilterObjects +#' @template lifecycle-deprecated +#' #' @examples #' FilterObjects(pbmc_small) #' -FilterObjects <- function(object, classes.keep = c('Assay', 'DimReduc')) { +FilterObjects <- function( + object, + classes.keep = c('Assay', 'StdAssay', 'DimReduc') +) { + .Deprecate(when = '5.0.0', what = 'FilterObjects()', with = '.FilterObjects()') object <- UpdateSlots(object = object) slots <- na.omit(object = Filter( f = function(x) { @@ -324,7 +301,7 @@ FilterObjects <- function(object, classes.keep = c('Assay', 'DimReduc')) { #' Graphs(pbmc_small) #' Graphs <- function(object, slot = NULL) { - graphs <- FilterObjects(object = object, classes.keep = "Graph") + graphs <- .FilterObjects(object = object, classes.keep = "Graph") if (is.null(x = slot)) { return(graphs) } @@ -361,7 +338,6 @@ Graphs <- function(object, slot = NULL) { #' } #' Images <- function(object, assay = NULL) { - object <- UpdateSlots(object = object) images <- names(x = slot(object = object, name = 'images')) if (!is.null(x = assay)) { assays <- c(assay, DefaultAssay(object = object[[assay]])) @@ -375,11 +351,108 @@ Images <- function(object, assay = NULL) { return(images) } +#' @inheritDotParams base::readRDS +#' +#' @rdname SaveSeuratRds +#' @export +#' +LoadSeuratRds <- function(file, ...) { + object <- readRDS(file = file, ...) + cache <- Tool(object = object, slot = 'SaveSeuratRds') + reqd.cols <- c('layer', 'path', 'class', 'pkg', 'fxn', 'assay') + emit <- ifelse( + test = isTRUE(x = getOption(x = 'Seurat.io.rds.strict', default = FALSE)), + yes = abort, + no = warn + ) + if (!is.null(x = cache)) { + if (interactive()) { + check_installed(pkg = 'fs', reason = 'for finding file paths') + } else if (!requireNamespace('fs', quietly = TRUE)) { + abort(message = "Loading layers from disk requires `fs`") + } + # Check the format of the cache + if (!is.data.frame(x = cache)) { + emit(message = "Malformed layer cache: not a data frame") + return(object) + } + if (!all(reqd.cols %in% names(x = cache))) { + emit(message = "Malformed layer cache: missing required columns") + return(object) + } + # Check the assays specified + assays <- .FilterObjects(object = object, classes.keep = 'StdAssay') + cache <- cache[cache$assay %in% assays, , drop = FALSE] + if (!nrow(x = cache)) { + emit(message = "Incorrect layer cache: none of the assays listed present") + return(object) + } + # Check the files + exists <- vapply( + X = cache$path, + FUN = function(x) { + x <- unlist(x = strsplit(x = x, split = ',')) + res <- vector(mode = 'logical', length = length(x = x)) + for (i in seq_along(along.with = x)) { + res[i] <- fs::is_file(path = x[i]) || fs::dir_exists(path = x[i]) + } + return(all(res)) + }, + FUN.VALUE = logical(length = 1L), + USE.NAMES = FALSE + ) + exists[is.na(exists)] <- FALSE + cache <- cache[exists, , drop = FALSE] + if (!nrow(x = cache)) { + emit(message = "Cannot find any of the layer files specified") + return(object) + } + # Check the packages + missing.pkgs <- pkgs <- unique(x = cache$pkg) + for (pkg in pkgs) { + if (interactive()) { + check_installed(pkg = pkg) + } + if (requireNamespace(pkg, quietly = TRUE)) { + missing.pkgs <- setdiff(x = missing.pkgs, y = pkg) + } else { + emit(message = paste("Cannot find required package:", sQuote(x = pkg))) + } + } + pkgs <- setdiff(x = pkgs, y = missing.pkgs) + if (!length(x = pkgs)) { + emit(message = "None of the required layer packages found") + return(object) + } + p <- progressor(steps = nrow(x = cache)) + # Load the layers + for (i in seq_len(length.out = nrow(x = cache))) { + lyr <- cache$layer[i] + pth <- cache$path[i] + fxn <- eval(expr = str2lang(s = cache$fxn[i])) + assay <- cache$assay[i] + p( + message = paste( + "Adding layer", + sQuote(x = lyr), + "to assay", + sQuote(x = assay) + ), + class = 'sticky', + amount = 0 + ) + LayerData(object = object, assay = assay, layer = lyr) <- fxn(pth) + p() + } + } + return(object) +} + #' @rdname ObjectAccess #' @export #' Neighbors <- function(object, slot = NULL) { - neighbors <- FilterObjects(object = object, classes.keep = "Neighbor") + neighbors <- .FilterObjects(object = object, classes.keep = "Neighbor") if (is.null(x = slot)) { return(neighbors) } @@ -402,18 +475,19 @@ Neighbors <- function(object, slot = NULL) { #' Reductions(object = pbmc_small) #' Reductions <- function(object, slot = NULL) { - reductions <- FilterObjects(object = object, classes.keep = 'DimReduc') + # reductions <- FilterObjects(object = object, classes.keep = 'DimReduc') + reductions <- .FilterObjects(object = object, classes.keep = 'DimReduc') if (is.null(x = slot)) { return(reductions) } if (!slot %in% reductions) { - warning( - "Cannot find a DimReduc of name ", - slot, - " in this Seurat object", - call. = FALSE, - immediate. = TRUE + warn( + message = paste( + 'Cannot find a DimReduc of name', + slot, + 'in this Seurat object') ) + return(NULL) } return(slot(object = object, name = 'reductions')[[slot]]) } @@ -421,6 +495,9 @@ Reductions <- function(object, slot = NULL) { #' Rename assays in a \code{Seurat} object #' #' @param object A \code{Seurat} object +#' @param assay.name original name of assay +#' @param new.assay.name new name of assay +#' @param verbose Whether to print messages #' @param ... Named arguments as \code{old.assay = new.assay} #' #' @return \code{object} with assays renamed @@ -432,14 +509,34 @@ Reductions <- function(object, slot = NULL) { #' @examples #' RenameAssays(object = pbmc_small, RNA = 'rna') #' -RenameAssays <- function(object, ...) { - assay.pairs <- tryCatch( - expr = as.list(x = ...), - error = function(e) { - return(list(...)) - } - ) - old.assays <- names(x = assay.pairs) +RenameAssays <- function( + object, + assay.name = NULL, + new.assay.name = NULL, + verbose = TRUE, + ...) { + op <- options(Seurat.object.assay.calcn = FALSE) + on.exit(expr = options(op), add = TRUE) + if ((!is.null(x = assay.name) & is.null(x = new.assay.name)) + | (is.null(x = assay.name) & !is.null(x = new.assay.name))) { + stop("Must provide both assay.name and new.assasy.name if using parameters. Otherwise, ", + "you can set arguments without parameters by doing ", + "{old.assay = new.assay} with your own assay names.", call. = FALSE) + } + if (!is.null(x = assay.name) & !is.null(x = new.assay.name)) { + assay.pairs <- new.assay.name + names(x = assay.pairs) <- assay.name + old.assays <- names(x = assay.pairs) + } else { + assay.pairs <- tryCatch( + expr = as.list(x = ...), + error = function(e) { + return(list(...)) + } + ) + old.assays <- names(x = assay.pairs) + names(x = assay.pairs) <- old.assays + } # Handle missing assays missing.assays <- setdiff(x = old.assays, y = Assays(object = object)) if (length(x = missing.assays) == length(x = old.assays)) { @@ -479,7 +576,9 @@ RenameAssays <- function(object, ...) { old.key <- Key(object = object[[old]]) suppressWarnings(expr = object[[new]] <- object[[old]]) if (old == DefaultAssay(object = object)) { - message("Renaming default assay from ", old, " to ", new) + if (verbose) { + message("Renaming default assay from ", old, " to ", new) + } DefaultAssay(object = object) <- new } Key(object = object[[new]]) <- old.key @@ -489,11 +588,238 @@ RenameAssays <- function(object, ...) { DefaultAssay(object = object[[i]]) <- new } } + # Add new metadata if it exists + if (isTRUE(paste0("nCount_", old) %in% colnames(object[[]]))) { + slot( + object = object, + name = 'meta.data' + )[paste0("nCount_", new)] <- object[[]][,paste0("nCount_",old)] + } + if (isTRUE(paste0("nFeature_", old) %in% colnames(object[[]]))) { + slot( + object = object, + name = 'meta.data' + )[paste0("nFeature_", new)] <- object[[]][,paste0("nFeature_", old)] + } object[[old]] <- NULL } return(object) } +#' Save and Load \code{Seurat} Objects from Rds files +#' +#' @param object A \code{\link{Seurat}} object +#' @param file Path to save \code{object} to; defaults to +#' \code{file.path(getwd(), paste0(Project(object), ".Rds"))} +#' @param destdir Destination directory for on-disk layers saved in +#' \dQuote{\code{\Sexpr[stage=render]{tempdir()}}} +#' @param relative Save relative paths instead of absolute ones +#' @inheritDotParams base::saveRDS +#' +#' @return Invisibly returns \code{file} +#' +#' @export +#' +#' @template section-progressr +#' +#' @templateVar pkg fs +#' @template note-reqdpkg +#' +#' @concept utils +#' +#' @seealso \code{\link{saveRDS}()} \code{\link{readRDS}()} +#' +#' @order 1 +#' +#' @examples +#' if (requireNamespace("fs", quietly = TRUE)) { +#' # Write out with DelayedArray +#' if (requireNamespace("HDF5Array", quietly = TRUE)) { +#' pbmc <- pbmc_small +#' +#' pbmc[["disk"]] <- CreateAssay5Object(list( +#' mem = LayerData(pbmc, "counts"), +#' disk = as(LayerData(pbmc, "counts"), "HDF5Array") +#' )) +#' +#' # Save `pbmc` to an Rds file +#' out <- tempfile(fileext = ".Rds") +#' SaveSeuratRds(pbmc, file = out) +#' +#' # Object cache +#' obj <- readRDS(out) +#' Tool(obj, "SaveSeuratRds") +#' +#' # Load the saved object with on-disk layers back into memory +#' pbmc2 <- LoadSeuratRds(out) +#' pbmc2 +#' pbmc2[["disk"]] +#' } +#' +#' # Write out with BPCells +#' if (requireNamespace("BPCells", quietly = TRUE)) { +#' pbmc <- pbmc_small +#' +#' bpm <- BPCells::write_matrix_dir(LayerData(pbmc, "counts"), dir = tempfile()) +#' bph <- BPCells::write_matrix_hdf5( +#' LayerData(pbmc, "counts"), +#' path = tempfile(fileext = ".h5"), +#' group = "counts" +#' ) +#' pbmc[["disk"]] <- CreateAssay5Object(list(dir = bpm, h5 = bph)) +#' +#' # Save `pbmc` to an Rds file +#' out <- tempfile(fileext = ".Rds") +#' SaveSeuratRds(pbmc, file = out) +#' +#' # Object cache +#' obj <- readRDS(out) +#' Tool(obj, "SaveSeuratRds") +#' +#' # Load the saved object with on-disk layers back into memory +#' pbmc2 <- LoadSeuratRds(out) +#' pbmc2 +#' pbmc2[["disk"]] +#' } +#' } +#' +SaveSeuratRds <- function( + object, + file = NULL, + destdir = NULL, + relative = FALSE, + ... +) { + file <- file %||% file.path(getwd(), paste0(Project(object = object), '.Rds')) + file <- normalizePath(path = file, winslash = '/', mustWork = FALSE) + # Cache v5 assays + assays <- .FilterObjects(object = object, classes.keep = 'StdAssay') + p <- progressor(along = assays, auto_finish = TRUE) + on.exit(expr = p(type = 'finish'), add = TRUE) + p( + message = paste( + "Looking for on-disk matrices in", + length(x = assays), + "assays" + ), + class = 'sticky', + amount = 0 + ) + cache <- vector(mode = 'list', length = length(x = assays)) + names(x = cache) <- assays + tdir <- normalizePath(path = tempdir(), winslash = '/') # because macOS is weird + destdir <- destdir %||% dirname(path = file) + if (!is_na(x = destdir) || isTRUE(x = relative)) { + check_installed( + pkg = 'fs', + reason = 'for moving on-disk matrices' + ) + } + for (assay in assays) { + p( + message = paste("Searching through assay", assay), + class = 'sticky', + amount = 0 + ) + df <- lapply( + X = Layers(object = object[[assay]]), + FUN = function(lyr) { + ldat <- LayerData(object = object[[assay]], layer = lyr) + path <- .FilePath(x = ldat) + path <- Filter(f = nzchar, x = path) + if (!length(x = path)) { + path <- NULL + } + if (is.null(x = path)) { + return(NULL) + } + return(data.frame( + layer = lyr, + path = path, + class = paste(class(x = ldat), collapse = ','), + pkg = .ClassPkg(object = ldat), + fxn = .DiskLoad(x = ldat) %||% identity + )) + } + ) + df <- do.call(what = 'rbind', args = df) + if (is.null(x = df) || !nrow(x = df)) { + p(message = "No on-disk layers found", class = 'sticky', amount = 0) + next + } + if (!is_na(x = destdir)) { + for (i in seq_len(length.out = nrow(x = df))) { + pth <- df$path[i] + mv <- substr(x = pth, start = 1L, stop = nchar(x = tdir)) == tdir || + isTRUE(x = relative) + if (isTRUE(x = mv)) { + p( + message = paste( + "Moving layer", + sQuote(x = df$layer[i]), + "to", + sQuote(x = destdir) + ), + class = 'sticky', + amount = 0 + ) + df[i, 'path'] <- as.character(x = .FileMove( + path = pth, + new_path = destdir + )) + } + } + } + if (isTRUE(x = relative)) { + p( + message = paste( + "Adjusting paths to be relative to", + sQuote(x = dirname(path = file), q = FALSE) + ), + class = 'sticky', + amount = 0 + ) + df$path <- as.character(x = fs::path_rel( + path = df$path, + start = dirname(path = file) + )) + } + df$assay <- assay + cache[[assay]] <- df + if (nrow(x = df) == length(x = Layers(object = object[[assay]]))) { + p( + message = paste("Clearing layers from", assay), + class = 'sticky', + amount = 0 + ) + adata <- S4ToList(object = object[[assay]]) + adata$layers <- list() + adata$default <- 0L + adata$cells <- LogMap(y = colnames(x = object[[assay]])) + adata$features <- LogMap(y = rownames(x = object[[assay]])) + object[[assay]] <- ListToS4(x = adata) + } else { + p( + message = paste("Clearing", nrow(x = df), "layers from", assay), + class = 'sticky', + amount = 0 + ) + for (layer in df$layer) { + LayerData(object = object[[assay]], layer = layer) <- NULL + } + } + p() + } + cache <- do.call(what = 'rbind', args = cache) + if (!is.null(x = cache) && nrow(x = cache)) { + p(message = "Saving on-disk cache to object", class = 'sticky', amount = 0) + row.names(x = cache) <- NULL + Tool(object = object) <- cache + } + saveRDS(object = object, file = file, ...) + return(invisible(x = file)) +} + #' Update old Seurat object to accommodate new features #' #' Updates Seurat objects to new structure for storing data/calculations. @@ -505,7 +831,6 @@ RenameAssays <- function(object, ...) { #' @return Returns a Seurat object compatible with latest changes #' #' @importFrom methods .hasSlot new slot -#' @importFrom utils packageVersion #' #' @export #' @@ -517,16 +842,20 @@ RenameAssays <- function(object, ...) { #' } #' UpdateSeuratObject <- function(object) { + op <- options(Seurat.object.validate = FALSE) + on.exit(expr = options(op), add = TRUE) if (.hasSlot(object, "version")) { if (slot(object = object, name = 'version') >= package_version(x = "2.0.0") && slot(object = object, name = 'version') < package_version(x = '3.0.0')) { # Run update message("Updating from v2.X to v3.X") - seurat.version <- packageVersion(pkg = "Seurat") + # seurat.version <- packageVersion(pkg = "SeuratObject") + seurat.version <- package_version(x = '3.0.0') new.assay <- UpdateAssay(old.assay = object, assay = "RNA") assay.list <- list(new.assay) names(x = assay.list) <- "RNA" for (i in names(x = object@assay)) { assay.list[[i]] <- UpdateAssay(old.assay = object@assay[[i]], assay = i) + } new.dr <- UpdateDimReduction(old.dr = object@dr, assay = "RNA") object <- new( @@ -573,19 +902,47 @@ UpdateSeuratObject <- function(object) { object = slot(object = object, name = FindObject(object, ko))[[ko]], name = 'key' ) <- UpdateKey(key) + if (inherits(x = slot(object = object, name = FindObject(object, ko))[[ko]], what = 'DimReduc')) { + message("Updating matrix keys for DimReduc ", sQuote(ko)) + for (m in c('cell.embeddings', 'feature.loadings', 'feature.loadings.projected')) { + mat <- slot( + object = slot(object = object, name = FindObject(object, ko))[[ko]], + name = m + ) + if (IsMatrixEmpty(mat)) { + next + } + colnames(x = mat) <- paste0(key, seq_len(ncol(mat))) + slot( + object = slot(object = object, name = FindObject(object, ko))[[ko]], + name = m + ) <- mat + } + } } # Rename assays assays <- make.names(names = Assays(object = object)) names(x = assays) <- Assays(object = object) object <- do.call(what = RenameAssays, args = c('object' = object, assays)) for (obj in FilterObjects(object = object, classes.keep = c('Assay', 'DimReduc', 'Graph'))) { - suppressWarnings(expr = object[[obj]] <- UpdateSlots(object = object[[obj]])) + suppressWarnings( + expr = object[[obj]] <- UpdateSlots(object = object[[obj]]), + classes = 'validationWarning' + ) } for (cmd in Command(object = object)) { slot(object = object, name = 'commands')[[cmd]] <- UpdateSlots( object = Command(object = object, command = cmd) ) } + # Validate object keys + message("Ensuring keys are in the proper structure") + for (ko in FilterObjects(object = object)) { + suppressWarnings( + expr = Key(object = object[[ko]]) <- UpdateKey(key = Key(object = object[[ko]])), + classes = 'validationWarning' + ) + } # Check feature names message("Ensuring feature names don't have underscores or pipes") for (assay.name in FilterObjects(object = object, classes.keep = 'Assay')) { @@ -627,7 +984,19 @@ UpdateSeuratObject <- function(object) { x = rownames(x = assay[[]]), fixed = TRUE ) - object[[assay.name]] <- assay + # reorder features in scale.data and meta.features to match counts + sd.features <- rownames(x = slot(object = assay, name = "scale.data")) + data.features <- rownames(x = slot(object = assay, name = "data")) + md.features <- rownames(x = slot(object = assay, name = "meta.features")) + if (!all.equal(target = md.features, current = data.features, check.attributes = FALSE)) { + slot(object = assay, name = "meta.features") <- slot(object = assay, name = "meta.features")[data.features, ] + } + sd.order <- sd.features[order(match(x = sd.features, table = data.features))] + slot(object = assay, name = "scale.data") <- slot(object = assay, name = "scale.data")[sd.order, ] + suppressWarnings( + expr = object[[assay.name]] <- assay, + classes = 'validationWarning' + ) } for (reduc.name in FilterObjects(object = object, classes.keep = 'DimReduc')) { reduc <- object[[reduc.name]] @@ -646,15 +1015,31 @@ UpdateSeuratObject <- function(object) { ) } } - object[[reduc.name]] <- reduc + suppressWarnings( + expr = object[[reduc.name]] <- reduc, + classes = 'validationWarning' + ) } - } - if (package_version(x = slot(object = object, name = 'version')) <= package_version(x = '3.1.1')) { # Update Assays, DimReducs, and Graphs for (x in names(x = object)) { message("Updating slots in ", x) xobj <- object[[x]] - xobj <- UpdateSlots(object = xobj) + xobj <- suppressWarnings( + expr = UpdateSlots(object = xobj), + classes = 'validationWarning' + ) + if (inherits(x = xobj, what = "SCTAssay")){ + sctmodels <- names(x = slot(object = xobj, name = "SCTModel.list")) + for (sctmodel in sctmodels){ + median_umi <- tryCatch( + expr = slot(object = xobj@SCTModel.list[[sctmodel]], name = "median_umi"), + error = function(...) { + return(0) + } + ) + xobj@SCTModel.list[[sctmodel]]@median_umi <- median_umi + } + } if (inherits(x = xobj, what = 'DimReduc')) { if (any(sapply(X = c('tsne', 'umap'), FUN = grepl, x = tolower(x = x)))) { message("Setting ", x, " DimReduc to global") @@ -664,10 +1049,29 @@ UpdateSeuratObject <- function(object) { graph.assay <- unlist(x = strsplit(x = x, split = '_'))[1] if (graph.assay %in% Assays(object = object)) { message("Setting default assay of ", x, " to ", graph.assay) - DefaultAssay(object = xobj) <- graph.assay + suppressWarnings( + expr = DefaultAssay(object = xobj) <- graph.assay, + classes = 'validationWarning' + ) + } else { + message( + "Cannot find ", + graph.assay, + " in the object, setting default assay of ", + x, + " to ", + DefaultAssay(object = object) + ) + suppressWarnings( + expr = DefaultAssay(object = xobj) <- DefaultAssay(object = object), + classes = 'validationWarning' + ) } } - object[[x]] <- xobj + suppressWarnings( + expr = object[[x]] <- xobj, + classes = 'validationWarning' + ) } # Update SeuratCommands for (cmd in Command(object = object)) { @@ -688,23 +1092,38 @@ UpdateSeuratObject <- function(object) { message("Setting assay used for ", cmd, " to ", cmd.assay) } slot(object = cobj, name = 'assay.used') <- cmd.assay - object[[cmd]] <- cobj + suppressWarnings( + expr = object[[cmd]] <- cobj, + classes = 'validationWarning' + ) } # Update object version slot(object = object, name = 'version') <- packageVersion(pkg = 'Seurat') } - object <- UpdateSlots(object = object) + object <- suppressWarnings( + expr = UpdateSlots(object = object), + classes = 'validationWarning' + ) if (package_version(x = slot(object = object, name = 'version')) <= package_version(x = '4.0.0')) { # Transfer the object to the SeuratObject namespace - object <- UpdateClassPkg( - object = object, - from = 'Seurat', - to = 'SeuratObject' + object <- suppressWarnings( + expr = UpdateClassPkg( + object = object, + from = 'Seurat', + to = 'SeuratObject' + ), + classes = 'validationWarning' ) - slot(object = object, name = 'version') <- max( - package_version(x = '4.0.0'), - packageVersion(pkg = 'SeuratObject') + } + slot(object = object, name = 'version') <- packageVersion(pkg = 'SeuratObject') + options(op) + validObject(object = object, complete = TRUE) + for (i in names(x = object)) { + message( + "Validating object structure for ", + paste(class(x = object[[i]])[1L], sQuote(x = i)) ) + validObject(object = object[[i]]) } message("Object representation is consistent with the most current Seurat version") return(object) @@ -726,6 +1145,56 @@ UpdateSeuratObject <- function(object) { #' AddMetaData.Seurat <- .AddMetaData +#' @rdname ObjectAccess +#' @method Assays Seurat +#' @export +#' +Assays.Seurat <- function(object, slot = deprecated(), ...) { + if (is_present(arg = slot)) { + .Deprecate( + when = '5.0.0', + what = 'Assays(slot = )', + with = 'LayerData()' + ) + return(methods::slot(object = object, name = 'assays')[[slot]]) + } + return(names(x = methods::slot(object = object, name = 'assays'))) +} + +#' @method CastAssay Seurat +#' @export +#' +CastAssay.Seurat <- function(object, to, assay = NULL, layers = NA, ...) { + assay <- assay[1L] %||% DefaultAssay(object = object) + assay <- arg_match0(arg = assay, values = Assays(object = object)) + to <- enquo(arg = to) + object[[assay]] <- CastAssay( + object = object[[assay]], + to = to, + layers = layers, + ... + ) + validObject(object = object) + return(object) +} + +#' @method Cells Seurat +#' @export +#' +Cells.Seurat <- function(x, assay = NULL, ...) { + assay <- assay[1L] %||% DefaultAssay(object = x) + if (is.na(x = assay)) { + return(colnames(x = x)) + } + assay <- tryCatch( + expr = match.arg(arg = assay, choices = Assays(object = x)), + error = function(e) { + return(NULL) + } + ) + return(Cells(x = x[[assay]], ...)) +} + #' @param command Name of the command to pull, pass \code{NULL} to get the #' names of all commands run #' @param value Name of the parameter to pull the value for @@ -755,54 +1224,56 @@ Command.Seurat <- function(object, command = NULL, value = NULL, ...) { return(params[[value]]) } -#' @param row.names When \code{counts} is a \code{data.frame} or -#' \code{data.frame}-derived object: an optional vector of feature names to be -#' used -#' +# @param row.names When \code{counts} is a \code{data.frame} or +# \code{data.frame}-derived object: an optional vector of feature names to be +# used +# #' @rdname CreateSeuratObject #' @method CreateSeuratObject default #' @export #' CreateSeuratObject.default <- function( counts, - project = 'SeuratProject', assay = 'RNA', - names.field = 1, + names.field = 1L, names.delim = '_', meta.data = NULL, + project = 'SeuratProject', min.cells = 0, min.features = 0, - row.names = NULL, ... ) { - if (!is.null(x = meta.data)) { - if (!all(rownames(x = meta.data) %in% colnames(x = counts))) { - warning("Some cells in meta.data not present in provided counts matrix") - } + assay.version <- getOption(x = 'Seurat.object.assay.version', default = 'v5') + if (.GetSeuratCompat() < '5.0.0') { + assay.version <- 'v3' + } else if (!inherits(counts, what = c('matrix', 'dgCMatrix')) && assay.version == 'v3') { + message( + "Counts matrix provided is not sparse; vreating v5 assay in Seurat object" + ) + assay.version <- 'v5' } - assay.data <- CreateAssayObject( - counts = counts, - min.cells = min.cells, - min.features = min.features, - row.names = row.names - ) - if (!is.null(x = meta.data)) { - common.cells <- intersect( - x = rownames(x = meta.data), y = colnames(x = assay.data) + assay.data <- if (tolower(x = assay.version) == 'v3') { + assay.data <- CreateAssayObject( + counts = counts, + min.cells = min.cells, + min.features = min.features, + ... + ) + } else { + CreateAssay5Object( + counts = counts, + min.cells = min.cells, + min.features = min.features, + ... ) - meta.data <- meta.data[common.cells, , drop = FALSE] } - Key(object = assay.data) <- suppressWarnings(expr = UpdateKey(key = tolower( - x = assay - ))) return(CreateSeuratObject( counts = assay.data, - project = project, assay = assay, names.field = names.field, names.delim = names.delim, meta.data = meta.data, - ... + project = project )) } @@ -812,36 +1283,21 @@ CreateSeuratObject.default <- function( #' CreateSeuratObject.Assay <- function( counts, - project = 'SeuratProject', assay = 'RNA', - names.field = 1, + names.field = 1L, names.delim = '_', meta.data = NULL, + project = 'SeuratProject', ... ) { - if (!is.null(x = meta.data)) { - if (is.null(x = rownames(x = meta.data))) { - stop("Row names not set in metadata. Please ensure that rownames of metadata match column names of data matrix") - } - if (length(x = setdiff(x = rownames(x = meta.data), y = colnames(x = counts)))) { - warning("Some cells in meta.data not present in provided counts matrix.") - meta.data <- meta.data[intersect(x = rownames(x = meta.data), y = colnames(x = counts)), , drop = FALSE] - } - if (is.data.frame(x = meta.data)) { - new.meta.data <- data.frame(row.names = colnames(x = counts)) - for (ii in 1:ncol(x = meta.data)) { - new.meta.data[rownames(x = meta.data), colnames(x = meta.data)[ii]] <- meta.data[, ii, drop = FALSE] - } - meta.data <- new.meta.data - } - } - # Check assay key - if (!length(x = Key(object = counts)) || !nchar(x = Key(object = counts))) { - Key(object = counts) <- UpdateKey(key = tolower(x = assay)) + # Check the assay key + if (!isTRUE(x = nzchar(x = Key(object = counts)))) { + Key(object = counts) <- Key(object = tolower(x = assay), quiet = TRUE) } + # Assemble the assay list assay.list <- list(counts) names(x = assay.list) <- assay - # Set idents + # Create identity classes idents <- factor(x = unlist(x = lapply( X = colnames(x = counts), FUN = ExtractField, @@ -849,41 +1305,80 @@ CreateSeuratObject.Assay <- function( delim = names.delim ))) if (any(is.na(x = idents))) { - warning( + warn( "Input parameters result in NA values for initial cell identities. Setting all initial idents to the project name", call. = FALSE, immediate. = TRUE ) + idents <- factor(x = rep_len(x = project, length.out = ncol(x = counts))) } - # if there are more than 100 idents, set all idents to ... name - ident.levels <- length(x = unique(x = idents)) - if (ident.levels > 100 || ident.levels == 0 || ident.levels == length(x = idents)) { - idents <- rep.int(x = factor(x = project), times = ncol(x = counts)) + nidents <- length(x = levels(x = idents)) + if (nidents > 100L || nidents == 0L || nidents == length(x = idents)) { + idents <- factor(x = rep_len(x = project, length.out = ncol(x = counts))) } names(x = idents) <- colnames(x = counts) - object <- new( + # Initialize meta data + meta.init <- EmptyDF(n = ncol(x = counts)) + row.names(x = meta.init) <- colnames(x = counts) + # Create the object + op <- options(Seurat.object.validate = FALSE) + on.exit(expr = options(op), add = TRUE) + object <- suppressWarnings(expr = new( Class = 'Seurat', assays = assay.list, - meta.data = data.frame(row.names = colnames(x = counts)), + meta.data = meta.init, active.assay = assay, active.ident = idents, + graphs = list(), + neighbors = list(), + reductions = list(), + images = list(), project.name = project, - version = packageVersion(pkg = 'SeuratObject') - ) + misc = list(), + version = packageVersion(pkg = 'SeuratObject'), + commands = list(), + tools = list() + )) + options(op) object[['orig.ident']] <- idents # Calculate nCount and nFeature - n.calc <- CalcN(object = counts) - if (!is.null(x = n.calc)) { - names(x = n.calc) <- paste(names(x = n.calc), assay, sep = '_') - object[[names(x = n.calc)]] <- n.calc + calcN_option <- getOption( + x = 'Seurat.object.assay.calcn', + default = Seurat.options$Seurat.object.assay.calcn + ) + calcN_option <- calcN_option %||% TRUE + if (isTRUE(x = calcN_option)) { + ncalc <- CalcN(object = counts) + if (!is.null(x = ncalc)) { + names(x = ncalc) <- paste(names(x = ncalc), assay, sep = '_') + object[[]] <- ncalc + } } - # Add metadata + # Add provided meta data if (!is.null(x = meta.data)) { - object <- AddMetaData(object = object, metadata = meta.data) + tryCatch( + expr = object[[]] <- meta.data, + error = function(e) { + warning(e$message, call. = FALSE, immediate. = TRUE) + } + ) } + # Validate and return + validObject(object = object) return(object) } +#' @method CreateSeuratObject StdAssay +#' @export +#' +CreateSeuratObject.StdAssay <- CreateSeuratObject.Assay + +#' @rdname CreateSeuratObject +#' @method CreateSeuratObject Assay5 +#' @export +#' +CreateSeuratObject.Assay5 <- CreateSeuratObject.StdAssay + #' @rdname DefaultAssay #' @export #' @method DefaultAssay Seurat @@ -894,8 +1389,11 @@ CreateSeuratObject.Assay <- function( #' DefaultAssay.Seurat <- function(object, ...) { CheckDots(...) - object <- UpdateSlots(object = object) - return(slot(object = object, name = 'active.assay')) + default <- slot(object = object, name = 'active.assay') + if (!length(x = default)) { + default <- NULL + } + return(default) } #' @rdname DefaultAssay @@ -913,10 +1411,8 @@ DefaultAssay.Seurat <- function(object, ...) { #' "DefaultAssay<-.Seurat" <- function(object, ..., value) { CheckDots(...) - object <- UpdateSlots(object = object) - if (!value %in% names(x = slot(object = object, name = 'assays'))) { - stop("Cannot find assay ", value) - } + value <- value[1L] + value <- match.arg(arg = value, choices = Assays(object = object)) slot(object = object, name = 'active.assay') <- value return(object) } @@ -1009,14 +1505,34 @@ DefaultFOV.Seurat <- function(object, assay = NULL, ...) { #' Embeddings(object = pbmc_small, reduction = "pca")[1:5, 1:5] #' Embeddings.Seurat <- function(object, reduction = 'pca', ...) { - object <- UpdateSlots(object = object) return(Embeddings(object = object[[reduction]], ...)) } +#' @method Features Seurat +#' @export +#' +Features.Seurat <- function(x, assay = NULL, ...) { + assay <- assay[1L] %||% DefaultAssay(object = x) + assay <- match.arg(arg = assay, choices = Assays(object = x)) + return(Features(x = x[[assay]], ...)) +} + #' @param vars List of all variables to fetch, use keyword \dQuote{ident} to #' pull identity classes #' @param cells Cells to collect data for (default is all cells) -#' @param slot Slot to pull feature data for +#' @param layer Layer to pull feature data for +#' @param clean Remove cells that are missing data; choose from: +#' \itemize{ +#' \item \dQuote{\code{all}}: consider all columns for cleaning +#' \item \dQuote{\code{ident}}: consider all columns except the identity +#' class for cleaning +#' \item \dQuote{\code{project}}: consider all columns except the identity +#' class for cleaning; fill missing identity values with the object's project +#' \item \dQuote{\code{none}}: do not clean +#' } +#' Passing \code{TRUE} is a shortcut for \dQuote{\code{ident}}; passing +#' \code{FALSE} is a shortcut for \dQuote{\code{none}} +#' @param slot Deprecated in favor of \code{layer} #' #' @return A data frame with cells as rows and cellular data as columns #' @@ -1031,139 +1547,159 @@ Embeddings.Seurat <- function(object, reduction = 'pca', ...) { #' head(x = pc1) #' head(x = FetchData(object = pbmc_small, vars = c('groups', 'ident'))) #' -FetchData.Seurat <- function(object, vars, cells = NULL, slot = 'data', ...) { +FetchData.Seurat <- function( + object, + vars, + cells = NULL, + layer = NULL, + clean = TRUE, + slot = deprecated(), + ... +) { + if (is_present(arg = slot)) { + .Deprecate( + when = '5.0.0', + what = 'FetchData(slot = )', + with = 'FetchData(layer = )' + ) + layer <- layer %||% slot + } object <- UpdateSlots(object = object) + if (isTRUE(x = clean)) { + clean <- 'ident' + } else if (isFALSE(x = clean)) { + clean <- 'none' + } + clean <- arg_match0(arg = clean, values = c('all', 'ident', 'none', 'project')) + # Find cells to use cells <- cells %||% colnames(x = object) if (is.numeric(x = cells)) { cells <- colnames(x = object)[cells] } if (is.null(x = vars)) { - df <- EmptyDF(n = length(x = cells)) - rownames(x = df) <- cells - return(df) + return(data.frame(row.names = cells)) + } + data.fetched <- EmptyDF(n = length(x = cells)) + row.names(x = data.fetched) <- cells + # Pull vars from object metadata + meta.vars <- intersect(x = vars, y = names(x = object[[]])) + meta.vars <- setdiff(x = meta.vars, y = names(x = data.fetched)) + if (length(x = meta.vars)) { + meta.default <- intersect(x = meta.vars, y = rownames(x = object)) + if (length(x = meta.default)) { + warn(message = paste0( + "The following variables were found in both object meta data and the default assay: ", + paste0(meta.default, collapse = ', '), + "\nReturning meta data; if you want the feature, please use the assay's key (eg. ", + paste0(Key(object = object)[DefaultAssay(object = object)], meta.default[1L]), + ")" + )) + } + meta.pull <- object[[meta.vars]] + cells.meta <- row.names(x = meta.pull) + cells.order <- MatchCells(new = cells.meta, orig = cells, ordered = TRUE) + cells.meta <- cells.meta[cells.order] + data.fetched[cells.meta, meta.vars] <- meta.pull[cells.meta, , drop = FALSE] } - # Get a list of all objects to search through and their keys - object.keys <- Key(object = object) # Find all vars that are keyed - keyed.vars <- lapply( - X = object.keys, + keyed.vars <- sapply( + X = Keys(object = object), FUN = function(key) { - if (length(x = key) == 0 || nchar(x = key) == 0) { - return(integer(length = 0L)) + if (!length(x = key) || !nzchar(x = key)) { + return(character(length = 0L)) } - return(grep(pattern = paste0('^', key), x = vars)) - } + return(grep(pattern = paste0('^', key), x = setdiff(vars, meta.vars), value = TRUE)) + }, + simplify = FALSE, + USE.NAMES = TRUE ) keyed.vars <- Filter(f = length, x = keyed.vars) + # Check spatial keyed vars ret.spatial2 <- vapply( X = names(x = keyed.vars), FUN = function(x) { return(inherits(x = object[[x]], what = 'FOV')) }, - FUN.VALUE = logical(length = 1L) + FUN.VALUE = logical(length = 1L), + USE.NAMES = FALSE ) - if (any(ret.spatial2) && !all(ret.spatial2)) { - warning( - "Data returned from spatial coordinates are incompatible with other data, returning only spatial coordinates", - call. = FALSE, - immediate. = TRUE - ) - keyed.vars <- keyed.vars[ret.spatial2] + if (any(ret.spatial2)) { + abort(message = "Spatial coordinates are no longer fetchable with FetchData") } - data.fetched <- lapply( + + # Find all keyed.vars + data.keyed <- lapply( X = names(x = keyed.vars), FUN = function(x) { - vars.use <- vars[keyed.vars[[x]]] - key.use <- object.keys[x] - data.return <- if (inherits(x = object[[x]], what = 'DimReduc')) { + data.return <- switch( + EXPR = x, + meta.data = { + md <- gsub(pattern = '^md', replacement = '', x = keyed.vars[[x]]) + df <- object[[md]][cells, , drop = FALSE] + names(x = df) <- paste0('md_', names(x = df)) + df + }, tryCatch( - expr = FetchData(object = object[[x]], vars = vars.use, cells = cells), - error = function(e) { + expr = FetchData( + object = object[[x]], + vars = keyed.vars[[x]], + cells = cells, + layer = layer, + ... + ), + varsNotFoundError = function(...) { + warn(message = paste0( + 'The following keyed vars could not be found in object ', + sQuote(x = x), + ':', + paste(keyed.vars[[x]], collapse = ', '), + '\nAttempting to pull from other locations' + )) return(NULL) } ) - } else if (inherits(x = object[[x]], what = 'Assay')) { - vars.use <- gsub(pattern = paste0('^', key.use), replacement = '', x = vars.use) - data.assay <- GetAssayData( - object = object, - slot = slot, - assay = x - ) - vars.use <- vars.use[vars.use %in% rownames(x = data.assay)] - data.vars <- t(x = as.matrix(data.assay[vars.use, cells, drop = FALSE])) - if (ncol(data.vars) > 0) { - colnames(x = data.vars) <- paste0(key.use, vars.use) - } - data.vars - } else if (inherits(x = object[[x]], what = 'FOV')) { - vars.use <- gsub(pattern = paste0('^', key.use), replacement = '', x = vars.use) - FetchData(object = object[[x]], vars = vars.use, cells = cells) - } else if (inherits(x = object[[x]], what = 'SpatialImage')) { - vars.unkeyed <- gsub(pattern = paste0('^', key.use), replacement = '', x = vars.use) - names(x = vars.use) <- vars.unkeyed - coords <- GetTissueCoordinates(object = object[[x]])[cells, vars.unkeyed, drop = FALSE] - colnames(x = coords) <- vars.use[colnames(x = coords)] - coords - } - data.return <- as.list(x = as.data.frame(x = data.return)) + ) return(data.return) } ) - data.fetched <- unlist(x = data.fetched, recursive = FALSE) - if (any(ret.spatial2)) { - return(as.data.frame(x = data.fetched)) - } - # Pull vars from object metadata - meta.vars <- vars[vars %in% colnames(x = object[[]]) & !(vars %in% names(x = data.fetched))] - data.fetched <- c(data.fetched, object[[meta.vars]][cells, , drop = FALSE]) - meta.default <- meta.vars[meta.vars %in% rownames(x = GetAssayData(object = object, slot = slot))] - if (length(x = meta.default)) { - warning( - "The following variables were found in both object metadata and the default assay: ", - paste0(meta.default, collapse = ", "), - "\nReturning metadata; if you want the feature, please use the assay's key (eg. ", - paste0(Key(object = object[[DefaultAssay(object = object)]]), meta.default[1]), - ")", - call. = FALSE - ) + for (i in seq_along(along.with = data.keyed)) { + df <- data.keyed[[i]] + data.fetched[row.names(x = df), names(x = df)] <- df } # Pull vars from the default assay - default.vars <- vars[vars %in% rownames(x = GetAssayData(object = object, slot = slot)) & !(vars %in% names(x = data.fetched))] - data.fetched <- c( - data.fetched, - tryCatch( - expr = as.data.frame(x = t(x = as.matrix(x = GetAssayData( - object = object, - slot = slot - )[default.vars, cells, drop = FALSE]))), - error = function(...) { - return(NULL) - } + default.vars <- intersect(x = vars, y = rownames(x = object)) + default.vars <- setdiff(x = default.vars, y = names(x = data.fetched)) + if (length(x = default.vars)) { + df <- FetchData( + object = object[[DefaultAssay(object = object)]], + vars = default.vars, + cells = cells, + layer = layer, + ... ) - ) + data.fetched[row.names(x = df), names(x = df)] <- df + } # Pull identities - if ('ident' %in% vars && !'ident' %in% colnames(x = object[[]])) { - data.fetched[['ident']] <- Idents(object = object)[cells] + if ('ident' %in% vars && !'ident' %in% names(x = object[[]])) { + data.fetched[cells, 'ident'] <- Idents(object = object)[cells] } # Try to find ambiguous vars - fetched <- names(x = data.fetched) - vars.missing <- setdiff(x = vars, y = fetched) - if (length(x = vars.missing) > 0) { - # Search for vars in alternative assays + vars.missing <- setdiff(x = vars, y = names(x = data.fetched)) + if (length(x = vars.missing)) { + # Search for vars in alternate assays + # Create a list to hold vars and the alternate assays they're found in vars.alt <- vector(mode = 'list', length = length(x = vars.missing)) names(x = vars.alt) <- vars.missing - for (assay in FilterObjects(object = object, classes.keep = 'Assay')) { + # Search through features in alternate assays to see if + # they contain our missing vars + for (assay in Assays(object = object)) { vars.assay <- Filter( f = function(x) { - features.assay <- rownames(x = GetAssayData( - object = object, - assay = assay, - slot = slot - )) - return(x %in% features.assay) + return(x %in% Features(x = object, assay = assay, layer = layer)) }, x = vars.missing ) + # Add the alternate assay to our holding list for our found vars for (var in vars.assay) { vars.alt[[var]] <- append(x = vars.alt[[var]], values = assay) } @@ -1175,14 +1711,14 @@ FetchData.Seurat <- function(object, vars, cells = NULL, slot = 'data', ...) { }, x = vars.alt )) - if (length(x = vars.many) > 0) { - warning( - "Found the following features in more than one assay, excluding the default. We will not include these in the final data frame: ", - paste(vars.many, collapse = ', '), - call. = FALSE, - immediate. = TRUE - ) + if (length(x = vars.many)) { + warn(message = paste( + "Found the following features in more than one assay, excluding the default.", + "We will not include these in the final data frame:", + paste(vars.many, collapse = ', ') + )) } + # Missing vars are either ambiguous or not found in exactly one assay vars.missing <- names(x = Filter( f = function(x) { return(length(x = x) != 1) @@ -1199,67 +1735,133 @@ FetchData.Seurat <- function(object, vars, cells = NULL, slot = 'data', ...) { ) for (var in names(x = vars.alt)) { assay <- vars.alt[[var]] - warning( - 'Could not find ', + warn(message = paste( + 'Could not find', var, - ' in the default search locations, found in ', - assay, - ' assay instead', - immediate. = TRUE, - call. = FALSE - ) + 'in the default search locations, found in', + sQuote(x = assay), + 'assay instead' + )) keyed.var <- paste0(Key(object = object[[assay]]), var) - data.fetched[[keyed.var]] <- as.vector( - x = GetAssayData(object = object, assay = assay, slot = slot)[var, cells] - ) - vars <- sub( - pattern = paste0('^', var, '$'), - replacement = keyed.var, - x = vars + vars[vars == var] <- keyed.var + df <- FetchData( + object = object[[assay]], + vars = keyed.var, + cells = cells, + layer = layer ) + data.fetched[row.names(x = df), names(x = df)] <- df } - fetched <- names(x = data.fetched) } # Name the vars not found in a warning (or error if no vars found) + # `m2` is an additional message if we're missing more than 10 vars m2 <- if (length(x = vars.missing) > 10) { - paste0(' (10 out of ', length(x = vars.missing), ' shown)') + paste(' (10 out of', length(x = vars.missing), 'shown)') } else { '' } if (length(x = vars.missing) == length(x = vars)) { - stop( - "None of the requested variables were found", - m2, - ': ', - paste(head(x = vars.missing, n = 10L), collapse = ', ') + abort( + message = paste0( + "None of the requested variables were found", + m2, + ': ', + paste(head(x = vars.missing, n = 10L), collapse = ', ') + ), + class = 'varsNotFoundError' ) - } else if (length(x = vars.missing) > 0) { - warning( + } else if (length(x = vars.missing)) { + warn(message = paste0( "The following requested variables were not found", m2, ': ', paste(head(x = vars.missing, n = 10L), collapse = ', ') - ) + )) } - # Assembled fetched vars in a data frame - data.fetched <- as.data.frame( - x = data.fetched, - row.names = cells, - stringsAsFactors = FALSE - ) - data.order <- na.omit(object = pmatch( - x = vars, - table = fetched - )) - if (length(x = data.order) > 1) { - data.fetched <- data.fetched[, data.order] + .FilterData <- function(df) { + return(which(x = apply(X = df, MARGIN = 1L, FUN = \(x) all(is.na(x = x))))) } - colnames(x = data.fetched) <- vars[vars %in% fetched] + # Clean the fetched data + data.fetched <- switch( + EXPR = clean, + all = { + # Clean all vars + no.data <- .FilterData(df = data.fetched) + if (length(x = no.data)) { + warn(message = paste( + "Removing", + length(x = no.data), + "cells missing data for vars requested" + )) + data.fetched[-no.data, , drop = FALSE] + } else { + data.fetched + } + }, + ident = { + # Clean all vars except ident + cols.clean <- names(x = data.fetched) + if (ncol(x = data.fetched) > 2L && !'ident' %in% names(x = object[[]])) { + cols.clean <- setdiff(x = cols.clean, y = 'ident') + } + no.data <- .FilterData(df = data.fetched[, cols.clean, drop = FALSE]) + if (length(x = no.data)) { + warn(message = paste( + "Removing", + length(x = no.data), + "cells missing data for vars requested" + )) + data.fetched[-no.data, , drop = FALSE] + } else { + data.fetched + } + }, + project = { + # Clean all vars except ident + cols.clean <- names(x = data.fetched) + if (ncol(x = data.fetched) > 2L && !'ident' %in% names(x = object[[]])) { + cols.clean <- setdiff(x = cols.clean, y = 'ident') + } + no.data <- .FilterData(df = data.fetched[, cols.clean, drop = FALSE]) + if (length(x = no.data)) { + warn(message = paste( + "Removing", + length(x = no.data), + "cells missing data for vars requested" + )) + data.fetched <- data.fetched[-no.data, , drop = FALSE] + } + # When all idents are `NA`, set to Project(object) + if ('ident' %in% names(x = data.fetched) && !'ident' %in% names(x = object[[]])) { + if (all(is.na(x = data.fetched$ident))) { + warn(message = paste( + "None of the cells requested have an identity class, returning", + sQuote(x = Project(object = object)), + "instead" + )) + data.fetched$ident <- Project(object = object) + } + } + data.fetched + }, + # Don't clean vars + data.fetched + ) + vars.return <- intersect(x = vars, y = names(x = data.fetched)) + data.fetched <- data.fetched[, vars.return, drop = FALSE] + # data.order <- na.omit(object = pmatch( + # x = vars, + # table = names(x = data.fetched) + # )) + # if (length(x = data.order) > 1) { + # data.fetched <- data.fetched[, data.order] + # } + # colnames(x = data.fetched) <- vars[vars %in% fetched] return(data.fetched) } -#' @param assay Specific assay to get data from or set data for; defaults to -#' the \link[SeuratObject:DefaultAssay]{default assay} +#' @param assay Specific assay to get data from or set data for; +#' defaults to the \link[=DefaultAssay]{default assay} #' #' @rdname AssayData #' @export @@ -1271,17 +1873,26 @@ FetchData.Seurat <- function(object, vars, cells = NULL, slot = 'data', ...) { #' # Get assay data from the default assay in a Seurat object #' GetAssayData(object = pbmc_small, slot = "data")[1:5,1:5] #' -GetAssayData.Seurat <- function(object, slot = 'data', assay = NULL, ...) { +GetAssayData.Seurat <- function( + object, + assay = NULL, + layer = NULL, + slot = deprecated(), + ... +) { CheckDots(...) + if (is_present(arg = slot)) { + .Deprecate( + when = '5.0.0', + what = 'GetAssayData(slot = )', + with = 'GetAssayData(layer = )' + ) + layer <- slot + } object <- UpdateSlots(object = object) assay <- assay %||% DefaultAssay(object = object) - if (!assay %in% Assays(object = object)) { - stop("'", assay, "' is not an assay", call. = FALSE) - } - return(GetAssayData( - object = object[[assay]], - slot = slot - )) + assay <- arg_match(arg = assay, values = Assays(object = object)) + return(GetAssayData(object = object[[assay]], layer = layer)) } #' @param image Name of \code{SpatialImage} object to pull image data for; if @@ -1336,19 +1947,28 @@ GetTissueCoordinates.Seurat <- function(object, image = NULL, ...) { #' HVFInfo.Seurat <- function( object, - selection.method = NULL, + method = NULL, status = FALSE, assay = NULL, + selection.method = deprecated(), ... ) { CheckDots(...) + if (is_present(arg = selection.method)) { + .Deprecate( + when = '5.0.0', + what = 'HVFInfo(selection.method = )', + with = 'HVFInfo(method = )' + ) + method <- selection.method + } object <- UpdateSlots(object = object) assay <- assay %||% DefaultAssay(object = object) - if (is.null(x = selection.method)) { + if (is.null(x = method)) { cmds <- apply( X = expand.grid( c('FindVariableFeatures', 'SCTransform'), - FilterObjects(object = object, classes.keep = 'Assay') + FilterObjects(object = object, classes.keep = c('Assay', 'Assay5')) ), MARGIN = 1, FUN = paste, @@ -1356,10 +1976,7 @@ HVFInfo.Seurat <- function( ) find.command <- Command(object = object)[Command(object = object) %in% cmds] if (length(x = find.command) < 1) { - stop( - "Please run either 'FindVariableFeatures' or 'SCTransform'", - call. = FALSE - ) + abort(message = "Please run either 'FindVariableFeatures' or 'SCTransform'") } find.command <- find.command[length(x = find.command)] test.command <- paste(file_path_sans_ext(x = find.command), assay, sep = '.') @@ -1368,7 +1985,7 @@ HVFInfo.Seurat <- function( yes = test.command, no = find.command ) - selection.method <- switch( + method <- switch( EXPR = file_path_sans_ext(x = find.command), 'FindVariableFeatures' = Command( object = object, @@ -1381,7 +1998,7 @@ HVFInfo.Seurat <- function( } return(HVFInfo( object = object[[assay]], - selection.method = selection.method, + method = method, status = status )) } @@ -1392,32 +2009,44 @@ HVFInfo.Seurat <- function( #' Idents.Seurat <- function(object, ...) { CheckDots(...) - object <- UpdateSlots(object = object) + # object <- UpdateSlots(object = object) return(slot(object = object, name = 'active.ident')) } #' @param cells Set cell identities for specific cells #' @param drop Drop unused levels +#' @param replace Replace identities for unset cells with \code{NA} #' #' @rdname Idents #' @export #' @method Idents<- Seurat #' -"Idents<-.Seurat" <- function(object, cells = NULL, drop = FALSE, ..., value) { +"Idents<-.Seurat" <- function( + object, + cells = NULL, + drop = FALSE, + replace = FALSE, + ..., + value +) { CheckDots(...) object <- UpdateSlots(object = object) - cells <- cells %||% colnames(x = object) + if (!(is.factor(x = value) || is.atomic(x = value))) { + abort(message = "'value' must be a factor or vector") + } + cells <- cells %||% names(x = value) %||% colnames(x = object) if (is.numeric(x = cells)) { cells <- colnames(x = object)[cells] } cells <- intersect(x = cells, y = colnames(x = object)) - cells <- match(x = cells, table = colnames(x = object)) - if (length(x = cells) == 0) { - warning("Cannot find cells provided") + # cells <- match(x = cells, table = colnames(x = object)) + if (!length(x = cells)) { + warn(message = 'Cannot find cells provided') return(object) } - idents.new <- if (length(x = value) == 1 && value %in% colnames(x = object[[]])) { - unlist(x = object[[value]], use.names = FALSE)[cells] + idents.new <- if (length(x = value) == 1 && value %in% names(x = object[[]])) { + # unlist(x = object[[value]], use.names = FALSE)[cells] + object[[value, drop = TRUE]][cells] } else { if (is.list(x = value)) { value <- unlist(x = value, use.names = FALSE) @@ -1429,10 +2058,14 @@ Idents.Seurat <- function(object, ...) { } else { unique(x = idents.new) } - old.levels <- levels(x = object) - levels <- c(new.levels, old.levels) + levels <- union(x = new.levels, y = levels(x = object)) idents.new <- as.vector(x = idents.new) - idents <- as.vector(x = Idents(object = object)) + idents <- if (isTRUE(x = replace)) { + rep_len(x = NA_character_, length.out = ncol(x = object)) + } else { + as.vector(x = Idents(object = object)) + } + names(x = idents) <- colnames(x = object) idents[cells] <- idents.new idents[is.na(x = idents)] <- 'NA' levels <- intersect(x = levels, y = unique(x = idents)) @@ -1443,12 +2076,35 @@ Idents.Seurat <- function(object, ...) { } idents <- factor(x = idents, levels = levels) slot(object = object, name = 'active.ident') <- idents - if (drop) { + if (isTRUE(x = drop)) { object <- droplevels(x = object) } return(object) } +#' @param assay Name of assay to split layers +#' +#' @rdname SplitLayers +#' @method JoinLayers Seurat +#' @export +#' +JoinLayers.Seurat <- function( + object, + assay = NULL, + layers = NULL, + new = NULL, + ... +) { + assay <- assay %||% DefaultAssay(object) + object[[assay]] <- JoinLayers( + object = object[[assay]], + layers = layers, + new = new, + ... + ) + return(object) +} + #' @rdname Key #' @export #' @method Key Seurat @@ -1461,20 +2117,20 @@ Idents.Seurat <- function(object, ...) { Key.Seurat <- function(object, ...) { CheckDots(...) object <- UpdateSlots(object = object) - keyed.objects <- FilterObjects( - object = object, - classes.keep = c('Assay', 'DimReduc', 'SpatialImage') - ) - keys <- vapply( - X = keyed.objects, - FUN = function(x) { - return(Key(object = object[[x]])) - }, - FUN.VALUE = character(length = 1L), - USE.NAMES = FALSE - ) - names(x = keys) <- keyed.objects - return(keys) + return(c( + meta.data = Key(object = 'md', quiet = TRUE), + vapply( + X = .FilterObjects( + object = object, + classes.keep = c('Assay', 'SpatialImage', 'KeyMixin') + ), + FUN = function(x) { + return(Key(object = object[[x]])) + }, + FUN.VALUE = character(length = 1L), + USE.NAMES = TRUE + ) + )) } #' @rdname Key @@ -1483,6 +2139,50 @@ Key.Seurat <- function(object, ...) { #' Keys.Seurat <- Key.Seurat +#' @param assay Name of assay to fetch layer data from or assign layer data to +#' +#' @rdname Layers +#' @method LayerData Seurat +#' @export +#' +LayerData.Seurat <- function( + object, + layer = NULL, + assay = NULL, + slot = deprecated(), + ... +) { + if (is_present(arg = slot)) { + deprecate_stop(when = "5.0.0", + what = "LayerData(slot = )", + with = "LayerData(layer = )") + } + assay <- assay %||% DefaultAssay(object = object) + assay <- arg_match(arg = assay, values = Assays(object = object)) + return(LayerData(object = object[[assay]], layer = layer, ...)) +} + +#' @rdname Layers +#' @method LayerData<- Seurat +#' @export +#' +"LayerData<-.Seurat" <- function(object, layer, assay = NULL, ..., value) { + assay <- assay %||% DefaultAssay(object = object) + assay <- arg_match(arg = assay, values = Assays(object = object)) + LayerData(object = object[[assay]], layer = layer, ...) <- value + return(object) +} + +#' @rdname Layers +#' @method Layers Seurat +#' @export +#' +Layers.Seurat <- function(object, search = NA, assay = NULL, ...) { + assay <- assay %||% DefaultAssay(object = object) + assay <- arg_match(arg = assay, values = Assays(object = object)) + return(Layers(object = object[[assay]], search = search, ...)) +} + #' @param reduction Name of reduction to pull feature loadings for #' #' @rdname Loadings @@ -1592,9 +2292,8 @@ ReorderIdent.Seurat <- function( return(object) } -#' @param for.merge Only rename slots needed for merging Seurat objects. -#' Currently only renames the raw.data and meta.data slots. #' @param add.cell.id prefix to add cell names +#' @param for.merge Deprecated #' #' @details #' If \code{add.cell.id} is set a prefix is added to existing cell names. If @@ -1612,68 +2311,85 @@ ReorderIdent.Seurat <- function( #' RenameCells.Seurat <- function( object, - add.cell.id = NULL, - new.names = NULL, - for.merge = FALSE, + add.cell.id = missing_arg(), + new.names = missing_arg(), + for.merge = deprecated(), ... ) { CheckDots(...) object <- UpdateSlots(object = object) - if (missing(x = add.cell.id) && missing(x = new.names)) { - stop("One of 'add.cell.id' and 'new.names' must be set") + working.cells <- Cells(x = object) + if (is_present(arg = for.merge)) { + .Deprecate(when = '5.0.0', what = 'RenameCells(for.merge = )') + } + if (is_missing(x = add.cell.id) && is_missing(x = new.names)) { + abort(message = "One of 'add.cell.id' and 'new.names' must be set") } - if (!missing(x = add.cell.id) && !missing(x = new.names)) { - stop("Only one of 'add.cell.id' and 'new.names' may be set") + if (!is_missing(x = add.cell.id) && !is_missing(x = new.names)) { + abort(message = "Only one of 'add.cell.id' and 'new.names' may be set") } if (!missing(x = add.cell.id)) { - new.cell.names <- paste(add.cell.id, colnames(x = object), sep = "_") + new.cell.names <- paste(add.cell.id, working.cells, sep = "_") } else { - if (length(x = new.names) == ncol(x = object)) { + if (length(x = new.names) == length(x = working.cells)) { new.cell.names <- new.names } else { - stop( + abort(message = paste0( "the length of 'new.names' (", length(x = new.names), ") must be the same as the number of cells (", - ncol(x = object), + length(x = working.cells), ")" - ) + )) } } old.names <- colnames(x = object) + new.cell.names.global <- old.names + new.cell.names.global[match(x = working.cells, table = old.names)] <- new.cell.names + new.cell.names <- new.cell.names.global + # rename the cell-level metadata first to rename colname() + old.meta.data <- object[[]] + row.names(x = old.meta.data) <- new.cell.names + slot(object = object, name = "meta.data") <- old.meta.data + # rename the active.idents + old.ids <- Idents(object = object) + names(x = old.ids) <- new.cell.names + Idents(object = object) <- old.ids + names(x = new.cell.names) <- old.names # rename in the assay objects assays <- FilterObjects(object = object, classes.keep = 'Assay') - for (assay in assays) { - slot(object = object, name = "assays")[[assay]] <- RenameCells( - object = object[[assay]], - new.names = new.cell.names + for (i in assays) { + slot(object = object, name = "assays")[[i]] <- RenameCells( + object = object[[i]], + new.names = new.cell.names[colnames(x = object[[i]])] + ) + } + # rename in the assay5 objects + assays5 <- FilterObjects(object = object, classes.keep = 'Assay5') + for (i in assays5) { + slot(object = object, name = "assays")[[i]] <- RenameCells( + object = object[[i]], + new.names = new.cell.names[colnames(x = object[[i]])] ) } # rename in the DimReduc objects dimreducs <- FilterObjects(object = object, classes.keep = 'DimReduc') - for (dr in dimreducs) { - object[[dr]] <- RenameCells( - object = object[[dr]], - new.names = new.cell.names + for (i in dimreducs) { + slot(object = object, name = "reductions")[[i]] <- RenameCells( + object = object[[i]], + new.names = new.cell.names[Cells(x = object[[i]])] ) } - # rename the active.idents - old.ids <- Idents(object = object) - names(x = old.ids) <- new.cell.names - Idents(object = object) <- old.ids - # rename the cell-level metadata - old.meta.data <- object[[]] - rownames(x = old.meta.data) <- new.cell.names - slot(object = object, name = "meta.data") <- old.meta.data # rename the graphs graphs <- FilterObjects(object = object, classes.keep = "Graph") for (g in graphs) { - rownames(x = object[[g]]) <- colnames(x = object[[g]]) <- new.cell.names + graph.g <- object[[g]] + rownames(graph.g) <- colnames(graph.g) <- new.cell.names[colnames(x = graph.g)] + slot(object = object, name = "graphs")[[g]] <- graph.g } # Rename the images - names(x = new.cell.names) <- old.names for (i in Images(object = object)) { - object[[i]] <- RenameCells( + slot(object = object, name = "images")[[i]] <- RenameCells( object = object[[i]], new.names = unname( obj = new.cell.names[Cells(x = object[[i]], boundary = NA)] @@ -1682,12 +2398,13 @@ RenameCells.Seurat <- function( } # Rename the Neighbor for (i in Neighbors(object = object)) { - object[[i]] <- RenameCells( + slot(object = object, name = "neighbors")[[i]] <- RenameCells( object = object[[i]], - old.names = old.names, - new.names = new.cell.names + old.names = Cells(x = object[[i]]), + new.names = new.cell.names[Cells(x = object[[i]])] ) } + validObject(object) return(object) } @@ -1729,20 +2446,21 @@ RenameIdents.Seurat <- function(object, ...) { #' @order 4 #' #' @examples -#' # Set an Assay slot through the Seurat object -#' count.data <- GetAssayData(object = pbmc_small[["RNA"]], slot = "counts") +#' # Set an Assay layer through the Seurat object +#' count.data <- GetAssayData(object = pbmc_small[["RNA"]], layer = "counts") #' count.data <- as.matrix(x = count.data + 1) #' new.seurat.object <- SetAssayData( #' object = pbmc_small, -#' slot = "counts", +#' layer = "counts", #' new.data = count.data, #' assay = "RNA" #' ) #' SetAssayData.Seurat <- function( object, - slot = 'data', + layer = 'data', new.data, + slot = deprecated(), assay = NULL, ... ) { @@ -1751,8 +2469,9 @@ SetAssayData.Seurat <- function( assay <- assay %||% DefaultAssay(object = object) object[[assay]] <- SetAssayData( object = object[[assay]], - slot = slot, + layer = layer, new.data = new.data, + slot = slot, ... ) return(object) @@ -1787,16 +2506,25 @@ SetIdent.Seurat <- function(object, cells = NULL, value, ...) { #' SpatiallyVariableFeatures.Seurat <- function( object, - selection.method = "markvariogram", + method = "moransi", assay = NULL, decreasing = TRUE, + selection.method = deprecated(), ... ) { CheckDots(...) + if (is_present(arg = selection.method)) { + .Deprecate( + when = '5.0.0', + what = 'SpatiallyVariableFeatures(selection.method = )', + with = 'SpatiallyVariableFeatures(method = )' + ) + method <- selection.method + } assay <- assay %||% DefaultAssay(object = object) return(SpatiallyVariableFeatures( object = object[[assay]], - selection.method = selection.method, + method = method, decreasing = decreasing )) } @@ -1808,14 +2536,18 @@ SpatiallyVariableFeatures.Seurat <- function( #' @method StashIdent Seurat #' StashIdent.Seurat <- function(object, save.name = 'orig.ident', ...) { - message( - 'With Seurat 3.X, stashing identity classes can be accomplished with the following:\n', - deparse(expr = substitute(expr = object)), - '[[', - deparse(expr = substitute(expr = save.name)), - ']] <- Idents(object = ', - deparse(expr = substitute(expr = object)), - ')' + deprecate_soft( + when = '3.0.0', + what = 'StashIdent()', + details = paste0( + "Please use ", + deparse(expr = substitute(expr = object)), + '[[', + deparse(expr = substitute(expr = save.name)), + ']] <- Idents(', + deparse(expr = substitute(expr = object)), + ')' + ) ) CheckDots(...) object <- UpdateSlots(object = object) @@ -1848,18 +2580,23 @@ Stdev.Seurat <- function(object, reduction = 'pca', ...) { #' SVFInfo.Seurat <- function( object, - selection.method = c("markvariogram", "moransi"), + method = c("markvariogram", "moransi"), status = FALSE, assay = NULL, + selection.method = deprecated(), ... ) { CheckDots(...) + if (is_present(arg = selection.method)) { + .Deprecate( + when = '5.0.0', + what = 'SVFInfo(selection.method = )', + with = 'SVFInfo(method = )' + ) + method <- selection.method + } assay <- assay %||% DefaultAssay(object = object) - return(SVFInfo( - object = object[[assay]], - selection.method = selection.method, - status = status - )) + return(SVFInfo(object = object[[assay]], method = method, status = status)) } #' @param slot Name of tool to pull @@ -1868,9 +2605,6 @@ SVFInfo.Seurat <- function( #' @export #' @method Tool Seurat #' -#' @examples -#' Tool(object = pbmc_small) -#' Tool.Seurat <- function(object, slot = NULL, ...) { CheckDots(...) object <- UpdateSlots(object = object) @@ -1884,12 +2618,6 @@ Tool.Seurat <- function(object, slot = NULL, ...) { #' @export #' @method Tool<- Seurat #' -#' @examples -#' \dontrun{ -#' sample.tool.output <- matrix(data = rnorm(n = 16), nrow = 4) -#' # must be run from within a function -#' Tool(object = pbmc_small) <- sample.tool.output -#' } "Tool<-.Seurat" <- function(object, ..., value) { CheckDots(...) object <- UpdateSlots(object = object) @@ -1928,14 +2656,32 @@ Tool.Seurat <- function(object, slot = NULL, ...) { #' VariableFeatures.Seurat <- function( object, - selection.method = NULL, + method = NULL, assay = NULL, + nfeatures = NULL, + layer = NA, + simplify = TRUE, + selection.method = deprecated(), ... ) { CheckDots(...) - object <- UpdateSlots(object = object) + if (is_present(arg = selection.method)) { + .Deprecate( + when = '5.0.0', + what = 'VariableFeatures(selection.method = )', + with = 'VariableFeatures(method = )' + ) + method <- selection.method + } assay <- assay %||% DefaultAssay(object = object) - return(VariableFeatures(object = object[[assay]], selection.method = selection.method)) + return(VariableFeatures( + object = object[[assay]], + method = method, + nfeatures = nfeatures, + layer = layer, + simplify = simplify, + ... + )) } #' @rdname VariableFeatures @@ -2011,7 +2757,7 @@ WhichCells.Seurat <- function( if (!missing(x = expression)) { objects.use <- FilterObjects( object = object, - classes.keep = c('Assay', 'DimReduc', 'SpatialImage') + classes.keep = c('Assay', 'StdAssay', 'DimReduc', 'SpatialImage') ) object.keys <- sapply( X = objects.use, @@ -2057,17 +2803,20 @@ WhichCells.Seurat <- function( cell.order <- colnames(x = object) cells <- colnames(x = object)[!colnames(x = object) %in% cells] } - cells <- CellsByIdentities(object = object, cells = cells, ...) - cells <- lapply( - X = cells, - FUN = function(x) { - if (length(x = x) > downsample) { - x <- sample(x = x, size = downsample, replace = FALSE) - } - return(x) - } - ) - cells <- as.character(x = na.omit(object = unlist(x = cells, use.names = FALSE))) + # only perform downsampling when "downsample" is smaller than the number of cells + if(downsample <= length(cells)){ + cells <- CellsByIdentities(object = object, cells = cells, ...) + cells <- lapply( + X = cells, + FUN = function(x) { + if (length(x = x) > downsample) { + x <- sample(x = x, size = downsample, replace = FALSE) + } + return(x) + } + ) + cells <- as.character(x = na.omit(object = unlist(x = cells, use.names = FALSE))) + } cells <- cells[na.omit(object = match(x = cell.order, table = cells))] return(cells) } @@ -2085,42 +2834,25 @@ Version.Seurat <- function(object, ...) { # Methods for R-defined generics #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -#' Seurat Methods -#' -#' Methods for \code{\link{Seurat}} objects for generics defined in other -#' packages -#' -#' @param x,object A \code{\link{Seurat}} object -#' @param i,features Depends on the method -#' \describe{ -#' \item{\code{[}, \code{subset}}{Feature names or indices} -#' \item{\code{$}, \code{$<-}}{Name of a single metadata column} -#' \item{\code{[[}, \code{[[<-}}{ -#' Name of one or more metadata columns or an associated object; associated -#' objects include \code{\link{Assay}}, \code{\link{DimReduc}}, -#' \code{\link{Graph}}, \code{\link{SeuratCommand}}, or -#' \code{\link{SpatialImage}} objects -#' } -#' } -#' @param j,cells Cell names or indices -#' @param n The number of rows of metadata to return -#' @param ... Arguments passed to other methods -#' -#' @name Seurat-methods -#' @rdname Seurat-methods -#' -#' @concept seurat +#' Dollar-sign Autocompletion #' -NULL - -#' @describeIn Seurat-methods Autocompletion for \code{$} access on a -#' \code{Seurat} object +#' Autocompletion for \code{$} access on a \code{\link{Seurat}} object #' #' @inheritParams utils::.DollarNames +#' @param x A \code{\link{Seurat}} object +#' +#' @return The meta data matches for \code{pattern} #' #' @importFrom utils .DollarNames -#' @export +#' +#' @keywords internal +#' #' @method .DollarNames Seurat +#' @export +#' +#' @concept seurat +#' +#' @inherit .DollarNames.Assay5 seealso #' ".DollarNames.Seurat" <- function(x, pattern = '') { meta.data <- as.list(x = colnames(x = x[[]])) @@ -2128,30 +2860,40 @@ NULL return(.DollarNames(x = meta.data, pattern = pattern)) } -#' @describeIn Seurat-methods Metadata access for \code{Seurat} objects +#' Cell-Level Meta Data +#' +#' Get and set cell-level meta data #' -#' @return \code{$}: metadata column \code{i} for object \code{x}; +#' @inheritParams .DollarNames.Seurat +#' @param i Name of cell-level meta data +#' @param j Ignored +#' @template param-dots-ignored +#' +#' @return {$}: Metadata column \code{i} for object \code{x}; #' \strong{note}: unlike \code{[[}, \code{$} drops the shape of the metadata #' to return a vector instead of a data frame #' -#' @export #' @method $ Seurat +#' @export +#' +#' @family seurat #' #' @examples #' # Get metadata using `$' #' head(pbmc_small$groups) #' -"$.Seurat" <- function(x, i, ...) { +"$.Seurat" <- function(x, i) { return(x[[i, drop = TRUE]]) } -#' @describeIn Seurat-methods Metadata setter for \code{Seurat} objects +#' @param value A vector to add as cell-level meta data #' -#' @return \code{$<-}: object \code{x} with metadata \code{value} saved as -#' \code{i} +#' @return \code{$<-}: \code{x} with metadata \code{value} saved as \code{i} +#' +#' @rdname cash-.Seurat #' -#' @export #' @method $<- Seurat +#' @export #' #' @examples #' # Add metadata using the `$' operator @@ -2164,15 +2906,17 @@ NULL return(x) } -#' @describeIn Seurat-methods Simple subsetter for \code{Seurat} objects -#' #' @return \code{[}: object \code{x} with features \code{i} and cells \code{j} #' -#' @export +#' @rdname subset.Seurat +#' #' @method [ Seurat +#' @export +#' +#' @order 2 #' #' @examples -#' # `[' examples +#' # `[` examples #' pbmc_small[VariableFeatures(object = pbmc_small), ] #' pbmc_small[, 1:10] #' @@ -2207,16 +2951,37 @@ NULL return(subset.Seurat(x = x, features = i, cells = j, ...)) } -#' @describeIn Seurat-methods Metadata and associated object accessor +#' Subobjects and Cell-Level Meta Data #' -#' @param drop See \code{\link[base]{drop}} +#' The \code{[[} operator pulls either subobjects +#' (eg. \link[=Assay]{v3} or \link[=Assay5]{v5} assays, +#' \link[=DimReduc]{dimensional reduction} information, +#' or \link[=Graph]{nearest-neighbor graphs}) or cell-level +#' meta data from a \code{\link{Seurat}} object #' -#' @return \code{[[}: If \code{i} is missing, the metadata data frame; if -#' \code{i} is a vector of metadata names, a data frame with the requested -#' metadata, otherwise, the requested associated object +#' @inheritParams $.Seurat +#' @param drop See \code{\link[base]{drop}} +#' @param na.rm Remove cells where meta data is all \code{NA} +#' +#' @return Varies based on the value of \code{i}: +#' \itemize{ +#' \item If \code{i} is missing, a data frame with cell-level meta data +#' \item If \code{i} is a vector with cell-level meta data names, a data frame +#' (or vector of \code{drop = TRUE}) with cell-level meta data requested +#' \item If \code{i} is a one-length character with the +#' \link[=names.Seurat]{name of a subobject}, the +#' subobject specified by \code{i} +#' } #' -#' @export #' @method [[ Seurat +#' @export +#' +#' @family seurat +#' +#' @seealso See \link[=$.Seurat]{here} for adding meta data with \code{[[<-}, +#' \link[=[[<-,Seurat]{here} for adding subobjects with \code{[[<-}, and +#' \link[=[[<-,Seurat,NULL]{here} for removing subobjects and cell-level meta +#' data with \code{[[<-} #' #' @examples #' # Get the cell-level metadata data frame @@ -2226,60 +2991,87 @@ NULL #' head(pbmc_small[[c("letter.idents", "groups")]]) #' head(pbmc_small[["groups", drop = TRUE]]) #' -#' # Get a sub-object (eg. an `Assay' or `DimReduc') +#' # Get a sub-object (eg. an `Assay` or `DimReduc`) #' pbmc_small[["RNA"]] #' pbmc_small[["pca"]] #' -"[[.Seurat" <- function(x, i, ..., drop = FALSE) { - x <- UpdateSlots(object = x) - if (missing(x = i)) { - i <- colnames(x = slot(object = x, name = 'meta.data')) +"[[.Seurat" <- function(x, i = missing_arg(), ..., drop = FALSE, na.rm = FALSE) { + md <- slot(object = x, name = 'meta.data') + if (is_missing(x = i)) { + return(md) + } else if (is.null(x = i)) { + return(NULL) + } else if (!length(x = i)) { + return(data.frame(row.names = row.names(x = md))) } - if (length(x = i) == 0) { - return(data.frame(row.names = colnames(x = x))) - } else if (length(x = i) > 1 || any(i %in% colnames(x = slot(object = x, name = 'meta.data')))) { - if (any(!i %in% colnames(x = slot(object = x, name = 'meta.data')))) { - warning( - "Cannot find the following bits of meta data: ", - paste0( - i[!i %in% colnames(x = slot(object = x, name = 'meta.data'))], - collapse = ', ' - ) - ) - } - i <- i[i %in% colnames(x = slot(object = x, name = 'meta.data'))] - data.return <- slot(object = x, name = 'meta.data')[, i, drop = FALSE, ...] - if (drop) { - data.return <- unlist(x = data.return, use.names = FALSE) - names(x = data.return) <- rep.int(x = colnames(x = x), times = length(x = i)) + # Correct invalid `i` + meta.cols <- names(x = md) + if (is_bare_integerish(x = i)) { + if (all(i > length(x = meta.cols))) { + abort(message = paste( + "Invalid integer indexing:", + "all integers greater than the number of meta columns" + )) } + i <- meta.cols[as.integer(x = i[i <= length(x = meta.cols)])] + } + if (!is.character(x = i)) { + abort(message = "'i' must be a character vector") + } + # Determine if we're pulling cell-level meta data + # or a sub-object + slot.use <- if (length(x = i) == 1L) { + .FindObject(object = x, name = i) } else { - slot.use <- unlist(x = lapply( - X = c('assays', 'reductions', 'graphs', 'neighbors', 'commands', 'images'), - FUN = function(s) { - if (any(i %in% names(x = slot(object = x, name = s)))) { - return(s) - } - return(NULL) + NULL + } + # Pull cell-level meta data + if (is.null(x = slot.use)) { + i <- tryCatch( + expr = arg_match(arg = i, values = meta.cols, multiple = TRUE), + error = function(e) { + #error message that indicates which colnames not found + abort( + message = paste( + paste(sQuote(x = setdiff(x = i, y = meta.cols)), collapse = ', '), + "not found in this Seurat object\n", + e$body + ), + call = rlang::caller_env(n = 4L) + ) } - )) - if (is.null(x = slot.use)) { - stop("Cannot find '", i, "' in this Seurat object", call. = FALSE) + ) + # Pull the cell-level meta data + data.return <- md[, i, drop = FALSE, ...] + # If requested, remove NAs + if (isTRUE(x = na.rm)) { + idx.na <- apply(X = is.na(x = data.return), MARGIN = 1L, FUN = all) + data.return <- data.return[!idx.na, , drop = FALSE] + } else { + idx.na <- rep_len(x = FALSE, length.out = ncol(x = x)) + } + # If requested, coerce to a vector + if (isTRUE(x = drop)) { + data.return <- unlist(x = data.return, use.names = FALSE) + names(x = data.return) <- rep.int( + x = colnames(x = x)[!idx.na], + times = length(x = i) + ) } - data.return <- slot(object = x, name = slot.use)[[i]] + return(data.return) } - return(data.return) + # Pull a sub-object + return(slot(object = x, name = slot.use)[[i]]) } -#' @describeIn Seurat-methods Number of cells and features for the active assay +#' @inherit dim.Assay5 return title description details #' -#' @return \code{dim}: The number of features (\code{nrow}) and cells -#' (\code{ncol}) for the default assay; \strong{note}: while the number of -#' features changes depending on the active assay, the number of cells remains -#' the same across all assays +#' @inheritParams .DollarNames.Seurat #' -#' @export #' @method dim Seurat +#' @export +#' +#' @family seurat #' #' @examples #' # Get the number of features in an object @@ -2289,49 +3081,189 @@ NULL #' ncol(pbmc_small) #' dim.Seurat <- function(x) { - x <- UpdateSlots(object = x) - return(dim(x = x[[DefaultAssay(object = x)]])) + return(c( + nrow(x = x[[DefaultAssay(object = x)]]) %||% 0L, + length(x = colnames(x = x)) %||% 0L + )) } -#' @describeIn Seurat-methods The cell and feature names for the active assay +#' Feature and Cell Names #' -#' @return \code{dimnames}: The feature (row) and cell (column) names; -#' \strong{note}: while the features change depending on the active assay, the -#' cell names remain the same across all assays +#' Get and set feature and cell inames in \code{\link{Seurat}} objects +#' +#' @inheritParams .DollarNames.Seurat +#' @inheritParams dimnames.Assay5 +#' +#' @return \code{dimnames}: A two-length list with the following values: +#' \itemize{ +#' \item A character vector with all features in the +#' \link[=DefaultAssay]{default assay} +#' \item A character vector with all cells in \code{x} +#' } #' -#' @export #' @method dimnames Seurat +#' @export +#' +#' @family seurat +#' @family dimnames #' #' @examples #' # Get the feature names of an object -#' rownames(pbmc_small) +#' head(rownames(pbmc_small)) #' #' # Get the cell names of an object -#' colnames(pbmc_small) +#' head(colnames(pbmc_small)) #' dimnames.Seurat <- function(x) { - x <- UpdateSlots(object = x) - return(dimnames(x = x[[DefaultAssay(object = x)]])) + return(list( + rownames(x = x[[DefaultAssay(object = x)]]), + row.names(x = slot(object = x, name = 'meta.data')) + )) } -#' @rdname Idents -#' @export -#' @method droplevels Seurat +#' @return \code{dimnames<-}: \code{x} with the feature and/or cell +#' names updated to \code{value} #' -droplevels.Seurat <- function(x, ...) { - x <- UpdateSlots(object = x) - slot(object = x, name = 'active.ident') <- droplevels(x = Idents(object = x), ...) - return(x) -} - -#' @describeIn Seurat-methods Get the first rows of cell-level metadata +#' @rdname dimnames.Seurat #' -#' @return \code{head}: The first \code{n} rows of cell-level metadata -#' -#' @importFrom utils head +#' @method dimnames<- Seurat +#' @export #' +#' @examples +#' colnames(pbmc_small)[1] <- "newcell" +#' head(colnames(pbmc_small)) +#' +"dimnames<-.Seurat" <- function(x, value) { + op <- options(Seurat.object.validate = FALSE) + on.exit(expr = options(op), add = TRUE) + # Check the provided dimnames + msg <- "Invalid 'dimnames' given for a Seurat object" + if (!is_bare_list(x = value, n = 2L)) { + abort(message = msg) + } else if (!all(sapply(X = value, FUN = length) == dim(x = x))) { + abort(message = msg) + } + value <- lapply(X = value, FUN = as.character) + onames <- dimnames(x = x) + # Rename cells at the Seurat level + names(x = slot(object = x, name = 'active.ident')) <- + row.names(x = slot(object = x, name = 'meta.data')) <- + value[[2L]] + # Rename features/cells at the Assay level + v3warn <- FALSE + for (assay in Assays(object = x)) { + anames <- dimnames(x = x[[assay]]) + if (inherits(x = x[[assay]], what = 'StdAssay')) { + afeatures <- MatchCells( + new = onames[[1L]], + orig = anames[[1L]], + ordered = TRUE + ) + if (length(x = afeatures)) { + idx <- MatchCells(new = anames[[1L]], orig = onames[[1L]]) + anames[[1L]][idx] <- value[[1L]][afeatures] + } + } else if (isFALSE(x = v3warn) && any(onames[[1L]] != value[[1L]])) { + warning( + "Renaming features in v3/v4 assays is not supported", + call. = FALSE, + immediate. = TRUE + ) + v3warn <- TRUE + } + acells <- MatchCells(new = onames[[2L]], orig = anames[[2L]]) + anames[[2L]] <- value[[2L]][acells] + suppressWarnings(expr = dimnames(x = x[[assay]]) <- anames) + } + # Rename features/cells at the DimReduc level + for (reduc in Reductions(object = x)) { + rnames <- Cells(x = x[[reduc]]) + rcells <- MatchCells(new = onames[[2L]], orig = rnames) + suppressWarnings( + expr = x[[reduc]] <- RenameCells( + object = x[[reduc]], + old.names = rnames, + new.names = value[[2L]][rcells] + ) + ) + if (!is.null(x = Features(x = x[[reduc]]))) { + rfnames <- Features(x = x[[reduc]]) + rfeatures <- MatchCells( + new = onames[[1L]], + orig = rfnames, + ordered = TRUE + ) + if (length(x = rfeatures)) { + suppressWarnings( + expr = x[[reduc]] <- .RenameFeatures( + object = x[[reduc]], + old.names = rfnames, + new.names = value[[1L]][rfeatures] + ) + ) + } + } + } + # TODO: Rename features/cells at the image level + for (img in Images(object = x)) { + inames <- Cells(x = x[[img]]) + icells <- MatchCells(new = onames[[2L]], orig = inames) + suppressWarnings( + # TODO: replace with `x[[img]] <-` + expr = slot(object = x, name = 'images')[[img]] <- RenameCells( + object = x[[img]], + old.names = inames, + new.names = value[[2L]][icells] + ) + ) + # TODO: rename features + } + # Rename cells at the Graph level + for (graph in Graphs(object = x)) { + gnames <- dimnames(x = x[[graph]]) + for (i in seq_along(along.with = gnames)) { + gcells <- MatchCells(new = onames[[2L]], orig = gnames[[i]]) + gnames[[i]] <- value[[2L]][gcells] + } + suppressWarnings(expr = dimnames(x = x[[graph]]) <- gnames) + } + # Rename cells at the Neighbor level + for (nn in Neighbors(object = x)) { + nnames <- Cells(x = x[[nn]]) + ncells <- MatchCells(new = onames[[2L]], orig = nnames) + suppressWarnings( + # TODO: replace with `x[[nn]] <-` + expr = slot(object = x, name = 'neighbors')[[nn]] <- RenameCells( + object = x[[nn]], + old.names = nnames, + new.names = value[[2L]][ncells] + ) + ) + } + # Validate and return + options(op) + validObject(object = x) + return(x) +} + +#' @rdname Idents #' @export +#' @method droplevels Seurat +#' +droplevels.Seurat <- function(x, ...) { + x <- UpdateSlots(object = x) + slot(object = x, name = 'active.ident') <- droplevels(x = Idents(object = x), ...) + return(x) +} + +#' @param n Number of meta data rows to show +#' +#' @return \code{head}: The first \code{n} rows of cell-level metadata +#' +#' @rdname sub-sub-.Seurat +#' #' @method head Seurat +#' @export #' #' @examples #' # Get the first 10 rows of cell-level metadata @@ -2373,18 +3305,25 @@ levels.Seurat <- function(x) { return(x) } -#' @describeIn Seurat-methods Merge two or more \code{Seurat} objects together +#' Merge Seurat Objects #' #' @inheritParams CreateSeuratObject +#' @inheritParams merge.Assay5 +#' @param x A \code{\link{Seurat}} object #' @param y A single \code{Seurat} object or a list of \code{Seurat} objects #' @param add.cell.ids A character vector of \code{length(x = c(x, y))}; #' appends the corresponding values to the start of each objects' cell names #' @param merge.data Merge the data slots instead of just merging the counts #' (which requires renormalization); this is recommended if the same #' normalization approach was applied to all objects -#' @param merge.dr Merge specified DimReducs that are present in all objects; -#' will only merge the embeddings slots for the first \code{N} dimensions that -#' are shared across all objects. +#' @param merge.dr Choose how to handle merging dimensional reductions: +#' \itemize{ +#' \item \dQuote{\code{TRUE}}: merge dimensional reductions with the same name +#' across objects; dimensional reductions with different names are added as-is +#' \item \dQuote{\code{NA}}: keep dimensional reductions from separate objects +#' separate; will append the project name for duplicate reduction names +#' \item \dQuote{\code{FALSE}}: do not add dimensional reductions +#' } #' #' @return \code{merge}: Merged object #' @@ -2403,10 +3342,12 @@ levels.Seurat <- function(x) { #' specified and any cell names are duplicated, cell names will be appended #' with _X, where X is the numeric index of the object in c(x, y). #' -#' @aliases merge MergeSeurat AddSamples -#' -#' @export #' @method merge Seurat +#' @export +#' +#' @family seurat +#' +#' @aliases merge MergeSeurat AddSamples #' #' @examples #' # `merge' examples @@ -2419,112 +3360,179 @@ merge.Seurat <- function( x = NULL, y = NULL, add.cell.ids = NULL, + collapse = FALSE, merge.data = TRUE, - merge.dr = NULL, - project = "SeuratProject", + merge.dr = FALSE, + project = getOption(x = 'Seurat.object.project', default = 'SeuratProject'), ... ) { CheckDots(...) objects <- c(x, y) + projects <- vapply( + X = objects, + FUN = Project, + FUN.VALUE = character(length = 1L) + ) + if (anyDuplicated(x = projects)) { + projects <- as.character(x = seq_along(along.with = objects)) + } + # Check cell names + if (is_na(x = add.cell.ids)) { + add.cell.ids <- as.character(x = seq_along(along.with = objects)) + } else if (isTRUE(x = add.cell.ids)) { + add.cell.ids <- projects + } if (!is.null(x = add.cell.ids)) { if (length(x = add.cell.ids) != length(x = objects)) { - stop("Please provide a cell identifier for each object provided to merge") + abort( + message = "Please provide a cell identifier for each object provided to merge" + ) } + # for (i in seq_along(along.with = add.cell.ids)) { + # colnames(x = objects[[i]]) <- paste( + # colnames(x = objects[[i]]), + # add.cell.ids[[i]], + # sep = '_' + # ) + # } for (i in 1:length(x = objects)) { objects[[i]] <- RenameCells(object = objects[[i]], add.cell.id = add.cell.ids[i]) } } - # ensure unique cell names objects <- CheckDuplicateCellNames(object.list = objects) - assays <- lapply( - X = objects, - FUN = FilterObjects, - classes.keep = 'Assay' + # Merge assays + assays <- Reduce(f = union, x = lapply(X = objects, FUN = Assays)) + assay.classes <- sapply( + X = assays, + FUN = function(a) { + cls <- vector(mode = 'character', length = length(x = objects)) + for (i in seq_along(along.with = cls)) { + cls[i] <- if (a %in% Assays(object = objects[[i]])) { + class(x = objects[[i]][[a]])[1L] + } else { + NA_character_ + } + } + return(unique(x = cls[!is.na(x = cls)])) + }, + simplify = FALSE, + USE.NAMES = TRUE ) - fake.feature <- RandomName(length = 17) - assays <- unique(x = unlist(x = assays, use.names = FALSE)) - combined.assays <- vector(mode = 'list', length = length(x = assays)) - names(x = combined.assays) <- assays + # TODO: Handle merging v3 and v5 assays + # if (any(sapply(X = assay.classes, FUN = length) != 1L)) { + # stop("Cannot merge assays of different classes") + # } + assays.all <- vector(mode = 'list', length = length(x = assays)) + names(x = assays.all) <- assays for (assay in assays) { - assays.merge <- lapply( - X = objects, - FUN = function(object) { - return(tryCatch( - expr = object[[assay]], - error = function(e) { - return(CreateAssayObject(counts = Matrix( - data = 0, - ncol = ncol(x = object), - dimnames = list(fake.feature, colnames(x = object)), - sparse = TRUE - ))) - } - )) - } - ) - merged.assay <- merge( - x = assays.merge[[1]], - y = assays.merge[2:length(x = assays.merge)], + assay.objs <- which(x = vapply( + X = lapply(X = objects, FUN = names), + FUN = '%in%', + FUN.VALUE = logical(length = 1L), + x = assay + )) + if (length(x = assay.objs) == 1L) { + assays.all[[assay]] <- objects[[assay.objs]][[assay]] + next + } + idx.x <- assay.objs[[1L]] + idx.y <- setdiff(x = assay.objs, y = idx.x) + assays.all[[assay]] <- merge( + x = objects[[idx.x]][[assay]], + y = lapply(X = objects[idx.y], FUN = '[[', assay), + labels = projects, + add.cell.ids = NULL, + collapse = collapse, merge.data = merge.data ) - merged.assay <- subset( - x = merged.assay, - features = rownames(x = merged.assay)[rownames(x = merged.assay) != fake.feature] - ) - if (length(x = Key(object = merged.assay)) == 0) { - Key(object = merged.assay) <- paste0(assay, '_') - } - combined.assays[[assay]] <- merged.assay - } - # Merge the meta.data - combined.meta.data <- data.frame(row.names = colnames(x = combined.assays[[1]])) - new.idents <- c() - for (object in objects) { - old.meta.data <- object[[]] - if (any(!colnames(x = old.meta.data) %in% colnames(x = combined.meta.data))) { - cols.to.add <- colnames(x = old.meta.data)[!colnames(x = old.meta.data) %in% colnames(x = combined.meta.data)] - combined.meta.data[, cols.to.add] <- NA - } - # unfactorize any factor columns - i <- sapply(X = old.meta.data, FUN = is.factor) - old.meta.data[i] <- lapply(X = old.meta.data[i], FUN = as.vector) - combined.meta.data[rownames(x = old.meta.data), colnames(x = old.meta.data)] <- old.meta.data - new.idents <- c(new.idents, as.vector(Idents(object = object))) - } - names(x = new.idents) <- rownames(x = combined.meta.data) - new.idents <- factor(x = new.idents) - if (DefaultAssay(object = x) %in% assays) { - new.default.assay <- DefaultAssay(object = x) - } else if (DefaultAssay(object = y) %in% assays) { - new.default.assay <- DefaultAssay(object = y) - } else { - new.default.assay <- assays[1] } - # Merge images - combined.images <- vector( - mode = 'list', - length = length(x = unlist(x = lapply(X = objects, FUN = Images))) + names(objects) <- NULL + all.cells <- Reduce(f = union, x = lapply(X = objects, FUN = colnames)) + idents.all <- unlist(x = lapply(X = objects, FUN = Idents)) + idents.all <- idents.all[all.cells] + md.all <- EmptyDF(n = length(x = all.cells)) + row.names(x = md.all) <- all.cells + obj.combined <- new( + Class = 'Seurat', + assays = assays.all, + reductions = list(), + images = list(), + meta.data = md.all, + active.assay = DefaultAssay(object = x), + active.ident = idents.all, + project.name = project ) - index <- 1L - for (i in 1:length(x = objects)) { - object <- objects[[i]] - for (image in Images(object = object)) { - image.obj <- object[[image]] - if (image %in% names(x = combined.images)) { - image <- if (is.null(x = add.cell.ids)) { - make.unique(names = c( - na.omit(object = names(x = combined.images)), - image - ))[index] + # Merge cell-level meta data, images + for (i in seq_along(along.with = objects)) { + df <- data.frame( + lapply(objects[[i]][[]], FUN = function(x) { + if (is.factor(x)) as.character(x) else x + }), stringsAsFactors=FALSE + ) + rownames(df) <- rownames(objects[[i]][[]]) + obj.combined[[]] <- df + for (img in Images(object = objects[[i]])) { + dest <- ifelse( + test = img %in% Images(object = obj.combined), + yes = paste(img, projects[i], sep = '.'), + no = img + ) + obj.combined[[dest]] <- objects[[i]][[img]] + } + } + # Merge dimensional reductions + reducs.combined <- list() + if (is.character(x = merge.dr)) { + warn(message = "'merge.Seurat' no longer supports filtering dimensional reductions; merging all dimensional reductions") + merge.dr <- TRUE + } + if (isTRUE(x = merge.dr)) { + for (i in seq_along(along.with = objects)) { + for (reduc in Reductions(object = objects[[i]])) { + reducs.combined[[reduc]] <- if (reduc %in% names(x = reducs.combined)) { + inform(message = paste("Merging reduction", sQuote(x = reduc))) + merge(x = reducs.combined[[reduc]], y = objects[[i]][[reduc]]) } else { - paste(image, add.cell.ids[i], sep = '_') + objects[[i]][[reduc]] + } + } + } + } else if (is_na(x = merge.dr)) { + reducs.all <- unlist( + x = lapply(X = objects, FUN = Reductions), + use.names = FALSE + ) + reducs.dup <- unique(x = reducs.all[duplicated(x = reducs.all)]) + for (i in seq_along(along.with = objects)) { + for (reduc in Reductions(object = objects[[i]])) { + rname <- ifelse( + test = reduc %in% reducs.dup, + yes = paste(reduc, projects[i], sep = '.'), + no = reduc + ) + reducs.combined[[rname]] <- objects[[i]][[reduc]] + if (rname != reduc) { + inform(message = paste( + "Changing", + reduc, + "in object", + projects[i], + "to", + rname + )) + new.key <- Key(object = rname, quiet = TRUE) + inform(message = paste("Updating key to", new.key)) + Key(object = reducs.combined[[rname]]) <- new.key } } - combined.images[[index]] <- image.obj - names(x = combined.images)[index] <- image - index <- index + 1L } } + for (reduc in names(x = reducs.combined)) { + obj.combined[[reduc]] <- reducs.combined[[reduc]] + } + # Validate and return + validObject(object = obj.combined) + return(obj.combined) # Merge DimReducs combined.reductions <- list() if (!is.null(x = merge.dr)) { @@ -2546,58 +3554,93 @@ merge.Seurat <- function( } } } - # Create merged Seurat object - merged.object <- new( - Class = 'Seurat', - assays = combined.assays, - reductions = combined.reductions, - images = combined.images, - meta.data = combined.meta.data, - active.assay = new.default.assay, - active.ident = new.idents, - project.name = project, - version = packageVersion(pkg = 'SeuratObject') - ) - return(merged.object) } -#' @describeIn Seurat-methods Common associated objects +#' Subobject Names #' -#' @return \code{names}: The names of all \code{\link{Assay}}, -#' \code{\link{DimReduc}}, \code{\link{Graph}}, and \code{\link{SpatialImage}} -#' objects in the \code{Seurat} object +#' Get the names of subobjects within a \code{\link{Seurat}} object +#' +#' @inheritParams .DollarNames.Seurat +#' +#' @return The names of all of the following subobjects within \code{x}: +#' \itemize{ +#' \item \link[=Assay]{v3} and \link[=Assay5]{v5} assays +#' \item \link[=DimReduc]{dimensional reductions} +#' \item \link[=SpatialImage]{images} and \link[=FOV]{FOVs} +#' \item \link[=Graph]{nearest-neighbor graphs} +#' } #' -#' @export #' @method names Seurat +#' @export +#' +#' @family seurat #' #' @examples #' names(pbmc_small) #' names.Seurat <- function(x) { - return(FilterObjects( + return(.FilterObjects( object = x, - classes.keep = c('Assay', 'DimReduc', 'Graph', 'SpatialImage') + classes.keep = c('Assay', 'StdAssay', 'DimReduc', 'Graph', 'SpatialImage') )) + +} + +#' @inherit split.Assay5 params return title description details sections +#' +#' @keywords internal +#' @method split Seurat +#' @export +#' +#' @family Seurat +#' +split.Seurat <- function( + x, + f, + drop = FALSE, + assay = NULL, + layers = NA, + ... +){ + assay <- assay %||% DefaultAssay(x) + x[[assay]] <- split( + x = x[[assay]], + f = f, + drop = drop, + layers = layers, + ret = 'assay', + ... + ) + return(x) } -#' @describeIn Seurat-methods Subset a \code{\link{Seurat}} object +#' Subset \code{Seurat} Objects #' +#' @inheritParams .DollarNames.Seurat #' @inheritParams CellsByIdentities #' @param subset Logical expression indicating features/variables to keep +#' @param cells,j A vector of cell names or indices to keep +#' @param features,i A vector of feature names or indices to keep #' @param idents A vector of identity classes to keep +#' @param ... Arguments passed to \code{\link{WhichCells}} #' #' @return \code{subset}: A subsetted \code{Seurat} object #' #' @importFrom rlang enquo -# -#' @aliases subset -#' @seealso \code{\link[base]{subset}} \code{\link{WhichCells}} #' #' @export #' @method subset Seurat #' +#' @family seurat +# +#' @seealso \code{\link{WhichCells}} +#' +#' @aliases subset +#' +#' @order 1 +#' #' @examples -#' # `subset' examples +#' # `subset` examples #' subset(pbmc_small, subset = MS4A1 > 4) #' subset(pbmc_small, subset = `DLGAP1-AS1` > 2) #' subset(pbmc_small, idents = '0', invert = TRUE) @@ -2613,7 +3656,7 @@ subset.Seurat <- function( return.null = FALSE, ... ) { - x <- UpdateSlots(object = x) + # var.features <- VariableFeatures(object = x) if (!missing(x = subset)) { subset <- enquo(arg = subset) } @@ -2629,62 +3672,106 @@ subset.Seurat <- function( if (isTRUE(x = return.null)) { return(NULL) } - stop("No cells found", call. = FALSE) + abort(message = "No cells found") } - if (all(cells %in% Cells(x = x)) && length(x = cells) == length(x = Cells(x = x)) && is.null(x = features)) { + if (all(cells %in% Cells(x = x)) && + length(x = cells) == length(x = colnames(x = x)) && + is.null(x = features) + ) { return(x) } - if (!all(colnames(x = x) %in% cells)) { - slot(object = x, name = 'graphs') <- list() + op <- options(Seurat.object.validate = FALSE, Seurat.object.assay.calcn = FALSE) + on.exit(expr = options(op), add = TRUE) + # Remove metadata for cells not present + orig.cells <- colnames(x = x) + cells <- intersect(x = orig.cells, y = cells) + slot(object = x, name = 'meta.data') <- x[[]][cells, , drop = FALSE] + if (!all(orig.cells %in% cells)) { + # Remove neighbors slot(object = x, name = 'neighbors') <- list() + # Filter Graphs + for (g in names(slot(object = x, name = 'graphs'))) { + cells.g <- intersect(colnames(x[[g]]), cells) + suppressWarnings( + expr = x[[g]] <- as.Graph(x = x[[g]][cells.g, cells.g, drop = FALSE]) + ) + } } - assays <- FilterObjects(object = x, classes.keep = 'Assay') + Idents(object = x, drop = TRUE) <- Idents(object = x)[cells] # Filter Assay objects - for (assay in assays) { - assay.features <- features %||% rownames(x = x[[assay]]) - slot(object = x, name = 'assays')[[assay]] <- tryCatch( - # because subset is also an argument, we need to explictly use the base::subset function - expr = base::subset(x = x[[assay]], cells = cells, features = assay.features), - error = function(e) { - if (e$message == "Cannot find features provided") { - return(NULL) - } else { - stop(e) - } + for (assay in Assays(object = x)) { + if (length(x = intersect(colnames(x = x[[assay]]), cells)) == 0) { + message(assay, " assay doesn't leave any cells, so it is removed") + if (DefaultAssay(x) == assay) { + stop('No cells left in the default assay, please change the default assay') } - ) + slot(object = x, name = 'assays')[[assay]] <- NULL + } else { + assay.features <- features %||% rownames(x = x[[assay]]) + suppressWarnings( + expr = slot(object = x, name = 'assays')[[assay]] <- tryCatch( + # because subset is also an argument, we need to explictly use the base::subset function + expr = suppressWarnings( + expr = base::subset( + x = x[[assay]], + cells = cells, + features = assay.features + ), + classes = 'validationWarning' + ), + error = function(e) { + if (e$message == "Cannot find features provided") { + return(NULL) + } else { + stop(e) + } + } + ) + ) + } } slot(object = x, name = 'assays') <- Filter( f = Negate(f = is.null), x = slot(object = x, name = 'assays') ) - if (length(x = FilterObjects(object = x, classes.keep = 'Assay')) == 0 || is.null(x = x[[DefaultAssay(object = x)]])) { - stop("Under current subsetting parameters, the default assay will be removed. Please adjust subsetting parameters or change default assay.", call. = FALSE) + if (length(x = FilterObjects(object = x, classes.keep = c('Assay', 'StdAssay'))) == 0 || is.null(x = x[[DefaultAssay(object = x)]])) { + abort(message = "Under current subsetting parameters, the default assay will be removed. Please adjust subsetting parameters or change default assay") } # Filter DimReduc objects for (dimreduc in FilterObjects(object = x, classes.keep = 'DimReduc')) { - x[[dimreduc]] <- tryCatch( - expr = subset.DimReduc(x = x[[dimreduc]], cells = cells, features = features), - error = function(e) { - if (e$message %in% c("Cannot find cell provided", "Cannot find features provided")) { - return(NULL) - } else { - stop(e) + suppressWarnings( + x[[dimreduc]] <- tryCatch( + expr = subset.DimReduc(x = x[[dimreduc]], cells = cells, features = features), + error = function(e) { + if (e$message %in% c("Cannot find cell provided", "Cannot find features provided")) { + return(NULL) + } else { + stop(e) + } } - } + ) ) } - # Remove metadata for cells not present - slot(object = x, name = 'meta.data') <- slot(object = x, name = 'meta.data')[cells, , drop = FALSE] # Recalculate nCount and nFeature - for (assay in FilterObjects(object = x, classes.keep = 'Assay')) { - n.calc <- CalcN(object = x[[assay]]) - if (!is.null(x = n.calc)) { - names(x = n.calc) <- paste(names(x = n.calc), assay, sep = '_') - x[[names(x = n.calc)]] <- n.calc + if (!is.null(features)) { + for (assay in FilterObjects(object = x, classes.keep = 'Assay')) { + n.calc <- CalcN(object = x[[assay]]) + if (!is.null(x = n.calc)) { + names(x = n.calc) <- paste(names(x = n.calc), assay, sep = '_') + suppressWarnings( + expr = x[[names(x = n.calc)]] <- n.calc, + classes = 'validationWarning' + ) + } } } - Idents(object = x, drop = TRUE) <- Idents(object = x)[cells] + # # set variable features + # if (!is.null(var.features)) { + # suppressWarnings( + # expr = VariableFeatures(object = x) <- var.features, + # classes = 'validationWarning' + # ) + # } # subset images for (image in Images(object = x)) { x[[image]] <- base::subset(x = x[[image]], cells = cells) @@ -2692,14 +3779,12 @@ subset.Seurat <- function( return(x) } -#' @describeIn Seurat-methods Get the last rows of cell-level metadata -#' #' @return \code{tail}: The last \code{n} rows of cell-level metadata #' -#' @importFrom utils tail +#' @rdname sub-sub-.Seurat #' -#' @export #' @method tail Seurat +#' @export #' #' @examples #' # Get the last 10 rows of cell-level metadata @@ -2707,24 +3792,77 @@ subset.Seurat <- function( #' tail.Seurat <- .tail +#' @method upgrade seurat +#' @export +#' +upgrade.seurat <- function(object, ...) { + # Run update + message("Updating from v2.X to v3.X") + seurat.version <- packageVersion(pkg = "SeuratObject") + new.assay <- UpdateAssay(old.assay = object, assay = "RNA") + assay.list <- list(RNA = new.assay) + for (i in names(x = object@assay)) { + assay.list[[i]] <- UpdateAssay(old.assay = object@assay[[i]], assay = i) + } + new.dr <- UpdateDimReduction(old.dr = object@dr, assay = "RNA") + object <- new( + Class = "Seurat", + version = seurat.version, + assays = assay.list, + active.assay = "RNA", + project.name = object@project.name, + misc = object@misc %||% list(), + active.ident = object@ident, + reductions = new.dr, + meta.data = object@meta.data, + tools = list() + ) + # Run CalcN + for (assay in Assays(object = object)) { + n.calc <- CalcN(object = object[[assay]]) + if (!is.null(x = n.calc)) { + names(x = n.calc) <- paste(names(x = n.calc), assay, sep = '_') + object[[names(x = n.calc)]] <- n.calc + } + for (i in c('nGene', 'nUMI')) { + if (i %in% colnames(x = object[[]])) { + object[[i]] <- NULL + } + } + } +} + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # S4 methods #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -#' @describeIn Seurat-methods Add cell-level metadata or associated objects +#' Original double-bracket assign #' -#' @param value Additional metadata or associated objects to add; \strong{note}: -#' can pass \code{NULL} to remove metadata or an associated object +#' This function has been replaced with value-specific double-bracket +#' assign methods and should generally not be called #' -#' @return \code{[[<-}: \code{x} with the metadata or associated objects added -#' as \code{i}; if \code{value} is \code{NULL}, removes metadata or associated -#' object \code{i} from object \code{x} +#' @param x A \code{\link{Seurat}} object +#' @param i The name to store a subobject or various cell-level meta data as +#' @param value New subobject or cell-level meta data #' -#' @export +#' @return \code{x} with \code{value} stored as \code{i} +#' +#' @name old-assign +#' @rdname old-assign +#' +#' @keywords internal +#' +#' @seealso See \link[=$.Seurat]{here} for adding metadata with \code{[[<-}, and +#' \link[=[[<-,Seurat,NULL]{here} for removing subobjects and cell-level meta +#' data with \code{[[<-} +#' +NULL + +#' @rdname old-assign #' setMethod( # because R doesn't allow S3-style [[<- for S4 classes f = '[[<-', - signature = c('x' = 'Seurat'), + signature = c('x' = 'Seurat', i = 'character', value = 'ANY'), definition = function(x, i, ..., value) { x <- UpdateSlots(object = x) # Require names, no index setting @@ -2898,7 +4036,7 @@ setMethod( # because R doesn't allow S3-style [[<- for S4 classes } else { # Add other object to Seurat object # Ensure cells match in value and order - if (!inherits(x = value, what = c('SeuratCommand', 'NULL', 'SpatialImage', 'Neighbor')) && !all(Cells(x = value) == Cells(x = x))) { + if (!inherits(x = value, what = c('SeuratCommand', 'NULL', 'SpatialImage', 'Neighbor')) && !all(Cells(x = value) == colnames(x = x))) { stop("All cells in the object being added must match the cells in this object", call. = FALSE) } # Ensure we're not duplicating object names @@ -3020,83 +4158,1147 @@ setMethod( # because R doesn't allow S3-style [[<- for S4 classes } ) -#' @describeIn Seurat-methods Calculate \code{\link[base]{colMeans}} on a -#' \code{Seurat} object +#' Add Subobjects #' -#' @param slot Name of assay expression matrix to calculate column/row -#' means/sums on -#' @inheritParams Matrix::colMeans +#' Add subobjects containing expression, dimensional reduction, or other +#' containerized data to a \code{\link{Seurat}} object. Subobjects can be +#' accessed with \code{\link[=[[.Seurat]{[[}} and manipulated directly within +#' the \code{Seurat} object or used independently #' -#' @importFrom Matrix colMeans +#' @inheritParams .DollarNames.Seurat +#' @inheritParams [[.Assay5 +#' @param i Name to add subobject as +#' @param value A valid subobject (eg. a \link[=Assay]{v3} or \link[=Assay5]{v5} +#' assay, or a \link[=DimReduc]{dimensional reduction}) #' -#' @export +#' @return \code{x} with \code{value} added as \code{i} #' -#' @examples -#' head(colMeans(pbmc_small)) +#' @name [[<-,Seurat +#' @rdname sub-subset-Seurat #' -setMethod( - f = 'colMeans', - signature = c('x' = 'Seurat'), - definition = function(x, na.rm = FALSE, dims = 1, ..., slot = 'data') { - return(colMeans( - x = GetAssayData(object = x, slot = slot), - na.rm = na.rm, - dims = dims, - ... - )) - } -) - -#' @describeIn Seurat-methods Calculate \code{\link[base]{colSums}} on a -#' \code{Seurat} object +#' @family seurat #' -#' @importFrom Matrix colSums +#' @seealso See \link[=[[.Seurat]{here} for pulling subobjects using \code{[[}, +#' \link[=$.Seurat]{here} for adding metadata with \code{[[<-}, and +#' \link[=[[<-,Seurat,NULL]{here} for removing subobjects and cell-level meta +#' data with \code{[[<-} #' -#' @export +#' @aliases [[<-.Seurat \S4method{[[<-}{Seurat,character,missing,Assay} #' -#' @examples -#' head(colSums(pbmc_small)) +NULL + +#' @rdname sub-subset-Seurat #' setMethod( - f = 'colSums', - signature = c('x' = 'Seurat'), - definition = function(x, na.rm = FALSE, dims = 1, ..., slot = 'data') { - return(Matrix::colSums( - x = GetAssayData(object = x, slot = slot), - na.rm = na.rm, - dims = dims, - ... - )) + f = '[[<-', + signature = c( + x = 'Seurat', + i = 'character', + j = 'missing', + value = 'Assay' + ), + definition = function(x, i, ..., value) { + if (.GetSeuratCompat() < '5.0.0') { + return(callNextMethod(x = x, i = i, value = value)) + } + validObject(object = value) + i <- make.names(names = i) + # Checks for if the assay or name already exists + if (i %in% names(x = x)) { + if (!inherits(x = x[[i]], what = c('Assay', 'StdAssay'))) { + abort( + message = paste( + sQuote(i), + "already exists as an object of class", + class(x = x[[i]])[1L] + ), + class = 'duplicateError' + ) + } + if (!identical(x = class(x = value), y = class(x = x[[i]]))) { + warning( + "Assay ", + i, + " changing from ", + class(x = x[[i]]), + " to ", + class(x = value), + call. = FALSE, + immediate. = TRUE + ) + } + if (!all(dim(x = value) == dim(x = x[[i]]))) { + warn( + message = paste0("Different cells and/or features from existing assay ", i), + class = 'dimWarning' + ) + } + } + # Check for cells + if (!all(colnames(x = value) %in% colnames(x = x))) { + abort(message = "Cannot add new cells with [[<-") + } + cell.order <- MatchCells( + new = colnames(x = value), + orig = colnames(x = x), + ordered = TRUE + ) + # TODO: enable reordering cells in assay + if (is.unsorted(x = cell.order)) { + if (inherits(x = value, what = 'Assay')) { + for (s in c('counts', 'data', 'scale.data')) { + if (!IsMatrixEmpty(x = slot(object = value, name = s))) { + slot(object = value, name = s) <- slot(object = value, name = s)[, cell.order] + } + } + } else { + abort(message = "Cannot add assays with unordered cells") + } + validObject(object = value) + } + # Check keys + Key(object = value) <- .CheckKey( + key = Key(object = value), + existing = Key(object = x), + name = i + ) + # Run CalcN + do.calcn <- Misc(object = value, slot = 'calcN') %||% FALSE + suppressWarnings(Misc(object = value, slot = 'calcN') <- NULL) + if (isTRUE(x = do.calcn)) { + n.calc <- suppressWarnings( + expr = .CalcN(object = value, layer = 'counts', simplify = TRUE), + classes = 'missingLayerWarning' + ) + if (!is.null(x = n.calc)) { + names(x = n.calc) <- paste(names(x = n.calc), i, sep = '_') + x[[]] <- n.calc + } + } + # Add the assay + slot(object = x, name = 'assays')[[i]] <- value + slot(object = x, name = 'assays') <- Filter( + f = Negate(f = is.null), + x = slot(object = x, name = 'assays') + ) + # Validate and return + validObject(object = x) + return(x) } ) -#' @describeIn Seurat-methods Calculate \code{\link[base]{rowMeans}} on a -#' \code{rowMeans} object -#' -#' @importFrom Matrix colSums -#' -#' @export -#' -#' @examples -#' head(rowMeans(pbmc_small)) +#' @rdname sub-subset-Seurat #' setMethod( - f = 'rowMeans', - signature = c('x' = 'Seurat'), - definition = function(x, na.rm = FALSE, dims = 1, ..., slot = 'data') { - return(Matrix::rowMeans( - x = GetAssayData(object = x, slot = slot), - na.rm = na.rm, - dims = dims, - ... - )) + f = '[[<-', + signature = c( + x = 'Seurat', + i = 'character', + j = 'missing', + value = 'Assay5' + ), + definition = function(x, i, ..., value) { + return(callNextMethod(x = x, i = i, ..., value = value)) } ) -#' @describeIn Seurat-methods Calculate \code{\link[base]{rowSums}} on a -#' \code{Seurat} object +#' @rdname cash-.Seurat #' -#' @importFrom Matrix rowSums +setMethod( + f = '[[<-', + signature = c( + x = 'Seurat', + i = 'character', + j = 'missing', + value = 'data.frame' + ), + definition = function(x, i, ..., value) { + # Because R is stupid sometimes + if (!length(x = i) && !ncol(x = value)) { + return(x) + } + # Check the names provided + if (length(x = i) == ncol(x = value)) { + # Add the names to the meta data + if (is.null(x = names(x = value))) { + names(x = value) <- i + } + if (ncol(x = value) == 1) { + v <- value[,1] + names(x = v) <- rownames(x = value) + x[[i]] <- v + return(x) + } + idx <- match(x = i, table = names(x = value)) + # If there are any mismatches in `i` and `names(value)` + # rename `value` to match `i` + # if (all(is.na(x = idx))) { + # warn(message = paste( + # "None of the column names are found in meta data names;", + # "replacing to provided meta data names" + # )) + # } + if (any(is.na(x = idx))) { + meta.missing <- setdiff( + x = seq_len(length.out = ncol(x = value)), + y = idx[!is.na(x = idx)] + ) + names(x = meta.missing) <- i[is.na(x = idx)] + # for (j in seq_along(along.with = meta.missing)) { + # warn(message = paste( + # "Column", + # sQuote(x = names(x = value)[meta.missing[j]]), + # "not found in meta data names, changing to", + # sQuote(x = names(x = meta.missing)[j]) + # )) + # } + names(x = value)[meta.missing] <- names(x = meta.missing) + } + } else if (is.null(x = names(x = value))) { + # Cannot add meta data without names + abort(message = paste( + "Cannot assign", + length(x = i), + ifelse(test = length(x = i) == 1L, yes = 'name', no = 'names'), + "to", + ncol(x = value), + ifelse(test = ncol(x = value) == 1L, yes = 'bit', no = 'bits'), + "of meta data" + )) + } else { + # Find matching `i` in `names(value)` + # Cannot rename as `length(i) != ncol(value)` + i.orig <- i + i <- intersect(x = i, y = names(x = value)) + # If no matching, abort + if (!length(x = i)) { + abort( + message = "None of the meta data requested was found in the data frame" + ) + } + # Alert user to `i` not found in `names(value)` + i.missing <- setdiff(x = i.orig, y = i) + if (length(x = i.missing)) { + warn(message = paste( + "The following bits of meta data in the data frame will not be added:", + paste(sQuote(x = i.missing), collapse = ', ') + )) + } + } + # Handle meta data for different cells + names.intersect <- intersect(x = row.names(x = value), y = colnames(x = x)) + if (length(x = names.intersect)) { + value <- value[names.intersect, , drop = FALSE] + if (!nrow(x = value)) { + abort(message = "None of the cells provided are in this Seurat object") + } + } else if (nrow(x = value) == ncol(x = x)) { + # When no cell names are provided in value, assume it's in cell order + row.names(x = value) <- colnames(x = x) + } else { + # Throw an error when no cell names provided and cannot assume cell order + abort( + message = "Cannot add more or less meta data without cell names" + ) + } + # Add the cell-level meta data using the `value = vector` method + for (n in i) { + v <- value[[n]] + names(x = v) <- row.names(x = value) + x[[n]] <- v + } + return(x) + } +) + +#' @rdname cash-.Seurat +#' +setMethod( + f = '[[<-', + signature = c( + x = 'Seurat', + i = 'missing', + j = 'missing', + value = 'data.frame' + ), + definition = function(x, i, ..., value) { + # Allow removing all meta data + if (IsMatrixEmpty(x = value)) { + x[[names(x = x[[]])]] <- NULL + } else { + # If no `i` provided, use the column names from value + x[[names(x = value)]] <- value + } + return(x) + } +) + +#' @rdname sub-subset-Seurat +#' +setMethod( + f = '[[<-', + signature = c( + x = 'Seurat', + i = 'character', + j = 'missing', + value = 'DimReduc' + ), + definition = function(x, i, ..., value) { + validObject(object = value) + i <- make.names(names = i) + # Checks for if the DimReduc or name already exists + if (i %in% .Subobjects(object = x)) { + if (!inherits(x = x[[i]], what = 'DimReduc')) { + abort( + message = paste( + sQuote(i), + "already exists as an object of class", + class(x = x[[i]])[1L] + ), + class = 'duplicateError' + ) + } + if (!identical(x = class(x = value), y = class(x = x[[i]]))) { + warning( + "DimReduc ", + i, + " changing from ", + class(x = x[[i]]), + " to ", + class(x = value), + call. = FALSE, + immediate. = TRUE + ) + } + if (length(x = value) != length(x = x[[i]])) { + warning( + "Number of dimensions changing from ", + length(x = x[[i]]), + " to ", + length(x = value), + call. = FALSE, + immediate. = TRUE + ) + } + if (length(x = Cells(x = value)) != length(x = Cells(x = x[[i]]))) { + warning( + "Number of cells changing from ", + length(x = Cells(x = x[[i]])), + " to ", + length(x = Cells(x = value)), + call. = FALSE, + immediate. = TRUE + ) + } + } + # Check default assay + if (is.null(x = DefaultAssay(object = value))) { + stop("Cannot add a DimReduc without an associated assay", call. = FALSE) + } else if (!any(DefaultAssay(object = value) %in% Assays(object = x))) { + warning( + "Adding a dimensional reduction (", + i, + ") without the associated assay being present", + call. = FALSE, + immediate. = TRUE + ) + } + # Check for cells + if (!all(Cells(x = value) %in% colnames(x = x))) { + stop("Cannot add new cells with [[<-", call. = FALSE) + } + cell.order <- MatchCells( + new = Cells(x = value), + orig = colnames(x = x), + ordered = TRUE + ) + # TODO: enable reordering cells in DimReducs + if (is.unsorted(x = cell.order)) { + ordered.cells <- intersect(colnames(x = x), Cells(x = value)) + slot(object = value, name = 'cell.embeddings') <- Embeddings(object = value)[ordered.cells,] + } + # Check keys + Key(object = value) <- .CheckKey( + key = Key(object = value), + existing = Key(object = x), + name = i + ) + # Check loadings and embeddings column name + emb.names <- paste0(sapply( + X = strsplit( + x = colnames(Embeddings(object = value)), + split = '_'), + FUN = '[', + 1)[1], + '_') + if (emb.names != Key(object = value)){ + colnames( + slot(object = value, name = 'cell.embeddings') + ) <- gsub(pattern = emb.names, + replacement = Key(object = value), + colnames(Embeddings(object = value)) + ) + } + if (!is.null(colnames(Loadings(object = value)))) { + loadings.names <- paste0(sapply( + X = strsplit( + x = colnames(Loadings(object = value)), + split = '_'), + FUN = '[', + 1)[1], + '_') + if (loadings.names != Key(object = value)) { + colnames( + slot(object = value, name = 'feature.loadings') + ) <- gsub(pattern = loadings.names, + replacement = Key(object = value), + colnames(Loadings(object = value)) + ) + } + } + + slot(object = x, name = 'reductions')[[i]] <- value + slot(object = x, name = 'reductions') <- Filter( + f = Negate(f = is.null), + x = slot(object = x, name = 'reductions') + ) + # check column names + + # Validate and return + validObject(object = x) + return(x) + } +) + +#' @rdname cash-.Seurat +#' +#' @importFrom methods selectMethod +#' +setMethod( + f = '[[<-', + signature = c(x = 'Seurat', i = 'character', j = 'missing', value = 'factor'), + definition = function(x, i, ..., value) { + # Add multiple objects + if (length(x = i) > 1L) { + value <- rep_len(x = value, length.out = length(x = i)) + for (idx in seq_along(along.with = i)) { + x[[i[idx]]] <- value[[idx]] + } + return(x) + } + objs <- .FilterObjects( + object = x, + classes.keep = c( + 'Assay', + 'StdAssay', + 'DimReduc', + 'Graph', + 'Neighbor', + 'SeuratCommand', + 'SpatialImage' + ) + ) + if (i %in% objs) { + cls <- class(x = x[[i]])[1L] + abort(message = paste( + sQuote(x = i, q = FALSE), + "already exists as", + ifelse( + test = tolower(x = substr(x = cls, start = 1, stop = 1)) %in% .Vowels(), + yes = 'an', + no = 'a' + ), + class(x = x[[i]])[1L] + )) + } + # fast way to add column + if (length(x = value) == ncol(x = x) && all(names(x = value) == colnames(x = x))) { + slot(object = x, name = 'meta.data')[,i] <- value + return(x) + } + # Add a column of cell-level meta data + if (is.null(x = names(x = value))) { + # Handle cases where new meta data is unnamed + value <- rep_len(x = value, length.out = ncol(x = x)) + names(x = value) <- colnames(x = x) + } else { + # Check cell names for new objects + names.intersect <- intersect(x = names(x = value), y = colnames(x = x)) + if (!length(x = names.intersect)) { + stop( + "No cell overlap between new meta data and Seurat object", + call. = FALSE + ) + } + value <- value[names.intersect] + } + df <- EmptyDF(n = ncol(x = x)) + row.names(x = df) <- colnames(x = x) + df[[i]] <- factor(x = NA, levels = levels(x = value)) + # df[[i]] <- if (i %in% names(x = x[[]])) { + # x[[i, na.rm = FALSE]] + # } else { + # factor(x = NA, levels = levels(x = value)) + # } + df[names(x = value), i] <- value + slot(object = x, name = 'meta.data')[, i] <- df[[i]] + validObject(object = x) + return(x) + } +) + +#' @rdname sub-subset-Seurat +#' +setMethod( + f = '[[<-', + signature = c(x = 'Seurat', i = 'character', j = 'missing', value = 'Graph'), + definition = function(x, i, ..., value) { + validObject(object = value) + i <- make.names(names = i) + # Checks for if the Graph or name already exists + if (i %in% names(x = x)) { + if (!inherits(x = x[[i]], what = 'Graph')) { + abort( + message = paste( + sQuote(i), + "already exists as an object of class", + class(x = x[[i]])[1L] + ), + class = 'duplicateError' + ) + } + if (!identical(x = class(x = value), y = class(x = x[[i]]))) { + warning( + "Graph ", + i, + " changing from ", + class(x = x[[i]]), + " to ", + class(x = value), + call. = FALSE, + immediate. = TRUE + ) + } + if (!all(dim(x = value) == dim(x = x[[i]]))) { + warning( + "Different cells from existing graph ", i, + call. = FALSE, + immediate. = TRUE + ) + } + } + # Check cells + gcells <- Cells(x = value, margin = NA_integer_) + if (!all(gcells %in% colnames(x = x))) { + stop("Cannot add cells with [[<-", call. = FALSE) + } + cell.order <- MatchCells( + new = gcells, + orig = colnames(x = x), + ordered = TRUE + ) + # TODO: enable reordering cells in graph + if (is.unsorted(x = cell.order)) { + stop("Cannot add graphs with unordered cells", call. = FALSE) + validObject(object = value) + } + # Add the graph + slot(object = x, name = 'graphs')[[i]] <- value + slot(object = x, name = 'graphs') <- Filter( + f = Negate(f = is.null), + x = slot(object = x, name = 'graphs') + ) + # Validate and return + validObject(object = x) + return(x) + } +) + +#' @rdname cash-.Seurat +#' +setMethod( + f = '[[<-', + signature = c(x = 'Seurat', i = 'character', j = 'missing', value = 'list'), + definition = function(x, i, ..., value) { + # Because R is stupid sometimes + if (!length(x = i) && !length(x = value)) { + return(x) + } + # Check that the `i` we're adding are present in the list + if (!is.null(x = names(x = value))) { + i <- arg_match(arg = i, values = names(x = value), multiple = TRUE) + } else if (length(x = i) != length(x = value)) { + abort(message = paste( + "Cannot assing", + length(x = i), + "names to", + length(x = value), + "bits of meta data" + )) + } else { + names(x = value) <- i + } + # Add the meta data + for (n in i) { + x[[n]] <- value[[n]] + } + return(x) + } +) + +#' @rdname cash-.Seurat +#' +setMethod( + f = '[[<-', + signature = c(x = 'Seurat', i = 'missing', j = 'missing', value = 'list'), + definition = function(x, i, ..., value) { + stopifnot(IsNamedList(x = value)) + for (y in names(x = value)) { + x[[y]] <- value[[y]] + } + return(x) + } +) + +#' @rdname sub-subset-Seurat +#' +setMethod( + f = '[[<-', + signature = c( + x = 'Seurat', + i = 'character', + j = 'missing', + value = 'Neighbor' + ), + definition = function(x, i, ..., value) { + validObject(object = value) + i <- make.names(names = i) + # Checks for if the Neighbor or name already exists + if (i %in% .Subobjects(object = x)) { + if (!inherits(x = x[[i]], what = 'Neighbor')) { + abort( + message = paste( + sQuote(i), + "already exists as an object of class", + class(x = x[[i]])[1L] + ), + class = 'duplicateError' + ) + } + if (!identical(x = class(x = value), y = class(x = x[[i]]))) { + warn(message = paste( + "Graph", + i, + "changing from", + class(x = x[[i]])[1L], + "to", + class(x = value)[1L] + )) + } + if (length(x = Cells(x = value)) != length(x = Cells(x = x[[i]]))) { + warn(message = paste( + "Number of cells changing from", + length(x = Cells(x = x[[i]])), + "to", + length(x = Cells(x = value)) + )) + } + } + # Check for cells + if (!all(Cells(x = value) %in% colnames(x = x))) { + abort(message = "Cannot add new cells with [[<-") + } + cell.order <- MatchCells( + new = Cells(x = value), + orig = colnames(x = x), + ordered = TRUE + ) + # TODO: enable reordering cells in Neighbors + if (is.unsorted(x = cell.order)) { + abort(message = "Cannot add Neighbors with unordered cells") + validObject(object = value) + } + slot(object = x, name = 'neighbors')[[i]] <- value + slot(object = x, name = 'neighbors') <- Filter( + f = Negate(f = is.null), + x = slot(object = x, name = 'neighbors') + ) + # Validate and return + validObject(object = x) + return(x) + } +) + +#' Remove Subobjects and Cell-Level Meta Data +#' +#' @inheritParams [[<-,Seurat +#' @param i Name(s) of subobject(s) or cell-level meta data to remove +#' @param value NULL +#' +#' @return \code{x} with \code{i} removed from the object +#' +#' @name [[<-,Seurat,NULL +#' @rdname sub-subset-Seurat-NULL +#' +#' @family seurat +#' +#' @seealso See \link[=[[.Seurat]{here} for pulling subobjects using \code{[[}, +#' \link[=$.Seurat]{here} for adding metadata with \code{[[<-}, and +#' \link[=[[<-,Seurat]{here} for adding subobjects with \code{[[<-} +#' +#' @aliases remove-object remove-objects \S4method{[[<-}{Seurat,character,missing,NULL} +#' +NULL + +#' @rdname sub-subset-Seurat-NULL +#' +setMethod( + f = '[[<-', + signature = c(x = 'Seurat', i = 'character', j = 'missing', value = 'NULL'), + definition = function(x, i, ..., value) { + # Allow removing multiple objects or bits of cell-level meta data at once + for (name in i) { + # Determine the slot to use + # If no subobject found, check cell-level meta data + slot.use <- .FindObject(object = x, name = name) %||% 'meta.data' + switch( + EXPR = slot.use, + 'meta.data' = { + # If we can't find the cell-level meta data, throw a warning and move + # to the next name + if (!name %in% names(x = x[[]])) { + warn(message = paste( + "Cannot find cell-level meta data named ", + name + )) + next + } + # Remove the column of meta data + slot(object = x, name = 'meta.data')[, name] <- value + }, + 'assays' = { + # Cannot remove the default assay + if (isTRUE(x = name == DefaultAssay(object = x))) { + stop("Cannot delete default assay", call. = FALSE) + } + # Remove the assay + slot(object = x, name = slot.use)[[i]] <- value + }, + # Remove other subobjects + slot(object = x, name = slot.use)[[name]] <- value + ) + } + # Validate and return + validObject(object = x) + return(x) + } +) + +#' @rdname sub-subset-Seurat +#' +setMethod( + f = '[[<-', + signature = c( + x = 'Seurat', + i = 'character', + j = 'missing', + value = 'SeuratCommand' + ), + definition = function(x, i, ..., value) { + validObject(object = value) + i <- make.names(names = i) + # Checks for if the SeuratCommand or name already exists + if (i %in% .Subobjects(object = x)) { + if (!inherits(x = x[[i]], what = 'SeuratCommand')) { + abort( + message = paste( + sQuote(i), + "already exists as an object of class", + class(x = x[[i]])[1L] + ), + class = 'duplicateError' + ) + } + if (!identical(x = class(x = value), y = class(x = x[[i]]))) { + warn(message = paste( + "Command", + i, + "changing from", + class(x = x[[i]])[1L], + "to", + class(x = value)[1L] + )) + } + } + if (is.null(x = DefaultAssay(object = value))) { + warn(message = "Adding a command log without an assay associated with it") + } + # Ensure the command gets put at the end of the list + # slot(object = x, name = 'commands')[[i]] <- NULL + suppressWarnings(expr = x[[i]] <- NULL) + slot(object = x, name = 'commands')[[i]] <- value + slot(object = x, name = 'commands') <- Filter( + f = Negate(f = is.null), + x = slot(object = x, name = 'commands') + ) + # Validate and return + validObject(object = x) + return(x) + } +) + +#' @rdname sub-subset-Seurat +#' +setMethod( + f = '[[<-', + signature = c( + x = 'Seurat', + i = 'character', + j = 'missing', + value = 'SpatialImage' + ), + definition = function(x, i, ..., value) { + validObject(object = value) + i <- make.names(names = i) + # Checks for if the image or name already exists + if (i %in% .Subobjects(object = x)) { + if (!inherits(x = x[[i]], what = 'SpatialImage')) { + abort( + message = paste( + sQuote(i), + "already exists as an object of class", + class(x = x[[i]])[1L] + ), + class = 'duplicateError' + ) + } + if (!identical(x = class(x = value), y = class(x = x[[i]]))) { + warn(message = paste( + "Image", + i, + "changing from", + class(x = x[[i]])[1L], + "to", + class(x = value)[1L] + )) + } + } + # Check cells + if (!all(Cells(x = value) %in% colnames(x = x))) { + abort(message = "Cannot add new cells with [[<-") + } + cell.order <- MatchCells( + new = Cells(x = value), + orig = colnames(x = x), + ordered = TRUE + ) + if (is.unsorted(x = cell.order)) { + warn(message = "Adding image with unordered cells") + } + # Check assay + if (!DefaultAssay(object = value) %in% Assays(object = x)) { + warn(message = "Adding image data that isn't associated with any assays") + } + # Check keys + Key(object = value) <- .CheckKey( + key = Key(object = value), + existing = Key(object = x), + name = i + ) + slot(object = x, name = 'images')[[i]] <- value + slot(object = x, name = 'images') <- Filter( + f = Negate(f = is.null), + x = slot(object = x, name = 'images') + ) + # Validate and return + validObject(object = x) + return(x) + } +) + +#' @inherit [[<-,Seurat +#' +#' @keywords internal +#' +setMethod( + f = '[[<-', + signature = c( + x = 'Seurat', + i = 'character', + j = 'missing', + value = 'StdAssay' + ), + definition = function(x, i, ..., value) { + # Reuse the `value = Assay` method + fn <- slot( + object = selectMethod( + f = '[[<-', + signature = c( + x = 'Seurat', + i = 'character', + j = 'missing', + value = 'Assay' + ) + ), + name = '.Data' + ) + cell.order <- MatchCells( + new = colnames(x = value), + orig = colnames(x = x), + ordered = TRUE + ) + if (is.unsorted(cell.order)) { + value.order <- new( + Class = 'Assay5', + layers = list(), + default = 0L, + features = value@features, + cells = LogMap(colnames(value)[cell.order]), + meta.data = value@meta.data, + misc = value@misc + ) + for (l in Layers(object = value)) { + LayerData(object = value.order, layer = l) <- + LayerData(object = value, layer = l) + } + value <- value.order + } + return(fn(x = x, i = i, value = value)) + } +) + +#' @rdname cash-.Seurat +#' +setMethod( + f = '[[<-', + signature = c( + x = 'Seurat', + i = 'character', + j = 'missing', + value = 'vector' + ), + definition = function(x, i, ..., value) { + # Add multiple objects + if (length(x = i) > 1L) { + value <- rep_len(x = value, length.out = length(x = i)) + for (idx in seq_along(along.with = i)) { + x[[i[idx]]] <- value[[idx]] + } + return(x) + } + objs <- .FilterObjects( + object = x, + classes.keep = c( + 'Assay', + 'StdAssay', + 'DimReduc', + 'Graph', + 'Neighbor', + 'SeuratCommand', + 'SpatialImage' + ) + ) + if (i %in% objs) { + cls <- class(x = x[[i]])[1L] + abort(message = paste( + sQuote(x = i, q = FALSE), + "already exists as", + ifelse( + test = tolower(x = substr(x = cls, start = 1, stop = 1)) %in% .Vowels(), + yes = 'an', + no = 'a' + ), + class(x = x[[i]])[1L] + )) + } + # fast way to add column + if (length(x = value) == ncol(x = x) && all(names(x = value) == colnames(x = x))) { + slot(object = x, name = 'meta.data')[,i] <- value + return(x) + } + # Add a column of cell-level meta data + if (is.null(x = names(x = value))) { + # Handle cases where new meta data is unnamed + value <- rep_len(x = value, length.out = ncol(x = x)) + names(x = value) <- colnames(x = x) + } else { + # Check cell names for new objects + names.intersect <- intersect(x = names(x = value), y = colnames(x = x)) + if (!length(x = names.intersect)) { + stop( + "No cell overlap between new meta data and Seurat object", + call. = FALSE + ) + } + value <- value[names.intersect] + } + df <- EmptyDF(n = ncol(x = x)) + row.names(x = df) <- colnames(x = x) + df[[i]] <- if (i %in% names(x = x[[]])) { + if (is.character(x = value)) { + as.character(x = x[[i, drop = TRUE, na.rm = FALSE]]) + } else { + as.vector(x = x[[i, drop = TRUE, na.rm = FALSE]]) + } + } else { + NA + } + df[names(x = value), i] <- value + slot(object = x, name = 'meta.data')[, i] <- df[[i]] + validObject(object = x) + return(x) + } +) + +#' Row and Column Sums and Means +#' +#' Calculate \code{\link{rowSums}}, \code{\link{colSums}}, +#' \code{\link{rowMeans}}, and \code{\link{colMeans}} on +#' \code{\link{Seurat}} objects +#' +#' @inheritParams .DollarNames.Seurat +#' @inheritParams Matrix::colMeans +#' @param slot Name of assay expression matrix to calculate column/row +#' means/sums on +#' +#' @return \code{colMeans}: the column (cell-wise) means of \code{slot} +#' +#' @importFrom Matrix colMeans +#' +#' @keywords internal +#' +#' @export +#' +#' @concept seurat +#' +#' @seealso \code{\link{Seurat}} +#' +#' @examples +#' head(colMeans(pbmc_small)) +#' +setMethod( + f = 'colMeans', + signature = c('x' = 'Seurat'), + definition = function(x, na.rm = FALSE, dims = 1, ..., slot = 'data') { + return(colMeans( + x = LayerData(object = x, layer = slot), + na.rm = na.rm, + dims = dims, + ... + )) + } +) + +#' @return \code{colSums}: the column (cell-wise) sums of \code{slot} +#' +#' @rdname colMeans-Seurat-method +#' +#' @importFrom Matrix colSums +#' +#' @export +#' +#' @examples +#' head(colSums(pbmc_small)) +#' +setMethod( + f = 'colSums', + signature = c('x' = 'Seurat'), + definition = function(x, na.rm = FALSE, dims = 1, ..., slot = 'data') { + return(Matrix::colSums( + x = LayerData(object = x, layer = slot), + na.rm = na.rm, + dims = dims, + ... + )) + } +) + +#' @importFrom methods initialize +#' +setMethod( + f = 'initialize', + signature = 'Seurat', + definition = function( + .Object, + assays = list(), + meta.data = NULL, + active.assay = character(length = 0L), + active.ident = NULL, + graphs = list(), + neighbors = list(), + reductions = list(), + images = list(), + project.name = getOption( + x = 'Seurat.object.project', + default = Seurat.options$Seurat.object.project + ), + misc = list(), + version = packageVersion(pkg = 'SeuratObject'), + commands = list(), + tools = list(), + ... + ) { + # Initialize the object + .Object <- callNextMethod(.Object, ...) + # Set defaults for meta data and idents + cells <- Reduce(f = union, x = lapply(X = assays, FUN = Cells)) + if (is.null(x = meta.data)) { + meta.data <- EmptyDF(n = length(x = cells)) + row.names(x = meta.data) <- cells + } + if (is.null(x = active.ident)) { + active.ident <- factor(x = cells) + } + # Add slots + slot(object = .Object, name = 'assays') <- assays + slot(object = .Object, name = 'meta.data') <- meta.data + slot(object = .Object, name = 'active.assay') <- active.assay + slot(object = .Object, name = 'active.ident') <- active.ident + slot(object = .Object, name = 'graphs') <- graphs + slot(object = .Object, name = 'neighbors') <- neighbors + slot(object = .Object, name = 'reductions') <- reductions + slot(object = .Object, name = 'images') <- images + slot(object = .Object, name = 'project.name') <- project.name + slot(object = .Object, name = 'misc') <- misc + slot(object = .Object, name = 'version') <- version + slot(object = .Object, name = 'commands') <- commands + slot(object = .Object, name = 'tools') <- tools + # Validate the object + validObject(object = .Object) + # Return + return(.Object) + } +) + +#' @return \code{rowMeans}: the row (feature-wise) means of \code{slot} +#' +#' @rdname colMeans-Seurat-method +#' +#' @importFrom Matrix colSums +#' +#' @export +#' +#' @examples +#' head(rowMeans(pbmc_small)) +#' +setMethod( + f = 'rowMeans', + signature = c('x' = 'Seurat'), + definition = function(x, na.rm = FALSE, dims = 1, ..., slot = 'data') { + return(Matrix::rowMeans( + x = LayerData(object = x, layer = slot), + na.rm = na.rm, + dims = dims, + ... + )) + } +) + +#' @return \code{rowSums}: the row (feature-wise) sums of \code{slot} +#' +#' @rdname colMeans-Seurat-method +#' +#' @importFrom Matrix rowSums #' #' @export #' @@ -3108,7 +5310,7 @@ setMethod( signature = c('x' = 'Seurat'), definition = function(x, na.rm = FALSE, dims = 1, ..., slot = 'data') { return(Matrix::rowSums( - x = GetAssayData(object = x, slot = slot), + x = LayerData(object = x, layer = slot), na.rm = na.rm, dims = dims, ... @@ -3116,27 +5318,35 @@ setMethod( } ) -#' @describeIn Seurat-methods Overview of a \code{Seurat} object +#' Seurat Object Overview #' -#' @return \code{show}: Prints summary to \code{\link[base]{stdout}} and -#' invisibly returns \code{NULL} +#' Overview of a \code{\link{Seurat}} object #' -#' @importFrom methods show +#' @template return-show #' -#' @export +#' @keywords internal +#' +#' @concept seurat +#' +#' @examples +#' pbmc_small #' setMethod( f = "show", signature = "Seurat", definition = function(object) { - object <- UpdateSlots(object = object) - assays <- FilterObjects(object = object, classes.keep = 'Assay') + #object <- UpdateSlots(object = object) + x <- tryCatch( + expr = slot(object = object, name = 'images'), + error = function(...) {stop("Please run UpdateSeuratObject on your object", call. = FALSE)}) + + assays <- .FilterObjects(object = object, classes.keep = c('Assay', 'StdAssay')) nfeatures <- sum(vapply( X = assays, FUN = function(x) { return(nrow(x = object[[x]])) }, - FUN.VALUE = integer(length = 1L) + FUN.VALUE = numeric(length = 1L) )) num.assays <- length(x = assays) cat("An object of class", class(x = object), "\n") @@ -3152,7 +5362,24 @@ setMethod( cat( "Active assay:", DefaultAssay(object = object), - paste0('(', nrow(x = object), ' features, ', length(x = VariableFeatures(object = object)), ' variable features)') + paste0( + '(', + nrow(x = object), + ' features, ', + length(x = suppressWarnings(expr = VariableFeatures(object = object))), + ' variable features)' + ) + ) + cat( + '\n', + length(x = Layers(object = object)), + ifelse( + test = length(x = Layers(object = object)) == 1L, + yes = 'layer', + no = 'layers' + ), + 'present:', + strwrap(x = paste(Layers(object = object), collapse = ', ')) ) other.assays <- assays[assays != DefaultAssay(object = object)] if (length(x = other.assays) > 0) { @@ -3165,7 +5392,7 @@ setMethod( strwrap(x = paste(other.assays, collapse = ', ')) ) } - reductions <- FilterObjects(object = object, classes.keep = 'DimReduc') + reductions <- .FilterObjects(object = object, classes.keep = 'DimReduc') if (length(x = reductions) > 0) { cat( '\n', @@ -3176,7 +5403,7 @@ setMethod( strwrap(x = paste(reductions, collapse = ', ')) ) } - fovs <- FilterObjects(object = object, classes.keep = 'FOV') + fovs <- .FilterObjects(object = object, classes.keep = 'FOV') if (length(x = fovs)) { cat( '\n', @@ -3187,7 +5414,7 @@ setMethod( strwrap(x = paste(fovs, sep = ', ')) ) } - images <- FilterObjects(object = object, classes.keep = 'SpatialImage') + images <- .FilterObjects(object = object, classes.keep = 'SpatialImage') images <- setdiff(x = images, y = fovs) if (length(x = images)) { cat( @@ -3202,11 +5429,19 @@ setMethod( } ) -#' @rdname oldseurat-class +#' Old Seurat Object Overview +#' +#' Overview of a \code{\link[=oldseurat]{seurat}} object overview +#' +#' @param object An old seurat object +#' +#' @template return-show +#' +#' @rdname show-oldseurat-method #' -#' @inheritParams Seurat-methods +#' @keywords internal #' -#' @importFrom methods show +#' @concept oldseurat #' setMethod( f = 'show', @@ -3222,10 +5457,213 @@ setMethod( } ) +#' Seurat Object Validity +#' +#' @templateVar cls Seurat +#' @template desc-validity +#' +#' @name Seurat-validity +#' +#' @family seurat +#' +#' @seealso \code{\link[methods]{validObject}} +#' +setValidity( + Class = 'Seurat', + method = function(object) { + if (.GetSeuratCompat() < '5.0.0') { + return(TRUE) + } + if (isFALSE(x = getOption(x = "Seurat.object.validate", default = TRUE))) { + warn( + message = paste("Not validating", class(x = object)[1L], "objects"), + class = 'validationWarning' + ) + return(TRUE) + } + valid <- NULL + # TODO: Check meta data + md <- slot(object = object, name = 'meta.data') + # if (length(x = class(x = md)) != 1L || class(x = md) != 'data.frame') { + if (!.IsDataFrame(x = md)) { + valid <- c(valid, "'meta.data' must be a base-R data.frame") + } + if (ncol(x = md)) { + if (is.null(x = names(x = md)) || any(!nzchar(x = names(x = md)))) { + valid <- c(valid, "all columns in 'meta.data' must be named") + } + } + # TODO: Check cells + ocells <- colnames(x = object) + if (anyDuplicated(x = ocells)) { + valid <- c(valid, "cell names may not be duplicated") + } + # TODO: Check assays + if (!IsNamedList(x = slot(object = object, name = 'assays'))) { + valid <- c(valid, "'assays' must be a named list") + } else { + for (assay in Assays(object = object)) { + if (!inherits(x = object[[assay]], what = c('Assay', 'StdAssay'))) { + valid <- c(valid, "'assays' must be a list of 'Assay' objects") + break + } + acells <- colnames(x = object[[assay]]) + if (!all(acells %in% ocells)) { + valid <- c(valid, "all cells in assays must be present in the Seurat object") + } else if (is.unsorted(x = MatchCells(new = acells, orig = ocells, ordered = TRUE))) { + valid <- c( + valid, + "all cells in assays must be in the same order as the Seurat object" + ) + } + if (!isTRUE(x = nzchar(x = Key(object = object[[assay]])))) { + valid <- c(valid, "all assays must have a key") + } + } + } + # TODO: Check reductions + if (!IsNamedList(x = slot(object = object, name = 'reductions'), pass.zero = TRUE)) { + valid <- c(valid, "'reductions' must be a named list") + } else { + for (reduc in Reductions(object = object)) { + # Check cells + rcells <- Cells(x = object[[reduc]]) + if (!all(rcells %in% ocells)) { + valid <- c(valid, "All cells in reductions must be present in the Seurat object") + } else if (is.unsorted(x = MatchCells(new = rcells, orig = ocells, ordered = TRUE))) { + valid <- c(valid, "all cells in reductions must be in the same order as the Seurat object") + } + # TODO: Check features + # TODO: Check default assay + } + } + # Check graphs + if (!IsNamedList(x = slot(object = object, name = 'graphs'), pass.zero = TRUE)) { + valid <- c(valid, "'graphs' must be a named list") + } else { + for (graph in Graphs(object = object)) { + gnames <- Cells(x = object[[graph]], margin = NA_integer_) + # if (!DefaultAssay(object = object[[graph]]) %in% Assays(object = object)) { + # valid <- c( + # valid, + # "the default assay for graphs must be present in the Seurat object" + # ) + # } + if (!all(gnames %in% colnames(x = object))) { + valid <- c(valid, "all cells in graphs must be present in the Seurat object") + } else if (is.unsorted(x = MatchCells(new = gnames, orig = ocells, ordered = TRUE))) { + valid <- c( + valid, + paste0( + "all cells in graphs must be in the same order as the Seurat object (offending: ", + graph, + ")" + ) + ) + } + } + } + # Check neighbors + if (!IsNamedList(x = slot(object = object, name = 'neighbors'), pass.zero = TRUE)) { + valid <- c(valid, "'neighbors' must be a named list") + } else { + for (nn in Neighbors(object = object)) { + ncells <- Cells(x = object[[nn]]) + if (!all(ncells %in% ocells)) { + valid <- c(valid, "All cells in neighbor objects must be present in the Seurat object") + } else if (is.unsorted(x = MatchCells(new = ncells, orig = ocells, ordered = TRUE))) { + valid <- c(valid, "All cells in neighbor objects must be in the same order as the Seurat object") + } + } + } + # Check images + if (!IsNamedList(x = slot(object = object, name = 'images'), pass.zero = TRUE)) { + valid <- c(valid, "'images' must be a named list") + } else { + for (img in Images(object = object)) { + icells <- Cells(x = object[[img]]) + if (!all(icells %in% ocells)) { + valid <- c(valid, "All cells in images must be present in the Seurat object") + } + # else if (is.unsorted(x = MatchCells(new = icells, orig = ocells, ordered = TRUE))) { + # valid <- c(valid, "All cells in images must be in the same order as the Seurat object") + # } + } + } + # TODO: Check project + proj <- Project(object = object) + if (length(x = proj) != 1L) { + valid <- c(valid, "'project' must be a 1-length character vector") + } else if (is.na(x = proj)) { + valid <- c(valid, "'project' cannot be NA") + } else if (!nzchar(x = proj)) { + valid <- c(valid, "'project' cannot be an empty character") + } + # TODO: Check idents + idents <- Idents(object = object) + if (length(x = idents) != ncol(x = object)) { + valid <- c( + valid, + "'active.idents' must be as long as the number of cells present" + ) + } else if (!all(names(x = idents) == colnames(x = object))) { + valid <- c(valid, "'active.idents' must be named with cell names") + } + # TODO: Check version + if (length(x = slot(object = object, name = 'version')) > 1) { + valid <- c(valid, "Only one version is allowed") + } + return(valid %||% TRUE) + } +) + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Internal #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +.FilterCells <- function(object, validate = TRUE) { + objs <- .FilterObjects( + object = object, + classes.keep = c( + 'Assay', # assays + 'StdAssay', # assays + 'Graph', # graphs + 'Neighbor', # neighbors + 'DimReduc', # reductions + 'SpatialImage' # images + ) + ) + '' +} + +.SubobjectAssign <- function() { + classes <- slot( + object = methods::findMethods(f = '[[<-', classes = 'Seurat'), + name = 'signatures' + ) + classes <- Filter(f = function(x) x[1] == 'Seurat', x = classes) + classes <- vapply( + X = classes, + FUN = function(x) { + return(x[length(x = x)]) + }, + FUN.VALUE = character(length = 1L) + ) + classes <- unique(x = classes) + classes <- setdiff( + x = classes, + y = c('Seurat', 'ANY', 'NULL', 'vector', 'list', 'StdAssay') + ) + classes <- Filter( + f = function(x) { + cdef <- methods::getClass(Class = x) + return(!'oldClass' %in% names(x = slot(object = cdef, name = 'contains'))) + }, + x = classes + ) + +} + #' Object Collections #' #' Find the names of collections in an object diff --git a/R/sparse.R b/R/sparse.R new file mode 100644 index 00000000..b3922780 --- /dev/null +++ b/R/sparse.R @@ -0,0 +1,172 @@ +#' @include zzz.R +#' @include generics.R +#' @importClassesFrom spam spam +#' @importClassesFrom Matrix CsparseMatrix RsparseMatrix +#' +NULL + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Generics +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#' Identify Sparse Slots +#' +#' @param x A sparse matrix +#' @param type ... +#' +#' @return ... +#' +#' @keywords internal +#' +#' @export +#' +#' @family sparse +#' +.SparseSlots <- function(x, type = c('pointers', 'indices', 'entries')) { + UseMethod(generic = '.SparseSlots', object = x) +} + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Functions +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#' Is a Matrix Sparse +#' +#' @param x A matrix +#' +#' @return ... +#' +#' @keywords internal +#' +#' @export +#' +#' @family sparse +#' +#' @examples +#' IsSparse(matrix()) +#' IsSparse(LayerData(pbmc_small, "counts")) +#' +IsSparse <- function(x) { + if (!isS4(x)) { + return(FALSE) + } + classkey <- unlist(x = strsplit( + x = ClassKey(class = class(x = x)), + split = ':' + )) + cls <- classkey[[2L]] + pkg <- classkey[[1L]] + sparse <- cls %in% sparse.classes[[pkg]] + if (!sparse) { + sparse <- any(sparse.classes[[pkg]] %in% .Contains(object = x)) + } + return(sparse) +} + +#' Register Sparse Matrix Classes +#' +#' @inheritParams ClassKey +#' +#' @return Invisibly returns \code{NULL} +#' +#' @keywords internal +#' +#' @export +#' +#' @family sparse +#' +RegisterSparseMatrix <- function(class, package = NULL) { + classkey <- unlist(x = strsplit( + x = ClassKey(class = class, package = package), + split = ':' + )) + sparse.classes[[classkey[[1L]]]] <- unique(c( + sparse.classes[[classkey[[1L]]]], + classkey[[2L]] + )) + return(invisible(x = NULL)) +} + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Methods for Seurat-defined generics +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#' @method .MARGIN CsparseMatrix +#' @export +#' +.MARGIN.CsparseMatrix <- function(x, ...) { + return(2L) +} + +#' @method .MARGIN RsparseMatrix +#' @export +#' +.MARGIN.RsparseMatrix <- function(x, ...) { + return(1L) +} + +#' @method .MARGIN spam +#' @export +#' +.MARGIN.spam <- .MARGIN.RsparseMatrix + +#' @rdname dot-SparseSlots +#' @method .SparseSlots CsparseMatrix +#' @export +#' +.SparseSlots.CsparseMatrix <- function( + x, + type = c('pointers', 'entries', 'indices') +) { + type <- arg_match(arg = type) + return(switch( + EXPR = type, + 'pointers' = 'p', + 'indices' = 'i', + 'entries' = 'x' + )) +} + +#' @rdname dot-SparseSlots +#' @method .SparseSlots RsparseMatrix +#' @export +#' +.SparseSlots.RsparseMatrix <- function( + x, + type = c('pointers', 'indices', 'entries') +) { + type <- arg_match(arg = type) + return(switch( + EXPR = type, + 'pointers' = 'p', + 'indices' = 'j', + 'entries' = 'x' + )) +} + +#' @rdname dot-SparseSlots +#' @method .SparseSlots spam +#' @export +#' +.SparseSlots.spam <- function(x, type = c('pointers', 'indices', 'entries')) { + check_installed(pkg = 'spam') + type <- arg_match(arg = type) + return(switch( + EXPR = type, + 'pointers' = 'rowpointers', + 'indices' = 'colindices', + 'entries' = 'entries' + )) +} + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Methods for R-defined generics +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Internal +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# S4 Methods +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/R/spatial.R b/R/spatial.R index f93828b6..3baf1333 100644 --- a/R/spatial.R +++ b/R/spatial.R @@ -17,7 +17,7 @@ NULL #' @slot assay Name of assay to associate image data with; will give this image #' priority for visualization when the assay is set as the active/default assay #' in a \code{Seurat} object -#' @slot key Key for the image +#' @template slot-key #' #' @name SpatialImage-class #' @rdname SpatialImage-class @@ -26,7 +26,9 @@ NULL #' @seealso \code{\link{SpatialImage-methods}} for a list of required and #' provided methods #' -SpatialImage <- setClass( +#' @aliases SpatialImage +#' +setClass( Class = 'SpatialImage', contains = 'VIRTUAL', slots = list( diff --git a/R/utils.R b/R/utils.R index d12f8fab..d4b7e691 100644 --- a/R/utils.R +++ b/R/utils.R @@ -2,36 +2,48 @@ #' @include generics.R #' @include centroids.R #' @include segmentation.R +#' @importFrom Rcpp evalCpp #' @importFrom methods as setAs #' NULL +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Generics +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Functions #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#' Set If or If Not \code{NULL} +#' #' Set a default value depending on if an object is \code{NULL} #' #' @param x An object to test #' @param y A default value #' -#' @return For \code{\%||\%}: \code{y} if \code{x} is \code{NULL} otherwise -#' \code{x} +#' @return For \code{\%||\%}: \code{y} if \code{x} is \code{NULL}; +#' otherwise \code{x} #' #' @importFrom rlang %||% #' #' @name set-if-null #' @rdname set-if-null #' +#' @author For \code{\%||\%}: \pkg{rlang} developers +#' +#' @seealso \code{\link[rlang:op-null-default]{rlang::\%||\%}} +#' #' @export #' #' @concept utils #' #' @examples +#' # Set if NULL #' 1 %||% 2 #' NULL %||% 2 #' -rlang::`%||%` +`%||%` <- rlang::`%||%` #' @rdname set-if-null #' @@ -43,6 +55,7 @@ rlang::`%||%` #' @export #' #' @examples +#' # Set if *not* NULL #' 1 %iff% 2 #' NULL %iff% 2 #' @@ -53,7 +66,7 @@ rlang::`%||%` return(x) } -#' Set if \code{NA} +#' Set If or If Not \code{NA} #' #' Set a default value depending on if an object is \code{\link[base]{NA}} #' @@ -67,11 +80,14 @@ rlang::`%||%` #' #' @importFrom rlang is_na #' +#' @keywords internal +#' #' @export #' #' @concept utils #' #' @examples +#' # Set if NA #' 1 %NA% 2 #' NA %NA% 2 #' @@ -98,6 +114,7 @@ rlang::`%||%` #' @export #' #' @examples +#' # Set if *not* NA #' 1 %!NA% 2 #' NA %!NA% 2 #' @@ -114,6 +131,509 @@ rlang::`%||%` #' `%!na%` <- `%!NA%` +#' \pkg{BPCells} Matrix Mode +#' +#' Get the mode (on-disk, in-memory) of an \code{IterableMatrix} object +#' from \pkg{BPCells} +#' +#' @param object An \code{IterableMatrix} +#' @param simplify Return \dQuote{\code{disk}} for on-disk matrices +#' +#' @return One of the following, depending on the mode of \code{object}: +#' \itemize{ +#' \item \dQuote{\code{memory}} +#' \item \dQuote{\code{file}} +#' \item \dQuote{\code{directory}} +#' } +#' If \code{simplify} is \code{TRUE}, returns \dQuote{\code{disk}} instead of +#' \dQuote{\code{file}} or \dQuote{\code{directory}} +#' +#' @keywords internal +#' +#' @export +#' +.BPMatrixMode <- function(object, simplify = FALSE) { + check_installed(pkg = 'BPCells', reason = 'for working with BPCells') + if (!inherits(x = object, what = 'IterableMatrix')) { + return(NULL) + } + stopifnot(rlang::is_bare_logical(x = simplify, n = 1L)) + # Get a vector of all the slots in all sub-matrices + slots <- Reduce( + f = union, + x = lapply( + X = BPCells::all_matrix_inputs(object), + FUN = \(x) methods::slotNames(x = methods::getClass(Class = class(x = x))) + ) + ) + # Figure out if any sub-matrix points to a directory or a file path + type <- c(path = FALSE, dir = FALSE) + for (s in slots) { + if (s %in% names(x = type)) { + type[s] <- TRUE + } + } + # If no matrix points to a directory or file, it's an in-memory one + if (!any(type)) { + return('memory') + } + # If any matrix points to a directory or file, it's an on-disk matrix + if (isTRUE(x = simplify) && any(type)) { + return("disk") + } + # Get the exact type; there should only be one + return(c(path = 'file', dir = 'directory')[[names(x = type)[type]]]) +} + +#' Identify Object Collections +#' +#' Find all collection (named lists) slots in an S4 object +#' +#' @inheritParams .Contains +#' @param exclude A character vector of slot names to exclude +#' @param ... Arguments passed to \code{\link{IsNamedList}} +#' +#' @return A character vector of names of collection slots +#' +#' @importFrom methods slotNames +#' +#' @keywords internal +#' +#' @export +#' +#' @family subobjects +#' @concept utils +#' +#' @examples +#' .Collections(pbmc_small) +#' +.Collections <- function(object, exclude = character(length = 0L), ...) { + if (!isS4(object)) { + abort(message = "'object' is not an S4 object") + } + collections <- slotNames(x = object) + collections <- Filter( + f = function(s) { + return(IsNamedList(x = slot(object = object, name = s), ...)) + }, + x = collections + ) + if (is.character(x = exclude) && length(x = exclude)) { + collections <- setdiff(x = collections, y = exclude) + } + return(collections) +} + +#' Get Parent S4 Classes +#' +#' @param object An \link[methods:Classes_Details]{S4} object +#' +#' @return A vector of class names that \code{object} inherits from +#' +#' @importFrom methods getClass slot +#' +#' @keywords internal +#' +#' @export +#' +#' @concept utils +#' +#' @examples +#' .Contains(pbmc_small) +#' +.Contains <- function(object) { + if (!isS4(object)) { + abort(message = "'object' not an S4 object") + } + return(names(x = slot( + object = getClass(Class = class(x = object)), + name = 'contains' + ))) +} + +#' Find the Default FOV +#' +#' Attempts to find the \dQuote{default} FOV using the revamped +#' spatial framework +#' +#' @param object A \code{{Seurat}} object +#' +#' @return ... +#' +#' @keywords internal +#' +#' @export +#' +#' @concept utils +#' +.DefaultFOV <- function(object, assay = NULL) { + images <- FilterObjects(object = object, classes.keep = 'FOV') + if (!is.null(x = assay)) { + assays <- c(assay, DefaultAssay(object = object[[assay]])) + images <- Filter( + f = function(x) { + return(DefaultAssay(object = object[[x]]) %in% assays) + }, + x = images + ) + } + if (!length(x = images)) { + return(NULL) + } + return(images) +} + +#' Deprecate Functions and Arguments +#' +#' Provides automatic deprecation and defunctation of functions and arguments; +#' +#' @inheritParams lifecycle::deprecate_soft +#' @inheritDotParams lifecycle::deprecate_soft +#' @param pkg Name of package to use for comparison +#' @param env,user_env Managed internally by \code{.Deprecate()} +#' +#' @return Run for its side effect and invisibly returns \code{NULL} +#' +#' @importFrom rlang ns_env_name +#' @importFrom utils packageVersion +#' @importFrom lifecycle deprecate_soft deprecate_stop deprecate_warn +#' +#' @keywords internal +#' +#' @export +#' +#' @seealso \code{\link[lifecycle:deprecate_soft]{lifecycle::deprecate_soft}()} +#' \code{\link[lifecycle:deprecate_warn]{lifecycle::deprecate_warn}()} +#' \code{\link[lifecycle:deprecate_stop]{lifecycle::deprecate_stop}()} +#' +.Deprecate <- function( + when, + what, + with = NULL, + ..., + pkg = NULL, + env = missing_arg(), + user_env = missing_arg() +) { + # Figure out current version, rounding up development versions + caller <- caller_env() + current <- .RoundVersion(current = packageVersion( + pkg = ns_env_name(x = caller) + )) + cv <- paste(current, collapse = '.') + # Ensure our 'when' is a valid version + wv <- when <- as.character(x = numeric_version(x = when, strict = TRUE)) + # If we haven't reached deprecation, exit out silently + if (cv < wv) { + return(invisible(x = NULL)) + } + # Figure out if this is a soft deprecation, a warning deprecation, or a defunct + when <- unlist(x = strsplit(x = when, split = '\\.')) + if (length(x = when) > 4L) { + when[4L] <- paste( + when[seq.int(from = 4L, to = length(x = when))], + collapse = '.' + ) + when <- when[1:4] + } + names(x = when) <- c('major', 'minor', 'patch', 'devel')[seq_along(along.with = when)] + when <- vapply( + X = when, + FUN = as.integer, + FUN.VALUE = integer(length = 1L), + USE.NAMES = TRUE + ) + diffs <- abs(current - when) + if (diffs['major'] >= 1L || diffs['minor'] >= 3L) { + deprecate_stop( + when = wv, + what = what, + with = with, + env = caller, + ... + ) + } + fn <- if (diffs['minor'] >= 1L) { + deprecate_warn + } else { + deprecate_soft + } + fn( + when = wv, + what = what, + with = with, + env = caller, + user_env = caller_env(n = 2L), + ... + ) + return(invisible(x = NULL)) +} + +#' Find Subobjects Of A Certain Class +#' +#' @inheritParams .Collections +#' @param classes.keep A vector of classes to keep +#' +#' @return A vector of object names that are of class \code{classes.keep} +#' +#' @keywords internal +#' +#' @export +#' +#' @family subobjects +#' @concept utils +#' +#' @examples +#' .FilterObjects(pbmc_small) +#' .FilterObjects(pbmc_small, "Graph") +#' +.FilterObjects <- function( + object, + classes.keep = c('Assay', 'StdAssay', 'DimReduc') +) { + collections <- .Collections(object = object, exclude = c('misc', 'tools')) + subobjects <- unlist(x = lapply( + X = collections, + FUN = function(x) { + return(Filter( + f = function(i) { + return(inherits( + x = slot(object = object, name = x)[[i]], + what = classes.keep + )) + }, + x = names(x = slot(object = object, name = x)) + )) + } + )) + if (!length(x = subobjects)) { + subobjects <- NULL + } + return(subobjects) +} + +#' Find A Subobject +#' +#' Determine the slot that a subobject is contained in +#' +#' @inheritParams .Collections +#' @param name Name of subobject to find +#' +#' @return The name of the slot that contains \code{name}; returns \code{NULL} +#' if a subobject named \code{name} cannot be found +#' +#' @keywords internal +#' +#' @export +#' +#' @family subobjects +#' @concept utils +#' +#' @examples +#' .FindObject(pbmc_small, "tsne") +#' +.FindObject <- function(object, name, exclude = c('misc', 'tools')) { + collections <- .Collections(object = object, exclude = exclude) + object.names <- sapply( + X = collections, + FUN = function(x) { + return(names(x = slot(object = object, name = x))) + }, + simplify = FALSE, + USE.NAMES = TRUE + ) + object.names <- Filter(f = Negate(f = is.null), x = object.names) + for (i in names(x = object.names)) { + if (name %in% names(x = slot(object = object, name = i))) { + return(i) + } + } + return(NULL) +} + +#' Get a Method +#' +#' @param fxn Name of a function as a character +#' @param cls The class to find a method of \code{fxn} for +#' +#' @return The method of \code{fxn} for class \code{cls}; if no method found, +#' returns the default method. If no default method found; returns \code{NULL} +#' +#' @importFrom utils getS3method isS3stdGeneric +#' @importFrom methods isClass isGeneric selectMethod +#' +#' @keywords internal +#' +#' @export +#' +#' @concept utils +#' +#' @examples +#' .GetMethod('t', 'Matrix') +#' .GetMethod('t', 'data.frame') +#' +.GetMethod <- function(fxn, cls) { + if (is.function(x = fxn)) { + fxn <- as.character(x = substitute(expr = fxn)) + } + if (!(isS3stdGeneric(f = fxn) || isGeneric(f = fxn))) { + abort(message = paste0("'", fxn, "' is not a generic function")) + } + default <- NULL + if (isGeneric(f = fxn) && isClass(Class = cls[1L])) { + method <- selectMethod(f = fxn, signature = cls) + if (!inherits(x = method, what = 'derivedDefaultMethod')) { + return(slot(object = method, name = '.Data')) + } + default <- slot(object = method, name = '.Data') + } + method <- NULL + for (i in c(cls, 'default')) { + method <- getS3method(f = fxn, class = i, optional = TRUE) + if (!is.null(x = method)) { + break + } + } + method <- method %||% default + if (is.null(x = method)) { + abort(message = paste0( + "Unable to find a method for '", + fxn, + "' for '", + cls[1L], + "' objects" + )) + } + return(method) +} + +#' Propagate a List +#' +#' @param x A list or character vector +#' @param names A vector of names to keep from \code{x} +#' @param default A default value for unassigned values of \code{x} +#' +#' @return A named list where the names are present in both \code{x} and +#' \code{names} and the values are either the values from \code{x} or +#' \code{default} +#' +#' @keywords internal +#' +#' @export +#' +#' @concept utils +#' +#' @examples +#' .PropagateList("counts", c("RNA", "ADT", "SCT")) +#' .PropagateList(c("counts", "data"), c("RNA", "ADT", "SCT")) +#' .PropagateList("ADT", c("RNA", "ADT", "SCT")) +#' .PropagateList(c("RNA", "SCT"), c("RNA", "ADT", "SCT")) +#' .PropagateList(c("RNA", ADT = "counts"), c("RNA", "ADT", "SCT")) +#' .PropagateList(list(SCT = c("counts", "data"), ADT = "counts"), c("RNA", "ADT", "SCT")) +#' .PropagateList(list(SCT = c("counts", "data"), "ADT"), c("RNA", "ADT", "SCT")) +#' +.PropagateList <- function(x, names, default = NA) { + # `names` must be a character vector + if (!is_bare_character(x = names)) { + abort(message = "'names' must be a character vector") + } + # `x` must be a list or character vector + if (!(is_bare_list(x = x) || is_bare_character(x = x))) { + abort(message = "'x' must be either a list or character vector") + } + # `x` cannot be empty + if (!length(x = x)) { + abort(message = "'x' cannot be empty") + } + # `x` is a character vector + if (is_bare_character(x = x)) { + if (!all(nzchar(x = x))) { + abort(message = "'x' cannot be empty") + } + # Handle cases where `x` is unnamed + if (!any(have_name(x = x))) { + # `x` is a vector with values in `names` + # Return a list for every value in `x` that's present in `names` + # with a value of `default` + if (any(x %in% names)) { + x <- intersect(x = x, y = names) + ret <- vector(mode = 'list', length = length(x = x)) + names(x = ret) <- x + for (i in seq_along(along.with = ret)) { + ret[[i]] <- default + } + return(ret) + } + # `x` is a vector of default values + # Return a list for every value in `names` with a value of `x` + ret <- vector(mode = 'list', length = length(x = names)) + names(x = ret) <- names + for (i in seq_along(along.with = ret)) { + ret[[i]] <- unique(x = x) + } + return(ret) + } + # `x` is named + # Turn `x` into a list and continue on + x <- as.list(x = x) + } + # `x` is a list + # Find entries of `x` that correspond to a value in `names` + # Assign new value of `default` + for (i in seq_along(along.with = x)) { + if (is_scalar_character(x = x[[i]]) && x[[i]] %in% names) { + names(x = x)[i] <- x[[i]] + x[[i]] <- default + } + } + # Identify values of `x` in `names` + x.use <- intersect(x = names(x = x), y = names) + if (!length(x = x.use) && is_named(x = x)) { + abort(message = "None of the values of 'x' match with 'names") + } + #`Return only values of `x` that are in `names`` + return(x[x.use]) +} + +#' Get the Subobject Names +#' +#' @inheritParams .Collections +#' @param collapse Collapse the list into a vector +#' +#' @return If \code{collapse = TRUE}, then a vector with the names of all +#' subobjects; otherwise, a named list where the names are the names of the +#' collections and the values are the names of subobjects within the collection +#' +#' @keywords internal +#' +#' @export +#' +#' @family subobjects +#' @keywords utils +#' +#' @examples +#' .Subobjects(pbmc_small) +#' +.Subobjects <- function( + object, + exclude = c('misc', 'tools'), + collapse = TRUE, + ... +) { + subobjects <- sapply( + X = .Collections(object = object, exclude = exclude, ...), + FUN = function(x) { + return(names(x = slot(object = object, name = x))) + }, + simplify = FALSE, + USE.NAMES = TRUE + ) + if (isTRUE(x = collapse)) { + subobjects <- unlist(x = subobjects, use.names = FALSE) + } + return(subobjects) +} + #' Attach Required Packages #' #' Helper function to attach required packages. Detects if a package is already @@ -121,12 +641,18 @@ rlang::`%||%` #' #' @param deps A character vector of packages to attach #' -#' @return Invisibly returns \code{NULL} +#' @template return-null #' #' @export #' #' @concept utils #' +#' @template lifecycle-superseded +#' @section Lifecycle: +#' \code{AttachDeps} has been superseded as of \pkg{SeuratObject} v5.0.0; +#' as an alternative, list dependencies in the \code{Depends} section of +#' \code{DESCRIPTION} +#' #' @examples #' # Use in your .onAttach hook #' if (FALSE) { @@ -145,13 +671,229 @@ AttachDeps <- function(deps) { return(invisible(x = NULL)) } +#' Check the Use of Dots +#' +#' Function to check the use of unused arguments passed to \code{...}; this +#' function is designed to be called from another function to see if an +#' argument passed to \code{...} remains unused and alert the user if so. Also +#' accepts a vector of function or function names to see if \code{...} can be +#' used in a downstream function +#' +#' Behavior of \code{CheckDots} can be controlled by the following option(s): +#' \describe{ +#' \item{\dQuote{\code{Seurat.checkdots}}}{Control how to alert the presence +#' of unused arguments in \code{...}; choose from +#' \itemize{ +#' \item \dQuote{\code{warn}}: emit a warning (default) +#' \item \dQuote{\code{error}}: throw an error +#' \item \dQuote{\code{silent}}: no not alert the presence of unused +#' arguments in \code{...} +#' } +#' } +#' } +#' +#' @param ... Arguments passed to a function that fall under \code{...} +#' @param fxns A list/vector of functions or function names +#' +#' @return Emits either an error or warning if an argument passed is unused; +#' invisibly returns \code{NULL} +#' +#' @importFrom utils isS3stdGeneric methods argsAnywhere isS3method +#' +#' @keywords internal +#' +#' @export +#' +#' @concept utils +#' +#' @examples +#' \dontrun{ +#' f <- function(x, ...) { +#' CheckDots(...) +#' return(x ^ 2) +#' } +#' f(x = 3, y = 9) +#' } +#' +CheckDots <- function(..., fxns = NULL) { + args.names <- names(x = list(...)) + if (length(x = list(...)) == 0) { + return(invisible(x = NULL)) + } + if (is.null(x = args.names)) { + abort(message = "No named arguments passed") + } + if (length(x = fxns) == 1) { + fxns <- list(fxns) + } + for (f in fxns) { + if (!(is.character(x = f) || is.function(x = f))) { + abort(message = paste( + "CheckDots only works on characters or functions, not", + class(x = f)[1L] + )) + } + } + fxn.args <- suppressWarnings(expr = sapply( + X = fxns, + FUN = function(x) { + x <- tryCatch( + expr = if (isS3stdGeneric(f = x)) { + as.character(x = methods(generic.function = x)) + } else { + x + }, + error = function(...) { + return(x) + } + ) + x <- if (is.character(x = x)) { + sapply(X = x, FUN = argsAnywhere, simplify = FALSE, USE.NAMES = TRUE) + } else if (length(x = x) <= 1) { + list(x) + } + return(sapply( + X = x, + FUN = function(f) { + return(names(x = formals(fun = f))) + }, + simplify = FALSE, + USE.NAMES = TRUE + )) + }, + simplify = FALSE, + USE.NAMES = TRUE + )) + fxn.args <- unlist(x = fxn.args, recursive = FALSE) + fxn.null <- vapply( + X = fxn.args, + FUN = is.null, + FUN.VALUE = logical(length = 1L) + ) + if (all(fxn.null) && !is.null(x = fxns)) { + stop("None of the functions passed could be found", call. = FALSE) + } else if (any(fxn.null)) { + warning( + "The following functions passed could not be found: ", + paste(names(x = which(x = fxn.null)), collapse = ', '), + call. = FALSE, + immediate. = TRUE + ) + fxn.args <- Filter(f = Negate(f = is.null), x = fxn.args) + } + dfxns <- vector(mode = 'logical', length = length(x = fxn.args)) + names(x = dfxns) <- names(x = fxn.args) + for (i in 1:length(x = fxn.args)) { + dfxns[i] <- any(grepl(pattern = '...', x = fxn.args[[i]], fixed = TRUE)) + } + if (any(dfxns)) { + dfxns <- names(x = which(x = dfxns)) + if (any(nchar(x = dfxns) > 0)) { + fx <- vapply( + X = Filter(f = nchar, x = dfxns), + FUN = function(x) { + if (isS3method(method = x)) { + x <- unlist(x = strsplit(x = x, split = '\\.')) + x <- x[length(x = x) - 1L] + } + return(x) + }, + FUN.VALUE = character(length = 1L) + ) + message( + "The following functions and any applicable methods accept the dots: ", + paste(unique(x = fx), collapse = ', ') + ) + if (any(nchar(x = dfxns) < 1)) { + message( + "In addition, there is/are ", + length(x = Filter(f = Negate(f = nchar), x = dfxns)), + " other function(s) that accept(s) the dots" + ) + } + } else { + message("There is/are ", length(x = dfxns), 'function(s) that accept(s) the dots') + } + } else { + unused <- Filter( + f = function(x) { + return(!x %in% unlist(x = fxn.args)) + }, + x = args.names + ) + if (length(x = unused) > 0) { + msg <- paste0( + "The following arguments are not used: ", + paste(unused, collapse = ', ') + ) + switch( + EXPR = getOption(x = "Seurat.checkdots", default = 'warn'), + "warn" = warning(msg, call. = FALSE, immediate. = TRUE), + "stop" = stop(msg), + "silent" = NULL, + stop("Invalid Seurat.checkdots option. Please choose one of warn, stop, silent") + ) + # unused.hints <- sapply(X = unused, FUN = OldParamHints) + # names(x = unused.hints) <- unused + # unused.hints <- na.omit(object = unused.hints) + # if (length(x = unused.hints) > 0) { + # message( + # "Suggested parameter: ", + # paste(unused.hints, "instead of", names(x = unused.hints), collapse = '; '), + # "\n" + # ) + # } + } + } + return(invisible(x = NULL)) +} + +#' Check features names format +#' +#' @param data a matrix input, rownames(data) are feature names +#' +#' @return \code{data} with update feature names +#' +#' @keywords internal +#' +#' @export +#' +CheckFeaturesNames <- function(data) { + if (any(grepl(pattern = "_", x = rownames(x = data)))) { + warning( + "Feature names cannot have underscores ('_'), replacing with dashes ('-')", + call. = FALSE, + immediate. = TRUE + ) + rownames(x = data) <- gsub( + pattern = "_", + replacement = "-", + x = rownames(x = data) + ) + } + if (any(grepl(pattern = "|", x = rownames(x = data), fixed = TRUE))) { + warning( + "Feature names cannot have pipe characters ('|'), replacing with dashes ('-')", + call. = FALSE, + immediate. = TRUE + ) + rownames(x = data) <- gsub( + pattern = "|", + replacement = "-", + x = rownames(x = data), + fixed = TRUE + ) + } + return(data) +} + #' Conditional Garbage Collection #' #' Call \code{gc} only when desired #' #' @param option ... #' -#' @return Invisibly returns \code{NULL} +#' @template return-null #' #' @export #' @@ -164,6 +906,87 @@ CheckGC <- function(option = 'SeuratObject.memsafe') { return(invisible(x = NULL)) } +#' Check layers names for the input list +#' +#' +#' @param matrix.list A list of matrices +#' @param layers.type layers type, such as counts or data +#' +#' +#' @export +#' +#' @concept utils +#' +CheckLayersName <- function( + matrix.list, + layers.type = c('counts', 'data') +) { + layers.type <- match.arg(arg = layers.type) + if (is.null(x = matrix.list)) { + return(matrix.list) + } + if (!inherits(x = matrix.list, what = 'list')) { + matrix.list <- list(matrix.list) + } + if (length(x = matrix.list) == 1) { + names(x = matrix.list) <- layers.type + } else { + endings <- seq_along(along.with = matrix.list) + for (i in 1:length(x = matrix.list)) { + name <- names(x = matrix.list)[i] + if (!is.null(name) && nzchar(x = name)) { + if (grepl(pattern = paste0('^', layers.type, '[._\\0-9-]+'), x = name)) { + name <- gsub( + pattern = paste0(layers.type, '[._\\0-9-]+'), + replacement = "", + x = name + ) + # If replacement leaves empty string + if (!nzchar(x = name)) { + name <- i + } + } + endings[i] <- name + } + } + names(x = matrix.list) <- paste0(paste0(layers.type, '.'), endings) + names(x = matrix.list) <- make.unique(names = names(x = matrix.list), sep = '') + } + return(matrix.list) +} + +#' Generate a Class Key +#' +#' Generate class keys for S4 classes. A class key follows the following +#' structure: \dQuote{\code{package:class}} +#' +#' @param class Class name +#' @param package Optional name of package; by default, will search namespaces +#' of loaded packages to determine the providing package +#' +#' @return The class key (\dQuote{\code{package:class}}) +#' +#' @importFrom methods getClass slot +#' +#' @keywords internal +#' +#' @export +#' +#' @concept utils +#' @family s4list +#' +#' @examples +#' ClassKey("Seurat") +#' +ClassKey <- function(class, package = NULL) { + class <- class[1L] + package <- package %||% slot( + object = getClass(Class = class), + name = 'package' + ) + return(paste(package, class, sep = ':')) +} + #' Find the default \code{\link{DimReduc}} #' #' Searches for \code{\link{DimReduc}s} matching \dQuote{umap}, \dQuote{tsne}, @@ -195,7 +1018,7 @@ DefaultDimReduc <- function(object, assay = NULL) { }, x = dim.reducs ) - if (length(x = drs.assay) > 0) { + if (length(x = drs.assay)) { index <- lapply( X = drs.use, FUN = grep, @@ -203,7 +1026,7 @@ DefaultDimReduc <- function(object, assay = NULL) { ignore.case = TRUE ) index <- Filter(f = length, x = index) - if (length(x = index) > 0) { + if (length(x = index)) { return(drs.assay[min(index[[1]])]) } } @@ -214,40 +1037,98 @@ DefaultDimReduc <- function(object, assay = NULL) { ignore.case = TRUE ) index <- Filter(f = length, x = index) - if (length(x = index) < 1) { - stop( - "Unable to find a DimReduc matching one of '", - paste(drs.use[1:(length(x = drs.use) - 1)], collapse = "', '"), - "', or '", - drs.use[length(x = drs.use)], - "', please specify a dimensional reduction to use", - call. = FALSE - ) + if (!length(x = index)) { + abort(message = paste0( + "Unable to find a DimReduc matching one of ", + .Oxford(drs.use), + "; please specify a dimensional reduction to use" + )) } return(dim.reducs[min(index[[1]])]) } -#' Check if a matrix is empty +#' Radian/Degree Conversions +#' +#' Convert degrees to radians and vice versa +#' +#' @param rad Angle in radians +#' +#' @return \code{Degrees}: \code{rad} in degrees +#' +#' @name Angles +#' @rdname angles +#' +#' @keywords internal +#' +#' @export +#' +#' @concept utils +#' @family angles +#' +#' @examples +#' Degrees(pi) +#' +Degrees <- function(rad) { + return(rad * (180 / pi)) +} + +#' Empty Data Frames +#' +#' Create an empty \link[base:data.frame]{data frame} with no row names and +#' zero columns +#' +#' @param n Number of rows for the data frame +#' +#' @return A \link[base:data.frame]{data frame} with \code{n} rows and +#' zero columns +#' +#' @keywords internal +#' +#' @export +#' +#' @concept utils +#' +#' @examples +#' EmptyDF(4L) +#' +EmptyDF <- function(n) { + return(as.data.frame(x = matrix(nrow = n, ncol = 0L))) +} + +#' Extract delimiter information from a string. +#' +#' Parses a string (usually a cell name) and extracts fields based +#' on a delimiter #' -#' Takes a matrix and asks if it's empty (either 0x0 or 1x1 with a value of NA) +#' @param string String to parse. +#' @param field Integer(s) indicating which field(s) to extract. Can be a +#' vector multiple numbers. +#' @param delim Delimiter to use, set to underscore by default. #' -#' @param x A matrix +#' @return A new string, that parses out the requested fields, and +#' (if multiple), rejoins them with the same delimiter #' -#' @return Whether or not \code{x} is empty +#' @keywords internal #' #' @export #' #' @concept utils #' #' @examples -#' IsMatrixEmpty(new("matrix")) -#' IsMatrixEmpty(matrix()) -#' IsMatrixEmpty(matrix(1:3)) +#' ExtractField('Hello World', field = 1, delim = '_') #' -IsMatrixEmpty <- function(x) { - matrix.dims <- dim(x = x) - matrix.na <- all(matrix.dims == 1) && all(is.na(x = x)) - return(all(matrix.dims == 0) || matrix.na) +ExtractField <- function(string, field = 1, delim = "_") { + fields <- as.numeric(x = unlist(x = strsplit( + x = as.character(x = field), + split = "," + ))) + if (length(x = fields) == 1) { + return(strsplit(x = string, split = delim)[[1]][field]) + } + return(paste( + strsplit(x = string, split = delim)[[1]][fields], + collapse = delim + )) } #' Check List Names @@ -266,6 +1147,18 @@ IsMatrixEmpty <- function(x) { #' #' @export #' +#' @concept utils +#' +#' @examples +#' IsNamedList(list()) +#' IsNamedList(list(), pass.zero = TRUE) +#' IsNamedList(list(1, 2, 3)) +#' IsNamedList(list(a = 1, b = 2, c = 3)) +#' IsNamedList(list(a = 1, 2, c = 3)) +#' IsNamedList(list(a = 1, 2, c = 3), allow.empty = TRUE) +#' IsNamedList(list(a = 1, a = 2, a = 3)) +#' IsNamedList(list(a = 1, a = 2, a = 3), all.unique = FALSE) +#' IsNamedList <- function( x, all.unique = TRUE, @@ -301,9 +1194,12 @@ IsNamedList <- function( #' #' @export #' +#' @examples +#' IsS4List(pbmc.list) +#' IsS4List <- function(x) { return( - inherits(x = x, what = 'list') && + is_bare_list(x = x) && isTRUE(x = grepl( pattern = '^[[:alnum:]]+:[[:alnum:]]+$', x = attr(x = x, which = 'classDef') @@ -321,8 +1217,15 @@ IsS4List <- function(x) { #' #' @export #' +#' @examples +#' pbmc2 <- ListToS4(pbmc.list) +#' pbmc2 +#' class(pbmc2) +#' Reductions(pbmc2) +#' validObject(pbmc2) +#' ListToS4 <- function(x) { - if (!inherits(x = x, what = 'list')) { + if (!is_bare_list(x = x)) { return(x) } for (i in seq_along(along.with = x)) { @@ -340,8 +1243,8 @@ ListToS4 <- function(x) { x = attr(x = x, which = 'classDef'), split = ':' )) - pkg <- classdef[1] - cls <- classdef[2] + pkg <- classdef[1L] + cls <- classdef[2L] formal <- getClassDef(Class = cls, package = pkg, inherits = FALSE) return(do.call(what = new, args = c(list(Class = formal), x))) } @@ -357,10 +1260,22 @@ ListToS4 <- function(x) { #' #' @concept utils #' +#' @section Lifecycle: +#' +#' \Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")} +#' +#' \code{PackageCheck} was deprecated in version 5.0.0; please use +#' \code{\link[rlang:check_installed]{rlang::check_installed}()} instead +#' #' @examples #' PackageCheck("SeuratObject", error = FALSE) #' PackageCheck <- function(..., error = TRUE) { + .Deprecate( + when = '5.0.0', + what = 'PackageCheck()', + with = 'rlang::check_installed()' + ) pkgs <- unlist(x = c(...), use.names = FALSE) package.installed <- vapply( X = pkgs, @@ -378,12 +1293,85 @@ PackageCheck <- function(..., error = TRUE) { invisible(x = package.installed) } +#' Polygon Vertices +#' +#' Calculate the vertices of a regular polygon given the number of sides and +#' its radius (distance from center to vertex). Also permits transforming the +#' resulting coordinates by moving the origin and altering the initial angle +#' +#' @param n Number of sides of the polygon +#' @param r Radius of the polygon +#' @param xc,yc X/Y coordinates for the center of the polygon +#' @param t1 Angle of the first vertex in degrees +#' +#' @return A \code{\link[base]{data.frame}} with \code{n} rows and two columns: +#' \describe{ +#' \item{\code{x}}{X positions of each coordinate} +#' \item{\code{y}}{Y positions of each coordinate} +#' } +#' +#' @keywords internal +#' +#' @export +#' +#' @concept utils +#' @family angles +#' +#' @references \url{https://stackoverflow.com/questions/3436453/calculate-coordinates-of-a-regular-polygons-vertices} +#' +#' @examples +#' coords <- PolyVtx(5, t1 = 90) +#' coords +#' if (requireNamespace("ggplot2", quietly = TRUE)) { +#' ggplot2::ggplot(coords, ggplot2::aes(x = x, y = y)) + ggplot2::geom_polygon() +#' } +#' +PolyVtx <- function(n, r = 1L, xc = 0L, yc = 0L, t1 = 0) { + if (!is_bare_integerish(x = n, n = 1L, finite = TRUE)) { + abort(message = "'n' must be a single integer") + } else if (n < 3L) { + abort(message = "'n' must be greater than or equal to 3") + } + stopifnot(is_bare_integerish(x = r, n = 1L, finite = TRUE)) + stopifnot(is_bare_integerish(x = xc, n = 1L, finite = TRUE)) + stopifnot(is_bare_integerish(x = yc, n = 1L, finite = TRUE)) + stopifnot(is_bare_numeric(x = t1, n = 1L)) + t1 <- Radians(deg = t1) + coords <- matrix(data = 0, nrow = n, ncol = 2) + colnames(x = coords) <- c('x', 'y') + for (i in seq_len(length.out = n)) { + theta <- 2 * pi * (i - 1) / n + t1 + coords[i, ] <- c( + xc + r * cos(x = theta), + yc + r * sin(x = theta) + ) + } + return(as.data.frame(x = coords)) +} + +#' @param deg Angle in degrees +#' +#' @return \code{Radians}: \code{deg} in radians +#' +#' @rdname angles +#' +#' @keywords internal +#' +#' @export +#' +#' @examples +#' Radians(180) +#' +Radians <- function(deg) { + return(deg * (pi / 180)) +} + #' Generate a random name #' -#' Make a name from randomly sampled lowercase letters, pasted together with no -#' spaces or other characters +#' Make a name from randomly sampled characters, pasted together with no spaces #' #' @param length How long should the name be +#' @param chars A vector of 1-length characters to use to generate the name #' @param ... Extra parameters passed to \code{\link[base]{sample}} #' #' @return A character with \code{nchar == length} of randomly sampled letters @@ -399,9 +1387,13 @@ PackageCheck <- function(..., error = TRUE) { #' RandomName() #' RandomName(7L, replace = TRUE) #' -RandomName <- function(length = 5L, ...) { +RandomName <- function(length = 5L, chars = letters, ...) { CheckDots(..., fxns = 'sample') - return(paste(sample(x = letters, size = length, ...), collapse = '')) + chars <- unique(x = unlist(x = strsplit( + x = as.character(x = chars), + split = '' + ))) + return(paste(sample(x = chars, size = length, ...), collapse = '')) } #' Merge Sparse Matrices by Row @@ -454,51 +1446,359 @@ RowMergeSparseMatrices <- function(mat1, mat2) { return(new.mat) } -#' Update slots in an object +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Methods for Seurat-defined generics +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#' @rdname dot-AssayClass +#' @method .AssayClass default +#' @export #' -#' @param object An object to update +.AssayClass.default <- function(object) { + return(class(x = object)[1L]) +} + +#' @importFrom methods getClass #' -#' @return \code{object} with the latest slot definitions +#' @rdname dot-ClassPkg #' -#' @importFrom methods slotNames slot +#' @method .ClassPkg default +#' @export #' +.ClassPkg.default <- function(object) { + if (!isS4(object)) { + return(NA_character_) + } + return(slot(object = getClass(Class = class(x = object)), name = 'package')) +} + +#' @rdname dot-ClassPkg +#' @method .ClassPkg DelayedArray #' @export #' -#' @concept utils +.ClassPkg.DelayedArray <- function(object) { + check_installed( + pkg = 'DelayedArray', + reason = 'for working with delayed arrays' + ) + return(.ClassPkg(object = DelayedArray::seed(x = object))) +} + +#' @rdname dot-ClassPkg +#' @method .ClassPkg R6 +#' @export #' -UpdateSlots <- function(object) { - object.list <- sapply( - X = slotNames(x = object), - FUN = function(x) { - return(tryCatch( - expr = slot(object = object, name = x), - error = function(...) { - return(NULL) - } - )) +.ClassPkg.R6 <- function(object) { + for (cls in class(x = object)) { + x <- eval(expr = as.symbol(x = cls)) + if (inherits(x = x, what = 'R6ClassGenerator')) { + return(.ClassPkg(object = x)) + } + } + warn(message = "No r6") + return('R6') +} + +#' @rdname dot-ClassPkg +#' @method .ClassPkg R6ClassGenerator +#' @export +#' +.ClassPkg.R6ClassGenerator <- function(object) { + return(environmentName(env = object$parent_env)) +} + +#' @rdname dot-DiskLoad +#' @method .DiskLoad default +#' @export +#' +.DiskLoad.default <- function(x) { + return(NULL) +} + +#' @rdname dot-DiskLoad +#' @method .DiskLoad 10xMatrixH5 +#' @export +#' +.DiskLoad.10xMatrixH5 <- function(x) { + abort(message = "Unable to determine the feature type of 10x-based BPCells matrices") + check_installed( + pkg = 'BPCells', + reason = 'for working with BPCells matrices' + ) + f <- paste( + 'function(x)', + 'BPCells::open_matrix_10x_hdf5(path = x, feature_type =', + sQuote(x = '', q = FALSE), + ')' + ) + return(f) +} + +#' @rdname dot-DiskLoad +#' @method .DiskLoad AnnDataMatrixH5 +#' @export +#' +.DiskLoad.AnnDataMatrixH5 <- function(x) { + check_installed( + pkg = 'BPCells', + reason = 'for working with BPCells matrices' + ) + f <- paste( + 'function(x)', + 'BPCells::open_matrix_anndata_hdf5(path = x, group =', + sQuote(x = slot(object = x, name = 'group'), q = FALSE), + ')' + ) + return(f) +} + +#' @rdname dot-DiskLoad +#' @method .DiskLoad DelayedMatrix +#' @export +#' +.DiskLoad.DelayedMatrix <- function(x) { + check_installed( + pkg = 'DelayedArray', + reason = 'for working with delayed matrices' + ) + seed <- DelayedArray::seed(x = x) + return(.DiskLoad(x = DelayedArray::DelayedArray(seed = seed))) +} + +#' @rdname dot-DiskLoad +#' @method .DiskLoad H5ADMatrix +#' @export +#' +.DiskLoad.H5ADMatrix <- function(x) { + check_installed( + pkg = 'HDF5Array', + reason = 'for working with H5AD matrices' + ) + sparse <- DelayedArray::is_sparse(x = x) + layer <- if (isTRUE(x = sparse)) { + slot(object = DelayedArray::seed(x = x), name = 'group') + } else { + slot(object = DelayedArray::seed(x = x), name = 'name') + } + layer <- if (layer == '/X') { + NULL + } else { + basename(path = layer) + } + f <- paste( + "function(x)", + "HDF5Array::H5ADMatrix(filepath = x", + if (!is.null(x = layer)) { + paste(", layer =", sQuote(x = layer, q = FALSE)) }, - simplify = FALSE, - USE.NAMES = TRUE + ")" ) - object.list <- Filter(f = Negate(f = is.null), x = object.list) - object.list <- c('Class' = class(x = object)[1], object.list) - object <- do.call(what = 'new', args = object.list) - for (x in setdiff(x = slotNames(x = object), y = names(x = object.list))) { - xobj <- slot(object = object, name = x) - if (is.vector(x = xobj) && !is.list(x = xobj) && length(x = xobj) == 0) { - slot(object = object, name = x) <- vector( - mode = class(x = xobj), - length = 1L + return(f) +} + +#' @rdname dot-DiskLoad +#' @method .DiskLoad HDF5Matrix +#' @export +#' +.DiskLoad.HDF5Matrix <- function(x) { + check_installed( + pkg = 'HDF5Array', + reason = 'for working with HDF5 matrices' + ) + sparse <- DelayedArray::is_sparse(x = x) + name <- slot(object = DelayedArray::seed(x = x), name = 'name') + f <- paste( + "function(x)", + "HDF5Array::HDF5Array(filepath = x, name =", + sQuote(x = name, q = FALSE), + ", as.sparse =", + sparse, + ")" + ) + return(f) +} + +#' @rdname dot-DiskLoad +#' @method .DiskLoad IterableMatrix +#' @export +#' +.DiskLoad.IterableMatrix <- function(x) { + check_installed( + pkg = 'BPCells', + reason = 'for working with BPCells matrices' + ) + fxns <- lapply( + X = BPCells::all_matrix_inputs(x = x), + FUN = .DiskLoad + ) + fxns <- Filter(f = Negate(f = is.null), x = fxns) + if (!length(x = fxns)) { + return(NULL) + } + fn <- if (length(x = fxns) > 1L) { + # fxns <- paste('list(', paste(sQuote(x = fxns, q = FALSE), collapse = ', '), ')') + fn <- paste( + "function(x) {", + "paths <- unlist(x = strsplit(x = x, split = ','));", + "fxns <- list(", paste(sQuote(x = fxns, q = FALSE), collapse = ', '), ");", + "mats <- vector(mode = 'list', length = length(x = paths));", + "for (i in seq_along(paths)) {", + "fn <- eval(str2lang(fxns[[i]]));", + "mats[[i]] <- fn(paths[i]);", + "};", + "return(Reduce(cbind, mats));", + "}" + ) + fn + # abort(message = "too many matrices") + } else { + fxns[[1L]] + } + return(fn) +} + +#' @rdname dot-DiskLoad +#' @method .DiskLoad MatrixDir +#' @export +#' +.DiskLoad.MatrixDir <- function(x) { + check_installed( + pkg = 'BPCells', + reason = 'for working with BPCells matrices' + ) + f <- paste( + 'function(x)', + 'BPCells::open_matrix_dir(dir = x)' + ) + return(f) +} + +#' @rdname dot-DiskLoad +#' @method .DiskLoad MatrixH5 +#' @export +#' +.DiskLoad.MatrixH5 <- function(x) { + check_installed( + pkg = 'BPCells', + reason = 'for working with BPCells matrices' + ) + f <- paste( + 'function(x)', + 'BPCells::open_matrix_hdf5(path = x, group =', + sQuote(x = slot(object = x, name = 'group'), q = FALSE), + ')' + ) + return(f) +} + +#' @rdname dot-DiskLoad +#' @method .DiskLoad TileDBMatrix +#' @export +#' +.DiskLoad.TileDBMatrix <- function(x) { + check_installed( + pkg = 'TileDBArray', + reason = 'for working with TileDB matrices' + ) + tdb.attr <- slot(object = DelayedArray::seed(x = x), name = 'attr') + f <- paste( + 'function(x)', + 'TileDBArray::TileDBArray(x = x, attr =', + sQuote(x = tdb.attr, q = FALSE), + ')' + ) + return(f) +} + +#' @rdname dot-FilePath +#' @method .FilePath default +#' @export +#' +.FilePath.default <- function(x) { + return(NULL) +} + +#' @rdname dot-FilePath +#' @method .FilePath DelayedMatrix +#' @export +#' +.FilePath.DelayedMatrix <- function(x) { + check_installed( + pkg = 'DelayedArray', + reason = 'for working with delayed matrices' + ) + path <- tryCatch( + expr = normalizePath(path = DelayedArray::path(object = x)), + error = \(...) NULL + ) + if (is.null(x = path)) { + warn(message = "The matrix provided does not exist on-disk") + } + return(path) +} + +#' @rdname dot-FilePath +#' @method .FilePath IterableMatrix +#' @export +#' +.FilePath.IterableMatrix <- function(x) { + check_installed(pkg = "BPCells", reason = "for working with BPCells matrices") + matrices <- BPCells::all_matrix_inputs(x = x) + paths <- vector(mode = 'character', length = length(x = matrices)) + for (i in seq_along(along.with = matrices)) { + mode <- .BPMatrixMode(object = matrices[[i]]) + paths[i] <- switch( + EXPR = mode, + memory = '', + file = slot(object = matrices[[i]], name = "path"), + directory = slot(object = matrices[[i]], name = 'dir'), + abort(message = paste("Unknown BPCells matrix mode:", sQuote(x = mode))) + ) + } + if (length(paths) > 1){ + paths <- paste(paths, collapse = ",") + } + return(paths) +} + +#' @rdname dot-SelectFeatures +#' @method .SelectFeatures list +#' @export +#' +.SelectFeatures.list <- function( + object, + all.features = NULL, + nfeatures = Inf, + ... +) { + if (length(x = object) == 1L) { + return(head(x = object[[1L]], n = nfeatures)) + } + features <- unlist(x = object, use.names = FALSE) + features <- sort(x = table(features), decreasing = TRUE) + # Select only features present in all entries + if (!is.null(x = all.features)) { + present <- intersect(x = names(x = features), y = all.features) + if (!length(x = present)) { + abort( + message = "None of the features provided are present in the feature set" ) } + features <- features[present] } - return(object) + tie.val <- features[min(nfeatures, length(x = features))] + # Select features + selected <- names(x = features[which(x = features > tie.val)]) + if (length(x = features)) { + selected <- .FeatureRank(features = selected, flist = object) + } + tied <- .FeatureRank( + features = names(x = features[which(x = features == tie.val)]), + flist = object + ) + return(head(x = c(selected, tied), n = nfeatures)) } -#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -# Methods for Seurat-defined generics -#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - #' @rdname as.Centroids #' @method as.Centroids Segmentation #' @export @@ -633,22 +1933,22 @@ CheckMatrix.dMatrix <- function( checks = c('infinite', 'logical', 'integer', 'na'), ... ) { - checks <- match.arg(arg = checks, several.ok = TRUE) + checks <- arg_match(arg = checks, multiple = TRUE) x <- slot(object = object, name = 'x') for (i in checks) { switch( EXPR = i, 'infinite' = if (any(is.infinite(x = x))) { - warning("Input matrix contains infinite values") + warn(message = "Input matrix contains infinite values") }, 'logical' = if (any(is.logical(x = x))) { - warning("Input matrix contains logical values") + warn(message = "Input matrix contains logical values") }, 'integer' = if (!all(round(x = x) == x, na.rm = TRUE)) { - warning("Input matrix contains non-integer values") + warn(message = "Input matrix contains non-integer values") }, 'na' = if (anyNA(x = x)) { - warning("Input matrix contains NA/NaN values") + warn(message = "Input matrix contains NA/NaN values") }, ) } @@ -664,10 +1964,23 @@ CheckMatrix.lMatrix <- function( checks = c('infinite', 'logical', 'integer', 'na'), ... ) { - warning("Input matrix contains logical values") + warn(message = "Input matrix contains logical values") return(invisible(x = NULL)) } +#' @rdname IsMatrixEmpty +#' @export +#' @method IsMatrixEmpty default +#' +IsMatrixEmpty.default <- function(x) { + matrix.dims <- dim(x = x) + if (is.null(x = matrix.dims)) { + return(FALSE) + } + matrix.na <- all(matrix.dims == 1) && all(is.na(x = x)) + return(all(matrix.dims == 0) || matrix.na) +} + #' @importFrom methods slotNames #' #' @rdname s4list @@ -715,21 +2028,20 @@ S4ToList.list <- function(object) { #' @param topologyPreserve Logical determining if the algorithm should attempt to preserve the topology of the original geometry #' #' @return A `Segmentation` object with simplified segmentation vertices -#' +#' #' @rdname Simplify #' @method Simplify Spatial #' @export #' Simplify.Spatial <- function(coords, tol, topologyPreserve = TRUE) { - if (!PackageCheck("sf", error = FALSE)) { - stop("'Simplify' requires sf to be installed", call. = FALSE) - } + check_installed(pkg = 'sf', reason = 'to simplify spatial data') class.orig <- class(x = coords) coords.orig <- coords dest <- ifelse( - test = grepl(pattern = "^Spatial", x = class.orig), + test = grepl(pattern = "^Spatial", x = class.orig), yes = class.orig, - no = grep(pattern = "^Spatial", x = .Contains(object = coords), value = TRUE)[1L]) + no = grep(pattern = "^Spatial", x = .Contains(object = coords), value = TRUE)[1L] + ) x <- sf::st_as_sfc(as(object = coords, Class = dest)) coords <- sf::st_simplify( x = x, @@ -748,354 +2060,404 @@ Simplify.Spatial <- function(coords, tol, topologyPreserve = TRUE) { return(coords) } -#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -# Methods for R-defined generics -#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -# S4 methods -#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -# Internal -#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -#' Get Parent S4 Classes -#' -#' @param object An \link[methods:Classes_Details]{S4} object -#' -#' @return A vector of class names that \code{object} inherits from +#' Generate empty dgC sparse matrix #' -#' @importFrom methods getClass slot +#' @param ncol,nrow Number of columns and rows in matrix +#' @param rownames,colnames Optional row- and column names for the matrix #' #' @keywords internal #' #' @export #' -#' @examples -#' .Contains(pbmc_small) -#' -.Contains <- function(object) { - if (!isS4(object)) { - stop("'object' not an S4 object") - } - return(names(x = slot( - object = getClass(Class = class(x = object)), - name = 'contains' - ))) +SparseEmptyMatrix <- function(nrow, ncol, rownames = NULL, colnames = NULL) { + return(new( + Class = 'dgCMatrix', + p = integer(length = ncol + 1L), + Dim = c(as.integer(x = nrow), as.integer(x = ncol)), + Dimnames = list(rownames, colnames) + )) } -#' Find the Default FOV -#' -#' Attempts to find the \dQuote{default} FOV using the revamped -#' spatial framework -#' -#' @param object A \code{{Seurat}} object -#' -#' @return ... -#' +#' @method StitchMatrix default #' @export #' -#' @keywords internal +StitchMatrix.default <- function(x, y, rowmap, colmap, ...) { + abort(message = paste( + "Stitching matrices of class", + dQuote(x = class(x = x)[1L]), + "is not yet supported" + )) +} + +#' @method StitchMatrix dgCMatrix +#' @export #' -.DefaultFOV <- function(object, assay = NULL) { - images <- FilterObjects(object = object, classes.keep = 'FOV') - if (!is.null(x = assay)) { - assays <- c(assay, DefaultAssay(object = object[[assay]])) - images <- Filter( - f = function(x) { - return(DefaultAssay(object = object[[x]]) %in% assays) - }, - x = images - ) +StitchMatrix.dgCMatrix <- function(x, y, rowmap, colmap, ...) { + on.exit(expr = CheckGC()) + if (!is_bare_list(x = y)) { + y <- list(y) } - if (!length(x = images)) { - return(NULL) + rowmap <- droplevels(x = rowmap) + colmap <- droplevels(x = colmap) + stopifnot(ncol(rowmap) == length(y) + 1L) + stopifnot(ncol(colmap) == length(y) + 1L) + stopifnot(identical(x = colnames(x = rowmap), y = colnames(x = colmap))) + dimnames(x = x) <- list(rowmap[[1L]], colmap[[1L]]) + for (i in seq_along(along.with = y)) { + j <- i + 1L + y[[i]] <- as(object = y[[i]], Class = 'dgCMatrix') + dimnames(x = y[[i]]) <- list(rowmap[[j]], colmap[[j]]) } - return(images) + return(RowMergeSparseMatrices(mat1 = x, mat2 = y)) } -#' Check the Use of Dots -#' -#' Function to check the use of unused arguments passed to \code{...}; this -#' function is designed to be called from another function to see if an -#' argument passed to \code{...} remains unused and alert the user if so. Also -#' accepts a vector of function or function names to see if \code{...} can be -#' used in a downstream function -#' -#' Behavior of \code{CheckDots} can be controlled by the following option(s): -#' \describe{ -#' \item{\dQuote{\code{Seurat.checkdots}}}{Control how to alert the presence -#' of unused arguments in \code{...}; choose from -#' \itemize{ -#' \item \dQuote{\code{warn}}: emit a warning (default) -#' \item \dQuote{\code{error}}: throw an error -#' \item \dQuote{\code{silent}}: no not alert the presence of unused -#' arguments in \code{...} -#' } -#' } -#' } -#' -#' @param ... Arguments passed to a function that fall under \code{...} -#' @param fxns A list/vector of functions or function names -#' -#' @return Emits either an error or warning if an argument passed is unused; -#' invisibly returns \code{NULL} -#' -#' @importFrom utils isS3stdGeneric methods argsAnywhere isS3method -#' -#' @keywords internal -#' +#' @method StitchMatrix IterableMatrix #' @export #' -#' @examples -#' \dontrun{ -#' f <- function(x, ...) { -#' CheckDots(...) -#' return(x ^ 2) -#' } -#' f(x = 3, y = 9) -#' } -#' -CheckDots <- function(..., fxns = NULL) { - args.names <- names(x = list(...)) - if (length(x = list(...)) == 0) { - return(invisible(x = NULL)) - } - if (is.null(x = args.names)) { - stop("No named arguments passed") +StitchMatrix.IterableMatrix <- function(x, y, rowmap, colmap, ...) { + on.exit(expr = CheckGC()) + if (!is_bare_list(x = y)) { + y <- list(y) } - if (length(x = fxns) == 1) { - fxns <- list(fxns) - } - for (f in fxns) { - if (!(is.character(x = f) || is.function(x = f))) { - stop("CheckDots only works on characters or functions, not ", class(x = f)) + rowmap <- droplevels(x = rowmap) + colmap <- droplevels(x = colmap) + stopifnot(ncol(rowmap) == length(y) + 1L) + stopifnot(ncol(colmap) == length(y) + 1L) + stopifnot(identical(x = colnames(x = rowmap), y = colnames(x = colmap))) + y <- c(x, y) + for (i in seq_along(along.with = y)) { + #expand matrix to the same size + missing_row <- setdiff(x = rownames(x = rowmap), y = rowmap[[i]]) + if (length(x = missing_row) > 0) { + zero_i <- SparseEmptyMatrix( + nrow = length(x = missing_row), + ncol = ncol(x = y[[i]]), + colnames = colmap[[i]], + rownames = missing_row + ) + zero_i <- as(object = zero_i, Class = 'IterableMatrix') + y[[i]] <- rbind(y[[i]], zero_i)[rownames(rowmap),] } } - fxn.args <- suppressWarnings(expr = sapply( - X = fxns, - FUN = function(x) { - x <- tryCatch( - expr = if (isS3stdGeneric(f = x)) { - as.character(x = methods(generic.function = x)) - } else { - x - }, - error = function(...) { - return(x) - } - ) - x <- if (is.character(x = x)) { - sapply(X = x, FUN = argsAnywhere, simplify = FALSE, USE.NAMES = TRUE) - } else if (length(x = x) <= 1) { - list(x) - } - return(sapply( - X = x, - FUN = function(f) { - return(names(x = formals(fun = f))) - }, - simplify = FALSE, - USE.NAMES = TRUE - )) - }, - simplify = FALSE, - USE.NAMES = TRUE - )) - fxn.args <- unlist(x = fxn.args, recursive = FALSE) - fxn.null <- vapply( - X = fxn.args, - FUN = is.null, - FUN.VALUE = logical(length = 1L) - ) - if (all(fxn.null) && !is.null(x = fxns)) { - stop("None of the functions passed could be found", call. = FALSE) - } else if (any(fxn.null)) { - warning( - "The following functions passed could not be found: ", - paste(names(x = which(x = fxn.null)), collapse = ', '), - call. = FALSE, - immediate. = TRUE - ) - fxn.args <- Filter(f = Negate(f = is.null), x = fxn.args) + m <- Reduce(f = cbind, x = y) + return(m) +} + + +#' @method StitchMatrix matrix +#' @export +#' +StitchMatrix.matrix <- function(x, y, rowmap, colmap, ...) { + on.exit(expr = CheckGC()) + if (!is_bare_list(x = y)) { + y <- list(y) } - dfxns <- vector(mode = 'logical', length = length(x = fxn.args)) - names(x = dfxns) <- names(x = fxn.args) - for (i in 1:length(x = fxn.args)) { - dfxns[i] <- any(grepl(pattern = '...', x = fxn.args[[i]], fixed = TRUE)) + rowmap <- droplevels(x = rowmap) + colmap <- droplevels(x = colmap) + stopifnot(ncol(rowmap) == length(y) + 1L) + stopifnot(ncol(colmap) == length(y) + 1L) + stopifnot(identical(x = colnames(x = rowmap), y = colnames(x = colmap))) + m <- matrix( + data = 0, + nrow = nrow(x = rowmap), + ncol = nrow(x = colmap), + dimnames = list(rownames(x = rowmap), rownames(x = colmap)) + ) + m[rowmap[[1L]], colmap[[1L]]] <- x + for (i in seq_along(along.with = y)) { + j <- i + 1L + m[rowmap[[j]], colmap[[j]]] <- as.matrix(x = y[[i]]) } - if (any(dfxns)) { - dfxns <- names(x = which(x = dfxns)) - if (any(nchar(x = dfxns) > 0)) { - fx <- vapply( - X = Filter(f = nchar, x = dfxns), - FUN = function(x) { - if (isS3method(method = x)) { - x <- unlist(x = strsplit(x = x, split = '\\.')) - x <- x[length(x = x) - 1L] - } - return(x) - }, - FUN.VALUE = character(length = 1L) - ) - message( - "The following functions and any applicable methods accept the dots: ", - paste(unique(x = fx), collapse = ', ') - ) - if (any(nchar(x = dfxns) < 1)) { - message( - "In addition, there is/are ", - length(x = Filter(f = Negate(f = nchar), x = dfxns)), - " other function(s) that accept(s) the dots" - ) - } - } else { - message("There is/are ", length(x = dfxns), 'function(s) that accept(s) the dots') - } - } else { - unused <- Filter( - f = function(x) { - return(!x %in% unlist(x = fxn.args)) - }, - x = args.names - ) - if (length(x = unused) > 0) { - msg <- paste0( - "The following arguments are not used: ", - paste(unused, collapse = ', ') - ) - switch( - EXPR = getOption(x = "Seurat.checkdots", default = 'warn'), - "warn" = warning(msg, call. = FALSE, immediate. = TRUE), - "stop" = stop(msg), - "silent" = NULL, - stop("Invalid Seurat.checkdots option. Please choose one of warn, stop, silent") - ) - # unused.hints <- sapply(X = unused, FUN = OldParamHints) - # names(x = unused.hints) <- unused - # unused.hints <- na.omit(object = unused.hints) - # if (length(x = unused.hints) > 0) { - # message( - # "Suggested parameter: ", - # paste(unused.hints, "instead of", names(x = unused.hints), collapse = '; '), - # "\n" - # ) - # } + return(m) +} + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Methods for R-defined generics +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# S4 methods +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Internal +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +.CheckNames <- function(x, n) { + stopifnot(length(x = x) == length(x = n)) + if (is.null(x = names(x = x))) { + names(x = x) <- n + } + if (any(!nzchar(x = names(x = x)))) { + idx <- which(x = !nzchar(x = names(x = x))) + n2 <- setdiff(x = n, y = names(x = x)) + if (length(x = idx) != length(x = n2)) { + stop("Not all provided names fit with the values provided", call. = FALSE) } + names(x = x)[idx] <- n2 } - return(invisible(x = NULL)) + return(x) } -#' Check a list of objects for duplicate cell names +#' @importFrom stats median #' -#' @param object.list List of Seurat objects -#' @param verbose Print message about renaming -#' @param stop Error out if any duplicate names exist +.FeatureRank <- function(features, flist, ranks = FALSE) { + franks <- vapply( + X = features, + FUN = function(x) { + return(median(x = unlist(x = lapply( + X = flist, + FUN = function(fl) { + if (x %in% fl) { + return(which(x = x == fl)) + } + return(NULL) + } + )))) + }, + FUN.VALUE = numeric(length = 1L) + ) + franks <- sort(x = franks) + if (!isTRUE(x = ranks)) { + franks <- names(x = franks) + } + return(franks) +} + +#' Move Files and Directories #' -#' @return Returns list of objects with duplicate cells renamed to be unique +#' Move files and directories with \pkg{fs}; includes a handler for when +#' \code{path} is a directory on a different filesystem than \code{new_path} +#' by explicitly copying and deleting \code{path} +#' +#' @inherit fs::file_move params return +#' @inheritParams rlang::caller_env #' #' @keywords internal #' -#' @noRd +#' @export #' -CheckDuplicateCellNames <- function(object.list, verbose = TRUE, stop = FALSE) { - cell.names <- unlist(x = lapply(X = object.list, FUN = colnames)) - if (any(duplicated(x = cell.names))) { - if (stop) { - stop("Duplicate cell names present across objects provided.") - } - if (verbose) { - warning("Some cell names are duplicated across objects provided. Renaming to enforce unique cell names.") - } - object.list <- lapply( - X = 1:length(x = object.list), - FUN = function(x) { - return(RenameCells( - object = object.list[[x]], - new.names = paste0(Cells(x = object.list[[x]]), "_", x) - )) - } +#' @templateVar pkg fs +#' @template note-reqdpkg +#' +#' @seealso \code{\link[fs:file_move]{fs::file_move}()} +#' +.FileMove <- function(path, new_path, overwrite = FALSE, n = 1L) { + check_installed(pkg = "fs", reason = "for moving on-disk files") + stopifnot( + is_scalar_character(x = path), + is_scalar_character(x = new_path), + rlang::is_bare_logical(x = overwrite, n = 1L), + is_bare_integerish(x = n, n = 1L, finite = TRUE) && n > 0 + ) + eexist <- function(err) { + warn( + message = paste( + strwrap(x = paste( + "Trying to move", + sQuote(x = path), + "to itself, skipping" + )), + collapse = '\n' + ), + class = c('WEXIST', 'EEXIST') ) + return(fs::as_fs_path(x = path)) } - return(object.list) + hndlr <- function(err) { + abort( + message = err$message, + class = class(x = err), + call = caller_env(n = 4L + n) + ) + } + if (fs::is_dir(path = path)) { + path <- fs::path_expand(path = path) + new_path <- fs::path_expand(path = new_path) + new_path <- fs::dir_create(path = new_path) + dest <- tryCatch( + expr = fs::dir_copy(path = path, new_path = new_path, overwrite = overwrite), + EEXIST = eexist, + error = hndlr + ) + } else if (fs::is_file(path = path)) { + dest <- tryCatch( + expr = fs::file_copy( + path = path, + new_path = new_path, + overwrite = overwrite + ), + EEXIST = eexist, + error = hndlr + ) + } else { + abort( + message = paste0( + "Can't find path: ", + sQuote(x = path), + "; if path is relative, change working directory." + ), + call = caller_env(n = 1L + n) + ) + } + return(invisible(x = dest)) } -#' Radian/Degree Conversions +#' @param pkg Name of package +#' @param external Include packages imported, but not defined, by \code{pkg} +#' @param old Includes S3 classes registered by +#' \code{\link[methods]{setOldClass}} +#' @param unions Include class unions #' -#' Convert degrees to radians and vice versa +#' @importFrom methods getClass getClasses isClassUnion isXS3Class #' -#' @param rad Angle in radians +#' @noRd #' -#' @return \code{Degrees}: \code{rad} in degrees +.PkgClasses <- function( + pkg = 'SeuratObject', + external = FALSE, + old = FALSE, + unions = FALSE, + virtual = NA, + collapse = TRUE, + include = NULL, + exclude = NULL +) { + classes <- getClasses(where = getNamespace(name = pkg)) + include <- intersect(x = include, y = classes) + # Filter out classes imported, but not defined by pkg + if (!isTRUE(x = external)) { + classes <- Filter( + f = function(x) { + return(slot(object = getClass(Class = x), name = 'package') == pkg) + }, + x = classes + ) + } + # Filter out S3 classes + if (!isTRUE(x = old)) { + classes <- Filter( + f = function(x) { + return(!isXS3Class(classDef = getClass(Class = x))) + }, + x = classes + ) + } + # Filter out class unions + if (!isTRUE(x = unions)) { + classes <- Filter(f = Negate(f = isClassUnion), x = classes) + } + # TODO: Remove virtual classes + if (isFALSE(x = virtual)) { + '' + } + # TODO: Collapse classes + if (isTRUE(x = collapse)) { + '' + } + # Add classes back + classes <- union(x = classes, y = include) + # Remove excluded classes + classes <- setdiff(x = classes, y = exclude) + return(classes) +} + +#' Get English Vowels #' -#' @name Angles -#' @rdname Angles +#' @return A vector with English vowels in lower case #' #' @keywords internal #' -#' @export -#' #' @examples -#' Degrees(pi) +#' .Vowels() #' -Degrees <- function(rad) { - return(rad * (180 / pi)) +#' @noRd +#' +.Vowels <- function() { + return(c('a', 'e', 'i', 'o', 'u')) } -#' Empty Data Frames -#' -#' Create an empty \link[base:data.frame]{data frame} with no row names and -#' zero columns +#' Check a list of objects for duplicate cell names #' -#' @param n Number of rows for the data frame +#' @param object.list List of Seurat objects +#' @param verbose Print message about renaming +#' @param stop Error out if any duplicate names exist #' -#' @return A \link[base:data.frame]{data frame} with \code{n} rows and -#' zero columns +#' @return Returns list of objects with duplicate cells renamed to be unique #' #' @keywords internal #' -#' @export -#' +#' @noRd #' -EmptyDF <- function(n) { - return(as.data.frame(x = matrix(nrow = n, ncol = 0L))) +CheckDuplicateCellNames <- function(object.list, verbose = TRUE, stop = FALSE) { + cell.names <- unlist(x = lapply(X = object.list, FUN = colnames)) + if (anyDuplicated(x = cell.names)) { + if (isTRUE(x = stop)) { + stop("Duplicate cell names present across objects provided.", call. = FALSE) + } + if (verbose) { + warning( + "Some cell names are duplicated across objects provided. Renaming to enforce unique cell names.", + call. = FALSE, + immediate. = TRUE + ) + } + for (i in seq_along(along.with = object.list)) { + object.list[[i]] <- RenameCells( + object = object.list[[i]], + new.names = paste( + colnames(x = object.list[[i]]), + i, + sep = '_' + )) + } + } + return(object.list) } -#' Extract delimiter information from a string. +#' Check List Names #' -#' Parses a string (usually a cell name) and extracts fields based -#' on a delimiter +#' Check to see if a list has names; also check to enforce that all names are +#' present and unique #' -#' @param string String to parse. -#' @param field Integer(s) indicating which field(s) to extract. Can be a -#' vector multiple numbers. -#' @param delim Delimiter to use, set to underscore by default. +#' @param x A list +#' @param all.unique Require that all names are unique from one another +#' @param allow.empty Allow empty (\code{nchar = 0}) names +#' @param pass.zero Pass on zero-length lists #' -#' @return A new string, that parses out the requested fields, and -#' (if multiple), rejoins them with the same delimiter +#' @return \code{TRUE} if ..., otherwise \code{FALSE} +#' +#' @importFrom rlang is_bare_list #' #' @keywords internal #' #' @noRd #' -#' @examples -#' \donttest{ -#' SeuratObject:::ExtractField('Hello World', field = 1, delim = '_') -#' } -#' -ExtractField <- function(string, field = 1, delim = "_") { - fields <- as.numeric(x = unlist(x = strsplit( - x = as.character(x = field), - split = "," - ))) - if (length(x = fields) == 1) { - return(strsplit(x = string, split = delim)[[1]][field]) +IsNamedList <- function( + x, + all.unique = TRUE, + allow.empty = FALSE, + pass.zero = FALSE +) { + if (!is_bare_list(x = x)) { + return(FALSE) } - return(paste( - strsplit(x = string, split = delim)[[1]][fields], - collapse = delim - )) + if (isTRUE(x = pass.zero) && !length(x = x)) { + return(TRUE) + } + n <- names(x = x) + named <- !is.null(x = n) + if (!isTRUE(x = allow.empty)) { + named <- named && all(vapply( + X = n, + FUN = nchar, + FUN.VALUE = integer(length = 1L) + )) + } + if (isTRUE(x = all.unique)) { + named <- named && (length(x = n) == length(x = unique(x = n))) + } + return(named) } #' Test Null Pointers @@ -1119,73 +2481,66 @@ IsNullPtr <- function(x) { return(.Call('isnull', x)) } -#' Polygon Vertices -#' -#' Calculate the vertices of a regular polygon given the number of sides and -#' its radius (distance from center to vertex). Also permits transforming the -#' resulting coordinates by moving the origin and altering the initial angle +#' Test Empty Characters #' -#' @param n Number of sides of the polygon -#' @param r Radius of the polygon -#' @param xc,yc X/Y coordinates for the center of the polygon -#' @param t1 Angle of the first vertex in degrees +#' Check to see if a \code{\link[base]{character}} vector is empty. A character +#' is empty if it has no length or an \code{nzchar == FALSE} #' -#' @return A \code{\link[base]{data.frame}} with \code{n} rows and two columns: +#' @param x A \code{\link[base]{character}} vector +#' @param mode Stringency of emptiness test: #' \describe{ -#' \item{\code{x}}{X positions of each coordinate} -#' \item{\code{y}}{Y positions of each coordinate} +#' \item{\dQuote{each}}{Return a single value for each member of \code{x}} +#' \item{\dQuote{any}}{Return \code{TRUE} if any member of \code{x} is empty} +#' \item{\dQuote{all}}{Return \code{TRUE} if \emph{every} member of \code{x} is +#' empty} #' } -#' -#' @keywords internal -#' -#' @export -#' -#' @references \url{https://stackoverflow.com/questions/3436453/calculate-coordinates-of-a-regular-polygons-vertices} -#' -#' @examples -#' coords <- PolyVtx(5, t1 = 90) -#' coords -#' if (requireNamespace("ggplot2", quietly = TRUE)) { -#' ggplot2::ggplot(coords, ggplot2::aes(x = x, y = y)) + ggplot2::geom_polygon() +#' @param na Control how \code{\link[base]{NA}} values are treated: +#' \describe{ +#' \item{\dQuote{empty}}{Treat \code{NA}s as empty values} +#' \item{\dQuote{keep}}{Keep \code{NA} values and treat them as \code{NA}} +#' \item{\dQuote{remove}}{Remove \code{NA} values before testing emptiness} #' } #' -PolyVtx <- function(n, r = 1L, xc = 0L, yc = 0L, t1 = 0) { - n <- n[1] - r <- r[1] - xc <- xc[1] - yc <- yc[1] - t1 <- t1[1] - if (n < 3) { - stop("'n' must be greater than or equal to 3", call. = FALSE) - } - t1 <- Radians(deg = t1) - coords <- matrix(data = 0, nrow = n, ncol = 2) - colnames(x = coords) <- c('x', 'y') - for (i in seq_len(length.out = n)) { - theta <- 2 * pi * (i - 1) / n + t1 - coords[i, ] <- c( - xc + r * cos(x = theta), - yc + r * sin(x = theta) - ) - } - return(as.data.frame(x = coords)) -} - -#' @param deg Angle in degrees -#' -#' @return \code{Radians}: \code{deg} in radians -#' -#' @rdname Angles +#' @return If \code{mode} is \dQuote{each}, a vector of logical values denoting +#' the emptiness of of each member of \code{x}; otherwise, a singular +#' \code{\link[base]{logical}} denoting the overall emptiness of \code{x} #' #' @keywords internal #' -#' @export -#' -#' @examples -#' Radians(180) +#' @noRd #' -Radians <- function(deg) { - return(deg * (pi / 180)) +IsCharEmpty <- function( + x, + mode = c('each', 'any', 'all'), + na = c('empty', 'keep', 'remove') +) { + if (!is.character(x = x)) { + return(FALSE) + } + mode <- arg_match(arg = mode) + na <- arg_match(arg = na) + x <- switch( + EXPR = na, + empty = x[is.na(x = x)] <- '', + remove = x <- x[!is.na(x = x)], + x + ) + if (!length(x = x)) { + return(TRUE) + } + empty <- vapply( + X = x, + FUN = Negate(f = nzchar), + FUN.VALUE = logical(length = 1L), + USE.NAMES = FALSE + ) + empty <- switch( + EXPR = mode, + any = any(empty), + all = all(empty), + empty + ) + return(empty) } #' Update a Class's Package @@ -1215,7 +2570,7 @@ Radians <- function(deg) { #' @noRd #' SwapClassPkg <- function(x, from = NULL, to = NULL) { - if (!inherits(x = x, what = 'list')) { + if (!is_bare_list(x = x)) { return(x) } to <- to[1] %||% environmentName(env = environment( @@ -1299,7 +2654,6 @@ Top <- function(data, num = 20, balanced = FALSE) { return(top) } -#' @name classpkg #' @rdname classpkg #' #' @return \code{UpdateClassPkg}: \code{object} with the updated @@ -1315,45 +2669,53 @@ UpdateClassPkg <- function(object, from = NULL, to = NULL) { } obj.list <- S4ToList(object = object) obj.list <- SwapClassPkg(x = obj.list, from = from, to = to) - # browser() return(ListToS4(x = obj.list)) } -#' Update a Key +#' Update slots in an object #' -#' @param key A character to become a Seurat Key +#' @param object An object to update #' -#' @return An updated Key that's valid for Seurat +#' @return \code{object} with the latest slot definitions #' -#' @section \code{Seurat} Object Keys: -#' blah +#' @importFrom methods slotNames slot #' -#' @keywords internal +#' @concept utils #' -#' @noRd +#' @export #' -UpdateKey <- function(key) { - if (grepl(pattern = '^[[:alnum:]]+_$', x = key)) { - return(key) - } else { - new.key <- regmatches( - x = key, - m = gregexpr(pattern = '[[:alnum:]]+', text = key) - ) - new.key <- paste0(paste(unlist(x = new.key), collapse = ''), '_') - if (new.key == '_') { - new.key <- paste0(RandomName(length = 3), '_') +UpdateSlots <- function(object) { + if (!isS4(object)) { + return(object) + } + object.list <- sapply( + X = slotNames(x = object), + FUN = function(x) { + return(tryCatch( + expr = slot(object = object, name = x), + error = function(...) { + return(NULL) + } + )) + }, + simplify = FALSE, + USE.NAMES = TRUE + ) + object.list <- Filter(f = Negate(f = is.null), x = object.list) + object.list <- c('Class' = class(x = object)[1], object.list) + op <- options(Seurat.object.validate = FALSE) + on.exit(expr = options(op), add = TRUE) + object <- suppressWarnings(expr = do.call(what = 'new', args = object.list)) + for (x in setdiff(x = slotNames(x = object), y = names(x = object.list))) { + xobj <- slot(object = object, name = x) + if (is.vector(x = xobj) && !is.list(x = xobj) && length(x = xobj) == 0) { + slot(object = object, name = x) <- vector( + mode = class(x = xobj), + length = 1L + ) } - warning( - "Keys should be one or more alphanumeric characters followed by an underscore, setting key from ", - key, - " to ", - new.key, - call. = FALSE, - immediate. = TRUE - ) - return(new.key) } + return(object) } #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/R/zzz.R b/R/zzz.R index 8c263034..92ddcc24 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,8 +1,15 @@ #' @importFrom sp bbox over #' @importFrom Rcpp evalCpp -#' @importFrom utils head tail packageVersion -#' @importFrom methods new setClass setClassUnion setMethod setOldClass -#' setValidity slot slot<- validObject +#' @importFrom Matrix nnzero +#' @importFrom progressr progressor +#' @importFrom lifecycle deprecated is_present +#' @importFrom utils head packageVersion tail upgrade +#' @importFrom methods new setClass setClassUnion setGeneric setMethod +#' setOldClass setValidity show slot slot<- validObject +#' @importFrom rlang abort arg_match arg_match0 caller_env check_installed +#' enquo eval_tidy have_name inform is_bare_character is_bare_integerish +#' is_bare_list is_bare_numeric is_missing is_na is_named is_quosure +#' missing_arg warn #' @importClassesFrom Matrix dgCMatrix #' @useDynLib SeuratObject #' @@ -18,9 +25,64 @@ NULL # Options #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#' \pkg{Seurat} Options +#' +#' Various options used in \pkg{Seurat} +#' +#' @section Package Options: +#' \subsection{Seurat.coords.short_range}{ +#' Defaults to +#' \dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.coords.short_range}}\cr +#' Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.coords.short_range")}} +#' } +#' \subsection{Seurat.input.sparse_ratio}{ +#' Defaults to +#' \dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.input.sparse_ratio}}\cr +#' Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.input.sparse_ratio")}} +#' } +#' \subsection{Seurat.io.rds.strict}{ +#' Defaults to +#' \dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.io.rds.strict}}\cr +#' Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.io.rds.strict")}} +#' } +#' \subsection{Seurat.object.assay.calcn}{ +#' Run \code{CalcN} when adding assay data to a \code{Seurat} object\cr +#' Defaults to +#' \dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.object.assay.calcn}}\cr +#' Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.object.assay.calcn")}} +#' } +#' \subsection{Seurat.object.assay.version}{ +#' Defaults to +#' \dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.object.assay.version}}\cr +#' Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.object.assay.version")}} +#' } +#' \subsection{Seurat.object.assay.v3.missing_layer}{ +#' Defaults to +#' \dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.object.assay.v3.missing_layer}}\cr +#' Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.object.assay.v3.missing_layer")}} +#' } +#' \subsection{Seurat.object.project}{ +#' Default project for new \code{\link{Seurat}} objects\cr +#' Defaults to +#' \dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.object.project}}\cr +#' Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.object.project")}} +#' } +#' +#' @name SeuratObject-options +#' +#' @keywords internal +#' +NULL + Seurat.options <- list( - Seurat.input.sparse_ratio = 0.4, Seurat.coords.short_range = 'max', + Seurat.input.sparse_ratio = 0.4, + Seurat.io.rds.strict = FALSE, + Seurat.object.assay.brackets = 'v5', + Seurat.object.assay.calcn = NULL, + Seurat.object.assay.version = 'v5', + Seurat.object.assay.v3.missing_layer = 'matrix', + Seurat.object.project = 'SeuratProject', progressr.clear = FALSE ) @@ -42,25 +104,15 @@ Seurat.options <- list( #' future::plan -#' @importFrom Matrix colMeans -#' @export -#' -Matrix::colMeans - -#' @importFrom Matrix colSums +#' @importFrom generics intersect #' @export #' -Matrix::colSums +generics::intersect -#' @importFrom Matrix rowMeans -#' @export -#' -Matrix::rowMeans - -#' @importFrom Matrix rowSums -#' @export -#' -Matrix::rowSums +# #' @importFrom Matrix colMeans +# #' @export +# #' +# Matrix::colMeans #' @importFrom progressr handlers #' @export @@ -72,6 +124,12 @@ progressr::handlers #' progressr::with_progress +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# Environments +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +sparse.classes <- new.env() + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Class definitions #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -85,35 +143,6 @@ setOldClass(Classes = 'package_version') # Internal #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -#' Add Object Metadata -#' -#' Internal \code{\link{AddMetaData}} definition -#' -#' @param object An object -#' @param metadata A vector, list, or data.frame with metadata to add -#' @param col.name A name for meta data if not a named list or data.frame -#' -#' @return object with metadata added -#' -#' @keywords internal -#' -#' @noRd -#' -.AddMetaData <- function(object, metadata, col.name = NULL) { - if (is.null(x = col.name) && is.atomic(x = metadata)) { - stop("'col.name' must be provided for atomic metadata types (eg. vectors)") - } - if (inherits(x = metadata, what = c('matrix', 'Matrix'))) { - metadata <- as.data.frame(x = metadata) - } - col.name <- col.name %||% names(x = metadata) %||% colnames(x = metadata) - if (is.null(x = col.name)) { - stop("No metadata name provided and could not infer it from metadata object") - } - object[[col.name]] <- metadata - return(object) -} - #' @keywords internal #' #' @noRd @@ -138,7 +167,7 @@ setOldClass(Classes = 'package_version') #' Test Intersections of Bounding Boxes #' -#' @param i,j \link[sp::bbox]{Bounding boxes} +#' @param i,j \link[sp:bbox]{Bounding boxes} #' @param constraint Type of intersection to perform; choose from: #' \itemize{ #' \item \dQuote{\code{intersect}}: \code{i} must fall at least @@ -200,185 +229,127 @@ setOldClass(Classes = 'package_version') return(all(check)) } -#' Internal Cropping Function -#' -#' @inheritParams Crop -#' -#' @return ... -#' -#' @keywords internal -#' -#' @noRd -#' -.Crop <- function(object, x = NULL, y = NULL, coords = c('plot','tissue'), ...) { - if (is.null(x = x) && is.null(x = y)) { - return(object) - } - coords <- coords[1L] - coords <- match.arg(arg = coords) - switch( - EXPR = coords, - 'plot' = { - cx <- 'y' - cy <- 'x' - }, - 'tissue' = { - cx <- 'x' - cy <- 'y' - } - ) - x <- range(x %||% bbox(obj = object)[cx, , drop = TRUE]) - y <- range(y %||% bbox(obj = object)[cy, , drop = TRUE]) - idx <- c(max = 1L, min = 2L)[[getOption( - x = 'Seurat.coords.short_range', - default = Seurat.options$Seurat.coords.short_range - )]] - if (x[1L] == x[2L]) { - x[idx] <- bbox(obj = object)[cx, idx] - } - if (y[1L] == y[2L]) { - y[idx] <- bbox(obj = object)[cy, idx] - } - args <- list(x, y) - names(x = args) <- switch( - EXPR = coords, - 'plot' = c('y', 'x'), - 'tissue' = c('x', 'y') - ) - args <- args[c('x', 'y')] - df <- do.call(what = expand.grid, args = args) - df <- df[c(1, 3, 4, 2), ] - df$cell <- 'cell' - return(Overlay(x = object, y = CreateSegmentation(coords = df))) +.IsDataFrame <- function(x) { + return(length(x = class(x = x)) == 1L && inherits(x = x, what = 'data.frame')) } -#' Test Finiteness of Centroids +#' Test Future Compatibility with \pkg{Seurat} +#' +#' Check to see if \pkg{SeuratObject} and/or \pkg{Seurat} are at least a +#' specific version or if they're configured to act as if they're a +#' specific version (see details below). This allows testing compatibility with +#' future requirements for both \pkg{SeuratObject} and \pkg{Seurat} #' -#' Determines if a \code{\link{Centroids}} object should be finite; for -#' \code{Centroids}, this means if their \code{nsides} slot is an integer >= 3 +#' Blah blah blah #' -#' @param x A \code{\link{Centroids}} object +#' @inheritParams utils::packageVersion +#' @param version A version string or object of class +#' \code{\link{package_version}} #' -#' @return \code{TRUE} if the \code{Centroids} are finite; otherwise -#' \code{FALSE} +#' @return \code{TRUE} if \pkg{SeuratObject} and/or \pkg{Seurat} #' #' @keywords internal #' -#' @noRd +#' @export +#' +#' @aliases IsFutureSeurat #' -.FiniteCentroids <- function(x) { - return(as.logical(x = length(x = x))) +.IsFutureSeurat <- function(version, lib.loc = NULL) { + version <- package_version(x = version) + opt <- paste0( + 'Seurat.future.v', + gsub(pattern = '\\.', replacement = '_', x = as.character(x = version)) + ) + future <- isTRUE(x = getOption(x = opt, default = FALSE)) || + packageVersion(pkg = 'SeuratObject', lib.loc = lib.loc) >= version + if (requireNamespace('Seurat', quietly = TRUE)) { + future <- future || + packageVersion(pkg = 'Seurat', lib.loc = lib.loc) >= version + } + return(future) } -#' Head and Tail Object Metadata -#' -#' Internal \code{\link[utils]{head}} and \code{\link[utils]{tail}} definitions -#' -#' @param x An object -#' @param n Number of rows to return -#' @inheritDotParams utils::head -#' -#' @return The first or last \code{n} rows of object metadata -#' -#' @keywords internal -#' -#' @noRd -#' -.head <- function(x, n = 10L, ...) { - return(head(x = x[[]], n = n, ...)) +.IsNull <- function(x) { + return(vapply(X = x, FUN = is.null, FUN.VALUE = logical(length = 1L))) } -.tail <- function(x, n = 10L, ...) { - return(tail(x = x[[]], n = n, ...)) +.MakeNames <- function(x, strict = FALSE, type = c('layers')) { + if (isTRUE(x = strict)) { + return(make.names(names = x, unique = TRUE)) + } + type <- type[[1L]] + type <- match.arg(arg = type) + x <- switch( + EXPR = type, + layers = { + # Remove white spaces + x <- gsub(pattern = '[[:space:]]+', replacement = '_', x = x) + # Remove illegal characters + x <- gsub( + pattern = '[\\;\\:\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)\\{\\}\\[]', + replacement = '', + x = x + ) + x <- gsub(pattern = '\\]', replacement = '', x = x) + x + } + ) + return(x) } -#' Miscellaneous Data -#' -#' Internal functions for getting and setting miscellaneous data +#' Create a List with a Serial Comma #' -#' @param object An object -#' @param slot Name of miscellaneous data to get or set -#' @param ... Arguments passed to other methods +#' @param ... A character vector to join +#' @param cnj Conjunction to use for final entry +#' @param quote Quote the entries of \code{...}; choose from: +#' \itemize{ +#' \item \dQuote{\code{single}}: regular single quotes +#' \item \dQuote{\code{fancysingle}}: fancy single quotes +#' \item \dQuote{\code{double}}: regular double quotes +#' \item \dQuote{\code{fancydouble}}: fancy double quotes +#' \item \dQuote{\code{none}}: no extra quoting +#' } #' -#' @return \code{.Misc}: If \code{slot} is \code{NULL}, all miscellaneous -#' data, otherwise the miscellaneous data for \code{slot} +#' @return \code{...} arranged into an English list with a serial comma +#' when needed #' #' @keywords internal #' -#' @noRd -#' -.Misc <- function(object, slot = NULL, ...) { - CheckDots(...) - if (is.null(x = slot)) { - return(slot(object = object, name = 'misc')) - } - return(slot(object = object, name = 'misc')[[slot]]) -} - -#' @param value Data to add -#' -#' @return \code{.Misc<-}: \code{object} with \code{value} added to the -#' miscellaneous data slot \code{slot} +#' @seealso \code{\link[base]{sQuote}} #' -#' @rdname dot-Misc +#' @examples +#' .Oxford('cell') +#' .Oxford('cell', 'ident') +#' .Oxford('cell', 'ident', 'gene') #' #' @noRd #' -".Misc<-" <- function(object, slot, ..., value) { - CheckDots(...) - if (slot %in% names(x = Misc(object = object))) { - warning( - "Overwriting miscellanous data for ", - slot, - call. = FALSE, - immediate. = TRUE - ) - } - if (is.list(x = value)) { - slot(object = object, name = 'misc')[[slot]] <- c(value) - } else { - slot(object = object, name = 'misc')[[slot]] <- value - } - return(object) -} - -.OverBbox <- function(x, y, invert = FALSE, ...) { - df <- .BboxDF(x = bbox(obj = y)) - df$cell <- 'cell' - return(Overlay( - x = x, - y = CreateSegmentation(coords = df), - invert = invert, - ... - )) -} - -.Overlay <- function(x, y, ...) { - idx <- over(x = x, y = y) - idx <- idx[!is.na(x = idx)] - names(x = idx) <- vapply( - X = strsplit(x = names(x = idx), split = '\\.'), - FUN = '[[', - FUN.VALUE = character(length = 1L), - 1L, - USE.NAMES = FALSE +.Oxford <- function( + ..., + cnj = c('or', 'and'), + quote = c('single', 'fancysingle', 'double', 'fancydouble', 'none') +) { + x <- as.character(x = c(...)) + cnj <- arg_match(arg = cnj) + quote <- arg_match(arg = quote) + x <- switch( + EXPR = quote, + single = sQuote(x = x, q = FALSE), + fancysingle = sQuote(x = x, q = TRUE), + double = dQuote(x = x, q = FALSE), + fancydouble = dQuote(x = x, q = TRUE), + x ) - return(x[names(x = idx)]) -} - -.PruneLogMap <- function(x) { - fidx <- which(x = apply( - X = x, - MARGIN = 1L, - FUN = function(row) { - return(all(vapply(X = row, FUN = isFALSE, FUN.VALUE = logical(length = 1L)))) - } - )) - if (length(x = fidx)) { - x <- as(object = x[-fidx, , drop = FALSE], Class = 'LogMap') + if (length(x = x) <= 1L) { + return(x) + } else if (length(x = x) == 2L) { + return(paste(x, collapse = paste0(' ', cnj, ' '))) } - validObject(object = x) - return(x) + return(paste( + paste0(paste(x[1:(length(x = x) - 1L)], collapse = ', '), ','), + cnj, + x[length(x = x)] + )) } #' Indexes from Run Length Encodings @@ -408,6 +379,62 @@ setOldClass(Classes = 'package_version') return(idx) } +#' Round Version Information +#' +#' @param current A package version +#' +#' @return ... +#' +#' @keywords internal +#' +#' @noRd +#' +.RoundVersion <- function(current) { + current <- as.character(x = numeric_version(x = current, strict = TRUE)) + current <- unlist(x = strsplit(x = current, split = '\\.')) + if (length(x = current) > 4L) { + if (length(x = current) > 4L) { + current[4L] <- paste( + current[seq.int(from = 4L, to = length(x = current))], + collapse = '.' + ) + current <- current[1:4] + } + } + names(x = current) <- c('major', 'minor', 'patch', 'devel')[seq_along(along.with = current)] + if (!is_na(x = current['devel'])) { + if (all(current[c('minor', 'patch')] == '9')) { + current['major'] <- as.character(x = as.integer(x = current['major']) + 1L) + current[c('minor', 'patch')] <- '0' + } else if (current['patch'] == '0') { + current['minor'] <- as.character(x = as.integer(x = current['minor']) + 1L) + current['patch'] <- '0' + } else { + current['patch'] <- as.character(x = as.integer(x = current['patch']) + 1L) + } + current <- current[c('major', 'minor', 'patch')] + } + current <- vapply( + X = current, + FUN = as.integer, + FUN.VALUE = integer(length = 1L), + USE.NAMES = TRUE + ) + if (!is_na(x = current['devel'])) { + if (all(current[c('minor', 'patch')] == '9')) { + current['major'] <- as.character(x = as.integer(x = current['major']) + 1L) + current[c('minor', 'patch')] <- '0' + } else if (current['patch'] == '0') { + current['minor'] <- as.character(x = as.integer(x = current['minor']) + 1L) + current['patch'] <- '0' + } else { + current['patch'] <- as.character(x = as.integer(x = current['patch']) + 1L) + } + current <- current[c('major', 'minor', 'patch')] + } + return(current) +} + #' Index of Names #' #' Get the index of row- or column-names @@ -500,5 +527,21 @@ NameIndex <- function(x, names, MARGIN) { if (length(x = toset)) { options(Seurat.options[toset]) } + setHook( + hookName = packageEvent(pkgname = 'Seurat', event = 'onLoad'), + value = .SetSeuratCompat + ) + setHook( + hookName = packageEvent(pkgname = 'Signac', event = 'onLoad'), + value = .SetSeuratCompat + ) + setHook( + hookName = packageEvent(pkgname = 'Seurat', event = 'attach'), + value = .SeuratCompatMessage + ) + setHook( + hookName = packageEvent(pkgname = 'Signac', event = 'attach'), + value = .SeuratCompatMessage + ) return(invisible(x = NULL)) } diff --git a/README.md b/README.md index 52f85e60..872b5d7e 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,9 @@ data access methods and R-native hooks to ensure the Seurat object is familiar to other R users. See Satija R, Farrell J, Gennert D, et al (2015) , Macosko E, Basu A, Satija R, et al (2015) , and Stuart T, Butler A, et al (2019) - for more details. +, Hao Y, Hao S, et al (2021) + and Hao Y, et al (2023) + for more details. ## Installation diff --git a/_pkgdown.yml b/_pkgdown.yml index d774ee47..494f053c 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -2,78 +2,63 @@ url: https://mojaveazure.github.io/seurat-object/ destination: docs template: params: - bootswatch: united + bootswatch: flatly + development: mode: auto -reference: -- title: The Seurat Class and Interaction Methods - contents: - - Seurat-class - - Seurat-methods - - has_concept("seurat") -- title: The Assay Class and Interaction Methods - contents: - - Assay-class - - Assay-methods - - has_concept("assay") -- title: The DimReduc and JackStrawData Classes and Interaction Methods - contents: - - DimReduc-class - - DimReduc-methods - - has_concept("dimreduc") - - JackStrawData-class - - JackStrawData-methods - - has_concept("jackstraw") -- title: The SeuratCommand Class and Interaction Methods - contents: - - SeuratCommand-class - - SeuratCommand-methods - - has_concept("command") -- title: The SpatialImage Class and Interaction Methods - contents: - - SpatialImage-class - - SpatialImage-methods - - has_concept("spatialimage") -- title: The Graph and Neighbor Classes and Interaction Methods - contents: - - Graph-class - - has_concept("graph") - - Neighbor-class - - has_concept("neighbor") -- title: Accessing and Setting Object Data - contents: has_concept("data-access") -- title: Utility Functions - contents: - - set-if-null - - has_concept("utils") -- title: Package Information - contents: - - SeuratObject-package - - pbmc_small - - reexports -- title: internal - contents: - - aggregate - - as.Centroids - - Boundaries - - Centroids-class - - Centroids-methods - - CreateCentroids - - CreateFOV - - CreateMolecules - - CreateSegmentation - - Crop - - DefaultFOV - - FOV-class - - FOV-methods - - IsNamedList - - LogMap-class - - MatchCells - - Molecules-class - - Molecules-methods - - Overlay - - Segmentation-class - - Segmentation-methods - - Simplify - - Theta +reference: + - title: The Seurat Class and Interaction Methods + contents: + - "Seurat-class" + - "Seurat-validity" + - has_concept("seurat") + - title: The v5 Assay Class and Interaction Methods + contents: + - has_concept("assay5") + - title: The v3 Assay Class and Interaction Methods + contents: + - "Assay-class" + - has_concept("assay") + - title: The DimReduc and JackStrawData Classes and Interaction Methods + contents: + - "DimReduc-class" + - "DimReduc-validity" + - has_concept("dimreduc") + - "JackStrawData-class" + - "JackStrawData-methods" + - has_concept("jackstraw") + - title: Imaging-Based Spatial Classes and Methods + - contents: + - has_concept("fov") + - has_concept("segmentation") + - has_concept("spatial") + - title: The SeuratCommand Class and Interaction Methods + contents: + - "SeuratCommand-class" + - has_concept("command") + - title: The SpatialImage Class and Interaction Methods + contents: + - "SpatialImage-class" + - "SpatialImage-methods" + - has_concept("spatialimage") + - title: The Graph and Neighbor Classes and Interaction Methods + contents: + - "Graph-class" + - has_concept("graph") + - "Neighbor-class" + - has_concept("neighbor") + - title: Accessing and Setting Object Data + contents: + - has_concept("data-access") + - title: Utility Functions + contents: + - "set-if-null" + - has_concept("utils") + - title: Package Information + contents: + - "SeuratObject-package" + - pbmc_small + - title: Logical Maps + contents: + - has_concept("logmap") diff --git a/cran-comments.md b/cran-comments.md index 7ccc7bd6..0b4c84d8 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,4 +1,4 @@ -# SeuratObject v4.1.4 +# SeuratObject v5.0.0 ## Test environments * local ubuntu 22.04 install, R 4.2.3 @@ -6,12 +6,31 @@ ## R CMD check results -There were no ERRORs, WARNINGs, or NOTEs +There were no ERRORs or WARNINGs + +There were two NOTEs + +> * checking CRAN incoming feasibility ... NOTE +> Suggests or Enhances not in mainstream repositories: +> BPCells +> Availability using Additional_repositories specification: +> BPCells yes https://bnprks.r-universe.dev + +> * checking package dependencies ... NOTE +> Package suggested but not available for checking: 'BPCells' + +BPCells is hosted on R-universe and used conditionally in SeuratObject ## Downstream dependencies -There is one package that depends on SeuratObject: tidyseurat; this update does not impact its functionality +The following reverse dependencies are impacted by this release of SeuratObject: + +- Seurat: + - new test failures and warnings: Seurat's tests expect errors that are now handled by SeuratObject. Warnings occur due to use of deprecated, but still accepted arguments. The authors of Seurat are aware of these changes, but the functionality of Seurat is not impacted + - S3 generic/method consistency: Seurat defines a method for a generic defined in SeuratObject. In the latest update, SeuratObject changes one of the parameters in the method signature, but still accepts the old arguments. The functionality of Seurat is not impacted by this update -There are six packages that import SeuratObject: bbknnR, CAMML, scCustomize, scpoisson, Seurat, and Signac; this update does not impact their functionality +- Signac + - new test failures: SeuratObject changes the order of the results, but not the actual values. The authors of Signac are aware of this, but the functionality of Signac is not impacted -There are seven packages that suggest SeuratObject: cellpypes, RESET, scOntoMatch, SCpubr, singleCellHaystack, SPECK, and VAM; this update does not impact their functionality +- tidyseurat + - new test failures: SeuratObject changes the order of the results, but not the actual values. The authors of tidyseurat are aware of this, but the functionality of tidyseurat is not impacted diff --git a/man/AddMetaData-StdAssay.Rd b/man/AddMetaData-StdAssay.Rd new file mode 100644 index 00000000..a4c2faf5 --- /dev/null +++ b/man/AddMetaData-StdAssay.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{AddMetaData-StdAssay} +\alias{AddMetaData-StdAssay} +\alias{AddMetaData.StdAssay} +\title{Add in metadata associated with either cells or features.} +\usage{ +\method{AddMetaData}{StdAssay}(object, metadata, col.name = NULL) +} +\arguments{ +\item{object}{An object} + +\item{metadata}{A vector, list, or data.frame with metadata to add} + +\item{col.name}{A name for meta data if not a named list or data.frame} +} +\value{ +\code{object} with metadata added +} +\description{ +Adds additional data to the object. Can be any piece of information +associated with a cell (examples include read depth, alignment rate, +experimental batch, or subpopulation identity) or feature (ENSG name, +variance). To add cell level information, add to the Seurat object. If adding +feature-level metadata, add to the Assay object (e.g. \code{object[["RNA"]]}) +} +\keyword{internal} diff --git a/man/AddMetaData.Rd b/man/AddMetaData.Rd index bacd2fe9..e4522552 100644 --- a/man/AddMetaData.Rd +++ b/man/AddMetaData.Rd @@ -1,9 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/generics.R, R/assay.R, R/seurat.R +% Please edit documentation in R/generics.R, R/assay.R, R/assay5.R, R/seurat.R \name{AddMetaData} \alias{AddMetaData} \alias{SeuratAccess} \alias{AddMetaData.Assay} +\alias{AddMetaData.Assay5} \alias{AddMetaData.Seurat} \title{Add in metadata associated with either cells or features.} \usage{ @@ -11,6 +12,8 @@ AddMetaData(object, metadata, col.name = NULL) \method{AddMetaData}{Assay}(object, metadata, col.name = NULL) +\method{AddMetaData}{Assay5}(object, metadata, col.name = NULL) + \method{AddMetaData}{Seurat}(object, metadata, col.name = NULL) } \arguments{ diff --git a/man/Assay-class.Rd b/man/Assay-class.Rd index 2380a1a8..2dfe667c 100644 --- a/man/Assay-class.Rd +++ b/man/Assay-class.Rd @@ -20,8 +20,6 @@ cell expression data such as RNA-seq, protein, or imputed expression data. \item{\code{scale.data}}{Scaled expression data} -\item{\code{key}}{Key for the Assay} - \item{\code{assay.orig}}{Original assay that this assay is based off of. Used to track assay provenance} @@ -30,10 +28,25 @@ single cells} \item{\code{meta.features}}{Feature-level metadata} -\item{\code{misc}}{Utility slot for storing additional data associated with the assay} +\item{\code{misc}}{A named list of unstructured miscellaneous data} + +\item{\code{key}}{A one-length character vector with the object's key; keys must +be one or more alphanumeric characters followed by an underscore +\dQuote{\code{_}} (regex pattern +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}})} }} \seealso{ -\code{\link{Assay-methods}} +v3 Assay object, validity, and interaction methods: +\code{\link{$.Assay}()}, +\code{\link{Assay-validity}}, +\code{\link{CreateAssayObject}()}, +\code{\link{[.Assay}()}, +\code{\link{[[.Assay}()}, +\code{\link{dim.Assay}()}, +\code{\link{dimnames.Assay}()}, +\code{\link{merge.Assay}()}, +\code{\link{split.Assay}()}, +\code{\link{subset.Assay}()} } \concept{assay} diff --git a/man/Assay-methods.Rd b/man/Assay-methods.Rd deleted file mode 100644 index 01be0f63..00000000 --- a/man/Assay-methods.Rd +++ /dev/null @@ -1,161 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assay.R -\name{Assay-methods} -\alias{Assay-methods} -\alias{[.Assay} -\alias{[[.Assay} -\alias{dim.Assay} -\alias{dimnames.Assay} -\alias{head.Assay} -\alias{merge.Assay} -\alias{subset.Assay} -\alias{tail.Assay} -\alias{[[<-,Assay,ANY,ANY,ANY-method} -\alias{colMeans,Assay-method} -\alias{colSums,Assay-method} -\alias{rowMeans,Assay-method} -\alias{rowSums,Assay-method} -\alias{show,Assay-method} -\title{\code{Assay} Methods} -\usage{ -\method{[}{Assay}(x, i, j, ...) - -\method{[[}{Assay}(x, i, ..., drop = FALSE) - -\method{dim}{Assay}(x) - -\method{dimnames}{Assay}(x) - -\method{head}{Assay}(x, n = 10L, ...) - -\method{merge}{Assay}(x = NULL, y = NULL, add.cell.ids = NULL, merge.data = TRUE, ...) - -\method{subset}{Assay}(x, cells = NULL, features = NULL, ...) - -\method{tail}{Assay}(x, n = 10L, ...) - -\S4method{[[}{Assay,ANY,ANY,ANY}(x, i, j, ...) <- value - -\S4method{colMeans}{Assay}(x, na.rm = FALSE, dims = 1, ..., slot = "data") - -\S4method{colSums}{Assay}(x, na.rm = FALSE, dims = 1, ..., slot = "data") - -\S4method{rowMeans}{Assay}(x, na.rm = FALSE, dims = 1, ..., slot = "data") - -\S4method{rowSums}{Assay}(x, na.rm = FALSE, dims = 1, ..., slot = "data") - -\S4method{show}{Assay}(object) -} -\arguments{ -\item{x, object}{An \code{\link{Assay}} object} - -\item{i, features}{For \code{[[}: metadata names; for all other methods, -feature names or indices} - -\item{j, cells}{Cell names or indices} - -\item{...}{Arguments passed to other methods} - -\item{drop}{See \code{\link[base]{drop}}} - -\item{n}{an integer vector of length up to \code{dim(x)} (or 1, - for non-dimensioned objects). Values specify the indices to be - selected in the corresponding dimension (or along the length) of the - object. A positive value of \code{n[i]} includes the first/last - \code{n[i]} indices in that dimension, while a negative value - excludes the last/first \code{abs(n[i])}, including all remaining - indices. \code{NA} or non-specified values (when \code{length(n) < - length(dim(x))}) select all indices in that dimension. Must - contain at least one non-missing value.} - -\item{y}{A vector or list of one or more objects to merge} - -\item{add.cell.ids}{A character vector of \code{length(x = c(x, y))}; -appends the corresponding values to the start of each objects' cell names} - -\item{merge.data}{Merge the data slots instead of just merging the counts -(which requires renormalization); this is recommended if the same -normalization approach was applied to all objects} - -\item{value}{Additional metadata to add} - -\item{na.rm}{logical. Should missing values (including \code{NaN}) - be omitted from the calculations?} - -\item{dims}{completely ignored by the \code{Matrix} methods.} - -\item{slot}{Name of assay expression matrix to calculate column/row -means/sums on} -} -\value{ -\code{[}: The \code{data} slot for features \code{i} and cells -\code{j} - -\code{[[}: The feature-level metadata for \code{i} - -\code{dim}: The number of features (\code{nrow}) and cells -(\code{ncol}) - -\code{dimnames}: Feature (row) and cell (column) names - -\code{head}: The first \code{n} rows of feature-level metadata - -\code{merge}: Merged object - -\code{subset}: A subsetted \code{Assay} - -\code{tail}: The last \code{n} rows of feature-level metadata - -\code{[[<-}: \code{x} with metadata \code{value} added as \code{i} - -\code{colMeans}: The column (cell-wise) means of \code{slot} - -\code{colSums}: The column (cell-wise) sums of \code{slot} - -\code{rowMeans}: The row (feature-wise) means of \code{slot} - -\code{rowSums}: The row (feature-wise) sums of \code{slot} - -\code{show}: Prints summary to \code{\link[base]{stdout}} and -invisibly returns \code{NULL} -} -\description{ -Methods for \code{\link{Assay}} objects for generics defined in -other packages -} -\section{Functions}{ -\itemize{ -\item \code{[}: Get expression data from an \code{Assay} - -\item \code{[[}: Get feature-level metadata - -\item \code{dim(Assay)}: Number of cells and features for an \code{Assay} - -\item \code{dimnames(Assay)}: Cell- and feature-names for an \code{Assay} - -\item \code{head(Assay)}: Get the first rows of feature-level metadata - -\item \code{merge(Assay)}: Merge \code{Assay} objects - -\item \code{subset(Assay)}: Subset an \code{Assay} - -\item \code{tail(Assay)}: Get the last rows of feature-level metadata - -\item \code{`[[`(x = Assay, i = ANY, j = ANY) <- value}: Add feature-level metadata - -\item \code{colMeans(Assay)}: Calculate \code{\link[base]{colMeans}} on an -\code{Assay} - -\item \code{colSums(Assay)}: Calculate \code{\link[base]{colSums}} on an -\code{Assay} - -\item \code{rowMeans(Assay)}: Calculate \code{\link[base]{rowMeans}} on an -\code{Assay} - -\item \code{rowSums(Assay)}: Calculate \code{\link[base]{rowSums}} on an -\code{Assay} - -\item \code{show(Assay)}: Overview of an \code{Assay} object - -}} -\concept{assay} diff --git a/man/Assay-validity.Rd b/man/Assay-validity.Rd new file mode 100644 index 00000000..f3c67835 --- /dev/null +++ b/man/Assay-validity.Rd @@ -0,0 +1,69 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay.R +\name{Assay-validity} +\alias{Assay-validity} +\title{V3 Assay Validity} +\description{ +Validation of \code{Assay} objects is handled by +\code{\link[methods]{validObject}} +} +\section{\code{data} Validation}{ + +blah +} + +\section{\code{counts} Validation}{ + +blah +} + +\section{\code{scale.data} Validation}{ + +blah +} + +\section{Feature-Level Meta Data Validation}{ + +blah +} + +\section{Variable Feature Validation}{ + +blah +} + +\section{Key Validation}{ + +Keys must be a one-length character vector; a key must be composed of one +of the following: +\itemize{ + \item An empty string (eg. \dQuote{\code{''}}) where \code{nzchar() == 0} + \item An string composed of one or more alphanumeric values + (both lower- and upper-case) that ends with an underscore + (\dQuote{\code{_}}); the first character must be a letter +} +Keys that are not empty strings are validated with the regex +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}} +} + +\examples{ +rna <- pbmc_small[["RNA"]] +validObject(rna) + +} +\seealso{ +\code{\link[methods]{validObject}} + +v3 Assay object, validity, and interaction methods: +\code{\link{$.Assay}()}, +\code{\link{Assay-class}}, +\code{\link{CreateAssayObject}()}, +\code{\link{[.Assay}()}, +\code{\link{[[.Assay}()}, +\code{\link{dim.Assay}()}, +\code{\link{dimnames.Assay}()}, +\code{\link{merge.Assay}()}, +\code{\link{split.Assay}()}, +\code{\link{subset.Assay}()} +} +\concept{assay} diff --git a/man/Assay5-class.Rd b/man/Assay5-class.Rd new file mode 100644 index 00000000..4e76fe89 --- /dev/null +++ b/man/Assay5-class.Rd @@ -0,0 +1,61 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\docType{class} +\name{Assay5-class} +\alias{Assay5-class} +\alias{Assay5} +\title{The v5 \code{Assay} Object} +\description{ +The v5 \code{Assay} is the typical \code{Assay} class used in \pkg{Seurat} +v5; ... +} +\section{Slots}{ + +\describe{ +\item{\code{layers}}{A named list containing expression matrices; each matrix should +be a two-dimensional object containing some subset of cells and features +defined in the \code{cells} and \code{features} slots. Cell and feature +membership is recorded in the \code{cells} and \code{features} slots, +respectively} + +\item{\code{cells}}{A \link[=LogMap]{logical mapping} of cell names +and layer membership; this map contains all the possible cells that this +assay can contain. New layers must have some subset of cells present +in this map} + +\item{\code{features}}{A \link[=LogMap]{logical mapping} of feature +names and layer membership; this map contains all the possible features that +this assay can contain. New layers must have some subset of features +present in this map} + +\item{\code{default}}{A one-length integer with the end index of the +\link[=DefaultLayer]{default layer}; the default layer be +all layers up to and including the layer at index \code{default}} + +\item{\code{assay.orig}}{Original assay that this assay is based off of; +used to track assay provenance} + +\item{\code{meta.data}}{A \link[base:data.frame]{data frame} with feature-level +meta data; should have the same number of rows as \code{features}} + +\item{\code{misc}}{A named list of unstructured miscellaneous data} + +\item{\code{key}}{A one-length character vector with the object's key; keys must +be one or more alphanumeric characters followed by an underscore +\dQuote{\code{_}} (regex pattern +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}})} +}} + +\seealso{ +v5 Assay object, validity, and interaction methods: +\code{\link{$.Assay5}()}, +\code{\link{Assay5-validity}}, +\code{\link{[.Assay5}()}, +\code{\link{[[.Assay5}()}, +\code{\link{dim.Assay5}()}, +\code{\link{dimnames.Assay5}()}, +\code{\link{merge.Assay5}()}, +\code{\link{split.Assay5}()}, +\code{\link{subset.Assay5}()} +} +\concept{assay5} diff --git a/man/Assay5-validity.Rd b/man/Assay5-validity.Rd new file mode 100644 index 00000000..9151beee --- /dev/null +++ b/man/Assay5-validity.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{Assay5-validity} +\alias{Assay5-validity} +\title{V5 Assay Validity} +\description{ +Validation of \code{Assay5} objects is handled by +\code{\link[methods]{validObject}} +} +\section{Layer Validation}{ + +blah +} + +\section{Key Validation}{ + +Keys must be a one-length character vector; a key must be composed of one +of the following: +\itemize{ + \item An empty string (eg. \dQuote{\code{''}}) where \code{nzchar() == 0} + \item An string composed of one or more alphanumeric values + (both lower- and upper-case) that ends with an underscore + (\dQuote{\code{_}}); the first character must be a letter +} +Keys that are not empty strings are validated with the regex +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}} +} + +\seealso{ +\code{\link[methods]{validObject}} + +v5 Assay object, validity, and interaction methods: +\code{\link{$.Assay5}()}, +\code{\link{Assay5-class}}, +\code{\link{[.Assay5}()}, +\code{\link{[[.Assay5}()}, +\code{\link{dim.Assay5}()}, +\code{\link{dimnames.Assay5}()}, +\code{\link{merge.Assay5}()}, +\code{\link{split.Assay5}()}, +\code{\link{subset.Assay5}()} +} +\concept{assay5} diff --git a/man/Assay5T-class.Rd b/man/Assay5T-class.Rd new file mode 100644 index 00000000..b01c992e --- /dev/null +++ b/man/Assay5T-class.Rd @@ -0,0 +1,57 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\docType{class} +\name{Assay5T-class} +\alias{Assay5T-class} +\alias{Assay5T} +\title{The Transposed v5 \code{Assay} Object} +\description{ +The Transposed v5 \code{Assay} Object +} +\section{Slots}{ + +\describe{ +\item{\code{layers}}{A named list containing expression matrices; each matrix should +be a two-dimensional object containing some subset of cells and features +defined in the \code{cells} and \code{features} slots. Cell and feature +membership is recorded in the \code{cells} and \code{features} slots, +respectively} + +\item{\code{cells}}{A \link[=LogMap]{logical mapping} of cell names +and layer membership; this map contains all the possible cells that this +assay can contain. New layers must have some subset of cells present +in this map} + +\item{\code{features}}{A \link[=LogMap]{logical mapping} of feature +names and layer membership; this map contains all the possible features that +this assay can contain. New layers must have some subset of features +present in this map} + +\item{\code{default}}{A one-length integer with the end index of the +\link[=DefaultLayer]{default layer}; the default layer be +all layers up to and including the layer at index \code{default}} + +\item{\code{assay.orig}}{Original assay that this assay is based off of; +used to track assay provenance} + +\item{\code{meta.data}}{A \link[base:data.frame]{data frame} with feature-level +meta data; should have the same number of rows as \code{features}} + +\item{\code{misc}}{A named list of unstructured miscellaneous data} + +\item{\code{key}}{A one-length character vector with the object's key; keys must +be one or more alphanumeric characters followed by an underscore +\dQuote{\code{_}} (regex pattern +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}})} +}} + +\section{Lifecycle}{ + + +\Sexpr[stage=build,results=rd]{lifecycle::badge("experimental")} + +\strong{Warning}: functionality described here is experimental and prone to +change without notice +} + +\keyword{internal} diff --git a/man/AssayData-StdAssay.Rd b/man/AssayData-StdAssay.Rd new file mode 100644 index 00000000..eda0cadb --- /dev/null +++ b/man/AssayData-StdAssay.Rd @@ -0,0 +1,48 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{AssayData-StdAssay} +\alias{AssayData-StdAssay} +\alias{GetAssayData.StdAssay} +\alias{SetAssayData.StdAssay} +\title{Get and Set Assay Data} +\usage{ +\method{GetAssayData}{StdAssay}(object, layer = NULL, slot = deprecated(), ...) + +\method{SetAssayData}{StdAssay}(object, layer, new.data, slot = deprecated(), ...) +} +\arguments{ +\item{object}{An object} + +\item{layer}{Name of layer to get or set} + +\item{slot}{\Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")} Specific assay data to get or set} + +\item{...}{Arguments passed to other methods} + +\item{new.data}{New assay data to add} +} +\value{ +\code{GetAssayData}: returns the specified assay data + +\code{SetAssayData}: \code{object} with the assay data set +} +\description{ +General accessor and setter functions for \code{\link{Assay}} objects. +\code{GetAssayData} can be used to pull information from any of the +expression matrices (eg. \dQuote{counts}, \dQuote{data}, or +\dQuote{scale.data}). \code{SetAssayData} can be used to replace one of these +expression matrices +} +\section{Lifecycle}{ + + +\Sexpr[stage=build,results=rd]{lifecycle::badge("superseded")} + + + +\code{GetAssayData} and \code{SetAssayData} have been superseded. To fetch +expression matrices, use \code{\link{LayerData}}; to set expression data, +use \code{\link{LayerData<-}} +} + +\keyword{internal} diff --git a/man/AssayData.Rd b/man/AssayData.Rd index 623b129a..bee78764 100644 --- a/man/AssayData.Rd +++ b/man/AssayData.Rd @@ -10,29 +10,49 @@ \alias{SetAssayData.Assay} \title{Get and Set Assay Data} \usage{ -GetAssayData(object, slot, ...) +GetAssayData(object, ...) -SetAssayData(object, slot, new.data, ...) +SetAssayData(object, layer, new.data, slot = deprecated(), ...) -\method{GetAssayData}{Seurat}(object, slot = "data", assay = NULL, ...) +\method{GetAssayData}{Seurat}(object, assay = NULL, layer = NULL, slot = deprecated(), ...) -\method{SetAssayData}{Seurat}(object, slot = "data", new.data, assay = NULL, ...) +\method{SetAssayData}{Seurat}( + object, + layer = "data", + new.data, + slot = deprecated(), + assay = NULL, + ... +) -\method{GetAssayData}{Assay}(object, slot = c("data", "scale.data", "counts"), ...) +\method{GetAssayData}{Assay}( + object, + layer = c("data", "scale.data", "counts"), + slot = deprecated(), + ... +) -\method{SetAssayData}{Assay}(object, slot = c("data", "scale.data", "counts"), new.data, ...) +\method{SetAssayData}{Assay}( + object, + layer = c("data", "scale.data", "counts"), + new.data, + slot = deprecated(), + ... +) } \arguments{ \item{object}{An object} -\item{slot}{Specific assay data to get or set} - \item{...}{Arguments passed to other methods} +\item{layer}{Name of layer to get or set} + \item{new.data}{New assay data to add} -\item{assay}{Specific assay to get data from or set data for; defaults to -the \link[SeuratObject:DefaultAssay]{default assay}} +\item{slot}{\Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")} Specific assay data to get or set} + +\item{assay}{Specific assay to get data from or set data for; +defaults to the \link[=DefaultAssay]{default assay}} } \value{ \code{GetAssayData}: returns the specified assay data @@ -46,27 +66,39 @@ expression matrices (eg. \dQuote{counts}, \dQuote{data}, or \dQuote{scale.data}). \code{SetAssayData} can be used to replace one of these expression matrices } +\section{Lifecycle}{ + + +\Sexpr[stage=build,results=rd]{lifecycle::badge("superseded")} + + + +\code{GetAssayData} and \code{SetAssayData} have been superseded. To fetch +expression matrices, use \code{\link{LayerData}}; to set expression data, +use \code{\link{LayerData<-}} +} + \examples{ # Get assay data from the default assay in a Seurat object GetAssayData(object = pbmc_small, slot = "data")[1:5,1:5] -# Set an Assay slot through the Seurat object -count.data <- GetAssayData(object = pbmc_small[["RNA"]], slot = "counts") +# Set an Assay layer through the Seurat object +count.data <- GetAssayData(object = pbmc_small[["RNA"]], layer = "counts") count.data <- as.matrix(x = count.data + 1) new.seurat.object <- SetAssayData( object = pbmc_small, - slot = "counts", + layer = "counts", new.data = count.data, assay = "RNA" ) # Get the data directly from an Assay object -GetAssayData(pbmc_small[["RNA"]], slot = "data")[1:5,1:5] +GetAssayData(pbmc_small[["RNA"]], layer = "data")[1:5,1:5] -# Set an Assay slot directly -count.data <- GetAssayData(pbmc_small[["RNA"]], slot = "counts") +# Set an Assay layer directly +count.data <- GetAssayData(pbmc_small[["RNA"]], layer = "counts") count.data <- as.matrix(x = count.data + 1) -new.assay <- SetAssayData(pbmc_small[["RNA"]], slot = "counts", new.data = count.data) +new.assay <- SetAssayData(pbmc_small[["RNA"]], layer = "counts", new.data = count.data) } \concept{data-access} diff --git a/man/AttachDeps.Rd b/man/AttachDeps.Rd index 65028afa..8c1f6e6f 100644 --- a/man/AttachDeps.Rd +++ b/man/AttachDeps.Rd @@ -16,6 +16,17 @@ Invisibly returns \code{NULL} Helper function to attach required packages. Detects if a package is already attached and if so, skips it. Should be called in \code{\link[base]{.onAttach}} } +\section{Lifecycle}{ + + +\Sexpr[stage=build,results=rd]{lifecycle::badge("superseded")} + + +\code{AttachDeps} has been superseded as of \pkg{SeuratObject} v5.0.0; +as an alternative, list dependencies in the \code{Depends} section of +\code{DESCRIPTION} +} + \examples{ # Use in your .onAttach hook if (FALSE) { diff --git a/man/Boundaries.Rd b/man/Boundaries.Rd index 50a368f0..a1e59b41 100644 --- a/man/Boundaries.Rd +++ b/man/Boundaries.Rd @@ -50,3 +50,4 @@ segmentation boundary set to \code{value} \description{ Get, Set, and Query Segmentation Boundaries } +\concept{spatial} diff --git a/man/CastAssay-StdAssay.Rd b/man/CastAssay-StdAssay.Rd new file mode 100644 index 00000000..b0568779 --- /dev/null +++ b/man/CastAssay-StdAssay.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{CastAssay-StdAssay} +\alias{CastAssay-StdAssay} +\alias{CastAssay.StdAssay} +\title{Cast Assay Layers} +\usage{ +\method{CastAssay}{StdAssay}(object, to, layers = NA, verbose = TRUE, ...) +} +\arguments{ +\item{object}{An object} + +\item{to}{Either a class name or a function that takes a layer and returns +the same layer as a new class} + +\item{layers}{A vector of layers to cast; defaults to all layers} + +\item{verbose}{Show progress updates} + +\item{...}{If \code{to} is a function, arguments passed to \code{to}} +} +\value{ +\code{object} with the layers cast to class specified by \code{to} +} +\description{ +Cast layers in v5 assays to other classes +} +\keyword{internal} diff --git a/man/CastAssay.Rd b/man/CastAssay.Rd new file mode 100644 index 00000000..d911036a --- /dev/null +++ b/man/CastAssay.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R, R/assay5.R +\name{CastAssay} +\alias{CastAssay} +\alias{CastAssay.Assay5} +\title{Cast Assay Layers} +\usage{ +CastAssay(object, to, ...) + +\method{CastAssay}{Assay5}(object, to, layers = NA, verbose = TRUE, ...) +} +\arguments{ +\item{object}{An object} + +\item{to}{Either a class name or a function that takes a layer and returns +the same layer as a new class} + +\item{...}{If \code{to} is a function, arguments passed to \code{to}} + +\item{layers}{A vector of layers to cast; defaults to all layers} + +\item{verbose}{Show progress updates} +} +\value{ +\code{object} with the layers cast to class specified by \code{to} +} +\description{ +Cast layers in v5 assays to other classes +} +\concept{assay5} diff --git a/man/Cells-StdAssay.Rd b/man/Cells-StdAssay.Rd new file mode 100644 index 00000000..0a7bb1e7 --- /dev/null +++ b/man/Cells-StdAssay.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{Cells-StdAssay} +\alias{Cells-StdAssay} +\alias{Cells.StdAssay} +\alias{Features.StdAssay} +\title{Cell and Feature Names} +\usage{ +\method{Cells}{StdAssay}(x, layer = NULL, simplify = TRUE, ...) + +\method{Features}{StdAssay}(x, layer = NULL, simplify = TRUE, ...) +} +\arguments{ +\item{x}{An object} + +\item{layer}{Layer to pull cells/features for; defaults to default layer; +if \code{NA}, returns all cells for the assay} + +\item{simplify}{Simplify the cell/feature names into a single vector; if +\code{FALSE}, separates each cell/feature names by layer} + +\item{...}{Arguments passed to other methods} +} +\value{ +\code{Cell}: A vector of cell names + +\code{Features}: A vector of feature names +} +\description{ +Get the cell and feature names of an object +} +\keyword{internal} diff --git a/man/Cells.Rd b/man/Cells.Rd index f16a1962..2965862f 100644 --- a/man/Cells.Rd +++ b/man/Cells.Rd @@ -1,10 +1,12 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/generics.R, R/default.R, R/dimreduc.R, -% R/neighbor.R +% Please edit documentation in R/generics.R, R/default.R, R/assay5.R, +% R/dimreduc.R, R/neighbor.R \name{Cells} \alias{Cells} \alias{Features} \alias{Cells.default} +\alias{Cells.Assay5} +\alias{Features.Assay5} \alias{Cells.DimReduc} \alias{Cells.Neighbor} \title{Cell and Feature Names} @@ -15,6 +17,10 @@ Features(x, ...) \method{Cells}{default}(x, ...) +\method{Cells}{Assay5}(x, layer = NULL, simplify = TRUE, ...) + +\method{Features}{Assay5}(x, layer = NULL, simplify = TRUE, ...) + \method{Cells}{DimReduc}(x, ...) \method{Cells}{Neighbor}(x, ...) @@ -23,6 +29,12 @@ Features(x, ...) \item{x}{An object} \item{...}{Arguments passed to other methods} + +\item{layer}{Layer to pull cells/features for; defaults to default layer; +if \code{NA}, returns all cells for the assay} + +\item{simplify}{Simplify the cell/feature names into a single vector; if +\code{FALSE}, separates each cell/feature names by layer} } \value{ \code{Cell}: A vector of cell names @@ -35,5 +47,12 @@ Get the cell and feature names of an object \examples{ Cells(x = pbmc_small) +} +\seealso{ + +\code{\link{dimnames.Assay5}()}, +\code{\link{dimnames.Assay}()}, +\code{\link{dimnames.Seurat}()} } \concept{data-access} +\concept{dimnames} diff --git a/man/CellsByIdentities.Rd b/man/CellsByIdentities.Rd index 3fdbad3d..5809ca9c 100644 --- a/man/CellsByIdentities.Rd +++ b/man/CellsByIdentities.Rd @@ -14,7 +14,7 @@ defaults to all identity class levels} \item{cells}{A vector of cells to grouping to} -\item{return.null}{If no cells are request, return a \code{NULL}; +\item{return.null}{If no cells are requested, return a \code{NULL}; by default, throws an error} } \value{ diff --git a/man/Centroids-class.Rd b/man/Centroids-class.Rd index 8674e44b..a52addc2 100644 --- a/man/Centroids-class.Rd +++ b/man/Centroids-class.Rd @@ -29,7 +29,10 @@ to adjust the shape when plotting the centroids} \code{Centroids} methods: \code{\link{Centroids-methods}} Segmentation layer classes: +\code{\link{Centroids-methods}}, \code{\link{Molecules-class}}, -\code{\link{Segmentation-class}} +\code{\link{Molecules-methods}}, +\code{\link{Segmentation-class}}, +\code{\link{Segmentation-methods}} } \concept{segmentation} diff --git a/man/Centroids-methods.Rd b/man/Centroids-methods.Rd index d50b5d56..6c92285c 100644 --- a/man/Centroids-methods.Rd +++ b/man/Centroids-methods.Rd @@ -119,4 +119,12 @@ certain cells } \seealso{ \code{\link{Centroids-class}} + +Segmentation layer classes: +\code{\link{Centroids-class}}, +\code{\link{Molecules-class}}, +\code{\link{Molecules-methods}}, +\code{\link{Segmentation-class}}, +\code{\link{Segmentation-methods}} } +\concept{segmentation} diff --git a/man/CheckDots.Rd b/man/CheckDots.Rd index 20e3f469..4788a580 100644 --- a/man/CheckDots.Rd +++ b/man/CheckDots.Rd @@ -46,4 +46,5 @@ f(x = 3, y = 9) } } +\concept{utils} \keyword{internal} diff --git a/man/CheckFeaturesNames.Rd b/man/CheckFeaturesNames.Rd new file mode 100644 index 00000000..20904093 --- /dev/null +++ b/man/CheckFeaturesNames.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{CheckFeaturesNames} +\alias{CheckFeaturesNames} +\title{Check features names format} +\usage{ +CheckFeaturesNames(data) +} +\arguments{ +\item{data}{a matrix input, rownames(data) are feature names} +} +\value{ +\code{data} with update feature names +} +\description{ +Check features names format +} +\keyword{internal} diff --git a/man/CheckLayersName.Rd b/man/CheckLayersName.Rd new file mode 100644 index 00000000..177a127f --- /dev/null +++ b/man/CheckLayersName.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{CheckLayersName} +\alias{CheckLayersName} +\title{Check layers names for the input list} +\usage{ +CheckLayersName(matrix.list, layers.type = c("counts", "data")) +} +\arguments{ +\item{matrix.list}{A list of matrices} + +\item{layers.type}{layers type, such as counts or data} +} +\description{ +Check layers names for the input list +} +\concept{utils} diff --git a/man/CheckMatrix.Rd b/man/CheckMatrix.Rd index 1cd4e197..ae4ab5b8 100644 --- a/man/CheckMatrix.Rd +++ b/man/CheckMatrix.Rd @@ -36,4 +36,5 @@ Emits warnings for each test and invisibly returns \code{NULL} \description{ Check Matrix Validity } +\concept{utils} \keyword{internal} diff --git a/man/ClassKey.Rd b/man/ClassKey.Rd new file mode 100644 index 00000000..42d17682 --- /dev/null +++ b/man/ClassKey.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{ClassKey} +\alias{ClassKey} +\title{Generate a Class Key} +\usage{ +ClassKey(class, package = NULL) +} +\arguments{ +\item{class}{Class name} + +\item{package}{Optional name of package; by default, will search namespaces +of loaded packages to determine the providing package} +} +\value{ +The class key (\dQuote{\code{package:class}}) +} +\description{ +Generate class keys for S4 classes. A class key follows the following +structure: \dQuote{\code{package:class}} +} +\examples{ +ClassKey("Seurat") + +} +\seealso{ + +\code{\link{s4list}} +} +\concept{s4list} +\concept{utils} +\keyword{internal} diff --git a/man/CreateAssay5Object.Rd b/man/CreateAssay5Object.Rd new file mode 100644 index 00000000..2582e548 --- /dev/null +++ b/man/CreateAssay5Object.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{CreateAssay5Object} +\alias{CreateAssay5Object} +\title{Create a v5 Assay object} +\usage{ +CreateAssay5Object( + counts = NULL, + data = NULL, + min.cells = 0, + min.features = 0, + csum = NULL, + fsum = NULL, + ... +) +} +\arguments{ +\item{counts}{A two-dimensional expression matrix} + +\item{data}{Optional prenormalized data matrix} + +\item{min.cells}{Include features detected in at least this many cells; +will subset the counts matrix as well. To reintroduce excluded features, +create a new object with a lower cutoff} + +\item{min.features}{Include cells where at least this many features +are detected} + +\item{csum}{Function for calculating cell sums} + +\item{fsum}{Function for calculating feature sums} + +\item{...}{Arguments passed to other methods} +} +\value{ +An \code{\link{Assay5}} object +} +\description{ +Create an \code{\link{Assay5}} object from a feature expression matrix; +the expected format of the matrix is features x cells +} +\concept{assay} diff --git a/man/CreateAssayObject.Rd b/man/CreateAssayObject.Rd index f40c12db..1be9aca0 100644 --- a/man/CreateAssayObject.Rd +++ b/man/CreateAssayObject.Rd @@ -9,6 +9,7 @@ CreateAssayObject( data, min.cells = 0, min.features = 0, + key = NULL, check.matrix = FALSE, ... ) @@ -20,12 +21,15 @@ CreateAssayObject( \item{min.cells}{Include features detected in at least this many cells. Will subset the counts matrix as well. To reintroduce excluded features, create a -new object with a lower cutoff.} +new object with a lower cutoff} \item{min.features}{Include cells where at least this many features are -detected.} +detected} -\item{check.matrix}{Check counts matrix for NA, NaN, Inf, and non-integer values} +\item{key}{Optional key to initialize assay with} + +\item{check.matrix}{Check counts matrix for NA, NaN, Inf, and +non-integer values} \item{...}{Arguments passed to \code{\link{as.sparse}}} } @@ -50,5 +54,18 @@ pbmc_rna <- CreateAssayObject(counts = pbmc_raw) pbmc_rna } +} +\seealso{ +v3 Assay object, validity, and interaction methods: +\code{\link{$.Assay}()}, +\code{\link{Assay-class}}, +\code{\link{Assay-validity}}, +\code{\link{[.Assay}()}, +\code{\link{[[.Assay}()}, +\code{\link{dim.Assay}()}, +\code{\link{dimnames.Assay}()}, +\code{\link{merge.Assay}()}, +\code{\link{split.Assay}()}, +\code{\link{subset.Assay}()} } \concept{assay} diff --git a/man/CreateCentroids.Rd b/man/CreateCentroids.Rd index 396eed58..cb33b5f3 100644 --- a/man/CreateCentroids.Rd +++ b/man/CreateCentroids.Rd @@ -22,3 +22,4 @@ A \code{\link[SeuratObject:Centroids-class]{Centroids}} object \description{ Create a \code{\link[SeuratObject:Centroids-class]{Centroids}} Objects } +\concept{spatial} diff --git a/man/CreateDimReducObject.Rd b/man/CreateDimReducObject.Rd index 812c149a..1cde635a 100644 --- a/man/CreateDimReducObject.Rd +++ b/man/CreateDimReducObject.Rd @@ -55,5 +55,16 @@ pca.dr <- CreateDimReducObject( assay = "RNA" ) +} +\seealso{ +Dimensional reduction object, validity, and interaction methods +\code{\link{DimReduc-class}}, +\code{\link{DimReduc-validity}}, +\code{\link{[.DimReduc}()}, +\code{\link{[[.DimReduc}()}, +\code{\link{dim.DimReduc}()}, +\code{\link{merge.DimReduc}()}, +\code{\link{print.DimReduc}()}, +\code{\link{subset.DimReduc}()} } \concept{dimreduc} diff --git a/man/CreateFOV.Rd b/man/CreateFOV.Rd index f0523a6f..e59ef5b3 100644 --- a/man/CreateFOV.Rd +++ b/man/CreateFOV.Rd @@ -80,3 +80,4 @@ Create Spatial Coordinates \seealso{ \code{\link{FOV-class}} } +\concept{spatial} diff --git a/man/CreateMolecules.Rd b/man/CreateMolecules.Rd index c4b3006a..3d22da8c 100644 --- a/man/CreateMolecules.Rd +++ b/man/CreateMolecules.Rd @@ -34,3 +34,4 @@ A \code{\link{Molecules}} object \description{ Create a \code{\link{Molecules}} Object } +\concept{spatial} diff --git a/man/CreateSegmentation.Rd b/man/CreateSegmentation.Rd index 467e527c..5d133bca 100644 --- a/man/CreateSegmentation.Rd +++ b/man/CreateSegmentation.Rd @@ -21,3 +21,4 @@ A \code{\link[SeuratObject:Segmentation-class]{Segmentation}} object \description{ Create a \code{\link[SeuratObject:Segmentation-class]{Segmentation}} Objects } +\concept{spatial} diff --git a/man/CreateSeuratObject.Rd b/man/CreateSeuratObject.Rd index 179a3393..9bf0e620 100644 --- a/man/CreateSeuratObject.Rd +++ b/man/CreateSeuratObject.Rd @@ -4,38 +4,48 @@ \alias{CreateSeuratObject} \alias{CreateSeuratObject.default} \alias{CreateSeuratObject.Assay} +\alias{CreateSeuratObject.Assay5} \title{Create a \code{Seurat} object} \usage{ CreateSeuratObject( counts, - project = "CreateSeuratObject", assay = "RNA", names.field = 1, names.delim = "_", meta.data = NULL, + project = "CreateSeuratObject", ... ) \method{CreateSeuratObject}{default}( counts, - project = "SeuratProject", assay = "RNA", - names.field = 1, + names.field = 1L, names.delim = "_", meta.data = NULL, + project = "SeuratProject", min.cells = 0, min.features = 0, - row.names = NULL, ... ) \method{CreateSeuratObject}{Assay}( counts, + assay = "RNA", + names.field = 1L, + names.delim = "_", + meta.data = NULL, project = "SeuratProject", + ... +) + +\method{CreateSeuratObject}{Assay5}( + counts, assay = "RNA", - names.field = 1, + names.field = 1L, names.delim = "_", meta.data = NULL, + project = "SeuratProject", ... ) } @@ -44,8 +54,6 @@ CreateSeuratObject( unnormalized data with cells as columns and features as rows or an \code{\link{Assay}}-derived object} -\item{project}{\link{Project} name for the \code{Seurat} object} - \item{assay}{Name of the initial assay} \item{names.field}{For the initial identity class for each cell, choose this @@ -63,18 +71,16 @@ Should be a \code{\link[base]{data.frame}} where the rows are cell names and the columns are additional metadata fields. Row names in the metadata need to match the column names of the counts matrix.} +\item{project}{\link{Project} name for the \code{Seurat} object} + \item{...}{Arguments passed to other methods} \item{min.cells}{Include features detected in at least this many cells. Will subset the counts matrix as well. To reintroduce excluded features, create a -new object with a lower cutoff.} +new object with a lower cutoff} \item{min.features}{Include cells where at least this many features are -detected.} - -\item{row.names}{When \code{counts} is a \code{data.frame} or -\code{data.frame}-derived object: an optional vector of feature names to be -used} +detected} } \value{ A \code{\link{Seurat}} object diff --git a/man/Crop.Rd b/man/Crop.Rd index 09d352c9..6230edd8 100644 --- a/man/Crop.Rd +++ b/man/Crop.Rd @@ -22,7 +22,7 @@ Crop(object, x = NULL, y = NULL, coords = c("plot", "tissue"), ...) \code{\link{GetTissueCoordinates}} }} -\item{...}{...} +\item{...}{Arguments passed to other methods} } \value{ \code{object} cropped to the region specified by \code{x} @@ -31,3 +31,4 @@ and \code{y} \description{ Crop Coordinates } +\concept{spatial} diff --git a/man/DefaultAssay-StdAssay.Rd b/man/DefaultAssay-StdAssay.Rd new file mode 100644 index 00000000..77e63de1 --- /dev/null +++ b/man/DefaultAssay-StdAssay.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{DefaultAssay-StdAssay} +\alias{DefaultAssay-StdAssay} +\alias{DefaultAssay.StdAssay} +\alias{DefaultAssay<-.StdAssay} +\title{Default Assay} +\usage{ +\method{DefaultAssay}{StdAssay}(object, ...) + +\method{DefaultAssay}{StdAssay}(object, ...) <- value +} +\arguments{ +\item{object}{An object} + +\item{...}{Arguments passed to other methods} + +\item{value}{Name of assay to set as default} +} +\value{ +\code{DefaultAssay}: The name of the default assay + +\code{DefaultAssay<-}: An object with the default assay updated +} +\description{ +Get and set the default assay +} +\keyword{internal} diff --git a/man/DefaultAssay.Rd b/man/DefaultAssay.Rd index 0991cef5..b80ae146 100644 --- a/man/DefaultAssay.Rd +++ b/man/DefaultAssay.Rd @@ -1,6 +1,6 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/generics.R, R/graph.R, R/assay.R, R/command.R, -% R/dimreduc.R, R/seurat.R +% Please edit documentation in R/generics.R, R/graph.R, R/assay.R, R/assay5.R, +% R/command.R, R/dimreduc.R, R/seurat.R \name{DefaultAssay} \alias{DefaultAssay} \alias{DefaultAssay<-} @@ -8,6 +8,8 @@ \alias{DefaultAssay<-.Graph} \alias{DefaultAssay.Assay} \alias{DefaultAssay<-.Assay} +\alias{DefaultAssay.Assay5} +\alias{DefaultAssay<-.Assay5} \alias{DefaultAssay.SeuratCommand} \alias{DefaultAssay.DimReduc} \alias{DefaultAssay<-.DimReduc} @@ -27,6 +29,10 @@ DefaultAssay(object, ...) <- value \method{DefaultAssay}{Assay}(object, ...) <- value +\method{DefaultAssay}{Assay5}(object, ...) + +\method{DefaultAssay}{Assay5}(object, ...) <- value + \method{DefaultAssay}{SeuratCommand}(object, ...) \method{DefaultAssay}{DimReduc}(object, ...) diff --git a/man/DefaultFOV.Rd b/man/DefaultFOV.Rd index b72fcb54..6d5f7783 100644 --- a/man/DefaultFOV.Rd +++ b/man/DefaultFOV.Rd @@ -34,3 +34,4 @@ to \code{value} \description{ Get and Set the Default FOV } +\concept{spatial} diff --git a/man/DefaultLayer-StdAssay.Rd b/man/DefaultLayer-StdAssay.Rd new file mode 100644 index 00000000..c8ced9c4 --- /dev/null +++ b/man/DefaultLayer-StdAssay.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{DefaultLayer-StdAssay} +\alias{DefaultLayer-StdAssay} +\alias{DefaultLayer.StdAssay} +\alias{DefaultLayer<-.StdAssay} +\title{Default Layer} +\usage{ +\method{DefaultLayer}{StdAssay}(object, ...) + +\method{DefaultLayer}{StdAssay}(object, ...) <- value +} +\arguments{ +\item{object}{An object} + +\item{...}{Arguments passed to other methods} + +\item{value}{Name of layer to set as default} +} +\value{ +\code{DefaultLayer}: The name of the default layer + +\code{DefaultLayer<-}: An object with the default layer updated +} +\description{ +Get and set the default layer +} +\keyword{internal} diff --git a/man/DefaultLayer.Rd b/man/DefaultLayer.Rd new file mode 100644 index 00000000..a51e58c4 --- /dev/null +++ b/man/DefaultLayer.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R, R/assay.R, R/assay5.R +\name{DefaultLayer} +\alias{DefaultLayer} +\alias{DefaultLayer<-} +\alias{DefaultLayer.Assay} +\alias{DefaultLayer.Assay5} +\alias{DefaultLayer<-.Assay5} +\title{Default Layer} +\usage{ +DefaultLayer(object, ...) + +DefaultLayer(object, ...) <- value + +\method{DefaultLayer}{Assay}(object, ...) + +\method{DefaultLayer}{Assay5}(object, ...) + +\method{DefaultLayer}{Assay5}(object, ...) <- value +} +\arguments{ +\item{object}{An object} + +\item{...}{Arguments passed to other methods} + +\item{value}{Name of layer to set as default} +} +\value{ +\code{DefaultLayer}: The name of the default layer + +\code{DefaultLayer<-}: An object with the default layer updated +} +\description{ +Get and set the default layer +} +\concept{assay5} diff --git a/man/DimReduc-class.Rd b/man/DimReduc-class.Rd index 417890b1..3e396eb6 100644 --- a/man/DimReduc-class.Rd +++ b/man/DimReduc-class.Rd @@ -26,13 +26,26 @@ removed when removing its associated assay} \item{\code{stdev}}{A vector of standard deviations} -\item{\code{key}}{Key for the \code{DimReduc}, must be alphanumeric characters -followed by an underscore} - \item{\code{jackstraw}}{A \code{\link{JackStrawData-class}} object associated with this \code{DimReduc}} -\item{\code{misc}}{Utility slot for storing additional data associated with the -\code{DimReduc} (e.g. the total variance of the PCA)} +\item{\code{misc}}{A named list of unstructured miscellaneous data} + +\item{\code{key}}{A one-length character vector with the object's key; keys must +be one or more alphanumeric characters followed by an underscore +\dQuote{\code{_}} (regex pattern +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}})} }} +\seealso{ +Dimensional reduction object, validity, and interaction methods +\code{\link{CreateDimReducObject}()}, +\code{\link{DimReduc-validity}}, +\code{\link{[.DimReduc}()}, +\code{\link{[[.DimReduc}()}, +\code{\link{dim.DimReduc}()}, +\code{\link{merge.DimReduc}()}, +\code{\link{print.DimReduc}()}, +\code{\link{subset.DimReduc}()} +} +\concept{dimreduc} diff --git a/man/DimReduc-methods.Rd b/man/DimReduc-methods.Rd deleted file mode 100644 index 2436f50a..00000000 --- a/man/DimReduc-methods.Rd +++ /dev/null @@ -1,123 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dimreduc.R -\name{DimReduc-methods} -\alias{DimReduc-methods} -\alias{[.DimReduc} -\alias{[[.DimReduc} -\alias{dim.DimReduc} -\alias{dimnames.DimReduc} -\alias{length.DimReduc} -\alias{merge.DimReduc} -\alias{names.DimReduc} -\alias{print.DimReduc} -\alias{print} -\alias{subset.DimReduc} -\alias{show,DimReduc-method} -\title{\code{DimReduc} Methods} -\usage{ -\method{[}{DimReduc}(x, i, j, drop = FALSE, ...) - -\method{[[}{DimReduc}(x, i, j, drop = FALSE, ...) - -\method{dim}{DimReduc}(x) - -\method{dimnames}{DimReduc}(x) - -\method{length}{DimReduc}(x) - -\method{merge}{DimReduc}(x = NULL, y = NULL, add.cell.ids = NULL, ...) - -\method{names}{DimReduc}(x) - -\method{print}{DimReduc}(x, dims = 1:5, nfeatures = 20, projected = FALSE, ...) - -\method{subset}{DimReduc}(x, cells = NULL, features = NULL, ...) - -\S4method{show}{DimReduc}(object) -} -\arguments{ -\item{x, object}{A \code{\link{DimReduc}} object} - -\item{i}{For \code{[}: feature names or indices; for \code{[[}: cell names -or indices} - -\item{j}{Dimensions to pull for} - -\item{drop}{See \code{\link[base]{drop}}} - -\item{...}{Arguments passed to other methods} - -\item{y}{A vector or list of one or more objects to merge} - -\item{add.cell.ids}{A character vector of \code{length(x = c(x, y))}; -appends the corresponding values to the start of each objects' cell names} - -\item{dims}{Number of dimensions to display} - -\item{nfeatures}{Number of genes to display} - -\item{projected}{Use projected slot} - -\item{cells, features}{Cells and features to keep during the subset} -} -\value{ -\code{[}: Feature loadings for features \code{i} and dimensions -\code{j} - -\code{[[}: Cell embeddings for cells \code{i} and dimensions \code{j} - -\code{dim}: The number of cells (\code{nrow}) and dimensions -(\code{ncol}) - -\code{dimnames}: The cell (row) and dimension (column) names - -\code{length}: The number of dimensions - -\code{names}: The names for the dimensions (eg. \dQuote{PC_1}) - -\code{print}: Displays set of features defining the components and -invisibly returns \code{x} - -\code{subset}: \code{x} for cells \code{cells} and features -\code{features} - -\code{show}: Prints summary to \code{\link[base]{stdout}} and -invisibly returns \code{NULL} -} -\description{ -Methods for \code{\link{DimReduc}} objects for generics defined in -other packages -} -\section{Functions}{ -\itemize{ -\item \code{[}: Pull feature loadings - -\item \code{[[}: Pull cell embeddings - -\item \code{dim(DimReduc)}: The number of cells and dimensions for a -\code{DimReduc} - -\item \code{dimnames(DimReduc)}: The cell and dimension names for a -\code{DimReduc} object - -\item \code{length(DimReduc)}: The number of dimensions for a \code{DimReduc} -object - -\item \code{merge(DimReduc)}: Merge two or more \code{DimReduc} objects -together - -\item \code{names(DimReduc)}: The dimension names for a \code{DimReduc} object - -\item \code{print(DimReduc)}: Prints a set of features that most strongly -define a set of components; \strong{note}: requires feature loadings to be -present in order to work - -\item \code{subset(DimReduc)}: Subset a \code{DimReduc} object - -\item \code{show(DimReduc)}: Show basic summary of a \code{DimReduc} object - -}} -\seealso{ -\code{\link[base]{cat}} -} -\concept{dimreduc} diff --git a/man/DimReduc-validity.Rd b/man/DimReduc-validity.Rd new file mode 100644 index 00000000..37fa89ab --- /dev/null +++ b/man/DimReduc-validity.Rd @@ -0,0 +1,54 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/dimreduc.R +\name{DimReduc-validity} +\alias{DimReduc-validity} +\title{Dimensional Reduction Validity} +\description{ +Validation of \code{DimReduc} objects is handled by +\code{\link[methods]{validObject}} +} +\section{Cell Embeddings Validation}{ + +The cell embeddings matrix must be a numeric matrix of dimensions +\eqn{n_{cells}} by \eqn{d_{dimensions}}; row names must be the cell names +and column names must be the dimension identifier. The dimension identifier +must be \dQuote{\code{key_dimension}} (eg. \dQuote{\code{PC_1}}). Dimension +identifiers must be in order and cannot be skipped +} + +\section{Feature and Projected Feature Loadings Validation}{ + +blah +} + +\section{Standard Deviations Validation}{ + +blah +} + +\section{Key Validation}{ + +Keys must be a one-length character vector; a key must be composed of one +of the following: +\itemize{ + \item An empty string (eg. \dQuote{\code{''}}) where \code{nzchar() == 0} + \item An string composed of one or more alphanumeric values + (both lower- and upper-case) that ends with an underscore + (\dQuote{\code{_}}); the first character must be a letter +} +Keys that are not empty strings are validated with the regex +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}} +} + +\seealso{ +Dimensional reduction object, validity, and interaction methods +\code{\link{CreateDimReducObject}()}, +\code{\link{DimReduc-class}}, +\code{\link{[.DimReduc}()}, +\code{\link{[[.DimReduc}()}, +\code{\link{dim.DimReduc}()}, +\code{\link{merge.DimReduc}()}, +\code{\link{print.DimReduc}()}, +\code{\link{subset.DimReduc}()} +} +\concept{dimreduc} diff --git a/man/EmptyDF.Rd b/man/EmptyDF.Rd index 53dcfa43..bc33e15c 100644 --- a/man/EmptyDF.Rd +++ b/man/EmptyDF.Rd @@ -17,4 +17,9 @@ zero columns Create an empty \link[base:data.frame]{data frame} with no row names and zero columns } +\examples{ +EmptyDF(4L) + +} +\concept{utils} \keyword{internal} diff --git a/man/ExtractField.Rd b/man/ExtractField.Rd new file mode 100644 index 00000000..6d48e74f --- /dev/null +++ b/man/ExtractField.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{ExtractField} +\alias{ExtractField} +\title{Extract delimiter information from a string.} +\usage{ +ExtractField(string, field = 1, delim = "_") +} +\arguments{ +\item{string}{String to parse.} + +\item{field}{Integer(s) indicating which field(s) to extract. Can be a +vector multiple numbers.} + +\item{delim}{Delimiter to use, set to underscore by default.} +} +\value{ +A new string, that parses out the requested fields, and +(if multiple), rejoins them with the same delimiter +} +\description{ +Parses a string (usually a cell name) and extracts fields based +on a delimiter +} +\examples{ +ExtractField('Hello World', field = 1, delim = '_') + +} +\concept{utils} +\keyword{internal} diff --git a/man/FOV-class.Rd b/man/FOV-class.Rd index b28c3d91..d136f5ae 100644 --- a/man/FOV-class.Rd +++ b/man/FOV-class.Rd @@ -14,24 +14,25 @@ Compatible with \code{\link{SpatialImage}} \section{Slots}{ \describe{ -\item{\code{molecules}}{(\code{\link[base]{list}}) A named list of +\item{\code{molecules}}{A named list of \code{\link[SeuratObject:Molecules-class]{Molecules}} objects defining spatially-resolved molecular coordinates} -\item{\code{boundaries}}{(\code{[named]\link[base]{list}} -\{\code{\link[SeuratObject:Segmentation-class]{Segmentation}}, -\code{\link[SeuratObject:Centroids-class]{Centroids}}\}) A named list of +\item{\code{boundaries}}{A named list of \code{\link[SeuratObject:Segmentation-class]{Segmentation}} and \code{\link[SeuratObject:Centroids-class]{Centroids}} objects defining spatially-resolved boundaries} -\item{\code{assay}}{(\code{\link[base:character]{character [1L]}}) A character -naming the associated assay of the spatial coordinates} +\item{\code{assay}}{A character naming the associated assay +of the spatial coordinates} -\item{\code{key}}{(\code{\link[base:character]{character [1L]}}) The key -for the spatial coordinates} +\item{\code{key}}{A one-length character vector with the object's key; keys must +be one or more alphanumeric characters followed by an underscore +\dQuote{\code{_}} (regex pattern +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}})} }} \seealso{ \code{\link{FOV-methods}} } +\concept{fov} diff --git a/man/FOV-methods.Rd b/man/FOV-methods.Rd index d1d0923d..48d90a76 100644 --- a/man/FOV-methods.Rd +++ b/man/FOV-methods.Rd @@ -179,3 +179,4 @@ information to/from a \code{FOV} object \seealso{ \code{\link{FOV-class}} } +\concept{fov} diff --git a/man/FOV-validity.Rd b/man/FOV-validity.Rd new file mode 100644 index 00000000..ac190497 --- /dev/null +++ b/man/FOV-validity.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/fov.R +\name{FOV-validity} +\alias{FOV-validity} +\title{FOV Validity} +\description{ +Validation of \code{FOV} objects is handled by +\code{\link[methods]{validObject}} +} +\section{Boundary Validation}{ + +blah +} + +\section{Molecule Validation}{ + +blah +} + +\seealso{ +\code{\link[methods]{validObject}} +} +\concept{fov} diff --git a/man/FetchData.Rd b/man/FetchData.Rd index 96d3aba3..440530cf 100644 --- a/man/FetchData.Rd +++ b/man/FetchData.Rd @@ -8,15 +8,17 @@ \usage{ FetchData(object, ...) -\method{FetchData}{DimReduc}( +\method{FetchData}{DimReduc}(object, vars, cells = NULL, ...) + +\method{FetchData}{Seurat}( object, vars, cells = NULL, - slot = c("embeddings", "loadings", "projected"), + layer = NULL, + clean = TRUE, + slot = deprecated(), ... ) - -\method{FetchData}{Seurat}(object, vars, cells = NULL, slot = "data", ...) } \arguments{ \item{object}{An object} @@ -28,7 +30,21 @@ pull identity classes} \item{cells}{Cells to collect data for (default is all cells)} -\item{slot}{Slot to pull feature data for} +\item{layer}{Layer to pull feature data for} + +\item{clean}{Remove cells that are missing data; choose from: +\itemize{ + \item \dQuote{\code{all}}: consider all columns for cleaning + \item \dQuote{\code{ident}}: consider all columns except the identity + class for cleaning + \item \dQuote{\code{project}}: consider all columns except the identity + class for cleaning; fill missing identity values with the object's project + \item \dQuote{\code{none}}: do not clean +} +Passing \code{TRUE} is a shortcut for \dQuote{\code{ident}}; passing +\code{FALSE} is a shortcut for \dQuote{\code{none}}} + +\item{slot}{Deprecated in favor of \code{layer}} } \value{ A data frame with cells as rows and cellular data as columns diff --git a/man/FilterObjects.Rd b/man/FilterObjects.Rd index e78151af..f1e7e751 100644 --- a/man/FilterObjects.Rd +++ b/man/FilterObjects.Rd @@ -4,7 +4,7 @@ \alias{FilterObjects} \title{Find Sub-objects of a Certain Class} \usage{ -FilterObjects(object, classes.keep = c("Assay", "DimReduc")) +FilterObjects(object, classes.keep = c("Assay", "StdAssay", "DimReduc")) } \arguments{ \item{object}{A \code{\link{Seurat}} object} @@ -19,6 +19,14 @@ that are of class \code{classes.keep} Get the names of objects within a \code{Seurat} object that are of a certain class } +\section{Lifecycle}{ + + +\Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")} + +\code{FilterObjects} was deprecated in version 5.0.0; use \code{\link{.FilterObjects}} instead +} + \examples{ FilterObjects(pbmc_small) diff --git a/man/Graph-class.Rd b/man/Graph-class.Rd index 59c47890..1743428e 100644 --- a/man/Graph-class.Rd +++ b/man/Graph-class.Rd @@ -17,4 +17,8 @@ We do this to enable future expandability of graphs. \seealso{ \code{\link[Matrix]{dgCMatrix-class}} + +Other graph: +\code{\link{as.Graph}()} } +\concept{graph} diff --git a/man/Idents.Rd b/man/Idents.Rd index 0516d801..6a99f820 100644 --- a/man/Idents.Rd +++ b/man/Idents.Rd @@ -33,7 +33,7 @@ StashIdent(object, save.name, ...) \method{Idents}{Seurat}(object, ...) -\method{Idents}{Seurat}(object, cells = NULL, drop = FALSE, ...) <- value +\method{Idents}{Seurat}(object, cells = NULL, drop = FALSE, replace = FALSE, ...) <- value \method{ReorderIdent}{Seurat}( object, @@ -72,6 +72,8 @@ identities themselves} \item{drop}{Drop unused levels} +\item{replace}{Replace identities for unset cells with \code{NA}} + \item{reverse}{Reverse ordering} \item{afxn}{Function to evaluate each identity class based on; default is diff --git a/man/Indices.Rd b/man/Indices.Rd index 72a67101..429ba710 100644 --- a/man/Indices.Rd +++ b/man/Indices.Rd @@ -12,7 +12,7 @@ Indices(object, ...) \arguments{ \item{object}{An object} -\item{...}{Arguments passed to other methods;} +\item{...}{Arguments passed to other methods} } \value{ A matrix with the nearest neighbor indices diff --git a/man/IsMatrixEmpty.Rd b/man/IsMatrixEmpty.Rd index ddfa4bf2..f88f8e26 100644 --- a/man/IsMatrixEmpty.Rd +++ b/man/IsMatrixEmpty.Rd @@ -1,10 +1,13 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/utils.R +% Please edit documentation in R/generics.R, R/utils.R \name{IsMatrixEmpty} \alias{IsMatrixEmpty} +\alias{IsMatrixEmpty.default} \title{Check if a matrix is empty} \usage{ IsMatrixEmpty(x) + +\method{IsMatrixEmpty}{default}(x) } \arguments{ \item{x}{A matrix} diff --git a/man/IsNamedList.Rd b/man/IsNamedList.Rd index e4b9f610..6b0d0825 100644 --- a/man/IsNamedList.Rd +++ b/man/IsNamedList.Rd @@ -22,3 +22,15 @@ IsNamedList(x, all.unique = TRUE, allow.empty = FALSE, pass.zero = FALSE) Check to see if a list has names; also check to enforce that all names are present and unique } +\examples{ +IsNamedList(list()) +IsNamedList(list(), pass.zero = TRUE) +IsNamedList(list(1, 2, 3)) +IsNamedList(list(a = 1, b = 2, c = 3)) +IsNamedList(list(a = 1, 2, c = 3)) +IsNamedList(list(a = 1, 2, c = 3), allow.empty = TRUE) +IsNamedList(list(a = 1, a = 2, a = 3)) +IsNamedList(list(a = 1, a = 2, a = 3), all.unique = FALSE) + +} +\concept{utils} diff --git a/man/IsSparse.Rd b/man/IsSparse.Rd new file mode 100644 index 00000000..d1095d02 --- /dev/null +++ b/man/IsSparse.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/sparse.R +\name{IsSparse} +\alias{IsSparse} +\title{Is a Matrix Sparse} +\usage{ +IsSparse(x) +} +\arguments{ +\item{x}{A matrix} +} +\value{ +... +} +\description{ +Is a Matrix Sparse +} +\examples{ +IsSparse(matrix()) +IsSparse(LayerData(pbmc_small, "counts")) + +} +\seealso{ + +\code{\link{.SparseSlots}()}, +\code{\link{RegisterSparseMatrix}()} +} +\concept{sparse} +\keyword{internal} diff --git a/man/Key-validity.Rd b/man/Key-validity.Rd new file mode 100644 index 00000000..19397e7c --- /dev/null +++ b/man/Key-validity.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/keymixin.R +\name{Key-validity} +\alias{Key-validity} +\title{Key Validity} +\description{ +Validation of \code{\link{KeyMixin}} objects is handled by +\code{\link[methods]{validObject}} +} +\section{Key Validation}{ + +Keys must be a one-length character vector; a key must be composed of one +of the following: +\itemize{ + \item An empty string (eg. \dQuote{\code{''}}) where \code{nzchar() == 0} + \item An string composed of one or more alphanumeric values + (both lower- and upper-case) that ends with an underscore + (\dQuote{\code{_}}); the first character must be a letter +} +Keys that are not empty strings are validated with the regex +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}} +} + +\seealso{ + +\code{\link{.KeyPattern}()}, +\code{\link{.RandomKey}()}, +\code{\link{KeyMixin-class}} +} +\concept{key} +\keyword{internal} diff --git a/man/Key.Rd b/man/Key.Rd index d50418f3..3f38c257 100644 --- a/man/Key.Rd +++ b/man/Key.Rd @@ -1,11 +1,14 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/generics.R, R/assay.R, R/dimreduc.R, R/seurat.R +% Please edit documentation in R/generics.R, R/assay.R, R/assay5.R, +% R/dimreduc.R, R/seurat.R \name{Key} \alias{Key} -\alias{Key<-} \alias{Keys} +\alias{Key<-} \alias{Key.Assay} \alias{Key<-.Assay} +\alias{Key.Assay5} +\alias{Key<-.Assay5} \alias{Key.DimReduc} \alias{Key<-.DimReduc} \alias{Key.Seurat} @@ -14,14 +17,18 @@ \usage{ Key(object, ...) -Key(object, ...) <- value - Keys(object, ...) +Key(object, ...) <- value + \method{Key}{Assay}(object, ...) \method{Key}{Assay}(object, ...) <- value +\method{Key}{Assay5}(object, ...) + +\method{Key}{Assay5}(object, ...) <- value + \method{Key}{DimReduc}(object, ...) \method{Key}{DimReduc}(object, ...) <- value @@ -40,9 +47,9 @@ Keys(object, ...) \value{ \code{Key}: the object key -\code{Key<-}: \code{object} with an updated key - \code{Keys}: a named vector of keys of sub-objects + +\code{Key<-}: \code{object} with an updated key } \description{ Get and set object keys diff --git a/man/KeyMixin-class.Rd b/man/KeyMixin-class.Rd index cdb1f8e0..7a966699 100644 --- a/man/KeyMixin-class.Rd +++ b/man/KeyMixin-class.Rd @@ -27,7 +27,8 @@ \value{ \code{Key.character}: \code{object} but as a syntactically-valid key -\code{Key.KeyMixin}: The key from \code{object} +\code{Key.KeyMixin}: The key from \code{object}; if no key set, +returns \code{NULL} \code{Key<-}: \code{object} with the key set to \code{value} } @@ -47,7 +48,15 @@ behavior for getting, setting, and validating keys \describe{ \item{\code{key}}{A one-length character vector with the object's key; keys must be one or more alphanumeric characters followed by an underscore -\dQuote{\code{_}} (regex pattern \dQuote{\code{^[[:alnum:]]+_$}})} +\dQuote{\code{_}} (regex pattern +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}})} }} +\seealso{ + +\code{\link{.KeyPattern}()}, +\code{\link{.RandomKey}()}, +\code{\link{Key-validity}} +} +\concept{key} \keyword{internal} diff --git a/man/Layers-StdAssay.Rd b/man/Layers-StdAssay.Rd new file mode 100644 index 00000000..4f4c7867 --- /dev/null +++ b/man/Layers-StdAssay.Rd @@ -0,0 +1,65 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{Layers-StdAssay} +\alias{Layers-StdAssay} +\alias{LayerData.StdAssay} +\alias{LayerData<-.StdAssay} +\alias{Layers.StdAssay} +\title{Query and Manipulate Assay Layers} +\usage{ +\method{LayerData}{StdAssay}( + object, + layer = NULL, + cells = NULL, + features = NULL, + fast = FALSE, + slot = deprecated(), + ... +) + +\method{LayerData}{StdAssay}(object, layer, features = NULL, cells = NULL, ...) <- value + +\method{Layers}{StdAssay}(object, search = NA, ...) +} +\arguments{ +\item{object}{An object} + +\item{layer}{Name of layer to fetch or set} + +\item{features, cells}{Vectors of features/cells to include} + +\item{fast}{Determine how to return the layer data; choose from: +\describe{ + \item{\code{FALSE}}{Apply any transpositions and attempt to add + feature/cell names (if supported) back to the layer data} + \item{\code{NA}}{Attempt to add feature/cell names back to the layer data, + skip any transpositions} + \item{\code{TRUE}}{Do not apply any transpositions or add feature/cell + names to the layer data} +}} + +\item{slot}{\Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")}} + +\item{...}{Arguments passed to other methods} + +\item{value}{New two-dimensional data to be added as a layer} + +\item{search}{A pattern to search layer names for; pass one of: +\itemize{ + \item \dQuote{\code{NA}} to pull all layers + \item \dQuote{\code{NULL}} to pull the default layer(s) + \item a \link[base:grep]{regular expression} that matches layer names +}} +} +\value{ +\code{LayerData}: the layer data for \code{layer} from \code{object} + +\code{Layer<-}: \code{object} with \code{value} added as a layer +named \code{layer} + +\code{Layers}: the names of the layers present in \code{object} +} +\description{ +Query and Manipulate Assay Layers +} +\keyword{internal} diff --git a/man/Layers.Rd b/man/Layers.Rd new file mode 100644 index 00000000..bf5b9cf6 --- /dev/null +++ b/man/Layers.Rd @@ -0,0 +1,100 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R, R/assay.R, R/assay5.R, R/seurat.R +\name{LayerData} +\alias{LayerData} +\alias{LayerData<-} +\alias{Layers} +\alias{LayerData.Assay} +\alias{LayerData<-.Assay} +\alias{Layers.Assay} +\alias{LayerData.Assay5} +\alias{LayerData<-.Assay5} +\alias{Layers.Assay5} +\alias{LayerData.Seurat} +\alias{LayerData<-.Seurat} +\alias{Layers.Seurat} +\title{Query and Manipulate Assay Layers} +\usage{ +LayerData(object, layer, ...) + +LayerData(object, layer, ...) <- value + +Layers(object, ...) + +\method{LayerData}{Assay}( + object, + layer = NULL, + cells = NULL, + features = NULL, + slot = deprecated(), + ... +) + +\method{LayerData}{Assay}(object, layer, ...) <- value + +\method{Layers}{Assay}(object, search = NA, ...) + +\method{LayerData}{Assay5}( + object, + layer = NULL, + cells = NULL, + features = NULL, + fast = FALSE, + slot = deprecated(), + ... +) + +\method{LayerData}{Assay5}(object, layer, features = NULL, cells = NULL, ...) <- value + +\method{Layers}{Assay5}(object, search = NA, ...) + +\method{LayerData}{Seurat}(object, layer = NULL, assay = NULL, slot = deprecated(), ...) + +\method{LayerData}{Seurat}(object, layer, assay = NULL, ...) <- value + +\method{Layers}{Seurat}(object, search = NA, assay = NULL, ...) +} +\arguments{ +\item{object}{An object} + +\item{layer}{Name of layer to fetch or set} + +\item{...}{Arguments passed to other methods} + +\item{value}{New two-dimensional data to be added as a layer} + +\item{features, cells}{Vectors of features/cells to include} + +\item{slot}{\Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")}} + +\item{search}{A pattern to search layer names for; pass one of: +\itemize{ + \item \dQuote{\code{NA}} to pull all layers + \item \dQuote{\code{NULL}} to pull the default layer(s) + \item a \link[base:grep]{regular expression} that matches layer names +}} + +\item{fast}{Determine how to return the layer data; choose from: +\describe{ + \item{\code{FALSE}}{Apply any transpositions and attempt to add + feature/cell names (if supported) back to the layer data} + \item{\code{NA}}{Attempt to add feature/cell names back to the layer data, + skip any transpositions} + \item{\code{TRUE}}{Do not apply any transpositions or add feature/cell + names to the layer data} +}} + +\item{assay}{Name of assay to fetch layer data from or assign layer data to} +} +\value{ +\code{LayerData}: the layer data for \code{layer} from \code{object} + +\code{Layer<-}: \code{object} with \code{value} added as a layer +named \code{layer} + +\code{Layers}: the names of the layers present in \code{object} +} +\description{ +Query and Manipulate Assay Layers +} +\concept{data-access} diff --git a/man/LogMap-class.Rd b/man/LogMap-class.Rd index f687291f..29cdeeb6 100644 --- a/man/LogMap-class.Rd +++ b/man/LogMap-class.Rd @@ -1,8 +1,7 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/logmap.R \docType{class} -\name{LogMap-class} -\alias{LogMap-class} +\name{LogMap} \alias{LogMap} \alias{[[,LogMap,character,missing-method} \alias{[[,LogMap,missing,missing-method} @@ -11,7 +10,7 @@ \alias{[[<-,LogMap,character,missing,integer-method} \alias{[[<-,LogMap,character,missing,NULL-method} \alias{[[<-,LogMap,character,missing,numeric-method} -\alias{show,LogMap-method} +\alias{LogMap-class} \title{A Logical Map} \usage{ LogMap(y) @@ -29,13 +28,11 @@ LogMap(y) \S4method{[[}{LogMap,character,missing,`NULL`}(x, i, j, ...) <- value \S4method{[[}{LogMap,character,missing,numeric}(x, i, j, ...) <- value - -\S4method{show}{LogMap}(object) } \arguments{ \item{y}{A character vector} -\item{x, object}{A \code{LogMap} object} +\item{x}{A \code{LogMap} object} \item{i}{A character vector of length 1, or \code{NULL}} @@ -60,9 +57,9 @@ the observations for \code{i}; otherwise, \code{x} with a new column for \description{ A simple container for storing mappings of values using logical matrices. Keeps track of which values (rows) are present in which observations -(columns). \code{LogMap} objects can be created with \code{LogMap()}; queries -can be performed with \code{[[} and observations can be added or removed -with \code{[[<-} +(columns). \code{LogMap} objects can be created with \code{LogMap()}; +queries can be performed with \code{[[} and observations can be added +or removed with \code{[[<-} } \section{Slots}{ @@ -81,6 +78,7 @@ rownames(map) # Add an observation to the LogMap map[['obs']] <- c(1, 3, 7) +map[['entry']] <- c(2, 7, 10) map # Get the names of observations in the LogMap @@ -94,6 +92,16 @@ map[[]] # Remove an observation from the LogMap map[['obs']] <- NULL +map[['entry']] <- NULL map } +\seealso{ +Logical map objects, validity, and interaction methods: +\code{\link{LogMap-validity}}, +\code{\link{as.matrix.LogMap}()}, +\code{\link{droplevels.LogMap}()}, +\code{\link{intersect.LogMap}()}, +\code{\link{labels.LogMap}()} +} +\concept{logmap} diff --git a/man/LogMap-validity.Rd b/man/LogMap-validity.Rd new file mode 100644 index 00000000..8dc62e61 --- /dev/null +++ b/man/LogMap-validity.Rd @@ -0,0 +1,44 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/logmap.R +\name{LogMap-validity} +\alias{LogMap-validity} +\title{Logical Map Validity} +\description{ +Validation of \code{LogMap} objects is handled by +\code{\link[methods]{validObject}} +} +\section{Data Validation}{ + +Logical maps must be a logical matrix containing only TRUE or FALSE values +} + +\section{Value Validation}{ + +All values must be named within the rownames of the object. Duplicate or +empty (\code{""}) values are not allowed +} + +\section{Observation Validation}{ + +All observations must be named within the column names of the object. +Duplicate or empty (\code{""}) observations are not allowed +} + +\examples{ +map <- LogMap(letters[1:10]) +map[['obs']] <- c(1, 3, 7) +map[['entry']] <- c(2, 7, 10) +validObject(map) + +} +\seealso{ +\code{\link[methods]{validObject}} + +Logical map objects, validity, and interaction methods: +\code{\link{LogMap}}, +\code{\link{as.matrix.LogMap}()}, +\code{\link{droplevels.LogMap}()}, +\code{\link{intersect.LogMap}()}, +\code{\link{labels.LogMap}()} +} +\concept{logmap} diff --git a/man/LogSeuratCommand.Rd b/man/LogSeuratCommand.Rd index 6c69fae5..a6fce4bc 100644 --- a/man/LogSeuratCommand.Rd +++ b/man/LogSeuratCommand.Rd @@ -9,11 +9,11 @@ LogSeuratCommand(object, return.command = FALSE) \arguments{ \item{object}{Name of Seurat object} -\item{return.command}{Return a \link{SeuratCommand} object instead} +\item{return.command}{Return a \code{\link{SeuratCommand}} object instead} } \value{ -If \code{return.command}, returns a SeuratCommand object. Otherwise, -returns the Seurat object with command stored +If \code{return.command}, returns a \code{\link{SeuratCommand}} +object; otherwise, returns the Seurat object with command stored } \description{ Logs command run, storing the name, timestamp, and argument list. Stores in @@ -21,5 +21,12 @@ the Seurat object } \seealso{ \code{\link{Command}} + +Command log object and interaction methods +\code{\link{$.SeuratCommand}()}, +\code{\link{.DollarNames.SeuratCommand}()}, +\code{\link{SeuratCommand-class}}, +\code{\link{[.SeuratCommand}()}, +\code{\link{as.list.SeuratCommand}()} } \concept{command} diff --git a/man/MatchCells.Rd b/man/MatchCells.Rd index de504fdd..cde8bf79 100644 --- a/man/MatchCells.Rd +++ b/man/MatchCells.Rd @@ -29,3 +29,5 @@ no match can be found, returns \code{NULL} \description{ Match Cells } +\concept{utils} +\keyword{internal} diff --git a/man/Misc-StdAssay.Rd b/man/Misc-StdAssay.Rd new file mode 100644 index 00000000..7d09101e --- /dev/null +++ b/man/Misc-StdAssay.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{Misc-StdAssay} +\alias{Misc-StdAssay} +\alias{Misc.StdAssay} +\alias{Misc<-.StdAssay} +\title{Get and set miscellaneous data} +\usage{ +\method{Misc}{StdAssay}(object, slot = NULL, ...) + +\method{Misc}{StdAssay}(object, slot, ...) <- value +} +\arguments{ +\item{object}{An object} + +\item{slot}{Name of specific bit of meta data to pull} + +\item{...}{Arguments passed to other methods} + +\item{value}{Data to add} +} +\value{ +Miscellaneous data + +An object with miscellaneous data added +} +\description{ +Get and set miscellaneous data +} +\keyword{internal} diff --git a/man/Misc.Rd b/man/Misc.Rd index 94af1c94..67d269b0 100644 --- a/man/Misc.Rd +++ b/man/Misc.Rd @@ -1,10 +1,13 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/generics.R, R/assay.R, R/dimreduc.R, R/seurat.R +% Please edit documentation in R/generics.R, R/assay.R, R/assay5.R, +% R/dimreduc.R, R/seurat.R \name{Misc} \alias{Misc} \alias{Misc<-} \alias{Misc.Assay} \alias{Misc<-.Assay} +\alias{Misc.Assay5} +\alias{Misc<-.Assay5} \alias{Misc.DimReduc} \alias{Misc<-.DimReduc} \alias{Misc.Seurat} @@ -19,6 +22,10 @@ Misc(object, ...) <- value \method{Misc}{Assay}(object, slot, ...) <- value +\method{Misc}{Assay5}(object, slot = NULL, ...) + +\method{Misc}{Assay5}(object, slot, ...) <- value + \method{Misc}{DimReduc}(object, slot = NULL, ...) \method{Misc}{DimReduc}(object, slot, ...) <- value diff --git a/man/Molecules-class.Rd b/man/Molecules-class.Rd index 69f4af3a..8c5ce1a3 100644 --- a/man/Molecules-class.Rd +++ b/man/Molecules-class.Rd @@ -20,6 +20,9 @@ The Spatial Molecules Class Segmentation layer classes: \code{\link{Centroids-class}}, -\code{\link{Segmentation-class}} +\code{\link{Centroids-methods}}, +\code{\link{Molecules-methods}}, +\code{\link{Segmentation-class}}, +\code{\link{Segmentation-methods}} } \concept{segmentation} diff --git a/man/Molecules-methods.Rd b/man/Molecules-methods.Rd index fa0483b3..3fa91818 100644 --- a/man/Molecules-methods.Rd +++ b/man/Molecules-methods.Rd @@ -55,4 +55,12 @@ molecule coordinates } \seealso{ \code{\link{Molecules-class}} + +Segmentation layer classes: +\code{\link{Centroids-class}}, +\code{\link{Centroids-methods}}, +\code{\link{Molecules-class}}, +\code{\link{Segmentation-class}}, +\code{\link{Segmentation-methods}} } +\concept{segmentation} diff --git a/man/NNIndex.Rd b/man/NNIndex.Rd index 79036ba6..7abf2401 100644 --- a/man/NNIndex.Rd +++ b/man/NNIndex.Rd @@ -18,7 +18,7 @@ Index(object, ...) <- value \arguments{ \item{object}{An object} -\item{...}{Arguments passed to other methods;} +\item{...}{Arguments passed to other methods} \item{value}{The index to store} } diff --git a/man/ObjectAccess.Rd b/man/ObjectAccess.Rd index ee0d9b90..eb601423 100644 --- a/man/ObjectAccess.Rd +++ b/man/ObjectAccess.Rd @@ -1,23 +1,28 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/seurat.R +% Please edit documentation in R/generics.R, R/seurat.R \name{Assays} \alias{Assays} \alias{Graphs} \alias{Neighbors} \alias{Reductions} +\alias{Assays.Seurat} \title{Query Specific Object Types} \usage{ -Assays(object, slot = NULL) +Assays(object, ...) Graphs(object, slot = NULL) Neighbors(object, slot = NULL) Reductions(object, slot = NULL) + +\method{Assays}{Seurat}(object, slot = deprecated(), ...) } \arguments{ \item{object}{A \code{\link{Seurat}} object} +\item{...}{Ignored} + \item{slot}{Name of component object to return} } \value{ @@ -29,7 +34,7 @@ List the names of \code{\link{Assay}}, \code{\link{DimReduc}}, \code{\link{Graph}}, \code{\link{Neighbor}} objects } \examples{ -Assays(object = pbmc_small) +Assays(pbmc_small) Graphs(pbmc_small) diff --git a/man/Overlay.Rd b/man/Overlay.Rd index 6919ee51..b0dbbc4b 100644 --- a/man/Overlay.Rd +++ b/man/Overlay.Rd @@ -44,3 +44,9 @@ Create an overlay of some query spatial object (\code{x}) against some target object (\code{y}). Basically, find all components of a query that fall within the bounds of a target spatial region } +\note{ +This function requires the +\href{https://cran.r-project.org/package=sf}{\pkg{sf}} package +to be installed +} +\concept{spatial} diff --git a/man/PackageCheck.Rd b/man/PackageCheck.Rd index 214c67ae..9e3ee008 100644 --- a/man/PackageCheck.Rd +++ b/man/PackageCheck.Rd @@ -17,6 +17,15 @@ Invisibly returns boolean denoting if the package is installed \description{ Check the existence of a package } +\section{Lifecycle}{ + + +\Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")} + +\code{PackageCheck} was deprecated in version 5.0.0; please use +\code{\link[rlang:check_installed]{rlang::check_installed}()} instead +} + \examples{ PackageCheck("SeuratObject", error = FALSE) diff --git a/man/PolyVtx.Rd b/man/PolyVtx.Rd index 74828815..1b2a1adb 100644 --- a/man/PolyVtx.Rd +++ b/man/PolyVtx.Rd @@ -38,4 +38,10 @@ if (requireNamespace("ggplot2", quietly = TRUE)) { \references{ \url{https://stackoverflow.com/questions/3436453/calculate-coordinates-of-a-regular-polygons-vertices} } +\seealso{ + +\code{\link{Angles}} +} +\concept{angles} +\concept{utils} \keyword{internal} diff --git a/man/RandomName.Rd b/man/RandomName.Rd index dbbf2df0..511f60f8 100644 --- a/man/RandomName.Rd +++ b/man/RandomName.Rd @@ -4,19 +4,20 @@ \alias{RandomName} \title{Generate a random name} \usage{ -RandomName(length = 5L, ...) +RandomName(length = 5L, chars = letters, ...) } \arguments{ \item{length}{How long should the name be} +\item{chars}{A vector of 1-length characters to use to generate the name} + \item{...}{Extra parameters passed to \code{\link[base]{sample}}} } \value{ A character with \code{nchar == length} of randomly sampled letters } \description{ -Make a name from randomly sampled lowercase letters, pasted together with no -spaces or other characters +Make a name from randomly sampled characters, pasted together with no spaces } \examples{ set.seed(42L) diff --git a/man/RegisterSparseMatrix.Rd b/man/RegisterSparseMatrix.Rd new file mode 100644 index 00000000..1213d530 --- /dev/null +++ b/man/RegisterSparseMatrix.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/sparse.R +\name{RegisterSparseMatrix} +\alias{RegisterSparseMatrix} +\title{Register Sparse Matrix Classes} +\usage{ +RegisterSparseMatrix(class, package = NULL) +} +\arguments{ +\item{class}{Class name} + +\item{package}{Optional name of package; by default, will search namespaces +of loaded packages to determine the providing package} +} +\value{ +Invisibly returns \code{NULL} +} +\description{ +Register Sparse Matrix Classes +} +\seealso{ + +\code{\link{.SparseSlots}()}, +\code{\link{IsSparse}()} +} +\concept{sparse} +\keyword{internal} diff --git a/man/RenameAssays.Rd b/man/RenameAssays.Rd index de306739..2667d980 100644 --- a/man/RenameAssays.Rd +++ b/man/RenameAssays.Rd @@ -4,11 +4,23 @@ \alias{RenameAssays} \title{Rename assays in a \code{Seurat} object} \usage{ -RenameAssays(object, ...) +RenameAssays( + object, + assay.name = NULL, + new.assay.name = NULL, + verbose = TRUE, + ... +) } \arguments{ \item{object}{A \code{Seurat} object} +\item{assay.name}{original name of assay} + +\item{new.assay.name}{new name of assay} + +\item{verbose}{Whether to print messages} + \item{...}{Named arguments as \code{old.assay = new.assay}} } \value{ diff --git a/man/RenameCells-StdAssay.Rd b/man/RenameCells-StdAssay.Rd new file mode 100644 index 00000000..36c9b129 --- /dev/null +++ b/man/RenameCells-StdAssay.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{RenameCells-StdAssay} +\alias{RenameCells-StdAssay} +\alias{RenameCells.StdAssay} +\title{Rename cells} +\usage{ +\method{RenameCells}{StdAssay}(object, new.names = NULL, ...) +} +\arguments{ +\item{object}{An object} + +\item{new.names}{vector of new cell names} + +\item{...}{Arguments passed to other methods} +} +\value{ +An object with new cell names +} +\description{ +Change the cell names in all the different parts of an object. Can be useful +before combining multiple objects. +} +\details{ +If \code{add.cell.id} is set a prefix is added to existing cell names. If +\code{new.names} is set these will be used to replace existing names. +} +\keyword{internal} diff --git a/man/RenameCells.Rd b/man/RenameCells.Rd index 79c0e717..5a7d0a01 100644 --- a/man/RenameCells.Rd +++ b/man/RenameCells.Rd @@ -1,9 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/generics.R, R/assay.R, R/dimreduc.R, -% R/neighbor.R, R/seurat.R +% Please edit documentation in R/generics.R, R/assay.R, R/assay5.R, +% R/dimreduc.R, R/neighbor.R, R/seurat.R \name{RenameCells} \alias{RenameCells} \alias{RenameCells.Assay} +\alias{RenameCells.Assay5} \alias{RenameCells.DimReduc} \alias{RenameCells.Neighbor} \alias{RenameCells.Seurat} @@ -13,15 +14,17 @@ RenameCells(object, ...) \method{RenameCells}{Assay}(object, new.names = NULL, ...) +\method{RenameCells}{Assay5}(object, new.names = NULL, ...) + \method{RenameCells}{DimReduc}(object, new.names = NULL, ...) \method{RenameCells}{Neighbor}(object, old.names = NULL, new.names = NULL, ...) \method{RenameCells}{Seurat}( object, - add.cell.id = NULL, - new.names = NULL, - for.merge = FALSE, + add.cell.id = missing_arg(), + new.names = missing_arg(), + for.merge = deprecated(), ... ) } @@ -36,8 +39,7 @@ RenameCells(object, ...) \item{add.cell.id}{prefix to add cell names} -\item{for.merge}{Only rename slots needed for merging Seurat objects. -Currently only renames the raw.data and meta.data slots.} +\item{for.merge}{Deprecated} } \value{ An object with new cell names diff --git a/man/SaveSeuratRds.Rd b/man/SaveSeuratRds.Rd new file mode 100644 index 00000000..8d0fa921 --- /dev/null +++ b/man/SaveSeuratRds.Rd @@ -0,0 +1,117 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{SaveSeuratRds} +\alias{SaveSeuratRds} +\alias{LoadSeuratRds} +\title{Save and Load \code{Seurat} Objects from Rds files} +\usage{ +SaveSeuratRds(object, file = NULL, destdir = NULL, relative = FALSE, ...) + +LoadSeuratRds(file, ...) +} +\arguments{ +\item{object}{A \code{\link{Seurat}} object} + +\item{file}{Path to save \code{object} to; defaults to +\code{file.path(getwd(), paste0(Project(object), ".Rds"))}} + +\item{destdir}{Destination directory for on-disk layers saved in +\dQuote{\code{\Sexpr[stage=render]{tempdir()}}}} + +\item{relative}{Save relative paths instead of absolute ones} + +\item{...}{ + Arguments passed on to \code{\link[base:readRDS]{base::saveRDS}}, \code{\link[base:readRDS]{base::readRDS}} + \describe{ + \item{\code{ascii}}{a logical. If \code{TRUE} or \code{NA}, an ASCII + representation is written; otherwise (default), a binary one is used. + See the comments in the help for \code{\link[base]{save}}.} + \item{\code{version}}{the workspace format version to use. \code{NULL} + specifies the current default version (3). The only other supported + value is 2, the default from \R 1.4.0 to \R 3.5.0.} + \item{\code{compress}}{a logical specifying whether saving to a named file is + to use \code{"gzip"} compression, or one of \code{"gzip"}, + \code{"bzip2"} or \code{"xz"} to indicate the type of compression to + be used. Ignored if \code{file} is a connection.} + \item{\code{refhook}}{a hook function for handling reference objects.} + }} +} +\value{ +Invisibly returns \code{file} +} +\description{ +Save and Load \code{Seurat} Objects from Rds files +} +\note{ +This function requires the +\href{https://cran.r-project.org/package=fs}{\pkg{fs}} package +to be installed +} +\section{Progress Updates with \pkg{progressr}}{ + +This function uses +\href{https://cran.r-project.org/package=progressr}{\pkg{progressr}} to +render status updates and progress bars. To enable progress updates, wrap +the function call in \code{\link[progressr]{with_progress}} or run +\code{\link[progressr:handlers]{handlers(global = TRUE)}} before running +this function. For more details about \pkg{progressr}, please read +\href{https://progressr.futureverse.org/articles/progressr-intro.html}{\code{vignette("progressr-intro")}} +} + +\examples{ +if (requireNamespace("fs", quietly = TRUE)) { + # Write out with DelayedArray + if (requireNamespace("HDF5Array", quietly = TRUE)) { + pbmc <- pbmc_small + + pbmc[["disk"]] <- CreateAssay5Object(list( + mem = LayerData(pbmc, "counts"), + disk = as(LayerData(pbmc, "counts"), "HDF5Array") + )) + + # Save `pbmc` to an Rds file + out <- tempfile(fileext = ".Rds") + SaveSeuratRds(pbmc, file = out) + + # Object cache + obj <- readRDS(out) + Tool(obj, "SaveSeuratRds") + + # Load the saved object with on-disk layers back into memory + pbmc2 <- LoadSeuratRds(out) + pbmc2 + pbmc2[["disk"]] + } + + # Write out with BPCells + if (requireNamespace("BPCells", quietly = TRUE)) { + pbmc <- pbmc_small + + bpm <- BPCells::write_matrix_dir(LayerData(pbmc, "counts"), dir = tempfile()) + bph <- BPCells::write_matrix_hdf5( + LayerData(pbmc, "counts"), + path = tempfile(fileext = ".h5"), + group = "counts" + ) + pbmc[["disk"]] <- CreateAssay5Object(list(dir = bpm, h5 = bph)) + + # Save `pbmc` to an Rds file + out <- tempfile(fileext = ".Rds") + SaveSeuratRds(pbmc, file = out) + + # Object cache + obj <- readRDS(out) + Tool(obj, "SaveSeuratRds") + + # Load the saved object with on-disk layers back into memory + pbmc2 <- LoadSeuratRds(out) + pbmc2 + pbmc2[["disk"]] + } +} + +} +\seealso{ +\code{\link{saveRDS}()} \code{\link{readRDS}()} +} +\concept{utils} diff --git a/man/Segmentation-class.Rd b/man/Segmentation-class.Rd index 3a3af06b..17f623d9 100644 --- a/man/Segmentation-class.Rd +++ b/man/Segmentation-class.Rd @@ -12,6 +12,9 @@ The \code{Segmentation} Class Segmentation layer classes: \code{\link{Centroids-class}}, -\code{\link{Molecules-class}} +\code{\link{Centroids-methods}}, +\code{\link{Molecules-class}}, +\code{\link{Molecules-methods}}, +\code{\link{Segmentation-methods}} } \concept{segmentation} diff --git a/man/Segmentation-methods.Rd b/man/Segmentation-methods.Rd index a840b162..4d4c8741 100644 --- a/man/Segmentation-methods.Rd +++ b/man/Segmentation-methods.Rd @@ -123,5 +123,13 @@ to \pkg{future}, see \seealso{ \code{\link{Segmentation-class}} + +Segmentation layer classes: +\code{\link{Centroids-class}}, +\code{\link{Centroids-methods}}, +\code{\link{Molecules-class}}, +\code{\link{Molecules-methods}}, +\code{\link{Segmentation-class}} } \concept{future} +\concept{segmentation} diff --git a/man/Seurat-class.Rd b/man/Seurat-class.Rd index a5cea05b..8f3b9e5b 100644 --- a/man/Seurat-class.Rd +++ b/man/Seurat-class.Rd @@ -51,3 +51,17 @@ settable using \code{\link{Idents}}} filled by developers only using \code{\link{Tool}<-}} }} +\seealso{ +Seurat object, validity, and interaction methods +\code{\link{$.Seurat}()}, +\code{\link{Seurat-validity}}, +\code{\link{[[.Seurat}()}, +\code{\link{[[<-,Seurat,NULL}}, +\code{\link{[[<-,Seurat}}, +\code{\link{dim.Seurat}()}, +\code{\link{dimnames.Seurat}()}, +\code{\link{merge.Seurat}()}, +\code{\link{names.Seurat}()}, +\code{\link{subset.Seurat}()} +} +\concept{seurat} diff --git a/man/Seurat-methods.Rd b/man/Seurat-methods.Rd deleted file mode 100644 index e18402b8..00000000 --- a/man/Seurat-methods.Rd +++ /dev/null @@ -1,317 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/seurat.R -\name{Seurat-methods} -\alias{Seurat-methods} -\alias{.DollarNames.Seurat} -\alias{$.Seurat} -\alias{$<-.Seurat} -\alias{[.Seurat} -\alias{[[.Seurat} -\alias{dim.Seurat} -\alias{dimnames.Seurat} -\alias{head.Seurat} -\alias{merge.Seurat} -\alias{merge} -\alias{MergeSeurat} -\alias{AddSamples} -\alias{names.Seurat} -\alias{subset.Seurat} -\alias{subset} -\alias{tail.Seurat} -\alias{[[<-,Seurat,ANY,ANY,ANY-method} -\alias{colMeans,Seurat-method} -\alias{colSums,Seurat-method} -\alias{rowMeans,Seurat-method} -\alias{rowSums,Seurat-method} -\alias{show,Seurat-method} -\title{Seurat Methods} -\usage{ -\method{.DollarNames}{Seurat}(x, pattern = "") - -\method{$}{Seurat}(x, i, ...) - -\method{$}{Seurat}(x, i, ...) <- value - -\method{[}{Seurat}(x, i, j, ...) - -\method{[[}{Seurat}(x, i, ..., drop = FALSE) - -\method{dim}{Seurat}(x) - -\method{dimnames}{Seurat}(x) - -\method{head}{Seurat}(x, n = 10L, ...) - -\method{merge}{Seurat}( - x = NULL, - y = NULL, - add.cell.ids = NULL, - merge.data = TRUE, - merge.dr = NULL, - project = "SeuratProject", - ... -) - -\method{names}{Seurat}(x) - -\method{subset}{Seurat}( - x, - subset, - cells = NULL, - features = NULL, - idents = NULL, - return.null = FALSE, - ... -) - -\method{tail}{Seurat}(x, n = 10L, ...) - -\S4method{[[}{Seurat,ANY,ANY,ANY}(x, i, j, ...) <- value - -\S4method{colMeans}{Seurat}(x, na.rm = FALSE, dims = 1, ..., slot = "data") - -\S4method{colSums}{Seurat}(x, na.rm = FALSE, dims = 1, ..., slot = "data") - -\S4method{rowMeans}{Seurat}(x, na.rm = FALSE, dims = 1, ..., slot = "data") - -\S4method{rowSums}{Seurat}(x, na.rm = FALSE, dims = 1, ..., slot = "data") - -\S4method{show}{Seurat}(object) -} -\arguments{ -\item{x, object}{A \code{\link{Seurat}} object} - -\item{pattern}{ A regular expression. Only matching names are - returned. - } - -\item{i, features}{Depends on the method -\describe{ - \item{\code{[}, \code{subset}}{Feature names or indices} - \item{\code{$}, \code{$<-}}{Name of a single metadata column} - \item{\code{[[}, \code{[[<-}}{ - Name of one or more metadata columns or an associated object; associated - objects include \code{\link{Assay}}, \code{\link{DimReduc}}, - \code{\link{Graph}}, \code{\link{SeuratCommand}}, or - \code{\link{SpatialImage}} objects - } -}} - -\item{...}{Arguments passed to other methods} - -\item{value}{Additional metadata or associated objects to add; \strong{note}: -can pass \code{NULL} to remove metadata or an associated object} - -\item{j, cells}{Cell names or indices} - -\item{drop}{See \code{\link[base]{drop}}} - -\item{n}{The number of rows of metadata to return} - -\item{y}{A single \code{Seurat} object or a list of \code{Seurat} objects} - -\item{add.cell.ids}{A character vector of \code{length(x = c(x, y))}; -appends the corresponding values to the start of each objects' cell names} - -\item{merge.data}{Merge the data slots instead of just merging the counts -(which requires renormalization); this is recommended if the same -normalization approach was applied to all objects} - -\item{merge.dr}{Merge specified DimReducs that are present in all objects; -will only merge the embeddings slots for the first \code{N} dimensions that -are shared across all objects.} - -\item{project}{\link{Project} name for the \code{Seurat} object} - -\item{subset}{Logical expression indicating features/variables to keep} - -\item{idents}{A vector of identity classes to keep} - -\item{return.null}{If no cells are request, return a \code{NULL}; -by default, throws an error} - -\item{na.rm}{logical. Should missing values (including \code{NaN}) - be omitted from the calculations?} - -\item{dims}{completely ignored by the \code{Matrix} methods.} - -\item{slot}{Name of assay expression matrix to calculate column/row -means/sums on} -} -\value{ -\code{$}: metadata column \code{i} for object \code{x}; -\strong{note}: unlike \code{[[}, \code{$} drops the shape of the metadata -to return a vector instead of a data frame - -\code{$<-}: object \code{x} with metadata \code{value} saved as -\code{i} - -\code{[}: object \code{x} with features \code{i} and cells \code{j} - -\code{[[}: If \code{i} is missing, the metadata data frame; if -\code{i} is a vector of metadata names, a data frame with the requested -metadata, otherwise, the requested associated object - -\code{dim}: The number of features (\code{nrow}) and cells -(\code{ncol}) for the default assay; \strong{note}: while the number of -features changes depending on the active assay, the number of cells remains -the same across all assays - -\code{dimnames}: The feature (row) and cell (column) names; -\strong{note}: while the features change depending on the active assay, the -cell names remain the same across all assays - -\code{head}: The first \code{n} rows of cell-level metadata - -\code{merge}: Merged object - -\code{names}: The names of all \code{\link{Assay}}, -\code{\link{DimReduc}}, \code{\link{Graph}}, and \code{\link{SpatialImage}} -objects in the \code{Seurat} object - -\code{subset}: A subsetted \code{Seurat} object - -\code{tail}: The last \code{n} rows of cell-level metadata - -\code{[[<-}: \code{x} with the metadata or associated objects added -as \code{i}; if \code{value} is \code{NULL}, removes metadata or associated -object \code{i} from object \code{x} - -\code{show}: Prints summary to \code{\link[base]{stdout}} and -invisibly returns \code{NULL} -} -\description{ -Methods for \code{\link{Seurat}} objects for generics defined in other -packages -} -\section{Functions}{ -\itemize{ -\item \code{.DollarNames(Seurat)}: Autocompletion for \code{$} access on a -\code{Seurat} object - -\item \code{$}: Metadata access for \code{Seurat} objects - -\item \code{`$`(Seurat) <- value}: Metadata setter for \code{Seurat} objects - -\item \code{[}: Simple subsetter for \code{Seurat} objects - -\item \code{[[}: Metadata and associated object accessor - -\item \code{dim(Seurat)}: Number of cells and features for the active assay - -\item \code{dimnames(Seurat)}: The cell and feature names for the active assay - -\item \code{head(Seurat)}: Get the first rows of cell-level metadata - -\item \code{merge(Seurat)}: Merge two or more \code{Seurat} objects together - -\item \code{names(Seurat)}: Common associated objects - -\item \code{subset(Seurat)}: Subset a \code{\link{Seurat}} object - -\item \code{tail(Seurat)}: Get the last rows of cell-level metadata - -\item \code{`[[`(x = Seurat, i = ANY, j = ANY) <- value}: Add cell-level metadata or associated objects - -\item \code{colMeans(Seurat)}: Calculate \code{\link[base]{colMeans}} on a -\code{Seurat} object - -\item \code{colSums(Seurat)}: Calculate \code{\link[base]{colSums}} on a -\code{Seurat} object - -\item \code{rowMeans(Seurat)}: Calculate \code{\link[base]{rowMeans}} on a -\code{rowMeans} object - -\item \code{rowSums(Seurat)}: Calculate \code{\link[base]{rowSums}} on a -\code{Seurat} object - -\item \code{show(Seurat)}: Overview of a \code{Seurat} object - -}} -\section{Merge Details}{ - -When merging Seurat objects, the merge procedure will merge the Assay level -counts and potentially the data slots (depending on the merge.data parameter). -It will also merge the cell-level meta data that was stored with each object -and preserve the cell identities that were active in the objects pre-merge. -The merge will optionally merge reductions depending on the values passed to -\code{merge.dr} if they have the same name across objects. Here the -embeddings slots will be merged and if there are differing numbers of -dimensions across objects, only the first N shared dimensions will be merged. -The feature loadings slots will be filled by the values present in the first -object.The merge will not preserve graphs, logged commands, or feature-level -metadata that were present in the original objects. If add.cell.ids isn't -specified and any cell names are duplicated, cell names will be appended -with _X, where X is the numeric index of the object in c(x, y). -} - -\examples{ -# Get metadata using `$' -head(pbmc_small$groups) - -# Add metadata using the `$' operator -set.seed(42) -pbmc_small$value <- sample(1:3, size = ncol(pbmc_small), replace = TRUE) -head(pbmc_small[["value"]]) - -# `[' examples -pbmc_small[VariableFeatures(object = pbmc_small), ] -pbmc_small[, 1:10] - -# Get the cell-level metadata data frame -head(pbmc_small[[]]) - -# Pull specific metadata information -head(pbmc_small[[c("letter.idents", "groups")]]) -head(pbmc_small[["groups", drop = TRUE]]) - -# Get a sub-object (eg. an `Assay' or `DimReduc') -pbmc_small[["RNA"]] -pbmc_small[["pca"]] - -# Get the number of features in an object -nrow(pbmc_small) - -# Get the number of cells in an object -ncol(pbmc_small) - -# Get the feature names of an object -rownames(pbmc_small) - -# Get the cell names of an object -colnames(pbmc_small) - -# Get the first 10 rows of cell-level metadata -head(pbmc_small) - -# `merge' examples -# merge two objects -merge(pbmc_small, y = pbmc_small) -# to merge more than two objects, pass one to x and a list of objects to y -merge(pbmc_small, y = c(pbmc_small, pbmc_small)) - -names(pbmc_small) - -# `subset' examples -subset(pbmc_small, subset = MS4A1 > 4) -subset(pbmc_small, subset = `DLGAP1-AS1` > 2) -subset(pbmc_small, idents = '0', invert = TRUE) -subset(pbmc_small, subset = MS4A1 > 3, slot = 'counts') -subset(pbmc_small, features = VariableFeatures(object = pbmc_small)) - -# Get the last 10 rows of cell-level metadata -tail(pbmc_small) - -head(colMeans(pbmc_small)) - -head(colSums(pbmc_small)) - -head(rowMeans(pbmc_small)) - -head(rowSums(pbmc_small)) - -} -\seealso{ -\code{\link[base]{subset}} \code{\link{WhichCells}} -} -\concept{seurat} diff --git a/man/Seurat-validity.Rd b/man/Seurat-validity.Rd new file mode 100644 index 00000000..48d57875 --- /dev/null +++ b/man/Seurat-validity.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{Seurat-validity} +\alias{Seurat-validity} +\title{Seurat Object Validity} +\description{ +Validation of \code{Seurat} objects is handled by +\code{\link[methods]{validObject}} +} +\seealso{ +\code{\link[methods]{validObject}} + +Seurat object, validity, and interaction methods +\code{\link{$.Seurat}()}, +\code{\link{Seurat-class}}, +\code{\link{[[.Seurat}()}, +\code{\link{[[<-,Seurat,NULL}}, +\code{\link{[[<-,Seurat}}, +\code{\link{dim.Seurat}()}, +\code{\link{dimnames.Seurat}()}, +\code{\link{merge.Seurat}()}, +\code{\link{names.Seurat}()}, +\code{\link{subset.Seurat}()} +} +\concept{seurat} diff --git a/man/SeuratCommand-class.Rd b/man/SeuratCommand-class.Rd index 0cac3d9f..075649c0 100644 --- a/man/SeuratCommand-class.Rd +++ b/man/SeuratCommand-class.Rd @@ -4,10 +4,10 @@ \name{SeuratCommand-class} \alias{SeuratCommand-class} \alias{SeuratCommand} -\title{The SeuratCommand Class} +\title{The \code{SeuratCommand} Class} \description{ -The SeuratCommand is used for logging commands that are run on a -\code{Seurat} object; it stores parameters and timestamps +The \code{SeuratCommand} is used for logging commands that are run +on a \code{Seurat} object; it stores parameters and timestamps } \section{Slots}{ @@ -24,3 +24,12 @@ The SeuratCommand is used for logging commands that are run on a \item{\code{params}}{List of parameters used in the command call} }} +\seealso{ +Command log object and interaction methods +\code{\link{$.SeuratCommand}()}, +\code{\link{.DollarNames.SeuratCommand}()}, +\code{\link{LogSeuratCommand}()}, +\code{\link{[.SeuratCommand}()}, +\code{\link{as.list.SeuratCommand}()} +} +\concept{command} diff --git a/man/SeuratCommand-methods.Rd b/man/SeuratCommand-methods.Rd deleted file mode 100644 index c5263c38..00000000 --- a/man/SeuratCommand-methods.Rd +++ /dev/null @@ -1,68 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/command.R -\name{SeuratCommand-methods} -\alias{SeuratCommand-methods} -\alias{.DollarNames.SeuratCommand} -\alias{$.SeuratCommand} -\alias{[.SeuratCommand} -\alias{as.list.SeuratCommand} -\alias{show,SeuratCommand-method} -\title{\code{SeuratCommand} Methods} -\usage{ -\method{.DollarNames}{SeuratCommand}(x, pattern = "") - -\method{$}{SeuratCommand}(x, i, ...) - -\method{[}{SeuratCommand}(x, i, ...) - -\method{as.list}{SeuratCommand}(x, complete = FALSE, ...) - -\S4method{show}{SeuratCommand}(object) -} -\arguments{ -\item{x, object}{A \code{\link{SeuratCommand}} object} - -\item{pattern}{ A regular expression. Only matching names are - returned. - } - -\item{i}{For a \code{$}, a parameter name; for \code{[}, a -\code{SeuratCommand} slot name} - -\item{...}{Arguments passed to other methods} - -\item{complete}{Include slots besides just parameters -(eg. call string, name, timestamp)} -} -\value{ -\code{$}: The value for parameter \code{i} - -\code{[}: Slot \code{i} from \code{x} - -\code{as.list}: A list with the parameters and, if -\code{complete = TRUE}, the call string, name, and timestamp - -\code{show}: Prints summary to \code{\link[base]{stdout}} and -invisibly returns \code{NULL} -} -\description{ -Methods for \code{\link{SeuratCommand}} objects for generics defined in -other packages -} -\section{Functions}{ -\itemize{ -\item \code{.DollarNames(SeuratCommand)}: Autocompletion for \code{$} access on a -\code{SeuratCommand} object - -\item \code{$}: Access a parameter from a -\code{SeuratCommand} object - -\item \code{[}: Access data from a \code{SeuratCommand} -object - -\item \code{as.list(SeuratCommand)}: Coerce a \code{SeuratCommand} to a list - -\item \code{show(SeuratCommand)}: Overview of a \code{SeuratCommand} object - -}} -\concept{command} diff --git a/man/SeuratObject-options.Rd b/man/SeuratObject-options.Rd new file mode 100644 index 00000000..e60dec98 --- /dev/null +++ b/man/SeuratObject-options.Rd @@ -0,0 +1,50 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/zzz.R +\name{SeuratObject-options} +\alias{SeuratObject-options} +\title{\pkg{Seurat} Options} +\description{ +Various options used in \pkg{Seurat} +} +\section{Package Options}{ + +\subsection{Seurat.coords.short_range}{ +Defaults to +\dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.coords.short_range}}\cr +Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.coords.short_range")}} +} +\subsection{Seurat.input.sparse_ratio}{ +Defaults to +\dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.input.sparse_ratio}}\cr +Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.input.sparse_ratio")}} +} +\subsection{Seurat.io.rds.strict}{ +Defaults to +\dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.io.rds.strict}}\cr +Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.io.rds.strict")}} +} +\subsection{Seurat.object.assay.calcn}{ +Run \code{CalcN} when adding assay data to a \code{Seurat} object\cr +Defaults to +\dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.object.assay.calcn}}\cr +Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.object.assay.calcn")}} +} +\subsection{Seurat.object.assay.version}{ +Defaults to +\dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.object.assay.version}}\cr +Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.object.assay.version")}} +} +\subsection{Seurat.object.assay.v3.missing_layer}{ +Defaults to +\dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.object.assay.v3.missing_layer}}\cr +Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.object.assay.v3.missing_layer")}} +} +\subsection{Seurat.object.project}{ +Default project for new \code{\link{Seurat}} objects\cr +Defaults to +\dQuote{\Sexpr[stage=build]{SeuratObject:::Seurat.options$Seurat.object.project}}\cr +Currently set to \dQuote{\Sexpr[stage=render]{getOption("Seurat.object.project")}} +} +} + +\keyword{internal} diff --git a/man/SeuratObject-package.Rd b/man/SeuratObject-package.Rd index 07dcf5af..7bf8c28c 100644 --- a/man/SeuratObject-package.Rd +++ b/man/SeuratObject-package.Rd @@ -6,7 +6,7 @@ \alias{SeuratObject-package} \title{SeuratObject: Data Structures for Single Cell Data} \description{ -Defines S4 classes for single-cell genomic data and associated information, such as dimensionality reduction embeddings, nearest-neighbor graphs, and spatially-resolved coordinates. Provides data access methods and R-native hooks to ensure the Seurat object is familiar to other R users. See Satija R, Farrell J, Gennert D, et al (2015) \doi{10.1038/nbt.3192}, Macosko E, Basu A, Satija R, et al (2015) \doi{10.1016/j.cell.2015.05.002}, and Stuart T, Butler A, et al (2019) \doi{10.1016/j.cell.2019.05.031} for more details. +Defines S4 classes for single-cell genomic data and associated information, such as dimensionality reduction embeddings, nearest-neighbor graphs, and spatially-resolved coordinates. Provides data access methods and R-native hooks to ensure the Seurat object is familiar to other R users. See Satija R, Farrell J, Gennert D, et al (2015) \doi{10.1038/nbt.3192}, Macosko E, Basu A, Satija R, et al (2015) \doi{10.1016/j.cell.2015.05.002}, and Stuart T, Butler A, et al (2019) \doi{10.1016/j.cell.2019.05.031}, Hao Y, Hao S, et al (2021) \doi{10.1016/j.cell.2021.04.048} and Hao Y, et al (2023) \doi{10.1101/2022.02.24.481684} for more details. } \seealso{ Useful links: @@ -23,17 +23,22 @@ Useful links: Authors: \itemize{ \item Rahul Satija \email{rsatija@nygenome.org} (\href{https://orcid.org/0000-0001-9448-8833}{ORCID}) + \item Yuhan Hao \email{yhao@nygenome.org} (\href{https://orcid.org/0000-0002-1810-0822}{ORCID}) + \item Austin Hartman \email{ahartman@nygenome.org} (\href{https://orcid.org/0000-0001-7278-1852}{ORCID}) + \item Gesmira Molla \email{gmolla@nygenome.org} (\href{https://orcid.org/0000-0002-8628-5056}{ORCID}) \item Andrew Butler \email{abutler@nygenome.org} (\href{https://orcid.org/0000-0003-3608-0463}{ORCID}) \item Tim Stuart \email{tstuart@nygenome.org} (\href{https://orcid.org/0000-0002-3044-0897}{ORCID}) } Other contributors: \itemize{ + \item Madeline Kowalski \email{mkowalski@nygenome.org} (\href{https://orcid.org/0000-0002-5655-7620}{ORCID}) [contributor] + \item Skylar Li \email{sli@nygenome.org} [contributor] + \item Longda Jiang \email{ljiang@nygenome.org} (\href{https://orcid.org/0000-0003-4964-6497}{ORCID}) [contributor] \item Jeff Farrell \email{jfarrell@g.harvard.edu} [contributor] \item Shiwei Zheng \email{szheng@nygenome.org} (\href{https://orcid.org/0000-0001-6682-6743}{ORCID}) [contributor] \item Christoph Hafemeister \email{chafemeister@nygenome.org} (\href{https://orcid.org/0000-0001-6365-8254}{ORCID}) [contributor] \item Patrick Roelli \email{proelli@nygenome.org} [contributor] - \item Yuhan Hao \email{yhao@nygenome.org} (\href{https://orcid.org/0000-0002-1810-0822}{ORCID}) [contributor] } } diff --git a/man/Simplify.Rd b/man/Simplify.Rd index 6159de55..0808dc66 100644 --- a/man/Simplify.Rd +++ b/man/Simplify.Rd @@ -17,7 +17,7 @@ Simplify(coords, tol, topologyPreserve = TRUE) \item{topologyPreserve}{Logical determining if the algorithm should attempt to preserve the topology of the original geometry} } \value{ -... +A simplified version of \code{coords} A `Segmentation` object with simplified segmentation vertices } @@ -26,3 +26,4 @@ Simplify Geometry Simplify segmentations by reducing the number of vertices } +\concept{spatial} diff --git a/man/SparseEmptyMatrix.Rd b/man/SparseEmptyMatrix.Rd new file mode 100644 index 00000000..21b4d23a --- /dev/null +++ b/man/SparseEmptyMatrix.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{SparseEmptyMatrix} +\alias{SparseEmptyMatrix} +\title{Generate empty dgC sparse matrix} +\usage{ +SparseEmptyMatrix(nrow, ncol, rownames = NULL, colnames = NULL) +} +\arguments{ +\item{ncol, nrow}{Number of columns and rows in matrix} + +\item{rownames, colnames}{Optional row- and column names for the matrix} +} +\description{ +Generate empty dgC sparse matrix +} +\keyword{internal} diff --git a/man/SpatialImage-class.Rd b/man/SpatialImage-class.Rd index 979120a7..8e7f2bac 100644 --- a/man/SpatialImage-class.Rd +++ b/man/SpatialImage-class.Rd @@ -17,7 +17,10 @@ class for use with \code{Seurat} objects priority for visualization when the assay is set as the active/default assay in a \code{Seurat} object} -\item{\code{key}}{Key for the image} +\item{\code{key}}{A one-length character vector with the object's key; keys must +be one or more alphanumeric characters followed by an underscore +\dQuote{\code{_}} (regex pattern +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}})} }} \seealso{ diff --git a/man/SplitLayers.Rd b/man/SplitLayers.Rd new file mode 100644 index 00000000..febcb41c --- /dev/null +++ b/man/SplitLayers.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R, R/assay5.R, R/seurat.R +\name{JoinLayers} +\alias{JoinLayers} +\alias{JoinLayers.Assay5} +\alias{JoinLayers.Seurat} +\title{Split and Join Layers Together} +\usage{ +JoinLayers(object, ...) + +\method{JoinLayers}{Assay5}(object, layers = NULL, new = NULL, ...) + +\method{JoinLayers}{Seurat}(object, assay = NULL, layers = NULL, new = NULL, ...) +} +\arguments{ +\item{object}{An object} + +\item{...}{Arguments passed to other methods} + +\item{layers}{Names of layers to split or join} + +\item{new}{Name of new layers} + +\item{assay}{Name of assay to split layers} +} +\value{ +\code{object} with the layers specified joined +} +\description{ +Split and Join Layers Together +} +\concept{assay5} diff --git a/man/StdAssay-class.Rd b/man/StdAssay-class.Rd new file mode 100644 index 00000000..26df0eae --- /dev/null +++ b/man/StdAssay-class.Rd @@ -0,0 +1,68 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\docType{class} +\name{StdAssay-class} +\alias{StdAssay-class} +\alias{StdAssay} +\title{Core Assay Infrastructure} +\description{ +The \code{StdAssay} class is a virtual class that provides core +infrastructure for assay data in \pkg{Seurat}. Assays contain expression +data (layers) and associated feature-level meta data. Derived classes +(eg. \link[=Assay5]{the v5 Assay}) may optionally +define additional functionality +} +\section{Slots}{ + +\describe{ +\item{\code{layers}}{A named list containing expression matrices; each matrix should +be a two-dimensional object containing some subset of cells and features +defined in the \code{cells} and \code{features} slots. Cell and feature +membership is recorded in the \code{cells} and \code{features} slots, +respectively} + +\item{\code{cells}}{A \link[=LogMap]{logical mapping} of cell names +and layer membership; this map contains all the possible cells that this +assay can contain. New layers must have some subset of cells present +in this map} + +\item{\code{features}}{A \link[=LogMap]{logical mapping} of feature +names and layer membership; this map contains all the possible features that +this assay can contain. New layers must have some subset of features +present in this map} + +\item{\code{default}}{A one-length integer with the end index of the +\link[=DefaultLayer]{default layer}; the default layer be +all layers up to and including the layer at index \code{default}} + +\item{\code{assay.orig}}{Original assay that this assay is based off of; +used to track assay provenance} + +\item{\code{meta.data}}{A \link[base:data.frame]{data frame} with feature-level +meta data; should have the same number of rows as \code{features}} + +\item{\code{misc}}{A named list of unstructured miscellaneous data} + +\item{\code{key}}{A one-length character vector with the object's key; keys must +be one or more alphanumeric characters followed by an underscore +\dQuote{\code{_}} (regex pattern +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}})} +}} + +\seealso{ +\code{\link{Assay5-class}} \code{\link{Assay5T-class}} + +v5 Standard Assay object, validity, and interaction methods +\code{\link{$.StdAssay}()}, +\code{\link{.DollarNames.StdAssay}()}, +\code{\link{StdAssay-validity}}, +\code{\link{[.StdAssay}()}, +\code{\link{[[.StdAssay}()}, +\code{\link{dim.StdAssay}()}, +\code{\link{dimnames.StdAssay}()}, +\code{\link{show,StdAssay-method}}, +\code{\link{split.StdAssay}()}, +\code{\link{subset.StdAssay}()} +} +\concept{stdassay} +\keyword{internal} diff --git a/man/StdAssay-validity.Rd b/man/StdAssay-validity.Rd new file mode 100644 index 00000000..5300c112 --- /dev/null +++ b/man/StdAssay-validity.Rd @@ -0,0 +1,45 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{StdAssay-validity} +\alias{StdAssay-validity} +\title{V5 Assay Validity} +\description{ +Validation of \code{StdAssay} objects is handled by +\code{\link[methods]{validObject}} +} +\section{Layer Validation}{ + +blah +} + +\section{Key Validation}{ + +Keys must be a one-length character vector; a key must be composed of one +of the following: +\itemize{ + \item An empty string (eg. \dQuote{\code{''}}) where \code{nzchar() == 0} + \item An string composed of one or more alphanumeric values + (both lower- and upper-case) that ends with an underscore + (\dQuote{\code{_}}); the first character must be a letter +} +Keys that are not empty strings are validated with the regex +\dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}} +} + +\seealso{ +\code{\link[methods]{validObject}} + +v5 Standard Assay object, validity, and interaction methods +\code{\link{$.StdAssay}()}, +\code{\link{.DollarNames.StdAssay}()}, +\code{\link{StdAssay-class}}, +\code{\link{[.StdAssay}()}, +\code{\link{[[.StdAssay}()}, +\code{\link{dim.StdAssay}()}, +\code{\link{dimnames.StdAssay}()}, +\code{\link{show,StdAssay-method}}, +\code{\link{split.StdAssay}()}, +\code{\link{subset.StdAssay}()} +} +\concept{stdassay} +\keyword{internal} diff --git a/man/StitchMatrix.Rd b/man/StitchMatrix.Rd new file mode 100644 index 00000000..6700f41e --- /dev/null +++ b/man/StitchMatrix.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R +\name{StitchMatrix} +\alias{StitchMatrix} +\title{Stitch Matrices Together} +\usage{ +StitchMatrix(x, y, rowmap, colmap, ...) +} +\arguments{ +\item{x}{A matrix} + +\item{y}{One or more matrices of the same class or coercible to the +same class as \code{x}} + +\item{rowmap, colmap}{\code{\link{LogMap}s} describing the row and cell +membership of each matrix; the \code{LogMap} entries are assumed to be in +the order of \code{c(x, y)}} + +\item{...}{Arguments passed to other methods} +} +\value{ +A single matrix of type \code{class(x)} consisting of all values +in component matrices +} +\description{ +Stitch Matrices Together +} +\concept{utils} diff --git a/man/Theta.Rd b/man/Theta.Rd index c849cae6..224df4ca 100644 --- a/man/Theta.Rd +++ b/man/Theta.Rd @@ -12,3 +12,4 @@ Theta(object) \description{ Get the offset angle } +\concept{spatial} diff --git a/man/Tool.Rd b/man/Tool.Rd index 0e9023e7..a482eb34 100644 --- a/man/Tool.Rd +++ b/man/Tool.Rd @@ -6,7 +6,7 @@ \alias{Tool<-} \alias{Tool.Seurat} \alias{Tool<-.Seurat} -\title{Get and set additional tool data} +\title{Get and Set Additional Tool Data} \usage{ Tool(object, ...) @@ -36,18 +36,30 @@ will return a vector with the names of tools in the object. \note{ For developers: set tool data using \code{Tool<-}. \code{Tool<-} will automatically set the name of the tool to the function that called -\code{Tool<-},so each function gets one entry in the tools list and cannot +\code{Tool<-}, so each function gets one entry in the tools list and cannot overwrite another function's entry. The automatic naming will also remove any -method identifiers (eg. RunPCA.Seurat will become RunPCA); please -plan accordingly. +method identifiers (eg. \code{RunPCA.Seurat} will become \code{RunPCA}); +please plan accordingly } \examples{ -Tool(object = pbmc_small) - -\dontrun{ -sample.tool.output <- matrix(data = rnorm(n = 16), nrow = 4) -# must be run from within a function -Tool(object = pbmc_small) <- sample.tool.output +# Example function that adds unstructured data to tools +MyTool <- function(object) { + sample.tool.output <- matrix(rnorm(n = 16), nrow = 4) + # Note: `Tool<-` must be called from within a function + # and the name of the tool will be generated from the function name + Tool(object) <- sample.tool.output + return(object) } + +# Run our tool +set.seed(42L) +pbmc_small <- MyTool(pbmc_small) + +# Get a list of tools run +Tool(pbmc_small) + +# Access specific tool data +Tool(pbmc_small, slot = "MyTool") + } \concept{data-access} diff --git a/man/VariableFeatures-StdAssay.Rd b/man/VariableFeatures-StdAssay.Rd new file mode 100644 index 00000000..658a65fe --- /dev/null +++ b/man/VariableFeatures-StdAssay.Rd @@ -0,0 +1,76 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{VariableFeatures-StdAssay} +\alias{VariableFeatures-StdAssay} +\alias{HVFInfo.StdAssay} +\alias{VariableFeatures.StdAssay} +\alias{VariableFeatures<-.StdAssay} +\title{Highly Variable Features} +\usage{ +\method{HVFInfo}{StdAssay}(object, method = NULL, status = FALSE, layer = NULL, strip = TRUE, ...) + +\method{VariableFeatures}{StdAssay}( + object, + method = NULL, + layer = NA, + simplify = TRUE, + nfeatures = Inf, + selection.method = deprecated(), + ... +) + +\method{VariableFeatures}{StdAssay}(object, method = "custom", layer = NULL, ...) <- value +} +\arguments{ +\item{object}{An object} + +\item{method}{Which method to pull. For \code{HVFInfo} and +\code{VariableFeatures}, choose one from one of the +following: +\itemize{ + \item \dQuote{vst} + \item \dQuote{sctransform} or \dQuote{sct} + \item \dQuote{mean.var.plot}, \dQuote{dispersion}, \dQuote{mvp}, or + \dQuote{disp} +} +For \code{SVFInfo} and \code{SpatiallyVariableFeatures}, choose from: +\itemize{ + \item \dQuote{markvariogram} + \item \dQuote{moransi} +}} + +\item{status}{Add variable status to the resulting data frame} + +\item{layer}{Layer to pull variable features for} + +\item{strip}{Remove method/layer identifiers from highly variable data frame} + +\item{...}{Arguments passed to other methods} + +\item{simplify}{When pulling for multiple layers, combine into a single +vector and select a common set of variable features for all layers} + +\item{nfeatures}{Maximum number of features to select when simplifying} + +\item{selection.method}{\Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")}} + +\item{value}{A character vector of variable features} +} +\value{ +\code{HVFInfo}: A data frame with feature means, dispersion, and +scaled dispersion + +\code{VariableFeatures}: a vector of the variable features + +\code{SVFInfo}: a data frame with the spatially variable features + +\code{SpatiallyVariableFeatures}: a character vector of the spatially +variable features +} +\description{ +Get and set variable feature information for an \code{\link{Assay}} object. +\code{HVFInfo} and \code{VariableFeatures} utilize generally variable +features, while \code{SVFInfo} and \code{SpatiallyVariableFeatures} are +restricted to spatially variable features +} +\keyword{internal} diff --git a/man/VariableFeatures.Rd b/man/VariableFeatures.Rd index 934afe08..848b07af 100644 --- a/man/VariableFeatures.Rd +++ b/man/VariableFeatures.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/generics.R, R/seurat.R, R/assay.R +% Please edit documentation in R/generics.R, R/seurat.R, R/assay.R, R/assay5.R \name{HVFInfo} \alias{HVFInfo} \alias{VariableFeatures} @@ -16,64 +16,101 @@ \alias{SVFInfo.Assay} \alias{VariableFeatures.Assay} \alias{VariableFeatures<-.Assay} +\alias{HVFInfo.Assay5} +\alias{VariableFeatures.Assay5} +\alias{VariableFeatures<-.Assay5} \title{Highly Variable Features} \usage{ -HVFInfo(object, selection.method, status = FALSE, ...) +HVFInfo(object, method, status = FALSE, ...) -VariableFeatures(object, selection.method = NULL, ...) +VariableFeatures(object, method = NULL, ...) VariableFeatures(object, ...) <- value -SVFInfo(object, selection.method, status, ...) +SVFInfo(object, method, status, ...) -SpatiallyVariableFeatures(object, selection.method, ...) +SpatiallyVariableFeatures(object, method, ...) -\method{HVFInfo}{Seurat}(object, selection.method = NULL, status = FALSE, assay = NULL, ...) +\method{HVFInfo}{Seurat}( + object, + method = NULL, + status = FALSE, + assay = NULL, + selection.method = deprecated(), + ... +) -\method{VariableFeatures}{Seurat}(object, selection.method = NULL, assay = NULL, ...) +\method{VariableFeatures}{Seurat}( + object, + method = NULL, + assay = NULL, + nfeatures = NULL, + layer = NA, + simplify = TRUE, + selection.method = deprecated(), + ... +) \method{VariableFeatures}{Seurat}(object, assay = NULL, ...) <- value \method{SVFInfo}{Seurat}( object, - selection.method = c("markvariogram", "moransi"), + method = c("markvariogram", "moransi"), status = FALSE, assay = NULL, + selection.method = deprecated(), ... ) \method{SpatiallyVariableFeatures}{Seurat}( object, - selection.method = "markvariogram", + method = "moransi", assay = NULL, decreasing = TRUE, + selection.method = deprecated(), ... ) -\method{HVFInfo}{Assay}(object, selection.method, status = FALSE, ...) +\method{HVFInfo}{Assay}(object, method, status = FALSE, selection.method = deprecated(), ...) \method{SpatiallyVariableFeatures}{Assay}( object, - selection.method = "markvariogram", + method = "moransi", decreasing = TRUE, + selection.method = deprecated(), ... ) \method{SVFInfo}{Assay}( object, - selection.method = c("markvariogram", "moransi"), + method = c("markvariogram", "moransi"), status = FALSE, + selection.method = deprecated(), ... ) -\method{VariableFeatures}{Assay}(object, selection.method = NULL, ...) +\method{VariableFeatures}{Assay}(object, method = NULL, selection.method = deprecated(), ...) \method{VariableFeatures}{Assay}(object, ...) <- value + +\method{HVFInfo}{Assay5}(object, method = NULL, status = FALSE, layer = NULL, strip = TRUE, ...) + +\method{VariableFeatures}{Assay5}( + object, + method = NULL, + layer = NA, + simplify = TRUE, + nfeatures = Inf, + selection.method = deprecated(), + ... +) + +\method{VariableFeatures}{Assay5}(object, method = "custom", layer = NULL, ...) <- value } \arguments{ \item{object}{An object} -\item{selection.method}{Which method to pull. For \code{HVFInfo} and +\item{method}{Which method to pull. For \code{HVFInfo} and \code{VariableFeatures}, choose one from one of the following: \itemize{ @@ -96,8 +133,19 @@ For \code{SVFInfo} and \code{SpatiallyVariableFeatures}, choose from: \item{assay}{Name of assay to pull highly variable feature information for} +\item{selection.method}{\Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")}} + +\item{nfeatures}{Maximum number of features to select when simplifying} + +\item{layer}{Layer to pull variable features for} + +\item{simplify}{When pulling for multiple layers, combine into a single +vector and select a common set of variable features for all layers} + \item{decreasing}{Return features in decreasing order (most spatially variable first).} + +\item{strip}{Remove method/layer identifiers from highly variable data frame} } \value{ \code{HVFInfo}: A data frame with feature means, dispersion, and @@ -121,7 +169,7 @@ restricted to spatially variable features HVFInfo(object = pbmc_small, assay = "RNA")[1:5, ] # Get the HVF info directly from an Assay object -HVFInfo(pbmc_small[["RNA"]], selection.method = 'vst')[1:5, ] +HVFInfo(pbmc_small[["RNA"]], method = 'vst')[1:5, ] } \concept{data-access} diff --git a/man/WhichCells.Rd b/man/WhichCells.Rd index f4d22552..321a238c 100644 --- a/man/WhichCells.Rd +++ b/man/WhichCells.Rd @@ -28,7 +28,7 @@ WhichCells(object, ...) \item{...}{ Arguments passed on to \code{\link[=CellsByIdentities]{CellsByIdentities}} \describe{ - \item{\code{return.null}}{If no cells are request, return a \code{NULL}; + \item{\code{return.null}}{If no cells are requested, return a \code{NULL}; by default, throws an error} }} diff --git a/man/aggregate.Rd b/man/aggregate.Rd index c1c6ef32..39c5bab7 100644 --- a/man/aggregate.Rd +++ b/man/aggregate.Rd @@ -58,3 +58,4 @@ to \pkg{future}, see } \concept{future} +\keyword{internal} diff --git a/man/Angles.Rd b/man/angles.Rd similarity index 87% rename from man/Angles.Rd rename to man/angles.Rd index 58007fde..2ff70a2c 100644 --- a/man/Angles.Rd +++ b/man/angles.Rd @@ -29,4 +29,10 @@ Degrees(pi) Radians(180) } +\seealso{ + +\code{\link{PolyVtx}()} +} +\concept{angles} +\concept{utils} \keyword{internal} diff --git a/man/as.Centroids.Rd b/man/as.Centroids.Rd index 8bffa70f..c9c67637 100644 --- a/man/as.Centroids.Rd +++ b/man/as.Centroids.Rd @@ -37,3 +37,4 @@ as.Segmentation(x, ...) \description{ Convert Segmentation Layers } +\concept{spatial} diff --git a/man/as.Graph.Rd b/man/as.Graph.Rd index c95fe1ed..35d01d8a 100644 --- a/man/as.Graph.Rd +++ b/man/as.Graph.Rd @@ -18,7 +18,7 @@ as.Graph(x, ...) \arguments{ \item{x}{The matrix to convert} -\item{...}{Arguments passed to other methods (ignored for now)} +\item{...}{Ignored} \item{weighted}{If TRUE, fill entries in Graph matrix with value from the nn.dist slot of the Neighbor object} @@ -43,5 +43,9 @@ rownames(x = mat) <- paste0("feature_", 1:4) colnames(x = mat) <- paste0("cell_", 1:4) g <- as.Graph(x = mat) +} +\seealso{ +Other graph: +\code{\link{Graph-class}} } \concept{graph} diff --git a/man/as.list.SeuratCommand.Rd b/man/as.list.SeuratCommand.Rd new file mode 100644 index 00000000..0bcb4b6d --- /dev/null +++ b/man/as.list.SeuratCommand.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/command.R +\name{as.list.SeuratCommand} +\alias{as.list.SeuratCommand} +\title{Coerce a \code{SeuratCommand} to a list} +\usage{ +\method{as.list}{SeuratCommand}(x, complete = FALSE, ...) +} +\arguments{ +\item{x}{A \code{\link{SeuratCommand}} object} + +\item{complete}{Include slots besides just parameters +(eg. call string, name, timestamp)} + +\item{...}{Ignored} +} +\value{ +A list with the parameters and, if \code{complete = TRUE}, +the call string, name, and timestamp +} +\description{ +Coerce a \code{SeuratCommand} to a list +} +\examples{ +cmd <- pbmc_small[["NormalizeData.RNA"]] +as.list(cmd) +as.list(cmd, complete = TRUE) + +} +\seealso{ +Command log object and interaction methods +\code{\link{$.SeuratCommand}()}, +\code{\link{.DollarNames.SeuratCommand}()}, +\code{\link{LogSeuratCommand}()}, +\code{\link{SeuratCommand-class}}, +\code{\link{[.SeuratCommand}()} +} +\concept{command} diff --git a/man/as.matrix.LogMap.Rd b/man/as.matrix.LogMap.Rd new file mode 100644 index 00000000..8f5111d0 --- /dev/null +++ b/man/as.matrix.LogMap.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/logmap.R +\name{as.matrix.LogMap} +\alias{as.matrix.LogMap} +\title{Coerce Logical Maps to Matrices} +\usage{ +\method{as.matrix}{LogMap}(x, ...) +} +\arguments{ +\item{x}{A \code{\link{LogMap}} object} + +\item{...}{Ignored} +} +\value{ +A base-R matrix created from \code{x} +} +\description{ +Coerce a logical map to a matrix; this removes all +\link[=LogMap]{logical map} class capabilities from +the object and returns a base-R matrix object +} +\examples{ +map <- LogMap(letters[1:10]) +map[['obs']] <- c(1, 3, 7) +mat <- as.matrix(map) +mat +class(mat) + +} +\seealso{ +Logical map objects, validity, and interaction methods: +\code{\link{LogMap-validity}}, +\code{\link{LogMap}}, +\code{\link{droplevels.LogMap}()}, +\code{\link{intersect.LogMap}()}, +\code{\link{labels.LogMap}()} +} +\concept{logmap} diff --git a/man/cash-.Assay.Rd b/man/cash-.Assay.Rd new file mode 100644 index 00000000..ee0d1727 --- /dev/null +++ b/man/cash-.Assay.Rd @@ -0,0 +1,51 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay.R +\name{$.Assay} +\alias{$.Assay} +\alias{$<-.Assay} +\title{Layer Data} +\usage{ +\method{$}{Assay}(x, i) + +\method{$}{Assay}(x, i) <- value +} +\arguments{ +\item{x}{An \code{\link{Assay}} object} + +\item{i}{Name of layer data to get or set} + +\item{value}{A matrix-like object to add as a new layer} +} +\value{ +{$}: Layer data for layer \code{i} + +\code{$<-}: \code{x} with layer data \code{value} saved as \code{i} +} +\description{ +Get and set layer data +} +\examples{ +rna <- pbmc_small[["RNA"]] + +# Fetch a layer with `$` +rna$data[1:10, 1:4] + +# Add a layer with `$` +rna$data <- rna$counts +rna$data[1:10, 1:4] + +} +\seealso{ +v3 Assay object, validity, and interaction methods: +\code{\link{Assay-class}}, +\code{\link{Assay-validity}}, +\code{\link{CreateAssayObject}()}, +\code{\link{[.Assay}()}, +\code{\link{[[.Assay}()}, +\code{\link{dim.Assay}()}, +\code{\link{dimnames.Assay}()}, +\code{\link{merge.Assay}()}, +\code{\link{split.Assay}()}, +\code{\link{subset.Assay}()} +} +\concept{assay} diff --git a/man/cash-.Assay5.Rd b/man/cash-.Assay5.Rd new file mode 100644 index 00000000..ebf9ffcb --- /dev/null +++ b/man/cash-.Assay5.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{$.Assay5} +\alias{$.Assay5} +\alias{$<-.Assay5} +\title{Layer Data} +\usage{ +\method{$}{Assay5}(x, i) + +\method{$}{Assay5}(x, i) <- value +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{i}{Name of layer data to get or set} + +\item{value}{A matrix-like object to add as a new layer} +} +\value{ +{$}: Layer data for layer \code{i} + +\code{$<-}: \code{x} with layer data \code{value} saved as \code{i} +} +\description{ +Get and set layer data +} +\seealso{ +v5 Assay object, validity, and interaction methods: +\code{\link{Assay5-class}}, +\code{\link{Assay5-validity}}, +\code{\link{[.Assay5}()}, +\code{\link{[[.Assay5}()}, +\code{\link{dim.Assay5}()}, +\code{\link{dimnames.Assay5}()}, +\code{\link{merge.Assay5}()}, +\code{\link{split.Assay5}()}, +\code{\link{subset.Assay5}()} +} +\concept{assay5} diff --git a/man/cash-.Seurat.Rd b/man/cash-.Seurat.Rd new file mode 100644 index 00000000..f4926cc3 --- /dev/null +++ b/man/cash-.Seurat.Rd @@ -0,0 +1,74 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{$.Seurat} +\alias{$.Seurat} +\alias{$<-.Seurat} +\alias{[[<-,Seurat,character,missing,data.frame-method} +\alias{[[<-,Seurat,missing,missing,data.frame-method} +\alias{[[<-,Seurat,character,missing,factor-method} +\alias{[[<-,Seurat,character,missing,list-method} +\alias{[[<-,Seurat,missing,missing,list-method} +\alias{[[<-,Seurat,character,missing,vector-method} +\title{Cell-Level Meta Data} +\usage{ +\method{$}{Seurat}(x, i) + +\method{$}{Seurat}(x, i, ...) <- value + +\S4method{[[}{Seurat,character,missing,data.frame}(x, i, j, ...) <- value + +\S4method{[[}{Seurat,missing,missing,data.frame}(x, i, j, ...) <- value + +\S4method{[[}{Seurat,character,missing,factor}(x, i, j, ...) <- value + +\S4method{[[}{Seurat,character,missing,list}(x, i, j, ...) <- value + +\S4method{[[}{Seurat,missing,missing,list}(x, i, j, ...) <- value + +\S4method{[[}{Seurat,character,missing,vector}(x, i, j, ...) <- value +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} + +\item{i}{Name of cell-level meta data} + +\item{...}{Ignored} + +\item{value}{A vector to add as cell-level meta data} + +\item{j}{Ignored} +} +\value{ +{$}: Metadata column \code{i} for object \code{x}; +\strong{note}: unlike \code{[[}, \code{$} drops the shape of the metadata +to return a vector instead of a data frame + +\code{$<-}: \code{x} with metadata \code{value} saved as \code{i} +} +\description{ +Get and set cell-level meta data +} +\examples{ +# Get metadata using `$' +head(pbmc_small$groups) + +# Add metadata using the `$' operator +set.seed(42) +pbmc_small$value <- sample(1:3, size = ncol(pbmc_small), replace = TRUE) +head(pbmc_small[["value"]]) + +} +\seealso{ +Seurat object, validity, and interaction methods +\code{\link{Seurat-class}}, +\code{\link{Seurat-validity}}, +\code{\link{[[.Seurat}()}, +\code{\link{[[<-,Seurat,NULL}}, +\code{\link{[[<-,Seurat}}, +\code{\link{dim.Seurat}()}, +\code{\link{dimnames.Seurat}()}, +\code{\link{merge.Seurat}()}, +\code{\link{names.Seurat}()}, +\code{\link{subset.Seurat}()} +} +\concept{seurat} diff --git a/man/cash-.SeuratCommand.Rd b/man/cash-.SeuratCommand.Rd new file mode 100644 index 00000000..0f0cd530 --- /dev/null +++ b/man/cash-.SeuratCommand.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/command.R +\name{$.SeuratCommand} +\alias{$.SeuratCommand} +\title{Command Log Parameter Access} +\usage{ +\method{$}{SeuratCommand}(x, i) +} +\arguments{ +\item{x}{A \code{\link{SeuratCommand}} object} + +\item{i}{A parameter name} +} +\value{ +The value for parameter \code{i} +} +\description{ +Pull parameter values from a \code{\link{SeuratCommand}} object +} +\examples{ +cmd <- pbmc_small[["NormalizeData.RNA"]] +cmd$normalization.method + +} +\seealso{ +Command log object and interaction methods +\code{\link{.DollarNames.SeuratCommand}()}, +\code{\link{LogSeuratCommand}()}, +\code{\link{SeuratCommand-class}}, +\code{\link{[.SeuratCommand}()}, +\code{\link{as.list.SeuratCommand}()} +} +\concept{command} diff --git a/man/cash-.StdAssay.Rd b/man/cash-.StdAssay.Rd new file mode 100644 index 00000000..24ab00d2 --- /dev/null +++ b/man/cash-.StdAssay.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{$.StdAssay} +\alias{$.StdAssay} +\alias{$<-.StdAssay} +\title{Layer Data} +\usage{ +\method{$}{StdAssay}(x, i) + +\method{$}{StdAssay}(x, i) <- value +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{i}{Name of layer data to get or set} + +\item{value}{A matrix-like object to add as a new layer} +} +\value{ +{$}: Layer data for layer \code{i} + +\code{$<-}: \code{x} with layer data \code{value} saved as \code{i} +} +\description{ +Get and set layer data +} +\seealso{ +v5 Standard Assay object, validity, and interaction methods +\code{\link{.DollarNames.StdAssay}()}, +\code{\link{StdAssay-class}}, +\code{\link{StdAssay-validity}}, +\code{\link{[.StdAssay}()}, +\code{\link{[[.StdAssay}()}, +\code{\link{dim.StdAssay}()}, +\code{\link{dimnames.StdAssay}()}, +\code{\link{show,StdAssay-method}}, +\code{\link{split.StdAssay}()}, +\code{\link{subset.StdAssay}()} +} +\concept{stdassay} +\keyword{internal} diff --git a/man/colMeans-Assay-method.Rd b/man/colMeans-Assay-method.Rd new file mode 100644 index 00000000..da452945 --- /dev/null +++ b/man/colMeans-Assay-method.Rd @@ -0,0 +1,60 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay.R +\name{colMeans,Assay-method} +\alias{colMeans,Assay-method} +\alias{colSums,Assay-method} +\alias{rowMeans,Assay-method} +\alias{rowSums,Assay-method} +\title{Row and Column Sums and Means} +\usage{ +\S4method{colMeans}{Assay}(x, na.rm = FALSE, dims = 1, ..., slot = "data") + +\S4method{colSums}{Assay}(x, na.rm = FALSE, dims = 1, ..., slot = "data") + +\S4method{rowMeans}{Assay}(x, na.rm = FALSE, dims = 1, ..., slot = "data") + +\S4method{rowSums}{Assay}(x, na.rm = FALSE, dims = 1, ..., slot = "data") +} +\arguments{ +\item{x}{An \code{\link{Assay}} object} + +\item{na.rm}{logical. Should missing values (including \code{NaN}) + be omitted from the calculations?} + +\item{dims}{completely ignored by the \code{Matrix} methods.} + +\item{...}{Ignored} + +\item{slot}{Name of assay expression matrix to calculate column/row +means/sums on} +} +\value{ +\code{colMeans}: The column (cell-wise) means of \code{slot} + +\code{colSums}: The column (cell-wise) sums of \code{slot} + +\code{rowMeans}: The row (feature-wise) means of \code{slot} + +\code{rowSums}: The row (feature-wise) sums of \code{slot} +} +\description{ +Calculate \code{\link{rowSums}}, \code{\link{colSums}}, +\code{\link{rowMeans}}, and \code{\link{colMeans}} on \code{Assay} objects +} +\examples{ +rna <- pbmc_small[["RNA"]] + +colMeans(rna) + +colSums(rna) + +rowMeans(rna) + +rowSums(rna) + +} +\seealso{ +\code{\link{Assay}} +} +\concept{assay} +\keyword{internal} diff --git a/man/colMeans-Seurat-method.Rd b/man/colMeans-Seurat-method.Rd new file mode 100644 index 00000000..712530ee --- /dev/null +++ b/man/colMeans-Seurat-method.Rd @@ -0,0 +1,60 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{colMeans,Seurat-method} +\alias{colMeans,Seurat-method} +\alias{colSums,Seurat-method} +\alias{rowMeans,Seurat-method} +\alias{rowSums,Seurat-method} +\title{Row and Column Sums and Means} +\usage{ +\S4method{colMeans}{Seurat}(x, na.rm = FALSE, dims = 1, ..., slot = "data") + +\S4method{colSums}{Seurat}(x, na.rm = FALSE, dims = 1, ..., slot = "data") + +\S4method{rowMeans}{Seurat}(x, na.rm = FALSE, dims = 1, ..., slot = "data") + +\S4method{rowSums}{Seurat}(x, na.rm = FALSE, dims = 1, ..., slot = "data") +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} + +\item{na.rm}{logical. Should missing values (including \code{NaN}) + be omitted from the calculations?} + +\item{dims}{completely ignored by the \code{Matrix} methods.} + +\item{...}{potentially further arguments, for method \code{<->} + generic compatibility.} + +\item{slot}{Name of assay expression matrix to calculate column/row +means/sums on} +} +\value{ +\code{colMeans}: the column (cell-wise) means of \code{slot} + +\code{colSums}: the column (cell-wise) sums of \code{slot} + +\code{rowMeans}: the row (feature-wise) means of \code{slot} + +\code{rowSums}: the row (feature-wise) sums of \code{slot} +} +\description{ +Calculate \code{\link{rowSums}}, \code{\link{colSums}}, +\code{\link{rowMeans}}, and \code{\link{colMeans}} on +\code{\link{Seurat}} objects +} +\examples{ +head(colMeans(pbmc_small)) + +head(colSums(pbmc_small)) + +head(rowMeans(pbmc_small)) + +head(rowSums(pbmc_small)) + +} +\seealso{ +\code{\link{Seurat}} +} +\concept{seurat} +\keyword{internal} diff --git a/man/dim.Assay.Rd b/man/dim.Assay.Rd new file mode 100644 index 00000000..a15a7d82 --- /dev/null +++ b/man/dim.Assay.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay.R +\name{dim.Assay} +\alias{dim.Assay} +\title{Feature and Cell Numbers} +\usage{ +\method{dim}{Assay}(x) +} +\arguments{ +\item{x}{An \code{\link{Assay}} object} +} +\value{ +A two-length numeric vector with the total number of +features and cells in \code{x} +} +\description{ +Feature and Cell Numbers +} +\examples{ +rna <- pbmc_small[["RNA"]] +dim(rna) + +} +\seealso{ +v3 Assay object, validity, and interaction methods: +\code{\link{$.Assay}()}, +\code{\link{Assay-class}}, +\code{\link{Assay-validity}}, +\code{\link{CreateAssayObject}()}, +\code{\link{[.Assay}()}, +\code{\link{[[.Assay}()}, +\code{\link{dimnames.Assay}()}, +\code{\link{merge.Assay}()}, +\code{\link{split.Assay}()}, +\code{\link{subset.Assay}()} +} +\concept{assay} diff --git a/man/dim.Assay5.Rd b/man/dim.Assay5.Rd new file mode 100644 index 00000000..93c90dff --- /dev/null +++ b/man/dim.Assay5.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{dim.Assay5} +\alias{dim.Assay5} +\title{Feature and Cell Numbers} +\usage{ +\method{dim}{Assay5}(x) +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} +} +\value{ +A two-length numeric vector with the total number of +features and cells in \code{x} +} +\description{ +Feature and Cell Numbers +} +\seealso{ +v5 Assay object, validity, and interaction methods: +\code{\link{$.Assay5}()}, +\code{\link{Assay5-class}}, +\code{\link{Assay5-validity}}, +\code{\link{[.Assay5}()}, +\code{\link{[[.Assay5}()}, +\code{\link{dimnames.Assay5}()}, +\code{\link{merge.Assay5}()}, +\code{\link{split.Assay5}()}, +\code{\link{subset.Assay5}()} +} +\concept{assay5} diff --git a/man/dim.DimReduc.Rd b/man/dim.DimReduc.Rd new file mode 100644 index 00000000..729b976d --- /dev/null +++ b/man/dim.DimReduc.Rd @@ -0,0 +1,71 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/dimreduc.R +\name{dim.DimReduc} +\alias{dim.DimReduc} +\alias{dimnames.DimReduc} +\alias{length.DimReduc} +\alias{names.DimReduc} +\title{Dimensional Reduction Meta-Information} +\usage{ +\method{dim}{DimReduc}(x) + +\method{dimnames}{DimReduc}(x) + +\method{length}{DimReduc}(x) + +\method{names}{DimReduc}(x) +} +\arguments{ +\item{x}{A \code{\link{DimReduc}} object} +} +\value{ +\code{dim}: The number of cells (\code{nrow}) and dimensions +(\code{ncol}) + +\code{dimnames}: The cell (row) and dimension (column) names + +\code{length}: The number of dimensions + +\code{names}: The dimension identifiers +} +\description{ +Pull meta-information about cells and dimensions for a given +\link[=DimReduc]{dimensional reduction}; cell meta-information is stored +as row meta-information (eg. \code{nrow}, \code{rownames}) and dimension +meta-information is stored as column meta-information (eg. \code{ncol}, +\code{colnames}) +} +\examples{ +pca <- pbmc_small[["pca"]] +pca +dim(pca) + +# nrow is number of cells +nrow(pca) + +# rownames pulls cell names +head(rownames(pca)) + +# ncol and length are number of dimensions +ncol(pca) +length(pca) + +# colnames and names pull dimension identifiers +head(colnames(pca)) +head(names(pca)) + +} +\seealso{ +\code{Cells} + +Dimensional reduction object, validity, and interaction methods +\code{\link{CreateDimReducObject}()}, +\code{\link{DimReduc-class}}, +\code{\link{DimReduc-validity}}, +\code{\link{[.DimReduc}()}, +\code{\link{[[.DimReduc}()}, +\code{\link{merge.DimReduc}()}, +\code{\link{print.DimReduc}()}, +\code{\link{subset.DimReduc}()} +} +\concept{dimreduc} diff --git a/man/dim.Seurat.Rd b/man/dim.Seurat.Rd new file mode 100644 index 00000000..9d796442 --- /dev/null +++ b/man/dim.Seurat.Rd @@ -0,0 +1,40 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{dim.Seurat} +\alias{dim.Seurat} +\title{Feature and Cell Numbers} +\usage{ +\method{dim}{Seurat}(x) +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} +} +\value{ +A two-length numeric vector with the total number of +features and cells in \code{x} +} +\description{ +Feature and Cell Numbers +} +\examples{ +# Get the number of features in an object +nrow(pbmc_small) + +# Get the number of cells in an object +ncol(pbmc_small) + +} +\seealso{ +Seurat object, validity, and interaction methods +\code{\link{$.Seurat}()}, +\code{\link{Seurat-class}}, +\code{\link{Seurat-validity}}, +\code{\link{[[.Seurat}()}, +\code{\link{[[<-,Seurat,NULL}}, +\code{\link{[[<-,Seurat}}, +\code{\link{dimnames.Seurat}()}, +\code{\link{merge.Seurat}()}, +\code{\link{names.Seurat}()}, +\code{\link{subset.Seurat}()} +} +\concept{seurat} diff --git a/man/dim.StdAssay.Rd b/man/dim.StdAssay.Rd new file mode 100644 index 00000000..aff2dda1 --- /dev/null +++ b/man/dim.StdAssay.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{dim.StdAssay} +\alias{dim.StdAssay} +\title{Feature and Cell Numbers} +\usage{ +\method{dim}{StdAssay}(x) +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} +} +\value{ +A two-length numeric vector with the total number of +features and cells in \code{x} +} +\description{ +Feature and Cell Numbers +} +\seealso{ +v5 Standard Assay object, validity, and interaction methods +\code{\link{$.StdAssay}()}, +\code{\link{.DollarNames.StdAssay}()}, +\code{\link{StdAssay-class}}, +\code{\link{StdAssay-validity}}, +\code{\link{[.StdAssay}()}, +\code{\link{[[.StdAssay}()}, +\code{\link{dimnames.StdAssay}()}, +\code{\link{show,StdAssay-method}}, +\code{\link{split.StdAssay}()}, +\code{\link{subset.StdAssay}()} +} +\concept{stdassay} +\keyword{internal} diff --git a/man/dimnames.Assay.Rd b/man/dimnames.Assay.Rd new file mode 100644 index 00000000..1d3bc594 --- /dev/null +++ b/man/dimnames.Assay.Rd @@ -0,0 +1,63 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay.R +\name{dimnames.Assay} +\alias{dimnames.Assay} +\alias{dimnames<-.Assay} +\title{Assay-Level Feature and Cell Names} +\usage{ +\method{dimnames}{Assay}(x) + +\method{dimnames}{Assay}(x) <- value +} +\arguments{ +\item{x}{An \code{\link{Assay}} object} + +\item{value}{A two-length list where the first entry is the existing feature +names for \code{x} and the second entry is the \emph{updated} cell names +for \code{x}} +} +\value{ +\code{dimnames}: A two-length list with the following values: +\itemize{ + \item A character vector will all features in \code{x} + \item A character vector will all cells in \code{x} +} + +\code{dimnames<-}: \code{x} with the cell names updated to those +in \code{value[[2L]]} +} +\description{ +Get and set feature and cell names in v5 Assays +} +\examples{ +rna <- pbmc_small[["RNA"]] + +# Feature and cell names can be acquired with `rownames` and `colnames` +head(rownames(rna)) +head(colnames(rna)) + +# Cell names can be updated with `colnames<-` +colnames(rna)[1] <- "newcell" +head(colnames(rna)) + +} +\seealso{ +v3 Assay object, validity, and interaction methods: +\code{\link{$.Assay}()}, +\code{\link{Assay-class}}, +\code{\link{Assay-validity}}, +\code{\link{CreateAssayObject}()}, +\code{\link{[.Assay}()}, +\code{\link{[[.Assay}()}, +\code{\link{dim.Assay}()}, +\code{\link{merge.Assay}()}, +\code{\link{split.Assay}()}, +\code{\link{subset.Assay}()} + + +\code{\link{Cells}()}, +\code{\link{dimnames.Assay5}()}, +\code{\link{dimnames.Seurat}()} +} +\concept{assay} +\concept{dimnames} diff --git a/man/dimnames.Assay5.Rd b/man/dimnames.Assay5.Rd new file mode 100644 index 00000000..612ccadd --- /dev/null +++ b/man/dimnames.Assay5.Rd @@ -0,0 +1,48 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{dimnames.Assay5} +\alias{dimnames.Assay5} +\alias{dimnames<-.Assay5} +\title{Assay-Level Feature and Cell Names} +\usage{ +\method{dimnames}{Assay5}(x) + +\method{dimnames}{Assay5}(x) <- value +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{value}{A two-length list with updated feature and/or cells names} +} +\value{ +\code{dimnames}: A two-length list with the following values: +\itemize{ + \item A character vector with all features in \code{x} + \item A character vector with all cells in \code{x} +} + +\code{dimnames<-}: \code{x} with the feature and/or cell +names updated to \code{value} +} +\description{ +Get and set feature and cell names in v5 Assays +} +\seealso{ +v5 Assay object, validity, and interaction methods: +\code{\link{$.Assay5}()}, +\code{\link{Assay5-class}}, +\code{\link{Assay5-validity}}, +\code{\link{[.Assay5}()}, +\code{\link{[[.Assay5}()}, +\code{\link{dim.Assay5}()}, +\code{\link{merge.Assay5}()}, +\code{\link{split.Assay5}()}, +\code{\link{subset.Assay5}()} + + +\code{\link{Cells}()}, +\code{\link{dimnames.Assay}()}, +\code{\link{dimnames.Seurat}()} +} +\concept{assay5} +\concept{dimnames} diff --git a/man/dimnames.Seurat.Rd b/man/dimnames.Seurat.Rd new file mode 100644 index 00000000..b9e930df --- /dev/null +++ b/man/dimnames.Seurat.Rd @@ -0,0 +1,61 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{dimnames.Seurat} +\alias{dimnames.Seurat} +\alias{dimnames<-.Seurat} +\title{Feature and Cell Names} +\usage{ +\method{dimnames}{Seurat}(x) + +\method{dimnames}{Seurat}(x) <- value +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} + +\item{value}{A two-length list with updated feature and/or cells names} +} +\value{ +\code{dimnames}: A two-length list with the following values: +\itemize{ + \item A character vector with all features in the + \link[=DefaultAssay]{default assay} + \item A character vector with all cells in \code{x} +} + +\code{dimnames<-}: \code{x} with the feature and/or cell +names updated to \code{value} +} +\description{ +Get and set feature and cell inames in \code{\link{Seurat}} objects +} +\examples{ +# Get the feature names of an object +head(rownames(pbmc_small)) + +# Get the cell names of an object +head(colnames(pbmc_small)) + +colnames(pbmc_small)[1] <- "newcell" +head(colnames(pbmc_small)) + +} +\seealso{ +Seurat object, validity, and interaction methods +\code{\link{$.Seurat}()}, +\code{\link{Seurat-class}}, +\code{\link{Seurat-validity}}, +\code{\link{[[.Seurat}()}, +\code{\link{[[<-,Seurat,NULL}}, +\code{\link{[[<-,Seurat}}, +\code{\link{dim.Seurat}()}, +\code{\link{merge.Seurat}()}, +\code{\link{names.Seurat}()}, +\code{\link{subset.Seurat}()} + + +\code{\link{Cells}()}, +\code{\link{dimnames.Assay5}()}, +\code{\link{dimnames.Assay}()} +} +\concept{dimnames} +\concept{seurat} diff --git a/man/dimnames.StdAssay.Rd b/man/dimnames.StdAssay.Rd new file mode 100644 index 00000000..866c5781 --- /dev/null +++ b/man/dimnames.StdAssay.Rd @@ -0,0 +1,46 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{dimnames.StdAssay} +\alias{dimnames.StdAssay} +\alias{dimnames<-.StdAssay} +\title{Assay-Level Feature and Cell Names} +\usage{ +\method{dimnames}{StdAssay}(x) + +\method{dimnames}{StdAssay}(x) <- value +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{value}{A two-length list with updated feature and/or cells names} +} +\value{ +\code{dimnames}: A two-length list with the following values: +\itemize{ + \item A character vector with all features in \code{x} + \item A character vector with all cells in \code{x} +} + +\code{dimnames<-}: \code{x} with the feature and/or cell +names updated to \code{value} +} +\description{ +Get and set feature and cell names in v5 Assays +} +\seealso{ +\code{\link{Cells}} \code{\link{Features}} + +v5 Standard Assay object, validity, and interaction methods +\code{\link{$.StdAssay}()}, +\code{\link{.DollarNames.StdAssay}()}, +\code{\link{StdAssay-class}}, +\code{\link{StdAssay-validity}}, +\code{\link{[.StdAssay}()}, +\code{\link{[[.StdAssay}()}, +\code{\link{dim.StdAssay}()}, +\code{\link{show,StdAssay-method}}, +\code{\link{split.StdAssay}()}, +\code{\link{subset.StdAssay}()} +} +\concept{stdassay} +\keyword{internal} diff --git a/man/dot-AssayClass.Rd b/man/dot-AssayClass.Rd new file mode 100644 index 00000000..e3586557 --- /dev/null +++ b/man/dot-AssayClass.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R, R/default.R, R/utils.R +\name{.AssayClass} +\alias{.AssayClass} +\alias{.AssayClass.StdAssay} +\alias{.AssayClass.default} +\title{Assay Class Label} +\usage{ +.AssayClass(object) + +\method{.AssayClass}{StdAssay}(object) + +\method{.AssayClass}{default}(object) +} +\arguments{ +\item{object}{A \code{\link{StdAssay}} object} +} +\value{ +The assay class label for \code{object} +} +\description{ +Assay Class Label +} +\keyword{internal} diff --git a/man/dot-BPMatrixMode.Rd b/man/dot-BPMatrixMode.Rd new file mode 100644 index 00000000..5bd8933f --- /dev/null +++ b/man/dot-BPMatrixMode.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.BPMatrixMode} +\alias{.BPMatrixMode} +\title{\pkg{BPCells} Matrix Mode} +\usage{ +.BPMatrixMode(object, simplify = FALSE) +} +\arguments{ +\item{object}{An \code{IterableMatrix}} + +\item{simplify}{Return \dQuote{\code{disk}} for on-disk matrices} +} +\value{ +One of the following, depending on the mode of \code{object}: +\itemize{ + \item \dQuote{\code{memory}} + \item \dQuote{\code{file}} + \item \dQuote{\code{directory}} +} +If \code{simplify} is \code{TRUE}, returns \dQuote{\code{disk}} instead of +\dQuote{\code{file}} or \dQuote{\code{directory}} +} +\description{ +Get the mode (on-disk, in-memory) of an \code{IterableMatrix} object +from \pkg{BPCells} +} +\keyword{internal} diff --git a/man/dot-CalcN.Rd b/man/dot-CalcN.Rd new file mode 100644 index 00000000..5b66a242 --- /dev/null +++ b/man/dot-CalcN.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R +\name{.CalcN} +\alias{.CalcN} +\title{Calculate nCount and nFeature} +\usage{ +.CalcN(object, ...) +} +\arguments{ +\item{object}{An assay-like object} + +\item{...}{Arguments passed to other methods} +} +\value{ +A named list with ... +} +\description{ +Calculate nCount and nFeature +} +\examples{ +calcn <- .CalcN(pbmc_small[["RNA"]]) +head(as.data.frame(calcn)) + +} +\keyword{internal} diff --git a/man/dot-CheckFmargin.Rd b/man/dot-CheckFmargin.Rd new file mode 100644 index 00000000..9e8217a0 --- /dev/null +++ b/man/dot-CheckFmargin.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/layers.R +\name{.CheckFmargin} +\alias{.CheckFmargin} +\title{Check Feature Margin} +\usage{ +.CheckFmargin(fmargin) +} +\arguments{ +\item{fmargin}{Either \code{1} or \code{2}} +} +\value{ +\code{fmargin} +} +\description{ +Check Feature Margin +} +\examples{ +.CheckFmargin(1L) +.CheckFmargin(2.3) + +# Error if `fmargin` is outside of [1:2] +if (FALSE) { + .CheckFmargin(3L) +} + +} +\keyword{internal} diff --git a/man/dot-ClassPkg.Rd b/man/dot-ClassPkg.Rd new file mode 100644 index 00000000..578e05f4 --- /dev/null +++ b/man/dot-ClassPkg.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R, R/utils.R +\name{.ClassPkg} +\alias{.ClassPkg} +\alias{.ClassPkg.default} +\alias{.ClassPkg.DelayedArray} +\alias{.ClassPkg.R6} +\alias{.ClassPkg.R6ClassGenerator} +\title{Get the Package that Defines a Class} +\usage{ +.ClassPkg(object) + +\method{.ClassPkg}{default}(object) + +\method{.ClassPkg}{DelayedArray}(object) + +\method{.ClassPkg}{R6}(object) + +\method{.ClassPkg}{R6ClassGenerator}(object) +} +\arguments{ +\item{object}{An object} +} +\value{ +The package that defines the class of \code{object} +} +\description{ +Get the Package that Defines a Class +} +\examples{ +.ClassPkg(pbmc_small) + +} +\keyword{internal} diff --git a/man/dot-Collections.Rd b/man/dot-Collections.Rd new file mode 100644 index 00000000..55dd55c0 --- /dev/null +++ b/man/dot-Collections.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.Collections} +\alias{.Collections} +\title{Identify Object Collections} +\usage{ +.Collections(object, exclude = character(length = 0L), ...) +} +\arguments{ +\item{object}{An \link[methods:Classes_Details]{S4} object} + +\item{exclude}{A character vector of slot names to exclude} + +\item{...}{Arguments passed to \code{\link{IsNamedList}}} +} +\value{ +A character vector of names of collection slots +} +\description{ +Find all collection (named lists) slots in an S4 object +} +\examples{ +.Collections(pbmc_small) + +} +\seealso{ + +\code{\link{.FilterObjects}()}, +\code{\link{.FindObject}()}, +\code{\link{.Subobjects}()} +} +\concept{subobjects} +\concept{utils} +\keyword{internal} diff --git a/man/dot-Contains.Rd b/man/dot-Contains.Rd index 8c263488..fab5a4ca 100644 --- a/man/dot-Contains.Rd +++ b/man/dot-Contains.Rd @@ -19,4 +19,5 @@ Get Parent S4 Classes .Contains(pbmc_small) } +\concept{utils} \keyword{internal} diff --git a/man/dot-CreateStdAssay.Rd b/man/dot-CreateStdAssay.Rd new file mode 100644 index 00000000..1da1cadc --- /dev/null +++ b/man/dot-CreateStdAssay.Rd @@ -0,0 +1,109 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R, R/assay5.R +\name{.CreateStdAssay} +\alias{.CreateStdAssay} +\alias{.CreateStdAssay.default} +\alias{.CreateStdAssay.list} +\alias{.CreateStdAssay.Matrix} +\alias{.CreateStdAssay.matrix} +\title{Generic Assay Creation} +\usage{ +.CreateStdAssay( + counts, + min.cells = 0, + min.features = 0, + cells = NULL, + features = NULL, + transpose = FALSE, + type = "Assay5", + ... +) + +\method{.CreateStdAssay}{default}( + counts, + min.cells = 0, + min.features = 0, + cells = NULL, + features = NULL, + transpose = FALSE, + type = "Assay5", + layer = "counts", + ... +) + +\method{.CreateStdAssay}{list}( + counts, + min.cells = 0, + min.features = 0, + cells = NULL, + features = NULL, + transpose = FALSE, + type = "Assay5", + csum = Matrix::colSums, + fsum = Matrix::rowSums, + ... +) + +\method{.CreateStdAssay}{Matrix}( + counts, + min.cells = 0, + min.features = 0, + cells = NULL, + features = NULL, + transpose = FALSE, + type = "Assay5", + layer = "counts", + ... +) + +\method{.CreateStdAssay}{matrix}( + counts, + min.cells = 0, + min.features = 0, + cells = NULL, + features = NULL, + transpose = FALSE, + type = "Assay5", + layer = "counts", + ... +) +} +\arguments{ +\item{counts}{A two-dimensional expression matrix} + +\item{min.cells}{Include features detected in at least this many cells; +will subset the counts matrix as well. To reintroduce excluded features, +create a new object with a lower cutoff} + +\item{min.features}{Include cells where at least this many features +are detected} + +\item{cells}{Vector of cell names} + +\item{features}{Vector of feature names} + +\item{type}{Type of assay object to create; must be the name of a class +that's derived from \code{\link{StdAssay}}} + +\item{...}{Extra parameters passed to \code{\link[methods]{new}} for +assay creation; used to set slots not defined by \code{\link{StdAssay}}} + +\item{layer}{Name of layer to store \code{counts} as} + +\item{csum}{Function for calculating cell sums} + +\item{fsum}{Function for calculating feature sums} +} +\value{ +An object of class \code{type} with a layer named \code{layer} +containing the data found in \code{counts} +} +\description{ +Create an assay object; runs a standardized filtering scheme that +works regardless of the direction of the data (eg. cells as columns +and features as rows or vice versa) and creates an assay object based +on the initialization scheme defined for \code{\link{StdAssay}}-derived +class \code{type} +} +\concept{assay} +\keyword{internal} diff --git a/man/dot-DefaultFOV.Rd b/man/dot-DefaultFOV.Rd index ad02eae0..8275814d 100644 --- a/man/dot-DefaultFOV.Rd +++ b/man/dot-DefaultFOV.Rd @@ -16,4 +16,5 @@ Attempts to find the \dQuote{default} FOV using the revamped spatial framework } +\concept{utils} \keyword{internal} diff --git a/man/dot-Deprecate.Rd b/man/dot-Deprecate.Rd new file mode 100644 index 00000000..7ddd54e0 --- /dev/null +++ b/man/dot-Deprecate.Rd @@ -0,0 +1,76 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.Deprecate} +\alias{.Deprecate} +\title{Deprecate Functions and Arguments} +\usage{ +.Deprecate( + when, + what, + with = NULL, + ..., + pkg = NULL, + env = missing_arg(), + user_env = missing_arg() +) +} +\arguments{ +\item{when}{A string giving the version when the behaviour was deprecated.} + +\item{what}{A string describing what is deprecated: +\itemize{ +\item Deprecate a whole function with \code{"foo()"}. +\item Deprecate an argument with \code{"foo(arg)"}. +\item Partially deprecate an argument with +\code{"foo(arg = 'must be a scalar integer')"}. +\item Deprecate anything else with a custom message by wrapping it in \code{I()}. +} + +You can optionally supply the namespace: \code{"ns::foo()"}, but this is +usually not needed as it will be inferred from the caller environment.} + +\item{with}{An optional string giving a recommended replacement for the +deprecated behaviour. This takes the same form as \code{what}.} + +\item{...}{ + Arguments passed on to \code{\link[lifecycle:deprecate_soft]{lifecycle::deprecate_soft}} + \describe{ + \item{\code{details}}{In most cases the deprecation message can be +automatically generated from \code{with}. When it can't, use \code{details} +to provide a hand-written message. + +\code{details} can either be a single string or a character vector, +which will be converted to a \link[cli:cli_bullets]{bulleted list}. +By default, info bullets are used. Provide a named vectors to +override.} + \item{\code{id}}{The id of the deprecation. A warning is issued only once +for each \code{id}. Defaults to the generated message, but you should +give a unique ID when the message in \code{details} is built +programmatically and depends on inputs, or when you'd like to +deprecate multiple functions but warn only once for all of them.} + \item{\code{env,user_env}}{Pair of environments that define where \verb{deprecate_*()} +was called (used to determine the package name) and where the function +called the deprecating function was called (used to determine if +\code{deprecate_soft()} should message). + +These are only needed if you're calling \verb{deprecate_*()} from an internal +helper, in which case you should forward \code{env = caller_env()} and +\code{user_env = caller_env(2)}.} + }} + +\item{pkg}{Name of package to use for comparison} + +\item{env, user_env}{Managed internally by \code{.Deprecate()}} +} +\value{ +Run for its side effect and invisibly returns \code{NULL} +} +\description{ +Provides automatic deprecation and defunctation of functions and arguments; +} +\seealso{ +\code{\link[lifecycle:deprecate_soft]{lifecycle::deprecate_soft}()} +\code{\link[lifecycle:deprecate_warn]{lifecycle::deprecate_warn}()} +\code{\link[lifecycle:deprecate_stop]{lifecycle::deprecate_stop}()} +} +\keyword{internal} diff --git a/man/dot-DiskLoad.Rd b/man/dot-DiskLoad.Rd new file mode 100644 index 00000000..87160a8b --- /dev/null +++ b/man/dot-DiskLoad.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R, R/utils.R +\name{.DiskLoad} +\alias{.DiskLoad} +\alias{.DiskLoad.default} +\alias{.DiskLoad.10xMatrixH5} +\alias{.DiskLoad.AnnDataMatrixH5} +\alias{.DiskLoad.DelayedMatrix} +\alias{.DiskLoad.H5ADMatrix} +\alias{.DiskLoad.HDF5Matrix} +\alias{.DiskLoad.IterableMatrix} +\alias{.DiskLoad.MatrixDir} +\alias{.DiskLoad.MatrixH5} +\alias{.DiskLoad.TileDBMatrix} +\title{Disk Loading Function} +\usage{ +.DiskLoad(x) + +\method{.DiskLoad}{default}(x) + +\method{.DiskLoad}{`10xMatrixH5`}(x) + +\method{.DiskLoad}{AnnDataMatrixH5}(x) + +\method{.DiskLoad}{DelayedMatrix}(x) + +\method{.DiskLoad}{H5ADMatrix}(x) + +\method{.DiskLoad}{HDF5Matrix}(x) + +\method{.DiskLoad}{IterableMatrix}(x) + +\method{.DiskLoad}{MatrixDir}(x) + +\method{.DiskLoad}{MatrixH5}(x) + +\method{.DiskLoad}{TileDBMatrix}(x) +} +\arguments{ +\item{x}{A file-backed object} +} +\value{ +A one-length character that defines a function to load a matrix from +a file +} +\description{ +Generate a function to load a matrix from an on-disk file +} +\keyword{internal} diff --git a/man/dot-DollarNames.Assay.Rd b/man/dot-DollarNames.Assay.Rd new file mode 100644 index 00000000..10de26ab --- /dev/null +++ b/man/dot-DollarNames.Assay.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay.R +\name{.DollarNames.Assay} +\alias{.DollarNames.Assay} +\title{Dollar-sign Autocompletion} +\usage{ +\method{.DollarNames}{Assay}(x, pattern = "") +} +\arguments{ +\item{x}{An \code{\link{Assay}} object} + +\item{pattern}{ A regular expression. Only matching names are + returned. + } +} +\value{ +The layer name matches for \code{pattern} +} +\description{ +Autocompletion for \code{$} access on an +\code{\link{Assay}} object +} +\seealso{ +\code{\link[utils:.DollarNames]{utils::.DollarNames}} +} +\concept{assay} +\keyword{internal} diff --git a/man/dot-DollarNames.Assay5.Rd b/man/dot-DollarNames.Assay5.Rd new file mode 100644 index 00000000..026cfafb --- /dev/null +++ b/man/dot-DollarNames.Assay5.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{.DollarNames.Assay5} +\alias{.DollarNames.Assay5} +\title{Dollar-sign Autocompletion} +\usage{ +\method{.DollarNames}{Assay5}(x, pattern = "") +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{pattern}{ A regular expression. Only matching names are + returned. + } +} +\value{ +The layer name matches for \code{pattern} +} +\description{ +Autocompletion for \code{$} access on an \code{\link{Assay5}} object +} +\seealso{ +\code{\link[utils:.DollarNames]{utils::.DollarNames}} +} +\concept{assay5} +\keyword{internal} diff --git a/man/dot-DollarNames.Seurat.Rd b/man/dot-DollarNames.Seurat.Rd new file mode 100644 index 00000000..9080a78b --- /dev/null +++ b/man/dot-DollarNames.Seurat.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{.DollarNames.Seurat} +\alias{.DollarNames.Seurat} +\title{Dollar-sign Autocompletion} +\usage{ +\method{.DollarNames}{Seurat}(x, pattern = "") +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} + +\item{pattern}{ A regular expression. Only matching names are + returned. + } +} +\value{ +The meta data matches for \code{pattern} +} +\description{ +Autocompletion for \code{$} access on a \code{\link{Seurat}} object +} +\seealso{ +\code{\link[utils:.DollarNames]{utils::.DollarNames}} +} +\concept{seurat} +\keyword{internal} diff --git a/man/dot-DollarNames.SeuratCommand.Rd b/man/dot-DollarNames.SeuratCommand.Rd new file mode 100644 index 00000000..b8b106d4 --- /dev/null +++ b/man/dot-DollarNames.SeuratCommand.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/command.R +\name{.DollarNames.SeuratCommand} +\alias{.DollarNames.SeuratCommand} +\title{Dollar-sign Autocompletion} +\usage{ +\method{.DollarNames}{SeuratCommand}(x, pattern = "") +} +\arguments{ +\item{x}{A \code{\link{SeuratCommand}} object} + +\item{pattern}{ A regular expression. Only matching names are + returned. + } +} +\value{ +The parameter name matches for \code{pattern} +} +\description{ +Autocompletion for \code{$} access on a +\code{\link{SeuratCommand}} object +} +\seealso{ +Command log object and interaction methods +\code{\link{$.SeuratCommand}()}, +\code{\link{LogSeuratCommand}()}, +\code{\link{SeuratCommand-class}}, +\code{\link{[.SeuratCommand}()}, +\code{\link{as.list.SeuratCommand}()} +} +\concept{command} diff --git a/man/dot-DollarNames.StdAssay.Rd b/man/dot-DollarNames.StdAssay.Rd new file mode 100644 index 00000000..cf14b467 --- /dev/null +++ b/man/dot-DollarNames.StdAssay.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{.DollarNames.StdAssay} +\alias{.DollarNames.StdAssay} +\title{Dollar-sign Autocompletion} +\usage{ +\method{.DollarNames}{StdAssay}(x, pattern = "") +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{pattern}{ A regular expression. Only matching names are + returned. + } +} +\value{ +The layer name matches for \code{pattern} +} +\description{ +Autocompletion for \code{$} access on an \code{\link{Assay5}} object +} +\seealso{ +v5 Standard Assay object, validity, and interaction methods +\code{\link{$.StdAssay}()}, +\code{\link{StdAssay-class}}, +\code{\link{StdAssay-validity}}, +\code{\link{[.StdAssay}()}, +\code{\link{[[.StdAssay}()}, +\code{\link{dim.StdAssay}()}, +\code{\link{dimnames.StdAssay}()}, +\code{\link{show,StdAssay-method}}, +\code{\link{split.StdAssay}()}, +\code{\link{subset.StdAssay}()} +} +\concept{stdassay} +\keyword{internal} diff --git a/man/dot-FileMove.Rd b/man/dot-FileMove.Rd new file mode 100644 index 00000000..8365fee5 --- /dev/null +++ b/man/dot-FileMove.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.FileMove} +\alias{.FileMove} +\title{Move Files and Directories} +\usage{ +.FileMove(path, new_path, overwrite = FALSE, n = 1L) +} +\arguments{ +\item{path}{A character vector of one or more paths.} + +\item{new_path}{New file path. If \code{new_path} is existing directory, the file +will be moved into that directory; otherwise it will be moved/renamed to +the full path. + +Should either be the same length as \code{path}, or a single directory.} + +\item{n}{The number of callers to go back.} +} +\value{ +The new path (invisibly). +} +\description{ +Move files and directories with \pkg{fs}; includes a handler for when +\code{path} is a directory on a different filesystem than \code{new_path} +by explicitly copying and deleting \code{path} +} +\note{ +This function requires the +\href{https://cran.r-project.org/package=fs}{\pkg{fs}} package +to be installed +} +\seealso{ +\code{\link[fs:file_move]{fs::file_move}()} +} +\keyword{internal} diff --git a/man/dot-FilePath.Rd b/man/dot-FilePath.Rd new file mode 100644 index 00000000..cea506d3 --- /dev/null +++ b/man/dot-FilePath.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R, R/utils.R +\name{.FilePath} +\alias{.FilePath} +\alias{.FilePath.default} +\alias{.FilePath.DelayedMatrix} +\alias{.FilePath.IterableMatrix} +\title{Find a File Path} +\usage{ +.FilePath(x) + +\method{.FilePath}{default}(x) + +\method{.FilePath}{DelayedMatrix}(x) + +\method{.FilePath}{IterableMatrix}(x) +} +\arguments{ +\item{x}{A file-backed object} +} +\value{ +The path to the file that backs \code{x}; if \code{x} is not a +file-backed object, returns \code{NULL} +} +\description{ +Find a File Path +} +\keyword{internal} diff --git a/man/dot-FilterObjects.Rd b/man/dot-FilterObjects.Rd new file mode 100644 index 00000000..05e04c46 --- /dev/null +++ b/man/dot-FilterObjects.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.FilterObjects} +\alias{.FilterObjects} +\title{Find Subobjects Of A Certain Class} +\usage{ +.FilterObjects(object, classes.keep = c("Assay", "StdAssay", "DimReduc")) +} +\arguments{ +\item{object}{An \link[methods:Classes_Details]{S4} object} + +\item{classes.keep}{A vector of classes to keep} +} +\value{ +A vector of object names that are of class \code{classes.keep} +} +\description{ +Find Subobjects Of A Certain Class +} +\examples{ +.FilterObjects(pbmc_small) +.FilterObjects(pbmc_small, "Graph") + +} +\seealso{ + +\code{\link{.Collections}()}, +\code{\link{.FindObject}()}, +\code{\link{.Subobjects}()} +} +\concept{subobjects} +\concept{utils} +\keyword{internal} diff --git a/man/dot-FindObject.Rd b/man/dot-FindObject.Rd new file mode 100644 index 00000000..c1615113 --- /dev/null +++ b/man/dot-FindObject.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.FindObject} +\alias{.FindObject} +\title{Find A Subobject} +\usage{ +.FindObject(object, name, exclude = c("misc", "tools")) +} +\arguments{ +\item{object}{An \link[methods:Classes_Details]{S4} object} + +\item{name}{Name of subobject to find} + +\item{exclude}{A character vector of slot names to exclude} +} +\value{ +The name of the slot that contains \code{name}; returns \code{NULL} +if a subobject named \code{name} cannot be found +} +\description{ +Determine the slot that a subobject is contained in +} +\examples{ +.FindObject(pbmc_small, "tsne") + +} +\seealso{ + +\code{\link{.Collections}()}, +\code{\link{.FilterObjects}()}, +\code{\link{.Subobjects}()} +} +\concept{subobjects} +\concept{utils} +\keyword{internal} diff --git a/man/dot-GetMethod.Rd b/man/dot-GetMethod.Rd new file mode 100644 index 00000000..bf8b5308 --- /dev/null +++ b/man/dot-GetMethod.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.GetMethod} +\alias{.GetMethod} +\title{Get a Method} +\usage{ +.GetMethod(fxn, cls) +} +\arguments{ +\item{fxn}{Name of a function as a character} + +\item{cls}{The class to find a method of \code{fxn} for} +} +\value{ +The method of \code{fxn} for class \code{cls}; if no method found, +returns the default method. If no default method found; returns \code{NULL} +} +\description{ +Get a Method +} +\examples{ +.GetMethod('t', 'Matrix') +.GetMethod('t', 'data.frame') + +} +\concept{utils} +\keyword{internal} diff --git a/man/dot-IsFutureSeurat.Rd b/man/dot-IsFutureSeurat.Rd new file mode 100644 index 00000000..7b66c04e --- /dev/null +++ b/man/dot-IsFutureSeurat.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/zzz.R +\name{.IsFutureSeurat} +\alias{.IsFutureSeurat} +\alias{IsFutureSeurat} +\title{Test Future Compatibility with \pkg{Seurat}} +\usage{ +.IsFutureSeurat(version, lib.loc = NULL) +} +\arguments{ +\item{version}{A version string or object of class +\code{\link{package_version}}} + +\item{lib.loc}{a character vector of directory names of \R libraries, + or \code{NULL}. The default value of \code{NULL} corresponds to all + libraries currently known. If the default is used, the loaded + packages and namespaces are searched before the libraries.} +} +\value{ +\code{TRUE} if \pkg{SeuratObject} and/or \pkg{Seurat} +} +\description{ +Check to see if \pkg{SeuratObject} and/or \pkg{Seurat} are at least a +specific version or if they're configured to act as if they're a +specific version (see details below). This allows testing compatibility with +future requirements for both \pkg{SeuratObject} and \pkg{Seurat} +} +\details{ +Blah blah blah +} +\keyword{internal} diff --git a/man/dot-KeyPattern.Rd b/man/dot-KeyPattern.Rd new file mode 100644 index 00000000..752e36a8 --- /dev/null +++ b/man/dot-KeyPattern.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/keymixin.R +\name{.KeyPattern} +\alias{.KeyPattern} +\title{Regex Pattern for Keys} +\usage{ +.KeyPattern() +} +\value{ +Returns the regex pattern for keys +(\dQuote{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}) +} +\description{ +Regex Pattern for Keys +} +\seealso{ + +\code{\link{.RandomKey}()}, +\code{\link{Key-validity}}, +\code{\link{KeyMixin-class}} +} +\concept{key} +\keyword{internal} diff --git a/man/dot-MARGIN.Rd b/man/dot-MARGIN.Rd new file mode 100644 index 00000000..08c4df3e --- /dev/null +++ b/man/dot-MARGIN.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R +\name{.MARGIN} +\alias{.MARGIN} +\title{Get the Margin of an Object} +\usage{ +.MARGIN(x, ...) +} +\arguments{ +\item{x}{An object} +} +\value{ +The margin, eg. \code{1} for rows or \code{2} for columns +} +\description{ +Get the Margin of an Object +} +\keyword{internal} diff --git a/man/dot-PropagateList.Rd b/man/dot-PropagateList.Rd new file mode 100644 index 00000000..3fb2d97b --- /dev/null +++ b/man/dot-PropagateList.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.PropagateList} +\alias{.PropagateList} +\title{Propagate a List} +\usage{ +.PropagateList(x, names, default = NA) +} +\arguments{ +\item{x}{A list or character vector} + +\item{names}{A vector of names to keep from \code{x}} + +\item{default}{A default value for unassigned values of \code{x}} +} +\value{ +A named list where the names are present in both \code{x} and +\code{names} and the values are either the values from \code{x} or +\code{default} +} +\description{ +Propagate a List +} +\examples{ +.PropagateList("counts", c("RNA", "ADT", "SCT")) +.PropagateList(c("counts", "data"), c("RNA", "ADT", "SCT")) +.PropagateList("ADT", c("RNA", "ADT", "SCT")) +.PropagateList(c("RNA", "SCT"), c("RNA", "ADT", "SCT")) +.PropagateList(c("RNA", ADT = "counts"), c("RNA", "ADT", "SCT")) +.PropagateList(list(SCT = c("counts", "data"), ADT = "counts"), c("RNA", "ADT", "SCT")) +.PropagateList(list(SCT = c("counts", "data"), "ADT"), c("RNA", "ADT", "SCT")) + +} +\concept{utils} +\keyword{internal} diff --git a/man/dot-RandomKey.Rd b/man/dot-RandomKey.Rd new file mode 100644 index 00000000..34bcfbc1 --- /dev/null +++ b/man/dot-RandomKey.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/keymixin.R +\name{.RandomKey} +\alias{.RandomKey} +\title{Generate a Random Key} +\usage{ +.RandomKey(length = 7L, ...) +} +\arguments{ +\item{length}{How long should the name be} + +\item{...}{Extra parameters passed to \code{\link[base]{sample}}} +} +\value{ +Returns a valid key +} +\description{ +Generate a Random Key +} +\examples{ +set.seed(42L) +.RandomKey() + +} +\seealso{ + +\code{\link{.KeyPattern}()}, +\code{\link{Key-validity}}, +\code{\link{KeyMixin-class}} +} +\concept{key} +\keyword{internal} diff --git a/man/dot-SelectFeatures.Rd b/man/dot-SelectFeatures.Rd new file mode 100644 index 00000000..b3636fd5 --- /dev/null +++ b/man/dot-SelectFeatures.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/generics.R, R/utils.R +\name{.SelectFeatures} +\alias{.SelectFeatures} +\alias{.SelectFeatures.list} +\title{Combine and Select Features} +\usage{ +.SelectFeatures(object, ...) + +\method{.SelectFeatures}{list}(object, all.features = NULL, nfeatures = Inf, ...) +} +\arguments{ +\item{object}{An object} + +\item{...}{Arguments passed to other methods} +} +\value{ +A vector of features selected +} +\description{ +Combine and Select Features +} +\keyword{internal} diff --git a/man/dot-SparseSlots.Rd b/man/dot-SparseSlots.Rd new file mode 100644 index 00000000..f79e6721 --- /dev/null +++ b/man/dot-SparseSlots.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/sparse.R +\name{.SparseSlots} +\alias{.SparseSlots} +\alias{.SparseSlots.CsparseMatrix} +\alias{.SparseSlots.RsparseMatrix} +\alias{.SparseSlots.spam} +\title{Identify Sparse Slots} +\usage{ +.SparseSlots(x, type = c("pointers", "indices", "entries")) + +\method{.SparseSlots}{CsparseMatrix}(x, type = c("pointers", "entries", "indices")) + +\method{.SparseSlots}{RsparseMatrix}(x, type = c("pointers", "indices", "entries")) + +\method{.SparseSlots}{spam}(x, type = c("pointers", "indices", "entries")) +} +\arguments{ +\item{x}{A sparse matrix} + +\item{type}{...} +} +\value{ +... +} +\description{ +Identify Sparse Slots +} +\seealso{ + +\code{\link{IsSparse}()}, +\code{\link{RegisterSparseMatrix}()} +} +\concept{sparse} +\keyword{internal} diff --git a/man/dot-Subobjects.Rd b/man/dot-Subobjects.Rd new file mode 100644 index 00000000..3c5a2e4f --- /dev/null +++ b/man/dot-Subobjects.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{.Subobjects} +\alias{.Subobjects} +\title{Get the Subobject Names} +\usage{ +.Subobjects(object, exclude = c("misc", "tools"), collapse = TRUE, ...) +} +\arguments{ +\item{object}{An \link[methods:Classes_Details]{S4} object} + +\item{exclude}{A character vector of slot names to exclude} + +\item{collapse}{Collapse the list into a vector} + +\item{...}{Arguments passed to \code{\link{IsNamedList}}} +} +\value{ +If \code{collapse = TRUE}, then a vector with the names of all +subobjects; otherwise, a named list where the names are the names of the +collections and the values are the names of subobjects within the collection +} +\description{ +Get the Subobject Names +} +\examples{ +.Subobjects(pbmc_small) + +} +\seealso{ + +\code{\link{.Collections}()}, +\code{\link{.FilterObjects}()}, +\code{\link{.FindObject}()} +} +\concept{subobjects} +\keyword{internal} +\keyword{utils} diff --git a/man/droplevels.LogMap.Rd b/man/droplevels.LogMap.Rd new file mode 100644 index 00000000..ba1f3042 --- /dev/null +++ b/man/droplevels.LogMap.Rd @@ -0,0 +1,40 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/logmap.R +\name{droplevels.LogMap} +\alias{droplevels.LogMap} +\title{Drop Unused Logical Map Values} +\usage{ +\method{droplevels}{LogMap}(x, ...) +} +\arguments{ +\item{x}{A \code{LogMap} object} + +\item{...}{Ignored} +} +\value{ +\code{x} with values not present in any +observation removed +} +\description{ +Remove any unused values from a \link[=LogMap]{logical map} +} +\examples{ +map <- LogMap(letters[1:10]) +map[['obs']] <- c(1, 3, 7) +map[['entry']] <- c(2, 7, 10) + +# Remove unused values +map <- droplevels(map) +map +map[[]] + +} +\seealso{ +Logical map objects, validity, and interaction methods: +\code{\link{LogMap-validity}}, +\code{\link{LogMap}}, +\code{\link{as.matrix.LogMap}()}, +\code{\link{intersect.LogMap}()}, +\code{\link{labels.LogMap}()} +} +\concept{logmap} diff --git a/man/figures/lifecycle-archived.svg b/man/figures/lifecycle-archived.svg new file mode 100644 index 00000000..48f72a6f --- /dev/null +++ b/man/figures/lifecycle-archived.svg @@ -0,0 +1 @@ + lifecyclelifecyclearchivedarchived \ No newline at end of file diff --git a/man/figures/lifecycle-defunct.svg b/man/figures/lifecycle-defunct.svg new file mode 100644 index 00000000..01452e5f --- /dev/null +++ b/man/figures/lifecycle-defunct.svg @@ -0,0 +1 @@ +lifecyclelifecycledefunctdefunct \ No newline at end of file diff --git a/man/figures/lifecycle-deprecated.svg b/man/figures/lifecycle-deprecated.svg new file mode 100644 index 00000000..4baaee01 --- /dev/null +++ b/man/figures/lifecycle-deprecated.svg @@ -0,0 +1 @@ +lifecyclelifecycledeprecateddeprecated \ No newline at end of file diff --git a/man/figures/lifecycle-experimental.svg b/man/figures/lifecycle-experimental.svg new file mode 100644 index 00000000..d1d060e9 --- /dev/null +++ b/man/figures/lifecycle-experimental.svg @@ -0,0 +1 @@ +lifecyclelifecycleexperimentalexperimental \ No newline at end of file diff --git a/man/figures/lifecycle-maturing.svg b/man/figures/lifecycle-maturing.svg new file mode 100644 index 00000000..df713101 --- /dev/null +++ b/man/figures/lifecycle-maturing.svg @@ -0,0 +1 @@ +lifecyclelifecyclematuringmaturing \ No newline at end of file diff --git a/man/figures/lifecycle-questioning.svg b/man/figures/lifecycle-questioning.svg new file mode 100644 index 00000000..08ee0c90 --- /dev/null +++ b/man/figures/lifecycle-questioning.svg @@ -0,0 +1 @@ +lifecyclelifecyclequestioningquestioning \ No newline at end of file diff --git a/man/figures/lifecycle-stable.svg b/man/figures/lifecycle-stable.svg new file mode 100644 index 00000000..e015dc81 --- /dev/null +++ b/man/figures/lifecycle-stable.svg @@ -0,0 +1 @@ +lifecyclelifecyclestablestable \ No newline at end of file diff --git a/man/figures/lifecycle-superseded.svg b/man/figures/lifecycle-superseded.svg new file mode 100644 index 00000000..75f24f55 --- /dev/null +++ b/man/figures/lifecycle-superseded.svg @@ -0,0 +1 @@ + lifecyclelifecyclesupersededsuperseded \ No newline at end of file diff --git a/man/intersect.LogMap.Rd b/man/intersect.LogMap.Rd new file mode 100644 index 00000000..6fa6dbc9 --- /dev/null +++ b/man/intersect.LogMap.Rd @@ -0,0 +1,40 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/logmap.R +\name{intersect.LogMap} +\alias{intersect.LogMap} +\title{Find Common Logical Map Values} +\usage{ +\method{intersect}{LogMap}(x, y = missing_arg(), ...) +} +\arguments{ +\item{x}{A \code{LogMap} object} + +\item{y}{Ignored} + +\item{...}{Ignored} +} +\value{ +The values of \code{x} that are present in \strong{every} observation +} +\description{ +Identify values in a \link[=LogMap]{logical map} that are +common to every observation +} +\examples{ +map <- LogMap(letters[1:10]) +map[['obs']] <- c(1, 3, 7) +map[['entry']] <- c(2, 7, 10) + +# Identify values that are present in every observation +intersect(map) + +} +\seealso{ +Logical map objects, validity, and interaction methods: +\code{\link{LogMap-validity}}, +\code{\link{LogMap}}, +\code{\link{as.matrix.LogMap}()}, +\code{\link{droplevels.LogMap}()}, +\code{\link{labels.LogMap}()} +} +\concept{logmap} diff --git a/man/labels.LogMap.Rd b/man/labels.LogMap.Rd new file mode 100644 index 00000000..81283a8f --- /dev/null +++ b/man/labels.LogMap.Rd @@ -0,0 +1,60 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/logmap.R +\name{labels.LogMap} +\alias{labels.LogMap} +\title{Find Observations by Value} +\usage{ +\method{labels}{LogMap}( + object, + values, + select = c("first", "last", "common", "all"), + simplify = TRUE, + ... +) +} +\arguments{ +\item{object}{A \code{\link{LogMap}} object} + +\item{values}{A vector of values to find observations for} + +\item{select}{Observation selection method; choose from: +\itemize{ + \item \dQuote{\code{first}}: the first observation the value is found in + \item \dQuote{\code{last}}: the last observation the value is found in + \item \dQuote{\code{common}}: the first most-common observation the value + is found in; most-common is determined by the observation that contains + the most of the values requested + \item \dQuote{\code{all}}: all observations the value is found in +}} + +\item{simplify}{Simplify the resulting list to a vector} + +\item{...}{Ignored} +} +\value{ +\code{labels}: A list, or vector if \code{simplify} is \code{TRUE}, +of all values and the observations they're found in, according +to the value of \code{select} +} +\description{ +Identify the observations that contain a specific +value in a \link[=LogMap]{logical map} +} +\examples{ +map <- LogMap(letters[1:10]) +map[['obs']] <- c(1, 3, 7) +map[['entry']] <- c(2, 7, 10) + +# Find observations for a set of values +labels(map, c('a', 'b', 'g')) + +} +\seealso{ +Logical map objects, validity, and interaction methods: +\code{\link{LogMap-validity}}, +\code{\link{LogMap}}, +\code{\link{as.matrix.LogMap}()}, +\code{\link{droplevels.LogMap}()}, +\code{\link{intersect.LogMap}()} +} +\concept{logmap} diff --git a/man/merge.Assay.Rd b/man/merge.Assay.Rd new file mode 100644 index 00000000..12d64929 --- /dev/null +++ b/man/merge.Assay.Rd @@ -0,0 +1,52 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay.R +\name{merge.Assay} +\alias{merge.Assay} +\title{Merge Assays} +\usage{ +\method{merge}{Assay}( + x = NULL, + y = NULL, + add.cell.ids = NULL, + merge.data = TRUE, + labels = NULL, + collapse = TRUE, + ... +) +} +\arguments{ +\item{x}{An \code{\link{Assay}} object} + +\item{y}{One or more \code{\link{Assay}} objects} + +\item{add.cell.ids}{A character vector of \code{length(x = c(x, y))}; +appends the corresponding values to the start of each objects' cell names} + +\item{merge.data}{Merge the data slots instead of just merging the counts +(which requires renormalization); this is recommended if the same +normalization approach was applied to all objects} + +\item{labels, collapse}{Currently unused} + +\item{...}{Ignored} +} +\value{ +A new assay with data merged from \code{c(x, y)} +} +\description{ +Merge one or more v3 assays together +} +\seealso{ +v3 Assay object, validity, and interaction methods: +\code{\link{$.Assay}()}, +\code{\link{Assay-class}}, +\code{\link{Assay-validity}}, +\code{\link{CreateAssayObject}()}, +\code{\link{[.Assay}()}, +\code{\link{[[.Assay}()}, +\code{\link{dim.Assay}()}, +\code{\link{dimnames.Assay}()}, +\code{\link{split.Assay}()}, +\code{\link{subset.Assay}()} +} +\concept{assay} diff --git a/man/merge.Assay5.Rd b/man/merge.Assay5.Rd new file mode 100644 index 00000000..ab1a1b4f --- /dev/null +++ b/man/merge.Assay5.Rd @@ -0,0 +1,47 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{merge.Assay5} +\alias{merge.Assay5} +\title{Merge Assays} +\usage{ +\method{merge}{Assay5}(x, y, labels = NULL, add.cell.ids = NULL, collapse = FALSE, ...) +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{y}{One or more \code{\link{Assay5}} objects} + +\item{labels}{A character vector equal to the number of objects; defaults to +\code{as.character(seq_along(c(x, y)))}} + +\item{add.cell.ids}{A character vector equal to the number of objects +provided to append to all cell names; if \code{TRUE}, uses \code{labels} as +\code{add.cell.ids}} + +\item{collapse}{If \code{TRUE}, merge layers of the same name together; if +\code{FALSE}, appends \code{labels} to the layer name} + +\item{...}{Ignored} +} +\value{ +A new v5 assay with data merged from \code{c(x, y)} +} +\description{ +Merge one or more v5 assays together +} +\details{ +\strong{Note}: collapsing layers is currently not supported +} +\seealso{ +v5 Assay object, validity, and interaction methods: +\code{\link{$.Assay5}()}, +\code{\link{Assay5-class}}, +\code{\link{Assay5-validity}}, +\code{\link{[.Assay5}()}, +\code{\link{[[.Assay5}()}, +\code{\link{dim.Assay5}()}, +\code{\link{dimnames.Assay5}()}, +\code{\link{split.Assay5}()}, +\code{\link{subset.Assay5}()} +} +\concept{assay5} diff --git a/man/merge.DimReduc.Rd b/man/merge.DimReduc.Rd new file mode 100644 index 00000000..b608938e --- /dev/null +++ b/man/merge.DimReduc.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/dimreduc.R +\name{merge.DimReduc} +\alias{merge.DimReduc} +\title{Merge Dimensional Reductions} +\usage{ +\method{merge}{DimReduc}(x = NULL, y = NULL, add.cell.ids = NULL, ...) +} +\arguments{ +\item{x}{A \code{\link{DimReduc}} object} + +\item{y}{One or more \code{\link{DimReduc}} objects} + +\item{add.cell.ids}{A character vector equal to the number of objects +provided to append to all cell names; if \code{TRUE}, uses \code{labels} as +\code{add.cell.ids}} + +\item{...}{Ignored} +} +\value{ +A new \code{DimReduc} object with data merged from \code{c(x, y)} +} +\description{ +Merge two or more \link[=DimReduc]{dimensional reduction} together +} +\seealso{ +Dimensional reduction object, validity, and interaction methods +\code{\link{CreateDimReducObject}()}, +\code{\link{DimReduc-class}}, +\code{\link{DimReduc-validity}}, +\code{\link{[.DimReduc}()}, +\code{\link{[[.DimReduc}()}, +\code{\link{dim.DimReduc}()}, +\code{\link{print.DimReduc}()}, +\code{\link{subset.DimReduc}()} +} +\concept{dimreduc} diff --git a/man/merge.Seurat.Rd b/man/merge.Seurat.Rd new file mode 100644 index 00000000..2ffa59a4 --- /dev/null +++ b/man/merge.Seurat.Rd @@ -0,0 +1,93 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{merge.Seurat} +\alias{merge.Seurat} +\alias{merge} +\alias{MergeSeurat} +\alias{AddSamples} +\title{Merge Seurat Objects} +\usage{ +\method{merge}{Seurat}( + x = NULL, + y = NULL, + add.cell.ids = NULL, + collapse = FALSE, + merge.data = TRUE, + merge.dr = FALSE, + project = getOption(x = "Seurat.object.project", default = "SeuratProject"), + ... +) +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} + +\item{y}{A single \code{Seurat} object or a list of \code{Seurat} objects} + +\item{add.cell.ids}{A character vector of \code{length(x = c(x, y))}; +appends the corresponding values to the start of each objects' cell names} + +\item{collapse}{If \code{TRUE}, merge layers of the same name together; if +\code{FALSE}, appends \code{labels} to the layer name} + +\item{merge.data}{Merge the data slots instead of just merging the counts +(which requires renormalization); this is recommended if the same +normalization approach was applied to all objects} + +\item{merge.dr}{Choose how to handle merging dimensional reductions: +\itemize{ + \item \dQuote{\code{TRUE}}: merge dimensional reductions with the same name + across objects; dimensional reductions with different names are added as-is + \item \dQuote{\code{NA}}: keep dimensional reductions from separate objects + separate; will append the project name for duplicate reduction names + \item \dQuote{\code{FALSE}}: do not add dimensional reductions +}} + +\item{project}{\link{Project} name for the \code{Seurat} object} + +\item{...}{Arguments passed to other methods} +} +\value{ +\code{merge}: Merged object +} +\description{ +Merge Seurat Objects +} +\section{Merge Details}{ + +When merging Seurat objects, the merge procedure will merge the Assay level +counts and potentially the data slots (depending on the merge.data parameter). +It will also merge the cell-level meta data that was stored with each object +and preserve the cell identities that were active in the objects pre-merge. +The merge will optionally merge reductions depending on the values passed to +\code{merge.dr} if they have the same name across objects. Here the +embeddings slots will be merged and if there are differing numbers of +dimensions across objects, only the first N shared dimensions will be merged. +The feature loadings slots will be filled by the values present in the first +object.The merge will not preserve graphs, logged commands, or feature-level +metadata that were present in the original objects. If add.cell.ids isn't +specified and any cell names are duplicated, cell names will be appended +with _X, where X is the numeric index of the object in c(x, y). +} + +\examples{ +# `merge' examples +# merge two objects +merge(pbmc_small, y = pbmc_small) +# to merge more than two objects, pass one to x and a list of objects to y +merge(pbmc_small, y = c(pbmc_small, pbmc_small)) + +} +\seealso{ +Seurat object, validity, and interaction methods +\code{\link{$.Seurat}()}, +\code{\link{Seurat-class}}, +\code{\link{Seurat-validity}}, +\code{\link{[[.Seurat}()}, +\code{\link{[[<-,Seurat,NULL}}, +\code{\link{[[<-,Seurat}}, +\code{\link{dim.Seurat}()}, +\code{\link{dimnames.Seurat}()}, +\code{\link{names.Seurat}()}, +\code{\link{subset.Seurat}()} +} +\concept{seurat} diff --git a/man/merge.StdAssay.Rd b/man/merge.StdAssay.Rd new file mode 100644 index 00000000..c5ebcd32 --- /dev/null +++ b/man/merge.StdAssay.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{merge.StdAssay} +\alias{merge.StdAssay} +\title{Merge Assays} +\usage{ +\method{merge}{StdAssay}(x, y, labels = NULL, add.cell.ids = NULL, collapse = FALSE, ...) +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{y}{One or more \code{\link{Assay5}} objects} + +\item{labels}{A character vector equal to the number of objects; defaults to +\code{as.character(seq_along(c(x, y)))}} + +\item{add.cell.ids}{A character vector equal to the number of objects +provided to append to all cell names; if \code{TRUE}, uses \code{labels} as +\code{add.cell.ids}} + +\item{collapse}{If \code{TRUE}, merge layers of the same name together; if +\code{FALSE}, appends \code{labels} to the layer name} + +\item{...}{Ignored} +} +\value{ +A new v5 assay with data merged from \code{c(x, y)} +} +\description{ +Merge one or more v5 assays together +} +\details{ +\strong{Note}: collapsing layers is currently not supported +} +\note{ +All assays must be of the same type; merging different v5 assays (eg. +\code{\link{Assay5}} and \code{\link{Assay5T}}) is currently unsupported +} +\keyword{internal} diff --git a/man/names.Seurat.Rd b/man/names.Seurat.Rd new file mode 100644 index 00000000..fd880162 --- /dev/null +++ b/man/names.Seurat.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{names.Seurat} +\alias{names.Seurat} +\title{Subobject Names} +\usage{ +\method{names}{Seurat}(x) +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} +} +\value{ +The names of all of the following subobjects within \code{x}: +\itemize{ + \item \link[=Assay]{v3} and \link[=Assay5]{v5} assays + \item \link[=DimReduc]{dimensional reductions} + \item \link[=SpatialImage]{images} and \link[=FOV]{FOVs} + \item \link[=Graph]{nearest-neighbor graphs} +} +} +\description{ +Get the names of subobjects within a \code{\link{Seurat}} object +} +\examples{ +names(pbmc_small) + +} +\seealso{ +Seurat object, validity, and interaction methods +\code{\link{$.Seurat}()}, +\code{\link{Seurat-class}}, +\code{\link{Seurat-validity}}, +\code{\link{[[.Seurat}()}, +\code{\link{[[<-,Seurat,NULL}}, +\code{\link{[[<-,Seurat}}, +\code{\link{dim.Seurat}()}, +\code{\link{dimnames.Seurat}()}, +\code{\link{merge.Seurat}()}, +\code{\link{subset.Seurat}()} +} +\concept{seurat} diff --git a/man/old-assign.Rd b/man/old-assign.Rd new file mode 100644 index 00000000..7e194282 --- /dev/null +++ b/man/old-assign.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{old-assign} +\alias{old-assign} +\alias{[[<-,Seurat,character,ANY,ANY-method} +\title{Original double-bracket assign} +\usage{ +\S4method{[[}{Seurat,character,ANY,ANY}(x, i, j, ...) <- value +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} + +\item{i}{The name to store a subobject or various cell-level meta data as} + +\item{value}{New subobject or cell-level meta data} +} +\value{ +\code{x} with \code{value} stored as \code{i} +} +\description{ +This function has been replaced with value-specific double-bracket +assign methods and should generally not be called +} +\seealso{ +See \link[=$.Seurat]{here} for adding metadata with \code{[[<-}, and +\link[=[[<-,Seurat,NULL]{here} for removing subobjects and cell-level meta +data with \code{[[<-} +} +\keyword{internal} diff --git a/man/oldseurat-class.Rd b/man/oldseurat-class.Rd index 64e71ccd..a3ca675f 100644 --- a/man/oldseurat-class.Rd +++ b/man/oldseurat-class.Rd @@ -3,12 +3,8 @@ \docType{class} \name{seurat-class} \alias{seurat-class} -\alias{seurat} -\alias{show,seurat-method} +\alias{oldseurat} \title{The Seurat Class} -\usage{ -\S4method{show}{seurat}(object) -} \description{ The Seurat object is the center of each single cell analysis. It stores all information associated with the dataset, including data, annotations, @@ -75,4 +71,5 @@ single cells} }} \concept{unsorted} +\concept{v2} \keyword{internal} diff --git a/man/print.DimReduc.Rd b/man/print.DimReduc.Rd new file mode 100644 index 00000000..ee9c47c6 --- /dev/null +++ b/man/print.DimReduc.Rd @@ -0,0 +1,47 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/dimreduc.R +\name{print.DimReduc} +\alias{print.DimReduc} +\alias{print} +\title{Print Top Feature Loadings} +\usage{ +\method{print}{DimReduc}(x, dims = 1:5, nfeatures = 20, projected = FALSE, ...) +} +\arguments{ +\item{x}{A \code{\link{DimReduc}} object} + +\item{dims}{Number of dimensions to display} + +\item{nfeatures}{Number of genes to display} + +\item{projected}{Use projected slot} + +\item{...}{Ignored} +} +\value{ +Displays set of features defining the components and +invisibly returns \code{x} +} +\description{ +Prints a set of features that most strongly define a set of components; +\strong{note}: requires feature loadings to be present in order to work +} +\examples{ +pca <- pbmc_small[["pca"]] +print(pca) + +} +\seealso{ +\code{\link[base]{cat}} + +Dimensional reduction object, validity, and interaction methods +\code{\link{CreateDimReducObject}()}, +\code{\link{DimReduc-class}}, +\code{\link{DimReduc-validity}}, +\code{\link{[.DimReduc}()}, +\code{\link{[[.DimReduc}()}, +\code{\link{dim.DimReduc}()}, +\code{\link{merge.DimReduc}()}, +\code{\link{subset.DimReduc}()} +} +\concept{dimreduc} diff --git a/man/reexports.Rd b/man/reexports.Rd index 7fda5a60..92b5a17e 100644 --- a/man/reexports.Rd +++ b/man/reexports.Rd @@ -4,10 +4,7 @@ \name{reexports} \alias{reexports} \alias{plan} -\alias{colMeans} -\alias{colSums} -\alias{rowMeans} -\alias{rowSums} +\alias{intersect} \alias{handlers} \alias{with_progress} \title{Objects exported from other packages} @@ -19,7 +16,7 @@ below to see their documentation. \describe{ \item{future}{\code{\link[future]{plan}}} - \item{Matrix}{\code{\link[Matrix:colSums]{colMeans}}, \code{\link[Matrix]{colSums}}, \code{\link[Matrix:colSums]{rowMeans}}, \code{\link[Matrix:colSums]{rowSums}}} + \item{generics}{\code{\link[generics:setops]{intersect}}} \item{progressr}{\code{\link[progressr]{handlers}}, \code{\link[progressr]{with_progress}}} }} diff --git a/man/roxygen/meta.R b/man/roxygen/meta.R index ef952e1f..a4449587 100644 --- a/man/roxygen/meta.R +++ b/man/roxygen/meta.R @@ -1,5 +1,19 @@ list( rd_family_title = list( - segmentation = "Segmentation layer classes:" + angles = "", + assay = "v3 Assay object, validity, and interaction methods:", + assay5 = "v5 Assay object, validity, and interaction methods:", + command = "Command log object and interaction methods", + dimnames = "", + dimreduc = "Dimensional reduction object, validity, and interaction methods", + fov = "", + key = "", + logmap = "Logical map objects, validity, and interaction methods:", + s4list = "", + segmentation = "Segmentation layer classes:", + seurat = "Seurat object, validity, and interaction methods", + sparse = "", + stdassay = "v5 Standard Assay object, validity, and interaction methods", + subobjects = "" ) ) diff --git a/man/roxygen/templates/desc-validity.R b/man/roxygen/templates/desc-validity.R new file mode 100644 index 00000000..ea8178dd --- /dev/null +++ b/man/roxygen/templates/desc-validity.R @@ -0,0 +1,2 @@ +#' @description Validation of \code{<%= cls %>} objects is handled by +#' \code{\link[methods]{validObject}} diff --git a/man/roxygen/templates/lifecycle-deprecated.R b/man/roxygen/templates/lifecycle-deprecated.R new file mode 100644 index 00000000..a42be9cb --- /dev/null +++ b/man/roxygen/templates/lifecycle-deprecated.R @@ -0,0 +1,5 @@ +#' @section Lifecycle: +#' +#' \Sexpr[stage=build,results=rd]{lifecycle::badge("deprecated")} +#' +#' \code{<%= fxn %>} <%= if (exists("ver")) paste("was deprecated in version", ver) else "has been deprecated" %><%= if (exists("repl")) paste0("; use \\code{\\link{", repl, "}} instead") %> diff --git a/man/roxygen/templates/lifecycle-experimental.R b/man/roxygen/templates/lifecycle-experimental.R new file mode 100644 index 00000000..debca6ed --- /dev/null +++ b/man/roxygen/templates/lifecycle-experimental.R @@ -0,0 +1,6 @@ +#' @section Lifecycle: +#' +#' \Sexpr[stage=build,results=rd]{lifecycle::badge("experimental")} +#' +#' \strong{Warning}: functionality described here is experimental and prone to +#' change without notice diff --git a/man/roxygen/templates/lifecycle-superseded.R b/man/roxygen/templates/lifecycle-superseded.R new file mode 100644 index 00000000..301fba69 --- /dev/null +++ b/man/roxygen/templates/lifecycle-superseded.R @@ -0,0 +1,3 @@ +#' @section Lifecycle: +#' +#' \Sexpr[stage=build,results=rd]{lifecycle::badge("superseded")} diff --git a/man/roxygen/templates/method-stdassay.R b/man/roxygen/templates/method-stdassay.R new file mode 100644 index 00000000..1c15010b --- /dev/null +++ b/man/roxygen/templates/method-stdassay.R @@ -0,0 +1,8 @@ +#' @inherit <%= fxn %> params return title description details sections +#' references author source +#' @name <%= fxn %>-StdAssay +#' @rdname <%= fxn %>-StdAssay +#' +#' @keywords internal +#' +#' @export diff --git a/man/roxygen/templates/name-oldv.R b/man/roxygen/templates/name-oldv.R new file mode 100644 index 00000000..4aafd439 --- /dev/null +++ b/man/roxygen/templates/name-oldv.R @@ -0,0 +1,8 @@ +#' @name <%= fname %>-v<%= as.integer(version) %> +#' @rdname <%= fname %>-v<%= as.integer(version) %> +#' +#' @note This is the documentation for \code{<%= fname %>} for +#' v<%= as.integer(version) %>; for the latest documentation, please see +#' \code{\link{<%= fname %>}} +#' +#' @seealso \code{\link{<%= fname %>}} diff --git a/man/roxygen/templates/param-dots-ignored.R b/man/roxygen/templates/param-dots-ignored.R new file mode 100644 index 00000000..4fd71ced --- /dev/null +++ b/man/roxygen/templates/param-dots-ignored.R @@ -0,0 +1 @@ +#' @param ... Ignored diff --git a/man/roxygen/templates/param-dots-method.R b/man/roxygen/templates/param-dots-method.R new file mode 100644 index 00000000..5924fb83 --- /dev/null +++ b/man/roxygen/templates/param-dots-method.R @@ -0,0 +1 @@ +#' @param ... Arguments passed to other methods diff --git a/man/roxygen/templates/param-verbose.R b/man/roxygen/templates/param-verbose.R new file mode 100644 index 00000000..bba4e40b --- /dev/null +++ b/man/roxygen/templates/param-verbose.R @@ -0,0 +1 @@ +#' @param verbose Show progress updates diff --git a/man/roxygen/templates/return-null.R b/man/roxygen/templates/return-null.R new file mode 100644 index 00000000..6ea1671a --- /dev/null +++ b/man/roxygen/templates/return-null.R @@ -0,0 +1 @@ +#' @return Invisibly returns \code{NULL} diff --git a/man/roxygen/templates/return-show.R b/man/roxygen/templates/return-show.R new file mode 100644 index 00000000..2280b5ad --- /dev/null +++ b/man/roxygen/templates/return-show.R @@ -0,0 +1,2 @@ +#' @return Prints summary to \code{\link[base]{stdout}} and invisibly +#' returns \code{NULL} diff --git a/man/roxygen/templates/slot-key.R b/man/roxygen/templates/slot-key.R new file mode 100644 index 00000000..e2628a4f --- /dev/null +++ b/man/roxygen/templates/slot-key.R @@ -0,0 +1,4 @@ +#' @slot key A one-length character vector with the object's key; keys must +#' be one or more alphanumeric characters followed by an underscore +#' \dQuote{\code{_}} (regex pattern +#' \dQuote{\code{\Sexpr[stage=build]{SeuratObject:::.KeyPattern()}}}) diff --git a/man/roxygen/templates/slot-misc.R b/man/roxygen/templates/slot-misc.R new file mode 100644 index 00000000..44fe0cc3 --- /dev/null +++ b/man/roxygen/templates/slot-misc.R @@ -0,0 +1 @@ +#' @slot misc A named list of unstructured miscellaneous data diff --git a/man/roxygen/templates/slot-stdassay.R b/man/roxygen/templates/slot-stdassay.R new file mode 100644 index 00000000..8784532d --- /dev/null +++ b/man/roxygen/templates/slot-stdassay.R @@ -0,0 +1,20 @@ +#' @slot layers A named list containing expression matrices; each matrix should +#' be a two-dimensional object containing some subset of cells and features +#' defined in the \code{cells} and \code{features} slots. Cell and feature +#' membership is recorded in the \code{cells} and \code{features} slots, +#' respectively +#' @slot cells A \link[=LogMap]{logical mapping} of cell names +#' and layer membership; this map contains all the possible cells that this +#' assay can contain. New layers must have some subset of cells present +#' in this map +#' @slot features A \link[=LogMap]{logical mapping} of feature +#' names and layer membership; this map contains all the possible features that +#' this assay can contain. New layers must have some subset of features +#' present in this map +#' @slot default A one-length integer with the end index of the +#' \link[=DefaultLayer]{default layer}; the default layer be +#' all layers up to and including the layer at index \code{default} +#' @slot assay.orig Original assay that this assay is based off of; +#' used to track assay provenance +#' @slot meta.data A \link[base:data.frame]{data frame} with feature-level +#' meta data; should have the same number of rows as \code{features} diff --git a/man/s4list.Rd b/man/s4list.Rd index eb339b95..d1a4e001 100644 --- a/man/s4list.Rd +++ b/man/s4list.Rd @@ -22,7 +22,8 @@ ListToS4(x) \arguments{ \item{object}{An S4 object} -\item{x}{A list with an S4 class definition attribute} +\item{x}{A list with an S4 class definition +(\dQuote{\code{classDef}}) attribute} } \value{ \code{S4ToList}: A list with an S4 class definition attribute @@ -37,16 +38,40 @@ attribute Convert S4 objects to lists and vice versa. Useful for declassing an S4 object while keeping track of it's class using attributes (see section \strong{S4 Class Definition Attributes} below for more details). Both -\code{ListToS4} and \code{S4ToList} are recursive functions, affecting all -lists/S4 objects contained as sub-lists/sub-objects. +\code{ListToS4} and \code{S4ToList} are recursive functions, affecting +all lists/S4 objects contained as sub-lists/sub-objects } \section{S4 Class Definition Attributes}{ S4 classes are scoped to the package and class name. In order to properly track which class a list is generated from in order to build a new one, these function use an \code{\link[base:attr]{attribute}} to denote the -class name and package of origin. This attribute is stored as -\dQuote{classDef} and takes the form of \dQuote{\code{package:class}}. +class name and package of origin. This attribute is stored as 1-length +character vector named \dQuote{\code{classDef}} and takes the form +of \dQuote{\code{package:class}} } +\examples{ +# Turn an S4 object into a list +pbmc.list <- S4ToList(pbmc_small) +class(pbmc.list) +attributes(pbmc.list) + +str(pbmc.list$reductions) + +IsS4List(pbmc.list) + +pbmc2 <- ListToS4(pbmc.list) +pbmc2 +class(pbmc2) +Reductions(pbmc2) +validObject(pbmc2) + +} +\seealso{ + +\code{\link{ClassKey}()} +} +\concept{s4list} \concept{utils} +\keyword{internal} diff --git a/man/set-if-na.Rd b/man/set-if-na.Rd index ead4424b..f95fa7d8 100644 --- a/man/set-if-na.Rd +++ b/man/set-if-na.Rd @@ -6,7 +6,7 @@ \alias{\%na\%} \alias{\%!NA\%} \alias{\%!na\%} -\title{Set if \code{NA}} +\title{Set If or If Not \code{NA}} \usage{ x \%NA\% y @@ -32,11 +32,14 @@ For \code{\%!NA\%}: \code{y} if \code{x} is \strong{not} Set a default value depending on if an object is \code{\link[base]{NA}} } \examples{ +# Set if NA 1 \%NA\% 2 NA \%NA\% 2 +# Set if *not* NA 1 \%!NA\% 2 NA \%!NA\% 2 } \concept{utils} +\keyword{internal} diff --git a/man/set-if-null.Rd b/man/set-if-null.Rd index 1a5193b1..d59ba343 100644 --- a/man/set-if-null.Rd +++ b/man/set-if-null.Rd @@ -1,12 +1,13 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R -\docType{import} \name{set-if-null} \alias{set-if-null} \alias{\%||\%} \alias{\%iff\%} -\title{Set a default value depending on if an object is \code{NULL}} +\title{Set If or If Not \code{NULL}} \usage{ +x \%||\% y + x \%iff\% y } \arguments{ @@ -15,27 +16,29 @@ x \%iff\% y \item{y}{A default value} } \value{ -For \code{\%||\%}: \code{y} if \code{x} is \code{NULL} otherwise -\code{x} +For \code{\%||\%}: \code{y} if \code{x} is \code{NULL}; +otherwise \code{x} For \code{\%iff\%}: \code{y} if \code{x} is \strong{not} \code{NULL}; otherwise \code{x} } +\description{ +Set a default value depending on if an object is \code{NULL} +} \examples{ +# Set if NULL 1 \%||\% 2 NULL \%||\% 2 +# Set if *not* NULL 1 \%iff\% 2 NULL \%iff\% 2 +} +\seealso{ +\code{\link[rlang:op-null-default]{rlang::\%||\%}} +} +\author{ +For \code{\%||\%}: \pkg{rlang} developers } \concept{utils} -\keyword{internal} -\description{ -These objects are imported from other packages. Follow the links -below to see their documentation. - -\describe{ - \item{rlang}{\code{\link[rlang:op-null-default]{\%||\%}}} -}} - diff --git a/man/show-Assay-method.Rd b/man/show-Assay-method.Rd new file mode 100644 index 00000000..239e9402 --- /dev/null +++ b/man/show-Assay-method.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay.R +\name{show,Assay-method} +\alias{show,Assay-method} +\title{V3 Assay Overview} +\usage{ +\S4method{show}{Assay}(object) +} +\value{ +Prints summary to \code{\link[base]{stdout}} and invisibly +returns \code{NULL} +} +\description{ +Overview of an \code{\link{Assay}} object +} +\examples{ +rna <- pbmc_small[["RNA"]] +rna + +} +\seealso{ +\code{\link{Assay}} +} +\concept{assay} +\keyword{internal} diff --git a/man/show-DimReduc-method.Rd b/man/show-DimReduc-method.Rd new file mode 100644 index 00000000..b8ea77a3 --- /dev/null +++ b/man/show-DimReduc-method.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/dimreduc.R +\name{show,DimReduc-method} +\alias{show,DimReduc-method} +\title{Dimensional Reduction Overview} +\usage{ +\S4method{show}{DimReduc}(object) +} +\arguments{ +\item{object}{A dimensional reduction} +} +\value{ +Prints summary to \code{\link[base]{stdout}} and invisibly +returns \code{NULL} +} +\description{ +Overview of a \code{\link{DimReduc}} object +} +\examples{ +pca <- pbmc_small[["pca"]] +pca + +} +\seealso{ +\code{\link{DimReduc}} +} +\keyword{internal} diff --git a/man/show-Graph-method.Rd b/man/show-Graph-method.Rd new file mode 100644 index 00000000..1c45f843 --- /dev/null +++ b/man/show-Graph-method.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/graph.R +\name{show,Graph-method} +\alias{show,Graph-method} +\title{Graph Object Overview} +\usage{ +\S4method{show}{Graph}(object) +} +\value{ +Prints summary to \code{\link[base]{stdout}} and invisibly +returns \code{NULL} +} +\description{ +Overview of a \code{\link{Graph}} Object +} +\examples{ +pbmc_small[["RNA_snn"]] + +} +\concept{graph} +\keyword{internal} diff --git a/man/show-LogMap-method.Rd b/man/show-LogMap-method.Rd new file mode 100644 index 00000000..f6a38f1a --- /dev/null +++ b/man/show-LogMap-method.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/logmap.R +\name{show,LogMap-method} +\alias{show,LogMap-method} +\title{\code{\link{LogMap}} Object Overview} +\usage{ +\S4method{show}{LogMap}(object) +} +\arguments{ +\item{object}{A \code{\link{LogMap}} object} +} +\value{ +Prints summary to \code{\link[base]{stdout}} and invisibly +returns \code{NULL} +} +\description{ +Overview of a \code{\link{LogMap}} object +} +\concept{logmap} diff --git a/man/show-Seurat-method.Rd b/man/show-Seurat-method.Rd new file mode 100644 index 00000000..c56ec5d2 --- /dev/null +++ b/man/show-Seurat-method.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{show,Seurat-method} +\alias{show,Seurat-method} +\title{Seurat Object Overview} +\usage{ +\S4method{show}{Seurat}(object) +} +\value{ +Prints summary to \code{\link[base]{stdout}} and invisibly +returns \code{NULL} +} +\description{ +Overview of a \code{\link{Seurat}} object +} +\examples{ +pbmc_small + +} +\concept{seurat} +\keyword{internal} diff --git a/man/show-SeuratCommand-method.Rd b/man/show-SeuratCommand-method.Rd new file mode 100644 index 00000000..e0826f2d --- /dev/null +++ b/man/show-SeuratCommand-method.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/command.R +\name{show,SeuratCommand-method} +\alias{show,SeuratCommand-method} +\title{Command Log Overview} +\usage{ +\S4method{show}{SeuratCommand}(object) +} +\value{ +Prints summary to \code{\link[base]{stdout}} and invisibly +returns \code{NULL} +} +\description{ +Overview of a \code{\link{SeuratCommand}} object +} +\examples{ +cmd <- pbmc_small[["NormalizeData.RNA"]] +cmd + +} +\concept{command} +\keyword{internal} diff --git a/man/show-StdAssay-method.Rd b/man/show-StdAssay-method.Rd new file mode 100644 index 00000000..12e5f57e --- /dev/null +++ b/man/show-StdAssay-method.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{show,StdAssay-method} +\alias{show,StdAssay-method} +\title{V5 Assay Overview} +\usage{ +\S4method{show}{StdAssay}(object) +} +\arguments{ +\item{object}{A v5 Assay} +} +\value{ +Prints summary to \code{\link[base]{stdout}} and invisibly +returns \code{NULL} +} +\description{ +Overview of a \code{\link{StdAssay}} object +} +\seealso{ +v5 Standard Assay object, validity, and interaction methods +\code{\link{$.StdAssay}()}, +\code{\link{.DollarNames.StdAssay}()}, +\code{\link{StdAssay-class}}, +\code{\link{StdAssay-validity}}, +\code{\link{[.StdAssay}()}, +\code{\link{[[.StdAssay}()}, +\code{\link{dim.StdAssay}()}, +\code{\link{dimnames.StdAssay}()}, +\code{\link{split.StdAssay}()}, +\code{\link{subset.StdAssay}()} +} +\concept{stdassay} +\keyword{internal} diff --git a/man/show-oldseurat-method.Rd b/man/show-oldseurat-method.Rd new file mode 100644 index 00000000..70cf554d --- /dev/null +++ b/man/show-oldseurat-method.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{show,seurat-method} +\alias{show,seurat-method} +\title{Old Seurat Object Overview} +\usage{ +\S4method{show}{seurat}(object) +} +\arguments{ +\item{object}{An old seurat object} +} +\value{ +Prints summary to \code{\link[base]{stdout}} and invisibly +returns \code{NULL} +} +\description{ +Overview of a \code{\link[=oldseurat]{seurat}} object overview +} +\concept{oldseurat} +\keyword{internal} diff --git a/man/split.Assay.Rd b/man/split.Assay.Rd new file mode 100644 index 00000000..14c2f92a --- /dev/null +++ b/man/split.Assay.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay.R +\name{split.Assay} +\alias{split.Assay} +\title{Split an Assay} +\usage{ +\method{split}{Assay}(x, f, drop = FALSE, layers = NA, ...) +} +\arguments{ +\item{x}{An \code{\link{Assay}} object} + +\item{f}{a \sQuote{factor} in the sense that \code{\link[base]{as.factor}(f)} + defines the grouping, or a list of such factors in which case their + interaction is used for the grouping. If \code{x} is a data frame, + \code{f} can also be a formula of the form \code{ ~ g} to split by + the variable \code{g}, or more generally of the form \code{ ~ g1 + + \dots + gk} to split by the interaction of the variables + \code{g1}, \dots, \code{gk}, where these variables are evaluated in + the data frame \code{x} using the usual non-standard evaluation + rules.} + +\item{drop}{logical indicating if levels that do not occur should be dropped + (if \code{f} is a \code{factor} or a list).} + +\item{layers}{Names of layers to include in the split; pass \code{NA} for +all layers; pass \code{NULL} for the \link[=DefaultLayer]{default layer}} + +\item{...}{Ignored} +} +\value{ +Returns a v5 assay with splitted layers +} +\description{ +Split an Assay +} +\seealso{ +v3 Assay object, validity, and interaction methods: +\code{\link{$.Assay}()}, +\code{\link{Assay-class}}, +\code{\link{Assay-validity}}, +\code{\link{CreateAssayObject}()}, +\code{\link{[.Assay}()}, +\code{\link{[[.Assay}()}, +\code{\link{dim.Assay}()}, +\code{\link{dimnames.Assay}()}, +\code{\link{merge.Assay}()}, +\code{\link{subset.Assay}()} +} +\concept{assay} diff --git a/man/split.Assay5.Rd b/man/split.Assay5.Rd new file mode 100644 index 00000000..fff655b4 --- /dev/null +++ b/man/split.Assay5.Rd @@ -0,0 +1,83 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{split.Assay5} +\alias{split.Assay5} +\title{Split an Assay} +\usage{ +\method{split}{Assay5}( + x, + f, + drop = FALSE, + layers = c("counts", "data"), + ret = c("assay", "multiassays", "layers"), + ... +) +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{f}{a \sQuote{factor} in the sense that \code{\link[base]{as.factor}(f)} + defines the grouping, or a list of such factors in which case their + interaction is used for the grouping. If \code{x} is a data frame, + \code{f} can also be a formula of the form \code{ ~ g} to split by + the variable \code{g}, or more generally of the form \code{ ~ g1 + + \dots + gk} to split by the interaction of the variables + \code{g1}, \dots, \code{gk}, where these variables are evaluated in + the data frame \code{x} using the usual non-standard evaluation + rules.} + +\item{drop}{logical indicating if levels that do not occur should be dropped + (if \code{f} is a \code{factor} or a list).} + +\item{layers}{Names of layers to include in the split; pass \code{NA} for +all layers; pass \code{NULL} for the \link[=DefaultLayer]{default layer}} + +\item{ret}{Type of return value; choose from: +\itemize{ + \item \dQuote{\code{assay}}: a single \code{\link{Assay5}} object + \item \dQuote{\code{multiassay}}: a list of \code{\link{Assay5}} objects + \item \dQuote{\code{layers}}: a list of layer matrices +}} + +\item{...}{Ignored} +} +\value{ +Depends on the value of \code{ret}: +\itemize{ + \item \dQuote{\code{assay}}: \code{x} with the layers requested in + \code{layers} split based on \code{f}; all other layers are left as-is + \item \dQuote{\code{multiassay}}: a list of \code{\link{Assay5}} objects; + the list contains one value per split and each assay contains only the + layers requested in \code{layers} with the \link[=Key]{key} set to the split + \item \dQuote{\code{layers}}: a list of matrices of length + \code{length(assays) * length(unique(f))}; the list is named as + \dQuote{\code{layer.split}} +} +} +\description{ +Split an Assay +} +\section{Progress Updates with \pkg{progressr}}{ + +This function uses +\href{https://cran.r-project.org/package=progressr}{\pkg{progressr}} to +render status updates and progress bars. To enable progress updates, wrap +the function call in \code{\link[progressr]{with_progress}} or run +\code{\link[progressr:handlers]{handlers(global = TRUE)}} before running +this function. For more details about \pkg{progressr}, please read +\href{https://progressr.futureverse.org/articles/progressr-intro.html}{\code{vignette("progressr-intro")}} +} + +\seealso{ +v5 Assay object, validity, and interaction methods: +\code{\link{$.Assay5}()}, +\code{\link{Assay5-class}}, +\code{\link{Assay5-validity}}, +\code{\link{[.Assay5}()}, +\code{\link{[[.Assay5}()}, +\code{\link{dim.Assay5}()}, +\code{\link{dimnames.Assay5}()}, +\code{\link{merge.Assay5}()}, +\code{\link{subset.Assay5}()} +} +\concept{assay5} diff --git a/man/split.Seurat.Rd b/man/split.Seurat.Rd new file mode 100644 index 00000000..9926f4df --- /dev/null +++ b/man/split.Seurat.Rd @@ -0,0 +1,58 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{split.Seurat} +\alias{split.Seurat} +\title{Split an Assay} +\usage{ +\method{split}{Seurat}(x, f, drop = FALSE, assay = NULL, layers = NA, ...) +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{f}{a \sQuote{factor} in the sense that \code{\link[base]{as.factor}(f)} + defines the grouping, or a list of such factors in which case their + interaction is used for the grouping. If \code{x} is a data frame, + \code{f} can also be a formula of the form \code{ ~ g} to split by + the variable \code{g}, or more generally of the form \code{ ~ g1 + + \dots + gk} to split by the interaction of the variables + \code{g1}, \dots, \code{gk}, where these variables are evaluated in + the data frame \code{x} using the usual non-standard evaluation + rules.} + +\item{drop}{logical indicating if levels that do not occur should be dropped + (if \code{f} is a \code{factor} or a list).} + +\item{layers}{Names of layers to include in the split; pass \code{NA} for +all layers; pass \code{NULL} for the \link[=DefaultLayer]{default layer}} + +\item{...}{Ignored} +} +\value{ +Depends on the value of \code{ret}: +\itemize{ + \item \dQuote{\code{assay}}: \code{x} with the layers requested in + \code{layers} split based on \code{f}; all other layers are left as-is + \item \dQuote{\code{multiassay}}: a list of \code{\link{Assay5}} objects; + the list contains one value per split and each assay contains only the + layers requested in \code{layers} with the \link[=Key]{key} set to the split + \item \dQuote{\code{layers}}: a list of matrices of length + \code{length(assays) * length(unique(f))}; the list is named as + \dQuote{\code{layer.split}} +} +} +\description{ +Split an Assay +} +\section{Progress Updates with \pkg{progressr}}{ + +This function uses +\href{https://cran.r-project.org/package=progressr}{\pkg{progressr}} to +render status updates and progress bars. To enable progress updates, wrap +the function call in \code{\link[progressr]{with_progress}} or run +\code{\link[progressr:handlers]{handlers(global = TRUE)}} before running +this function. For more details about \pkg{progressr}, please read +\href{https://progressr.futureverse.org/articles/progressr-intro.html}{\code{vignette("progressr-intro")}} +} + +\concept{Seurat} +\keyword{internal} diff --git a/man/split.StdAssay.Rd b/man/split.StdAssay.Rd new file mode 100644 index 00000000..7ae1527d --- /dev/null +++ b/man/split.StdAssay.Rd @@ -0,0 +1,95 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{split.StdAssay} +\alias{split.StdAssay} +\alias{split,StdAssay-method} +\title{Split an Assay} +\usage{ +\method{split}{StdAssay}( + x, + f, + drop = FALSE, + layers = c("counts", "data"), + ret = c("assay", "multiassays", "layers"), + ... +) + +\S4method{split}{StdAssay}( + x, + f, + drop = FALSE, + layers = c("counts", "data"), + ret = c("assay", "multiassays", "layers"), + ... +) +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{f}{a \sQuote{factor} in the sense that \code{\link[base]{as.factor}(f)} + defines the grouping, or a list of such factors in which case their + interaction is used for the grouping. If \code{x} is a data frame, + \code{f} can also be a formula of the form \code{ ~ g} to split by + the variable \code{g}, or more generally of the form \code{ ~ g1 + + \dots + gk} to split by the interaction of the variables + \code{g1}, \dots, \code{gk}, where these variables are evaluated in + the data frame \code{x} using the usual non-standard evaluation + rules.} + +\item{drop}{logical indicating if levels that do not occur should be dropped + (if \code{f} is a \code{factor} or a list).} + +\item{layers}{Names of layers to include in the split; pass \code{NA} for +all layers; pass \code{NULL} for the \link[=DefaultLayer]{default layer}} + +\item{ret}{Type of return value; choose from: +\itemize{ + \item \dQuote{\code{assay}}: a single \code{\link{Assay5}} object + \item \dQuote{\code{multiassay}}: a list of \code{\link{Assay5}} objects + \item \dQuote{\code{layers}}: a list of layer matrices +}} + +\item{...}{Ignored} +} +\value{ +Depends on the value of \code{ret}: +\itemize{ + \item \dQuote{\code{assay}}: \code{x} with the layers requested in + \code{layers} split based on \code{f}; all other layers are left as-is + \item \dQuote{\code{multiassay}}: a list of \code{\link{Assay5}} objects; + the list contains one value per split and each assay contains only the + layers requested in \code{layers} with the \link[=Key]{key} set to the split + \item \dQuote{\code{layers}}: a list of matrices of length + \code{length(assays) * length(unique(f))}; the list is named as + \dQuote{\code{layer.split}} +} +} +\description{ +Split an Assay +} +\section{Progress Updates with \pkg{progressr}}{ + +This function uses +\href{https://cran.r-project.org/package=progressr}{\pkg{progressr}} to +render status updates and progress bars. To enable progress updates, wrap +the function call in \code{\link[progressr]{with_progress}} or run +\code{\link[progressr:handlers]{handlers(global = TRUE)}} before running +this function. For more details about \pkg{progressr}, please read +\href{https://progressr.futureverse.org/articles/progressr-intro.html}{\code{vignette("progressr-intro")}} +} + +\seealso{ +v5 Standard Assay object, validity, and interaction methods +\code{\link{$.StdAssay}()}, +\code{\link{.DollarNames.StdAssay}()}, +\code{\link{StdAssay-class}}, +\code{\link{StdAssay-validity}}, +\code{\link{[.StdAssay}()}, +\code{\link{[[.StdAssay}()}, +\code{\link{dim.StdAssay}()}, +\code{\link{dimnames.StdAssay}()}, +\code{\link{show,StdAssay-method}}, +\code{\link{subset.StdAssay}()} +} +\concept{stdassay} +\keyword{internal} diff --git a/man/sub-.Assay.Rd b/man/sub-.Assay.Rd new file mode 100644 index 00000000..c06fdb1c --- /dev/null +++ b/man/sub-.Assay.Rd @@ -0,0 +1,60 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay.R +\name{[.Assay} +\alias{[.Assay} +\alias{[<-,Assay,character,ANY,ANY-method} +\title{Layer Data} +\usage{ +\method{[}{Assay}(x, i = missing_arg(), j = missing_arg(), ...) + +\S4method{[}{Assay,character,ANY,ANY}(x, i, j, ...) <- value +} +\arguments{ +\item{x}{An \code{\link{Assay}} object} + +\item{i}{Name of layer data to get or set} + +\item{j}{Ignored} + +\item{...}{Arguments passed to \code{\link{LayerData}}} + +\item{value}{A matrix-like object to add as a new layer} +} +\value{ +\code{[}: The layer data for layer \code{i} + +\code{[<-}: \code{x} with layer data \code{value} saved as \code{i} +} +\description{ +Get and set layer data +} +\examples{ +rna <- pbmc_small[["RNA"]] + +# Get a vector of layer names in this assay +rna[] + +# Fetch layer data +rna["data"][1:10, 1:4] + +# Set layer data +rna["data"] <- rna["counts"] +rna["data"][1:10, 1:4] + +} +\seealso{ +\code{\link{LayerData}} + +v3 Assay object, validity, and interaction methods: +\code{\link{$.Assay}()}, +\code{\link{Assay-class}}, +\code{\link{Assay-validity}}, +\code{\link{CreateAssayObject}()}, +\code{\link{[[.Assay}()}, +\code{\link{dim.Assay}()}, +\code{\link{dimnames.Assay}()}, +\code{\link{merge.Assay}()}, +\code{\link{split.Assay}()}, +\code{\link{subset.Assay}()} +} +\concept{assay} diff --git a/man/sub-.Assay5.Rd b/man/sub-.Assay5.Rd new file mode 100644 index 00000000..bbb23932 --- /dev/null +++ b/man/sub-.Assay5.Rd @@ -0,0 +1,45 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{[.Assay5} +\alias{[.Assay5} +\alias{[<-,Assay5,character,ANY,ANY-method} +\title{Layer Data} +\usage{ +\method{[}{Assay5}(x, i = missing_arg(), j = missing_arg(), ...) + +\S4method{[}{Assay5,character,ANY,ANY}(x, i, j, ...) <- value +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{i}{Name of layer data to get or set} + +\item{j}{Ignored} + +\item{...}{Arguments passed to \code{\link{LayerData}}} + +\item{value}{A matrix-like object to add as a new layer} +} +\value{ +\code{[}: The layer data for layer \code{i} + +\code{[<-}: \code{x} with layer data \code{value} saved as \code{i} +} +\description{ +Get and set layer data +} +\seealso{ +\code{\link{LayerData}} + +v5 Assay object, validity, and interaction methods: +\code{\link{$.Assay5}()}, +\code{\link{Assay5-class}}, +\code{\link{Assay5-validity}}, +\code{\link{[[.Assay5}()}, +\code{\link{dim.Assay5}()}, +\code{\link{dimnames.Assay5}()}, +\code{\link{merge.Assay5}()}, +\code{\link{split.Assay5}()}, +\code{\link{subset.Assay5}()} +} +\concept{assay5} diff --git a/man/sub-.DimReduc.Rd b/man/sub-.DimReduc.Rd new file mode 100644 index 00000000..83f02fef --- /dev/null +++ b/man/sub-.DimReduc.Rd @@ -0,0 +1,50 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/dimreduc.R +\name{[.DimReduc} +\alias{[.DimReduc} +\title{Get Feature Loadings} +\usage{ +\method{[}{DimReduc}(x, i, j, drop = FALSE, ...) +} +\arguments{ +\item{x}{A \code{\link{DimReduc}} object} + +\item{i}{Feature identifiers or indices} + +\item{j}{Dimension identifiers or indices} + +\item{drop}{Coerce the result to the lowest possible dimension; see +\code{\link{drop}} for further details} + +\item{...}{Arguments passed to other methods} +} +\value{ +Feature loadings for features \code{i} and dimensions \code{j} +} +\description{ +Pull feature loadings from a \link[=DimReduc]{dimensional reduction} +} +\details{ +\code{[} does not distinguish between projected and unprojected feature +loadings; to select whether projected or unprojected loadings should be +pulled, please use \code{\link{Loadings}} +} +\examples{ +pca <- pbmc_small[["pca"]] +pca[1:10, 1:5] + +} +\seealso{ +\code{\link{Loadings}} + +Dimensional reduction object, validity, and interaction methods +\code{\link{CreateDimReducObject}()}, +\code{\link{DimReduc-class}}, +\code{\link{DimReduc-validity}}, +\code{\link{[[.DimReduc}()}, +\code{\link{dim.DimReduc}()}, +\code{\link{merge.DimReduc}()}, +\code{\link{print.DimReduc}()}, +\code{\link{subset.DimReduc}()} +} +\concept{dimreduc} diff --git a/man/sub-.SeuratCommand.Rd b/man/sub-.SeuratCommand.Rd new file mode 100644 index 00000000..3812e3d5 --- /dev/null +++ b/man/sub-.SeuratCommand.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/command.R +\name{[.SeuratCommand} +\alias{[.SeuratCommand} +\title{Command Log Data Access} +\usage{ +\method{[}{SeuratCommand}(x, i, ...) +} +\arguments{ +\item{x}{A \code{\link{SeuratCommand}} object} + +\item{i}{The name of a command log slot} + +\item{...}{Ignored} +} +\value{ +\code{[}: Slot \code{i} from \code{x} +} +\description{ +Access data from a \code{SeuratCommand} object +} +\examples{ +cmd <- pbmc_small[["NormalizeData.RNA"]] +cmd["call.string"] + +} +\seealso{ +Command log object and interaction methods +\code{\link{$.SeuratCommand}()}, +\code{\link{.DollarNames.SeuratCommand}()}, +\code{\link{LogSeuratCommand}()}, +\code{\link{SeuratCommand-class}}, +\code{\link{as.list.SeuratCommand}()} +} +\concept{command} diff --git a/man/sub-.StdAssay.Rd b/man/sub-.StdAssay.Rd new file mode 100644 index 00000000..786eb1c9 --- /dev/null +++ b/man/sub-.StdAssay.Rd @@ -0,0 +1,45 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{[.StdAssay} +\alias{[.StdAssay} +\alias{[<-,StdAssay,character,ANY,ANY-method} +\title{Layer Data} +\usage{ +\method{[}{StdAssay}(x, i = missing_arg(), j = missing_arg(), ...) + +\S4method{[}{StdAssay,character,ANY,ANY}(x, i, j, ...) <- value +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{i}{Name of layer data to get or set} + +\item{j}{Ignored} + +\item{...}{Arguments passed to \code{\link{LayerData}}} + +\item{value}{A matrix-like object to add as a new layer} +} +\value{ +\code{[}: The layer data for layer \code{i} + +\code{[<-}: \code{x} with layer data \code{value} saved as \code{i} +} +\description{ +Get and set layer data +} +\seealso{ +v5 Standard Assay object, validity, and interaction methods +\code{\link{$.StdAssay}()}, +\code{\link{.DollarNames.StdAssay}()}, +\code{\link{StdAssay-class}}, +\code{\link{StdAssay-validity}}, +\code{\link{[[.StdAssay}()}, +\code{\link{dim.StdAssay}()}, +\code{\link{dimnames.StdAssay}()}, +\code{\link{show,StdAssay-method}}, +\code{\link{split.StdAssay}()}, +\code{\link{subset.StdAssay}()} +} +\concept{stdassay} +\keyword{internal} diff --git a/man/sub-LogMap-method.Rd b/man/sub-LogMap-method.Rd new file mode 100644 index 00000000..8159e1a8 --- /dev/null +++ b/man/sub-LogMap-method.Rd @@ -0,0 +1,66 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/logmap.R +\name{[,LogMap} +\alias{[,LogMap} +\alias{[,LogMap,missing,missing,ANY-method} +\alias{[,LogMap,character,character,ANY-method} +\alias{[,LogMap,character,missing,ANY-method} +\alias{[,LogMap,missing,character,ANY-method} +\alias{[,LogMap,numeric,missing,ANY-method} +\alias{[,LogMap,missing,numeric,ANY-method} +\alias{[,LogMap,numeric,numeric,ANY-method} +\title{Matrix-like Subsetting for \link[=LogMap]{Logical Maps}} +\usage{ +\S4method{[}{LogMap,missing,missing,ANY}(x, i, j, ..., drop = FALSE) + +\S4method{[}{LogMap,character,character,ANY}(x, i, j, ..., drop = FALSE) + +\S4method{[}{LogMap,character,missing,ANY}(x, i, j, ..., drop = FALSE) + +\S4method{[}{LogMap,missing,character,ANY}(x, i, j, ..., drop = FALSE) + +\S4method{[}{LogMap,numeric,missing,ANY}(x, i, j, ..., drop = FALSE) + +\S4method{[}{LogMap,missing,numeric,ANY}(x, i, j, ..., drop = FALSE) + +\S4method{[}{LogMap,numeric,numeric,ANY}(x, i, j, ..., drop = FALSE) +} +\arguments{ +\item{x}{A \code{LogMap} object} + +\item{i, j}{Vectors of values (\code{i}) and observations (\code{j}) to pull +from \code{x}} + +\item{...}{Arguments passed to other methods} + +\item{drop}{For matrices and arrays. If \code{TRUE} the result is + coerced to the lowest possible dimension (see the examples). This + only works for extracting elements, not for the replacement. See + \code{\link[base]{drop}} for further details. + } +} +\description{ +Matrix-like Subsetting for \link[=LogMap]{Logical Maps} +} +\note{ +\code{i} is not reordable; passing a different order for \code{i} +will return a subset with rows in the same order as \code{x} +} +\examples{ +map <- LogMap(letters[1:10]) +map[['obs']] <- c(1, 3, 7) +map[['entry']] <- c(2, 7, 10) + +map[] +map[1:5, 2L] +map[c("b", "c", "f"), "obs"] + +# Pass `drop = TRUE` to cast to `matrix` +map[1:3, , drop = TRUE] + +# Note that `i` is non-reordable +rownames(map)[1:3] +map[c("b", "c", "a"), , drop = TRUE] + +} +\keyword{internal} diff --git a/man/sub-sub-.Assay.Rd b/man/sub-sub-.Assay.Rd new file mode 100644 index 00000000..c6bee5fa --- /dev/null +++ b/man/sub-sub-.Assay.Rd @@ -0,0 +1,78 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay.R +\name{[[.Assay} +\alias{[[.Assay} +\alias{[[<-,Assay,ANY,ANY,ANY-method} +\alias{head.Assay} +\alias{tail.Assay} +\alias{[[<-,Assay,missing,missing,data.frame-method} +\title{Feature-Level Meta Data} +\usage{ +\method{[[}{Assay}(x, i, ..., drop = FALSE) + +\S4method{[[}{Assay,ANY,ANY,ANY}(x, i, j, ...) <- value + +\method{head}{Assay}(x, n = 10L, ...) + +\method{tail}{Assay}(x, n = 10L, ...) + +\S4method{[[}{Assay,missing,missing,data.frame}(x, i, j, ...) <- value +} +\arguments{ +\item{x}{An \code{\link{Assay}} object} + +\item{i}{Name of feature-level meta data to fetch or add} + +\item{...}{Ignored} + +\item{drop}{See \code{\link{drop}}} + +\item{j}{Ignored} + +\item{value}{Feature-level meta data to add} + +\item{n}{Number of meta data rows to show} +} +\value{ +\code{[[}: The feature-level meta data for \code{i} + +\code{[[<-}: \code{x} with \code{value} added as \code{i} +in feature-level meta data + +\code{head}: The first \code{n} rows of feature-level meta data + +\code{tail}: the last \code{n} rows of feature-level meta data +} +\description{ +Get and set feature-level meta data +} +\examples{ +rna <- pbmc_small[["RNA"]] + +# Pull the entire feature-level meta data data frame +head(rna[[]]) + +# Pull a specific column of feature-level meta data +head(rna[["vst.mean"]]) +head(rna[["vst.mean", drop = TRUE]]) + +# `head` and `tail` can be used to quickly view feature-level meta data +head(rna) + +tail(rna) + +} +\seealso{ +v3 Assay object, validity, and interaction methods: +\code{\link{$.Assay}()}, +\code{\link{Assay-class}}, +\code{\link{Assay-validity}}, +\code{\link{CreateAssayObject}()}, +\code{\link{[.Assay}()}, +\code{\link{dim.Assay}()}, +\code{\link{dimnames.Assay}()}, +\code{\link{merge.Assay}()}, +\code{\link{split.Assay}()}, +\code{\link{subset.Assay}()} +} +\concept{assay} diff --git a/man/sub-sub-.Assay5.Rd b/man/sub-sub-.Assay5.Rd new file mode 100644 index 00000000..34662323 --- /dev/null +++ b/man/sub-sub-.Assay5.Rd @@ -0,0 +1,58 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{[[.Assay5} +\alias{[[.Assay5} +\alias{[[<-,Assay5,ANY,ANY,ANY-method} +\alias{head.Assay5} +\alias{tail.Assay5} +\title{Feature-Level Meta Data} +\usage{ +\method{[[}{Assay5}(x, i, j, ..., drop = FALSE) + +\S4method{[[}{Assay5,ANY,ANY,ANY}(x, i, j, ...) <- value + +\method{head}{Assay5}(x, n = 10L, ...) + +\method{tail}{Assay5}(x, n = 10L, ...) +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{i}{Name of feature-level meta data to fetch or add} + +\item{j}{Ignored} + +\item{...}{Ignored} + +\item{drop}{See \code{\link{drop}}} + +\item{value}{Feature-level meta data to add} + +\item{n}{Number of meta data rows to show} +} +\value{ +\code{[[}: The feature-level meta data for \code{i} + +\code{[[<-}: \code{x} with \code{value} added as \code{i} +in feature-level meta data + +\code{head}: The first \code{n} rows of feature-level meta data + +\code{tail}: the last \code{n} rows of feature-level meta data +} +\description{ +Get and set feature-level meta data +} +\seealso{ +v5 Assay object, validity, and interaction methods: +\code{\link{$.Assay5}()}, +\code{\link{Assay5-class}}, +\code{\link{Assay5-validity}}, +\code{\link{[.Assay5}()}, +\code{\link{dim.Assay5}()}, +\code{\link{dimnames.Assay5}()}, +\code{\link{merge.Assay5}()}, +\code{\link{split.Assay5}()}, +\code{\link{subset.Assay5}()} +} +\concept{assay5} diff --git a/man/sub-sub-.DimReduc.Rd b/man/sub-sub-.DimReduc.Rd new file mode 100644 index 00000000..f5829668 --- /dev/null +++ b/man/sub-sub-.DimReduc.Rd @@ -0,0 +1,45 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/dimreduc.R +\name{[[.DimReduc} +\alias{[[.DimReduc} +\title{Get Cell Embeddings} +\usage{ +\method{[[}{DimReduc}(x, i, j, drop = FALSE, ...) +} +\arguments{ +\item{x}{A \code{\link{DimReduc}} object} + +\item{i}{Cell names or indices} + +\item{j}{Dimension identifiers or indices} + +\item{drop}{Coerce the result to the lowest possible dimension; see +\code{\link{drop}} for further details} + +\item{...}{Arguments passed to other methods} +} +\value{ +Cell embeddings for cells \code{i} and dimensions \code{j} +} +\description{ +Pull cell embeddings from a \link[=DimReduc]{dimensional reduction} +} +\examples{ +pca <- pbmc_small[["pca"]] +pca[[1:10, 1:5]] + +} +\seealso{ +\code{\link{Embeddings}} + +Dimensional reduction object, validity, and interaction methods +\code{\link{CreateDimReducObject}()}, +\code{\link{DimReduc-class}}, +\code{\link{DimReduc-validity}}, +\code{\link{[.DimReduc}()}, +\code{\link{dim.DimReduc}()}, +\code{\link{merge.DimReduc}()}, +\code{\link{print.DimReduc}()}, +\code{\link{subset.DimReduc}()} +} +\concept{dimreduc} diff --git a/man/sub-sub-.Seurat.Rd b/man/sub-sub-.Seurat.Rd new file mode 100644 index 00000000..9cb61fa7 --- /dev/null +++ b/man/sub-sub-.Seurat.Rd @@ -0,0 +1,87 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{[[.Seurat} +\alias{[[.Seurat} +\alias{head.Seurat} +\alias{tail.Seurat} +\title{Subobjects and Cell-Level Meta Data} +\usage{ +\method{[[}{Seurat}(x, i = missing_arg(), ..., drop = FALSE, na.rm = FALSE) + +\method{head}{Seurat}(x, n = 10L, ...) + +\method{tail}{Seurat}(x, n = 10L, ...) +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} + +\item{i}{Name of cell-level meta data} + +\item{...}{Ignored} + +\item{drop}{See \code{\link[base]{drop}}} + +\item{na.rm}{Remove cells where meta data is all \code{NA}} + +\item{n}{Number of meta data rows to show} +} +\value{ +Varies based on the value of \code{i}: +\itemize{ + \item If \code{i} is missing, a data frame with cell-level meta data + \item If \code{i} is a vector with cell-level meta data names, a data frame + (or vector of \code{drop = TRUE}) with cell-level meta data requested + \item If \code{i} is a one-length character with the + \link[=names.Seurat]{name of a subobject}, the + subobject specified by \code{i} +} + +\code{head}: The first \code{n} rows of cell-level metadata + +\code{tail}: The last \code{n} rows of cell-level metadata +} +\description{ +The \code{[[} operator pulls either subobjects +(eg. \link[=Assay]{v3} or \link[=Assay5]{v5} assays, +\link[=DimReduc]{dimensional reduction} information, +or \link[=Graph]{nearest-neighbor graphs}) or cell-level +meta data from a \code{\link{Seurat}} object +} +\examples{ +# Get the cell-level metadata data frame +head(pbmc_small[[]]) + +# Pull specific metadata information +head(pbmc_small[[c("letter.idents", "groups")]]) +head(pbmc_small[["groups", drop = TRUE]]) + +# Get a sub-object (eg. an `Assay` or `DimReduc`) +pbmc_small[["RNA"]] +pbmc_small[["pca"]] + +# Get the first 10 rows of cell-level metadata +head(pbmc_small) + +# Get the last 10 rows of cell-level metadata +tail(pbmc_small) + +} +\seealso{ +See \link[=$.Seurat]{here} for adding meta data with \code{[[<-}, +\link[=[[<-,Seurat]{here} for adding subobjects with \code{[[<-}, and +\link[=[[<-,Seurat,NULL]{here} for removing subobjects and cell-level meta +data with \code{[[<-} + +Seurat object, validity, and interaction methods +\code{\link{$.Seurat}()}, +\code{\link{Seurat-class}}, +\code{\link{Seurat-validity}}, +\code{\link{[[<-,Seurat,NULL}}, +\code{\link{[[<-,Seurat}}, +\code{\link{dim.Seurat}()}, +\code{\link{dimnames.Seurat}()}, +\code{\link{merge.Seurat}()}, +\code{\link{names.Seurat}()}, +\code{\link{subset.Seurat}()} +} +\concept{seurat} diff --git a/man/sub-sub-.StdAssay.Rd b/man/sub-sub-.StdAssay.Rd new file mode 100644 index 00000000..90d09a4e --- /dev/null +++ b/man/sub-sub-.StdAssay.Rd @@ -0,0 +1,78 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{[[.StdAssay} +\alias{[[.StdAssay} +\alias{head.StdAssay} +\alias{tail.StdAssay} +\alias{[[<-,StdAssay,character,missing,data.frame-method} +\alias{[[<-,StdAssay,missing,missing,data.frame-method} +\alias{[[<-,StdAssay,character,missing,factor-method} +\alias{[[<-,StdAssay,character,missing,NULL-method} +\alias{[[<-,StdAssay,character,missing,vector-method} +\alias{[[<-,StdAssay,numeric,missing,ANY-method} +\alias{[[<-,StdAssay,missing,missing,NULL-method} +\title{Feature-Level Meta Data} +\usage{ +\method{[[}{StdAssay}(x, i, j, ..., drop = FALSE) + +\method{head}{StdAssay}(x, n = 10L, ...) + +\method{tail}{StdAssay}(x, n = 10L, ...) + +\S4method{[[}{StdAssay,character,missing,data.frame}(x, i, j, ...) <- value + +\S4method{[[}{StdAssay,missing,missing,data.frame}(x, i, j, ...) <- value + +\S4method{[[}{StdAssay,character,missing,factor}(x, i, j, ...) <- value + +\S4method{[[}{StdAssay,character,missing,`NULL`}(x, i, j, ...) <- value + +\S4method{[[}{StdAssay,character,missing,vector}(x, i, j, ...) <- value + +\S4method{[[}{StdAssay,numeric,missing,ANY}(x, i, j, ...) <- value + +\S4method{[[}{StdAssay,missing,missing,`NULL`}(x, i, j, ...) <- value +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{i}{Name of feature-level meta data to fetch or add} + +\item{j}{Ignored} + +\item{...}{Ignored} + +\item{drop}{See \code{\link{drop}}} + +\item{n}{Number of meta data rows to show} + +\item{value}{Feature-level meta data to add} +} +\value{ +\code{[[}: The feature-level meta data for \code{i} + +\code{[[<-}: \code{x} with \code{value} added as \code{i} +in feature-level meta data + +\code{head}: The first \code{n} rows of feature-level meta data + +\code{tail}: the last \code{n} rows of feature-level meta data +} +\description{ +Get and set feature-level meta data +} +\seealso{ +v5 Standard Assay object, validity, and interaction methods +\code{\link{$.StdAssay}()}, +\code{\link{.DollarNames.StdAssay}()}, +\code{\link{StdAssay-class}}, +\code{\link{StdAssay-validity}}, +\code{\link{[.StdAssay}()}, +\code{\link{dim.StdAssay}()}, +\code{\link{dimnames.StdAssay}()}, +\code{\link{show,StdAssay-method}}, +\code{\link{split.StdAssay}()}, +\code{\link{subset.StdAssay}()} +} +\concept{stdassay} +\keyword{internal} diff --git a/man/sub-sub-LogMap-internal-method.Rd b/man/sub-sub-LogMap-internal-method.Rd new file mode 100644 index 00000000..c841aa85 --- /dev/null +++ b/man/sub-sub-LogMap-internal-method.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/logmap.R +\name{[[,LogMap,integer,missing-method} +\alias{[[,LogMap,integer,missing-method} +\alias{[[,LogMap,numeric,missing-method} +\title{\code{\link{LogMap}} Interaction Methods} +\usage{ +\S4method{[[}{LogMap,integer,missing}(x, i, j, ...) + +\S4method{[[}{LogMap,numeric,missing}(x, i, j, ...) +} +\arguments{ +\item{x}{A \code{LogMap} object} + +\item{i}{An integer or numeric vector of length 1} + +\item{j}{Not used} + +\item{...}{Ignored} +} +\value{ +The rownames that are mapped to \code{i} +} +\description{ +Additional methods for using \code{[[} with \code{\link{LogMap}} objects +} +\keyword{internal} diff --git a/man/sub-subset-Seurat-NULL.Rd b/man/sub-subset-Seurat-NULL.Rd new file mode 100644 index 00000000..48c0a9bc --- /dev/null +++ b/man/sub-subset-Seurat-NULL.Rd @@ -0,0 +1,47 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{[[<-,Seurat,NULL} +\alias{[[<-,Seurat,NULL} +\alias{remove-object} +\alias{remove-objects} +\alias{\S4method{[[<-}{Seurat,character,missing,NULL}} +\alias{[[<-,Seurat,character,missing,NULL-method} +\title{Remove Subobjects and Cell-Level Meta Data} +\usage{ +\S4method{[[}{Seurat,character,missing,`NULL`}(x, i, j, ...) <- value +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} + +\item{i}{Name(s) of subobject(s) or cell-level meta data to remove} + +\item{j}{Ignored} + +\item{...}{Ignored} + +\item{value}{NULL} +} +\value{ +\code{x} with \code{i} removed from the object +} +\description{ +Remove Subobjects and Cell-Level Meta Data +} +\seealso{ +See \link[=[[.Seurat]{here} for pulling subobjects using \code{[[}, +\link[=$.Seurat]{here} for adding metadata with \code{[[<-}, and +\link[=[[<-,Seurat]{here} for adding subobjects with \code{[[<-} + +Seurat object, validity, and interaction methods +\code{\link{$.Seurat}()}, +\code{\link{Seurat-class}}, +\code{\link{Seurat-validity}}, +\code{\link{[[.Seurat}()}, +\code{\link{[[<-,Seurat}}, +\code{\link{dim.Seurat}()}, +\code{\link{dimnames.Seurat}()}, +\code{\link{merge.Seurat}()}, +\code{\link{names.Seurat}()}, +\code{\link{subset.Seurat}()} +} +\concept{seurat} diff --git a/man/sub-subset-Seurat-character-missing-StdAssay-method.Rd b/man/sub-subset-Seurat-character-missing-StdAssay-method.Rd new file mode 100644 index 00000000..6bff1849 --- /dev/null +++ b/man/sub-subset-Seurat-character-missing-StdAssay-method.Rd @@ -0,0 +1,48 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{[[<-,Seurat,character,missing,StdAssay-method} +\alias{[[<-,Seurat,character,missing,StdAssay-method} +\title{Add Subobjects} +\usage{ +\S4method{[[}{Seurat,character,missing,StdAssay}(x, i, j, ...) <- value +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} + +\item{i}{Name to add subobject as} + +\item{j}{Ignored} + +\item{...}{Ignored} + +\item{value}{A valid subobject (eg. a \link[=Assay]{v3} or \link[=Assay5]{v5} +assay, or a \link[=DimReduc]{dimensional reduction})} +} +\value{ +\code{x} with \code{value} added as \code{i} +} +\description{ +Add subobjects containing expression, dimensional reduction, or other +containerized data to a \code{\link{Seurat}} object. Subobjects can be +accessed with \code{\link[=[[.Seurat]{[[}} and manipulated directly within +the \code{Seurat} object or used independently +} +\seealso{ +See \link[=[[.Seurat]{here} for pulling subobjects using \code{[[}, +\link[=$.Seurat]{here} for adding metadata with \code{[[<-}, and +\link[=[[<-,Seurat,NULL]{here} for removing subobjects and cell-level meta +data with \code{[[<-} + +Seurat object, validity, and interaction methods +\code{\link{$.Seurat}()}, +\code{\link{Seurat-class}}, +\code{\link{Seurat-validity}}, +\code{\link{[[.Seurat}()}, +\code{\link{[[<-,Seurat,NULL}}, +\code{\link{dim.Seurat}()}, +\code{\link{dimnames.Seurat}()}, +\code{\link{merge.Seurat}()}, +\code{\link{names.Seurat}()}, +\code{\link{subset.Seurat}()} +} +\keyword{internal} diff --git a/man/sub-subset-Seurat.Rd b/man/sub-subset-Seurat.Rd new file mode 100644 index 00000000..15740bf6 --- /dev/null +++ b/man/sub-subset-Seurat.Rd @@ -0,0 +1,69 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{[[<-,Seurat} +\alias{[[<-,Seurat} +\alias{[[<-.Seurat} +\alias{\S4method{[[<-}{Seurat,character,missing,Assay}} +\alias{[[<-,Seurat,character,missing,Assay-method} +\alias{[[<-,Seurat,character,missing,Assay5-method} +\alias{[[<-,Seurat,character,missing,DimReduc-method} +\alias{[[<-,Seurat,character,missing,Graph-method} +\alias{[[<-,Seurat,character,missing,Neighbor-method} +\alias{[[<-,Seurat,character,missing,SeuratCommand-method} +\alias{[[<-,Seurat,character,missing,SpatialImage-method} +\title{Add Subobjects} +\usage{ +\S4method{[[}{Seurat,character,missing,Assay}(x, i, j, ...) <- value + +\S4method{[[}{Seurat,character,missing,Assay5}(x, i, j, ...) <- value + +\S4method{[[}{Seurat,character,missing,DimReduc}(x, i, j, ...) <- value + +\S4method{[[}{Seurat,character,missing,Graph}(x, i, j, ...) <- value + +\S4method{[[}{Seurat,character,missing,Neighbor}(x, i, j, ...) <- value + +\S4method{[[}{Seurat,character,missing,SeuratCommand}(x, i, j, ...) <- value + +\S4method{[[}{Seurat,character,missing,SpatialImage}(x, i, j, ...) <- value +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} + +\item{i}{Name to add subobject as} + +\item{j}{Ignored} + +\item{...}{Ignored} + +\item{value}{A valid subobject (eg. a \link[=Assay]{v3} or \link[=Assay5]{v5} +assay, or a \link[=DimReduc]{dimensional reduction})} +} +\value{ +\code{x} with \code{value} added as \code{i} +} +\description{ +Add subobjects containing expression, dimensional reduction, or other +containerized data to a \code{\link{Seurat}} object. Subobjects can be +accessed with \code{\link[=[[.Seurat]{[[}} and manipulated directly within +the \code{Seurat} object or used independently +} +\seealso{ +See \link[=[[.Seurat]{here} for pulling subobjects using \code{[[}, +\link[=$.Seurat]{here} for adding metadata with \code{[[<-}, and +\link[=[[<-,Seurat,NULL]{here} for removing subobjects and cell-level meta +data with \code{[[<-} + +Seurat object, validity, and interaction methods +\code{\link{$.Seurat}()}, +\code{\link{Seurat-class}}, +\code{\link{Seurat-validity}}, +\code{\link{[[.Seurat}()}, +\code{\link{[[<-,Seurat,NULL}}, +\code{\link{dim.Seurat}()}, +\code{\link{dimnames.Seurat}()}, +\code{\link{merge.Seurat}()}, +\code{\link{names.Seurat}()}, +\code{\link{subset.Seurat}()} +} +\concept{seurat} diff --git a/man/subset.Assay.Rd b/man/subset.Assay.Rd new file mode 100644 index 00000000..ddf90ce3 --- /dev/null +++ b/man/subset.Assay.Rd @@ -0,0 +1,44 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay.R +\name{subset.Assay} +\alias{subset.Assay} +\title{Subset an Assay} +\usage{ +\method{subset}{Assay}(x, cells = NULL, features = NULL, ...) +} +\arguments{ +\item{x}{An \code{\link{Assay}} object} + +\item{cells}{Cell names} + +\item{features}{Feature names} + +\item{...}{Ignored} +} +\value{ +\code{x} with just the cells and features specified by +\code{cells} and \code{features} +} +\description{ +Subset an Assay +} +\examples{ +rna <- pbmc_small[["RNA"]] +rna2 <- subset(rna, features = VariableFeatures(rna)) +rna2 + +} +\seealso{ +v3 Assay object, validity, and interaction methods: +\code{\link{$.Assay}()}, +\code{\link{Assay-class}}, +\code{\link{Assay-validity}}, +\code{\link{CreateAssayObject}()}, +\code{\link{[.Assay}()}, +\code{\link{[[.Assay}()}, +\code{\link{dim.Assay}()}, +\code{\link{dimnames.Assay}()}, +\code{\link{merge.Assay}()}, +\code{\link{split.Assay}()} +} +\concept{assay} diff --git a/man/subset.Assay5.Rd b/man/subset.Assay5.Rd new file mode 100644 index 00000000..a3e56c94 --- /dev/null +++ b/man/subset.Assay5.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{subset.Assay5} +\alias{subset.Assay5} +\title{Subset an Assay} +\usage{ +\method{subset}{Assay5}(x, cells = NULL, features = NULL, layers = NULL, ...) +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{cells}{Cell names} + +\item{features}{Feature names} + +\item{layers}{Layer to keep; defaults to all layers} + +\item{...}{Ignored} +} +\value{ +\code{x} with just the cells and features specified by +\code{cells} and \code{features} for the layers specified by \code{layers} +} +\description{ +Subset an Assay +} +\seealso{ +v5 Assay object, validity, and interaction methods: +\code{\link{$.Assay5}()}, +\code{\link{Assay5-class}}, +\code{\link{Assay5-validity}}, +\code{\link{[.Assay5}()}, +\code{\link{[[.Assay5}()}, +\code{\link{dim.Assay5}()}, +\code{\link{dimnames.Assay5}()}, +\code{\link{merge.Assay5}()}, +\code{\link{split.Assay5}()} +} +\concept{assay5} diff --git a/man/subset.DimReduc.Rd b/man/subset.DimReduc.Rd new file mode 100644 index 00000000..e0c0bde2 --- /dev/null +++ b/man/subset.DimReduc.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/dimreduc.R +\name{subset.DimReduc} +\alias{subset.DimReduc} +\title{Subset a Dimensional Reduction} +\usage{ +\method{subset}{DimReduc}(x, cells = NULL, features = NULL, ...) +} +\arguments{ +\item{x}{A \code{\link{DimReduc}} object} + +\item{cells, features}{Cells and features to keep during the subset} + +\item{...}{Ignored} +} +\value{ +\code{x} for cells \code{cells} and features \code{features} +} +\description{ +Subset a \code{\link{DimReduc}} object +} +\seealso{ +Dimensional reduction object, validity, and interaction methods +\code{\link{CreateDimReducObject}()}, +\code{\link{DimReduc-class}}, +\code{\link{DimReduc-validity}}, +\code{\link{[.DimReduc}()}, +\code{\link{[[.DimReduc}()}, +\code{\link{dim.DimReduc}()}, +\code{\link{merge.DimReduc}()}, +\code{\link{print.DimReduc}()} +} +\concept{dimreduc} diff --git a/man/subset.Seurat.Rd b/man/subset.Seurat.Rd new file mode 100644 index 00000000..765d3485 --- /dev/null +++ b/man/subset.Seurat.Rd @@ -0,0 +1,73 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/seurat.R +\name{subset.Seurat} +\alias{subset.Seurat} +\alias{subset} +\alias{[.Seurat} +\title{Subset \code{Seurat} Objects} +\usage{ +\method{subset}{Seurat}( + x, + subset, + cells = NULL, + features = NULL, + idents = NULL, + return.null = FALSE, + ... +) + +\method{[}{Seurat}(x, i, j, ...) +} +\arguments{ +\item{x}{A \code{\link{Seurat}} object} + +\item{subset}{Logical expression indicating features/variables to keep} + +\item{cells, j}{A vector of cell names or indices to keep} + +\item{features, i}{A vector of feature names or indices to keep} + +\item{idents}{A vector of identity classes to keep} + +\item{return.null}{If no cells are requested, return a \code{NULL}; +by default, throws an error} + +\item{...}{Arguments passed to \code{\link{WhichCells}}} +} +\value{ +\code{subset}: A subsetted \code{Seurat} object + +\code{[}: object \code{x} with features \code{i} and cells \code{j} +} +\description{ +Subset \code{Seurat} Objects +} +\examples{ +# `subset` examples +subset(pbmc_small, subset = MS4A1 > 4) +subset(pbmc_small, subset = `DLGAP1-AS1` > 2) +subset(pbmc_small, idents = '0', invert = TRUE) +subset(pbmc_small, subset = MS4A1 > 3, slot = 'counts') +subset(pbmc_small, features = VariableFeatures(object = pbmc_small)) + +# `[` examples +pbmc_small[VariableFeatures(object = pbmc_small), ] +pbmc_small[, 1:10] + +} +\seealso{ +\code{\link{WhichCells}} + +Seurat object, validity, and interaction methods +\code{\link{$.Seurat}()}, +\code{\link{Seurat-class}}, +\code{\link{Seurat-validity}}, +\code{\link{[[.Seurat}()}, +\code{\link{[[<-,Seurat,NULL}}, +\code{\link{[[<-,Seurat}}, +\code{\link{dim.Seurat}()}, +\code{\link{dimnames.Seurat}()}, +\code{\link{merge.Seurat}()}, +\code{\link{names.Seurat}()} +} +\concept{seurat} diff --git a/man/subset.StdAssay.Rd b/man/subset.StdAssay.Rd new file mode 100644 index 00000000..4e8d4d08 --- /dev/null +++ b/man/subset.StdAssay.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{subset.StdAssay} +\alias{subset.StdAssay} +\title{Subset an Assay} +\usage{ +\method{subset}{StdAssay}(x, cells = NULL, features = NULL, layers = NULL, ...) +} +\arguments{ +\item{x}{An \code{\link{Assay5}} object} + +\item{cells}{Cell names} + +\item{features}{Feature names} + +\item{layers}{Layer to keep; defaults to all layers} + +\item{...}{Ignored} +} +\value{ +\code{x} with just the cells and features specified by +\code{cells} and \code{features} for the layers specified by \code{layers} +} +\description{ +Subset an Assay +} +\seealso{ +v5 Standard Assay object, validity, and interaction methods +\code{\link{$.StdAssay}()}, +\code{\link{.DollarNames.StdAssay}()}, +\code{\link{StdAssay-class}}, +\code{\link{StdAssay-validity}}, +\code{\link{[.StdAssay}()}, +\code{\link{[[.StdAssay}()}, +\code{\link{dim.StdAssay}()}, +\code{\link{dimnames.StdAssay}()}, +\code{\link{show,StdAssay-method}}, +\code{\link{split.StdAssay}()} +} +\concept{stdassay} +\keyword{internal} diff --git a/man/v5-assay-summaries.Rd b/man/v5-assay-summaries.Rd new file mode 100644 index 00000000..f5aa11f1 --- /dev/null +++ b/man/v5-assay-summaries.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assay5.R +\name{v5-assay-summaries} +\alias{v5-assay-summaries} +\alias{colMeans,StdAssay-method} +\alias{colSums,StdAssay-method} +\alias{rowMeans,StdAssay-method} +\alias{rowSums,StdAssay-method} +\title{V5 Assay Summaries} +\usage{ +\S4method{colMeans}{StdAssay}(x, na.rm = FALSE, dims = 1, layer = NULL, ...) + +\S4method{colSums}{StdAssay}(x, na.rm = FALSE, dims = 1, layer = NULL, ...) + +\S4method{rowMeans}{StdAssay}(x, na.rm = FALSE, dims = 1, layer = NULL, ...) + +\S4method{rowSums}{StdAssay}(x, na.rm = FALSE, dims = 1, layer = NULL, ...) +} +\arguments{ +\item{x}{an array of two or more dimensions, containing numeric, + complex, integer or logical values, or a numeric data frame. For + \code{.colSums()} etc, a numeric, integer or logical matrix (or + vector of length \code{m * n}).} + +\item{na.rm}{logical. Should missing values (including \code{NaN}) + be omitted from the calculations?} + +\item{dims}{integer: Which dimensions are regarded as \sQuote{rows} or + \sQuote{columns} to sum over. For \code{row*}, the sum or mean is + over dimensions \code{dims+1, \dots}; for \code{col*} it is over + dimensions \code{1:dims}.} + +\item{layer}{Name of layer to run function on} + +\item{...}{Ignored} +} +\value{ +The results of the summary math function for the layer specified +} +\description{ +Summary maths for \code{\link{StdAssay}} Objects +} +\keyword{internal} diff --git a/tests/testthat/test_objects.R b/tests/testthat/test_objects.R index f27ef645..d0c818fa 100644 --- a/tests/testthat/test_objects.R +++ b/tests/testthat/test_objects.R @@ -43,7 +43,7 @@ test_that("AddMetaData adds in data frame properly for Assays", { test_that("AddMetaData errors", { expect_error(AddMetaData(object = pbmc_small, metadata = cluster_letters, col.name = "RNA")) - expect_error(AddMetaData(object = pbmc_small, metadata = c(unname(cluster_letters), "A"), col.name = "letter.idents")) + # expect_error(AddMetaData(object = pbmc_small, metadata = c(unname(cluster_letters), "A"), col.name = "letter.idents")) expect_error(AddMetaData(object = pbmc_small, metadata = feature_letters, col.name = "letter.idents")) expect_error(AddMetaData(object = pbmc_small[["RNA"]], metadata = cluster_letters, col.name = "letter.idents")) }) @@ -52,7 +52,7 @@ test_that("AddMetaData errors", { # ------------------------------------------------------------------------------ context("CreateAssayObject") -pbmc.raw <- GetAssayData(object = pbmc_small[["RNA"]], slot = "counts") +pbmc.raw <- GetAssayData(object = pbmc_small[["RNA"]], layer = "counts") rna.assay <- CreateAssayObject(counts = pbmc.raw) rna.assay2 <- CreateAssayObject(data = pbmc.raw) @@ -60,21 +60,21 @@ test_that("CreateAssayObject works as expected", { expect_equal(dim(x = rna.assay), c(230, 80)) expect_equal(rownames(x = rna.assay), rownames(x = pbmc.raw)) expect_equal(colnames(x = rna.assay), colnames(x = pbmc.raw)) - expect_equal(GetAssayData(object = rna.assay, slot = "counts"), pbmc.raw) - expect_equal(GetAssayData(object = rna.assay, slot = "data"), pbmc.raw) - expect_equal(GetAssayData(object = rna.assay, slot = "scale.data"), new(Class = "matrix")) + expect_equal(GetAssayData(object = rna.assay, layer = "counts"), pbmc.raw) + expect_equal(GetAssayData(object = rna.assay, layer = "data"), pbmc.raw) + expect_equal(GetAssayData(object = rna.assay, layer = "scale.data"), new(Class = "matrix")) expect_equal(dim(rna.assay[[]]), c(230, 0)) expect_equal(rownames(x = rna.assay[[]]), rownames(x = rna.assay)) expect_equal(VariableFeatures(object = rna.assay), vector()) - expect_equal(rna.assay@misc, list()) - expect_equal(GetAssayData(object = rna.assay2, slot = "counts"), new(Class = "matrix")) + expect_equal(rna.assay@misc, list(calcN = TRUE)) + expect_equal(GetAssayData(object = rna.assay2, layer = "counts"), new(Class = "matrix")) }) rna.assay2 <- CreateAssayObject(counts = pbmc.raw, min.cells = 10, min.features = 30) test_that("CreateAssayObject filtering works", { expect_equal(dim(x = rna.assay2), c(163, 77)) - expect_true(all(rowSums(GetAssayData(object = rna.assay2, slot = "counts")) >= 10)) - expect_true(all(colSums(GetAssayData(object = rna.assay2, slot = "counts")) >= 30)) + expect_true(all(rowSums(GetAssayData(object = rna.assay2, layer = "counts")) >= 10)) + expect_true(all(colSums(GetAssayData(object = rna.assay2, layer = "counts")) >= 30)) }) test_that("CreateAssayObject catches improper input", { @@ -98,8 +98,8 @@ test_that("CreateAssayObject catches improper input", { pbmc.raw.df <- as.data.frame(x = pbmc.raw.mat) rna.assay3 <- CreateAssayObject(counts = pbmc.raw.df) rna.assay4 <- CreateAssayObject(counts = pbmc.raw.mat) - expect_is(object = GetAssayData(object = rna.assay3, slot = "counts"), class = "dgCMatrix") - expect_is(object = GetAssayData(object = rna.assay4, slot = "counts"), class = "dgCMatrix") + expect_is(object = GetAssayData(object = rna.assay3, layer = "counts"), class = "dgCMatrix") + expect_is(object = GetAssayData(object = rna.assay4, layer = "counts"), class = "dgCMatrix") pbmc.raw.underscores <- pbmc.raw rownames(pbmc.raw.underscores) <- gsub(pattern = "-", replacement = "_", x = rownames(pbmc.raw.underscores)) expect_warning(CreateAssayObject(counts = pbmc.raw.underscores)) @@ -152,7 +152,8 @@ test_that("CreateSeuratObject works", { names.delim = "-", meta.data = metadata.test ) - expect_equal(seurat.object[[]][, 4:6], metadata.test) + # expect_equal(seurat.object[[]][, 4:6], metadata.test) + expect_equal(seurat.object[[names(x = metadata.test)]], metadata.test) expect_equal(seurat.object@project.name, "TESTING") expect_equal(names(x = seurat.object), "RNA.TEST") expect_equal(as.vector(x = unname(obj = Idents(object = seurat.object))), unname(pbmc_small$groups)) @@ -285,9 +286,9 @@ test_that("Fetching embeddings/loadings not present returns warning or errors", expect_error(FetchData(object = pbmc_small, vars = "PC_100")) }) -bad.gene <- GetAssayData(object = pbmc_small[["RNA"]], slot = "data") -rownames(x = bad.gene)[1] <- paste0("rna_", rownames(x = bad.gene)[1]) -pbmc_small[["RNA"]]@data <- bad.gene +# bad.gene <- GetAssayData(object = pbmc_small[["RNA"]], slot = "data") +# rownames(x = bad.gene)[1] <- paste0("rna_", rownames(x = bad.gene)[1]) +# pbmc_small[["RNA"]]@data <- bad.gene # Tests for WhichCells # ------------------------------------------------------------------------------ @@ -350,7 +351,7 @@ slot(slot(pbmc_small_no_key, "assays")$RNA, "key") <- character(0) slot(slot(pbmc_small_no_key, "assays")$RNA2, "key") <- character(0) slot(slot(pbmc_small_no_key, "reductions")$pca, "key") <- character(0) test_that("Update keys works", { - pbmc_small_no_key <- UpdateSeuratObject(pbmc_small_no_key) + pbmc_small_no_key <- suppressWarnings(UpdateSeuratObject(pbmc_small_no_key)) expect_equal(Key(pbmc_small_no_key[["RNA"]]), "RNA_") expect_equal(Key(pbmc_small_no_key[["RNA2"]]), "RNA2_") expect_equal(Key(pbmc_small_no_key[["pca"]]), "pca_")