读《R语言面向对象编程》

2021-01-12  本文已影响0人  周运来就是我

R主要面向统计计算,为数据科学家青睐,代码量一般不会很大,使用面向过程的编程方式就可以很好的完成编程任务。而且还是有RStudio这样的交互操作集成开发环境,所以大部分的R语言用户对R语言是不是面向对象的很疑惑,虽然我们都知道在R中一切皆对象,比如Seurat对象。其实用过Seurat的话,会感受到它既有S3 面向对象结构,又有S4对象结构。阅读源码也验证了这一点:其实Seurat这个R包用的主要是S3面向对象结构,但是在创建数据对象的时候用的是S4类。如下:

Seurat <- setClass(
  Class = 'Seurat',
  slots = c(
    assays = 'list',
    meta.data = 'data.frame',
    active.assay = 'character',
    active.ident = 'factor',
    graphs = 'list',
    neighbors = 'list',
    reductions = 'list',
    images = 'list',
    project.name = 'character',
    misc = 'list',
    version = 'package_version',
    commands = 'list',
    tools = 'list'
  )
)

那么,R语言中的面向对象到底是怎样的呢?这个问题需要答案。面向对象对很多程序员来说都不陌生,不管是真的会用还是讲笑话的时候听过。在提到这个名词的时候总是和一些听不懂的词汇联系到一起,如抽象,封装,继承,多态等。但是面向对象却是写给人看的。在面向对象的程序设计中,对象(object)是最基本的元素,不过对象指的是具体的实例,在对象之上还有一个类(class)的概念。

在R语言的中文世界里,R语言面向对象编程的知识很少被提及,大部分的R语言书籍是偏应用的。有的话也散见于《R语言核心技术手册》《高级R语言编程指南》《R语言编程艺术》等,很少有专门来讲这一节的。这与R语言的用户大多不是程序员不无关系,也与R语言的面向对象结构发展曲折有一定关系。在R中有四种面向对象结构。

徐静2018-08-06

但是正当我们苦于没有系统的R语言面向对象资料的时候,我们发现了这本在线书:R语言面向对象编程。里面有对这四种类型的详细介绍,而且还有可执行的实例代码,这无疑会加快一般用户对R语言面向对象的理解。

这本书并不厚,代码也很简单,我们就不再这里一一演示了。就着Seurat的对象结构,我们来演示S3和S4的实现,这为我们理解Seurat的源码也是有所裨益的。

zgetEMB3 <- function(object,ebm='pca',...) UseMethod("zgetEMB3")
zgetEMB3.Seurat<- function(object,ebm='pca',...){
  (object@reductions[[ebm]]@cell.embeddings[,1:2])
}

> head(zgetEMB3(pbmc_small,ebm = 'tsne'))
                    tSNE_1      tSNE_2
ATGCCAGAACGACT   0.8675977  -8.1007483
CATGGCCTGTGCAT  -7.3925306  -8.7717451
GAACCTGATGAACC -28.2064258   0.2410102
TGACTGGATTCTCA  16.3480689 -11.1633255
AGTCAGACTGCACA   1.9113998 -11.1929311
TCTGATACACGTGT   3.1475998  -9.9369312
> head(zgetEMB3.Seurat(pbmc_small))
                      PC_1       PC_2
ATGCCAGAACGACT -0.77403708 -0.8996461
CATGGCCTGTGCAT -0.02602702 -0.3466795
GAACCTGATGAACC -0.45650250  0.1795811
TGACTGGATTCTCA -0.81163243 -1.3795340
AGTCAGACTGCACA -0.77403708 -0.8996461
TCTGATACACGTGT -0.77403708 -0.8996461
> showMethods(zgetEMB3)

Function "zgetEMB3":
 <not an S4 generic function>
> getMethod("zgetEMB3","Seurat")
Error in getMethod("zgetEMB3", "Seurat") : 
  no generic function found for 'zgetEMB3'
> getS3method("zgetEMB3","Seurat")
function(object,ebm='pca',...){
  (object@reductions[[ebm]]@cell.embeddings[,1:2])
}
<bytecode: 0x00000196ab91f060>
> ftype(zgetEMB3)
[1] "s3"      "generic"
> otype(zgetEMB3)
[1] "base"
> 
setGeneric("zgetEMB4",function(obj,...){
  standardGeneric("zgetEMB4")
})
setMethod("zgetEMB4","Seurat",function(obj,ebm='pca',...){
  (obj@reductions[[ebm]]@cell.embeddings[,1:2])
  
})
> head( zgetEMB4(pbmc_small,'tsne'))
                    tSNE_1      tSNE_2
ATGCCAGAACGACT   0.8675977  -8.1007483
CATGGCCTGTGCAT  -7.3925306  -8.7717451
GAACCTGATGAACC -28.2064258   0.2410102
TGACTGGATTCTCA  16.3480689 -11.1633255
AGTCAGACTGCACA   1.9113998 -11.1929311
TCTGATACACGTGT   3.1475998  -9.9369312
> head(zgetEMB4(pbmc_small,'pca'))
                      PC_1       PC_2
ATGCCAGAACGACT -0.77403708 -0.8996461
CATGGCCTGTGCAT -0.02602702 -0.3466795
GAACCTGATGAACC -0.45650250  0.1795811
TGACTGGATTCTCA -0.81163243 -1.3795340
AGTCAGACTGCACA -0.77403708 -0.8996461
TCTGATACACGTGT -0.77403708 -0.8996461
> head(zgetEMB4(pbmc_small))
                      PC_1       PC_2
ATGCCAGAACGACT -0.77403708 -0.8996461
CATGGCCTGTGCAT -0.02602702 -0.3466795
GAACCTGATGAACC -0.45650250  0.1795811
TGACTGGATTCTCA -0.81163243 -1.3795340
AGTCAGACTGCACA -0.77403708 -0.8996461
TCTGATACACGTGT -0.77403708 -0.8996461
> getMethod("zgetEMB4","Seurat")
Method Definition:

function (obj, ...) 
{
    .local <- function (obj, ebm = "pca", ...) 
    {
        (obj@reductions[[ebm]]@cell.embeddings[, 1:2])
    }
    .local(obj, ...)
}
<bytecode: 0x00000196abc3d068>

Signatures:
        obj     
target  "Seurat"
defined "Seurat"
> getS3method("zgetEMB4","Seurat")
Error in getS3method("zgetEMB4", "Seurat") : 
  no function 'zgetEMB4' could be found
In addition: Warning message:
In findGeneric(f, envir) :
  'zgetEMB4' is a formal generic function; S3 methods will not likely be found
> showMethods(zgetEMB4)
Function: zgetEMB4 (package .GlobalEnv)
obj="Seurat"

> ftype(zgetEMB4)
[1] "function"
> otype(zgetEMB4)     
[1] "S4"

查看目前Seurat的方法:

methods(class="Seurat")
 [1] $                             $<-                           [                             [[                           
 [5] [[<-                          AddMetaData                   as.CellDataSet                as.loom                      
 [9] as.SingleCellExperiment       Command                       DefaultAssay                  DefaultAssay<-               
[13] dim                           dimnames                      droplevels                    Embeddings                   
[17] FindClusters                  FindMarkers                   FindNeighbors                 FindSpatiallyVariableFeatures
[21] FindVariableFeatures          GetAssay                      GetAssayData                  GetImage                     
[25] GetTissueCoordinates          HVFInfo                       Idents                        Idents<-                     
[29] Key                           levels                        levels<-                      Loadings                     
[33] merge                         Misc                          Misc<-                        names                        
[37] NormalizeData                 OldWhichCells                 Project                       Project<-                    
[41] RenameCells                   RenameIdents                  ReorderIdent                  RunALRA                      
[45] RunCCA                        RunICA                        RunLSI                        RunPCA                       
[49] RunTSNE                       RunUMAP                       ScaleData                     ScoreJackStraw               
[53] SetAssayData                  SetIdent                      show                          SpatiallyVariableFeatures    
[57] StashIdent                    Stdev                         subset                        SubsetData                   
[61] SVFInfo                       Tool                          Tool<-                        VariableFeatures             
[65] VariableFeatures<-            WhichCells                    zgetEMB3                      zgetEMB4                     
see '?methods' for accessing help and source code

可以看到我们创建的 zgetEMB3 和 zgetEMB4 已经在其中了。S4对象系统具有明显的结构化特征,更适合面向对象的程序设计。S3对象简答,具有动态性,结构化特征不明显,S4对象结构化,功能强大。Seurat 目前的结构是结合这两者,但是开发者文档写的很清楚:作者是建议使用S3的。而Bioconductor社区以S4对象作为基础框架,只接受S4定义的R包。这也某种程度上解释了为什么我们从来不没有在Bioconductor上安装过Seurat。loomR是基于R6面向对象类型实现的。

另外,在读Seurat源码的时候我们发现了下面的语法,作为思考题:这两种函数定义的方式有什么不同,说说其中的缘由。

#' @rdname DefaultAssay
#' @export
#' @method DefaultAssay Assay
#'
DefaultAssay.Assay <- function(object, ...) {
  object <- UpdateSlots(object = object)
  return(slot(object = object, name = 'assay.orig'))
}
#' @export
#' @method DefaultAssay<- Assay
#'
"DefaultAssay<-.Assay" <- function(object, ..., value) {
  object <- UpdateSlots(object = object)
  return(slot(object = object, name = 'assay.used'))
  object <- UpdateSlots(object = object)
  slot(object = object, name = 'assay.orig') <- value
  return(object)
}

在线电子书地址,见所附第一条链接


https://dataxujing.github.io/R_oop/index.html
https://github.com/satijalab/seurat/wiki/S3-methods
https://github.com/satijalab/seurat/issues/990
https://www.tutorialspoint.com/r/r_functions.htm
https://adv-r.hadley.nz/functions.html
https://stat.ethz.ch/R-manual/R-patched/library/methods/html/Methods_for_S3.html
https://satijalab.org/loomR/loomR_tutorial.html
https://satijalab.org/seurat/install.html

上一篇下一篇

猜你喜欢

热点阅读