内容

1简介

CellBench是一个帮助创建单细胞分析方法基准的包。我们提供了用于处理的函数SingleCellExperiments对象和一个框架,用于跨不同方法或方法组合为不同的单细胞数据集构建基准。

这个包的目的是使开发人员更容易构建组合设计,并提供一个扁平的数据结构来存储分析方法的有组织的输出。2021欧洲杯体育投注开户我们为一组单单元基准测试数据集提供了一些完全构建的基准测试管道,我们希望该框架将允许用户以有组织和有表现力的方式轻松地构建基准测试。

要获得更实际的示例,请运行cellbench_case_study ()查看使用CellBench的案例研究。

2快速启动

这个套件中有三个基本的基准组件,列表数据的S,列表函数S和a宠物猫与一个list-column我们称之为abenchmark_tbl.为了简单起见,我们使用随机生成的数据和简单的函数,但希望您能够清楚地了解该思想如何扩展到更复杂的函数和基准测试。

作为一个激励的例子,我们将简单地看看每百万计数的库大小标准化和日志转换对我们的MDS图有什么作用。除了CellBench我们将要求limma包中。

library(CellBench) library(limma) datasets <- list(sample_10x = readRDS(cellbench_file("10x_sce_sample.rds")) norm_method <- list(none = counts, cpm = function(x) t(t(1e6 * counts(x))) / colsum (counts(x)))) transform <- list(none = identity, log2 = function(x) log2(x+1)))

现在我们有3个列表年代。

res1 <- datasets %>% apply_methods(norm_method) res1 #> # A tibble: 2 x 3 #> data norm_method result #>    #> 1 sample_10x none  #> 2 sample_10x cpm 

我们看到有一个结果对于每一个组合数据而且norm_method方法应用。然后我们可以应用转换方法。

res2 <- res1 %>% apply_methods(transform) res2 #> # A tibble: 4 × 4 #> data norm_method transform result #>     #> 1 sample_10x none none  #> 2 sample_10x none log2  #> 3 sample_10x cpm none  #> 4 sample_10x cpm log2 

现在,结果列已更新,以反映应用上一个表的每个结果的每个转换方法生成的矩阵。因此,只需依次应用,就可以简单地生成组合基准方案列表函数的S。

最后,我们想要可视化每个管道的最终结果,在这里我们将使用plotMDSlimma包装和颜色分cell_linecolData ()(列数据)的原始数据。

为了设置这个,我们从cell_line原始数据中的信息。然后我们使用pipeline_collapse ()将数据和方法名折叠成单个列,用作图中的标题。

#从细胞系生成颜色值信息cell_line < -因子(colData(数据集sample_10x美元)cell_line美元)cell_line_col < - c(“红”,“蓝色”,“绿色”)[cell_line] collapsed_res < -它% > % pipeline_collapse () collapsed_res # > #宠物猫:4 x 2 # >管道结果# > < fct > <列表> # > 1 sample_10x»»没有< int[60][3000×60]> # > 2 sample_10x»»log2没有<双[60][3000×60]> > 3号sample_10x»cpm»没有<双[60][3000×60]> > 4号sample_10x»cpm»log2 <双[60][3000×60]>

然后,我们可以循环遍历这些行并生成图表,显示每个归一化和转换的组合如何影响我们的MDS可视化。

par(mfrow = c(2,2)) #声明2x2绘图网格#循环通过汇总管道表的行(i in 1:nrow(collapsed_res)) {title <- collapsed_res$pipeline[i] expr_mat <- collapsed_res$result[[i]] #注意使用[[]]由于列表limma::plotMDS(expr_mat, main = title, pch = 19, #画圆而不是标签名称col = cell_line_col)}

Par (mfrow = c(1,1)) #撤销未来绘图网格

所以我们可以看到,应用一个简单的库大小归一化和log2变换可以极大地提高我们PCA图中的视觉推理。

3.下载基准测试数据

该包提供了Tian等人(2018)生成的单细胞混合数据的访问。可以通过load_ * _data ()函数,当第一次运行时,它们将从web下载数据。在随后的运行中,它们将从本地缓存加载数据。

每组数据都作为singlecel实验对象的列表加载。它们被分组到用于生成数据集的混合策略中,相同混合策略中的每个数据集在其colData中都有相同的列。

#加载单个数据集sc_data <- load_sc_data() mrna_mix_data <- load_mrna_mix_data() cell_mix_data <- load_cell_mix_data() #加载所有数据集all_data <- load_all_data()

需要清除本地缓存中的数据,使用命令clear_cached_datasets ()

删除所有本地缓存的CellBench数据集

4关键对象和概念

4.1函数的管道

在这个包中,许多示例都大量使用了管道操作符% > %magrittr.这对于编写更容易调试的更清晰的代码非常有用。

#以下两个语句是等价的f (x) x % > % f() #这些f (x, y) x % > % f (y) #这些h (g (f (x))) x % > % f () % > % g () % > % h() #或这些h (g (f (x), b), c) x % > % f () % > % g (b) % > % h (c)

在上一个例子中,我们可以看到,当许多函数组合在一起时,管道形式从左到右读取,并且很清楚哪个参数属于哪个函数,而在嵌套形式中,则更难清楚地识别正在发生什么。一般情况下,将数据输送到函数中,并将数据作为第一个参数调用该函数,可以实现更复杂的行为,并在magrittrweb页面。

4.2映射或列表应用

R中的列表是任意对象集合的容器。在这个包中,我们鼓励用户将列表用作一系列相同类型对象的容器,就像使用向量来存储向量不能包含的数据类型一样。例如,我们将数据集存储在singlecel实验对象的列表中,并将分析方法存储在函数的列表中,这些数据类型将不会在vector中被接受。

要使用我们鼓励使用的列表拉普兰人purrr:地图,这些函数允许将函数应用于列表的每个元素,并在列表中返回结果。

x < -列表(a = 1, b = 2, c = 3)拉普兰人(x,√6)# > $ # > [1]1 # > # > $ b # > [1] c# 1.414214 # > # > > 1.732051 [1]

4.3数据集列表

基准测试工作流从一个数据集列表开始,即使你只有一个数据集,你也需要将它存储在一个列表中,以便工作流运行。在我们的示例中,数据集是10X单元混合数据集的示例。

sample_10x < - readRDS (cellbench_file(“10 x_sce_sample.rds”))#即使我们需要构建一个单一数据集列表数据集< - (sample_10x = sample_10x) #我们可以添加更多的数据集管道通过添加到列表中#这里我们有两个数据集的基因是随机抽样10 x #示例数据集< -列表(subsample1_10x = sample_genes (sample_10x, n = 1000), subsample2_10x = sample_genes (sample_10x,N = 1000)) #可以是任何其他类型的对象,只要它们是一致的数据集<- list(set1 =矩阵(rnorm(500, mean = 2, sd = 1), ncol = 5, nrow = 10), set2 =矩阵(rnorm(500, mean = 2, sd = 1), ncol = 5, nrow = 10))

任何类型的对象都可以存储在列表中,因此在基准测试工作流可以使用哪种起点方面有很大的灵活性。

4.4函数列表

在R中,函数本身是一种类型的对象,所以它们也可以存储在列表中,这在普通R中很少使用,但这允许非常简单的添加方法。

# counts是一个可以与counts(x)一起运行的函数,这里它被命名为# "none",因为它表示缺少归一化norm_method <- list(none = counts, cpm = function(x) t(t(1e6 * counts(x)) / colsum (counts(x)))) # "identity"是一个有用的函数,简单地返回其输入#它允许比较应用和不应用方法转换<- list(none = identity, log2 = function(x) log2(x+1)))

需要注意的关键是,函数必须是可调用的,并且只有一个参数。这可能意味着您需要编写一个包装器函数或使用purrr:部分()填充一些论点。比如两者的意思是而且sdna.rm参数,因为列表的元素本身必须是一个函数,简单地写成意味着(na。rm = TRUE)将不起作用,因为它是一个不完整的函数调用。相反,我们有两个主要选择:

#使用匿名函数包装器metric <- list(mean = function(x) {mean(x, na。rm = TRUE)}, sd =函数(x) {sd(x, na。rm = TRUE)} ) # using purrr partial function partial <- purrr::partial # explicit namespacing to avoid ambiguity metric <- list( mean = partial(mean, na.rm = TRUE), sd = partial(sd, na.rm = TRUE) ) # example use with kmeans clustering <- list( kmeans_4 = partial(kmeans, centers = 4), kmeans_5 = partial(kmeans, centers = 5), kmeans_6 = partial(kmeans, centers = 6) )

purrr:部分()被称为函数的部分应用:它接受一个函数和该函数的参数,然后返回一个新函数,该函数是填充了所提供参数的函数。这比创建函数包装器稍微显式一些,因为函数包装器可以在其主体内执行更多的任务,而不仅仅是设置参数purrr:部分()清楚地表明你所做的只是设置一些参数。

4.5基准测试tibble和列表列

benchmark_tbl是一个非常轻的包装周围的标准tibble提供宠物猫:宠物猫().这是常规的data.frame ()除了它有一些漂亮的打印功能特别有用list-columns.列表列是一种特殊类型的列,其中的值不是原子的,即不能存储在向量中。这允许将任意数据类型存储在列中,但需要注意的是,提取该列将返回一个列表而不是一个向量。这意味着如何使用dplyr动词和一般情况下不会与向量化函数表现出预期的行为。

在这个包建立的框架中,第一列是数据的名称,后面是指定分析步骤名称的列,最后是包含指定数据集经过分析方法链处理后的结果的list-column。

类(res2) #> [1] "benchmark_tbl" "tbl_df" "tbl" "data.frame"

因为它们是小猫咪,所以反应很好dplyr动词,或者最规则的data.frame操作。

res2 %>% dplyr::filter(norm_method == "cpm") #> # A tibble: 2 × 4 #> data norm_method transform result #> #> 1 sample_10x cpm none  #> 2 sample_10x cpm log2 

4.6应用方法

将CellBench框架联系在一起的最后一个思想是apply_methods ()函数,取abenchmark_tbl并应用列表的功能。结果是,通过每个方法处理每一行,添加一个指定应用方法的新列,并将结果更新为新值。

# datasets datasets <- list(sample_10x = readRDS(cellbench_file("10x_sce_sample.rds"))) #管道中的第一套方法norm_method <- list(none = counts, cpm = function(x) t(t(1e6 * counts(x))) / colsum (counts(x)))) #管道转换中的第二套方法<- list(none = identity, log2 = function(x) log2(x+1)) datasets %>% apply_methods(norm_method) #> #一个tibble:2 x 3 #> data norm_method result #>    #> 1 sample_10x none  #> 2 sample_10x CPM 

apply_methods获取保存方法列表的变量的名称,并将其用作这些方法的列名,而列表中的方法名称则用作该列中的值。的数据列将存储数据集列表中名称的名称,但不继承持有数据集列表的变量的名称。

这种方式apply_methods写入意味着您可以简单地通过方法管道传输数据,而不保存任何中间结果。

datasets %>% apply_methods(norm_method) %>% apply_methods(transform) #> # A tibble: 4 × 4 #> data norm_method transform result #>     #> 1 sample_10x none none  #> 2 sample_10x none log2  #> #> 4 sample_10x cpm log2 

5高级用法

5.1多线程

方法的应用可以并行完成,这是通过设置CellBench使用的全局线程来完成的。如果应用的方法有自己的内部并行性,则此选项可能会导致冲突。如果有任何方法具有内部并行性,那么建议将CellBench设置为单线程模式。

谨慎:使用CellBench的多线程使用的内存比预期的要多得多,每个线程可以潜在地对环境中的所有数据进行完整的复制。在处理内存密集型任务时要注意这一点。

set_cellbench_threads(4)

5.2函数返回缓存

CellBench可以使用memoise缓存函数结果,以便调用具有相同参数的函数时,只需从本地缓存加载结果,而不是重复计算。由于CellBench调用函数的非典型方式(作为列表的成员),使用memoise在内存中缓存似乎不起作用,因此有必要在磁盘上缓存。

要在CellBench中使用函数返回值缓存,我们首先声明一个文件夹来存储我们的返回值,然后用它们的缓存版本替换常规方法。

请注意:缓存具有伪随机行为的方法意味着将从缓存中检索相同的结果,否定该方法的伪随机属性。这通常是不可取的。

谨慎:在多线程中使用缓存时要小心,如果一个函数的多个实例使用完全相同的参数运行,那么这些实例将试图同时写入缓存并破坏缓存。

谨慎:由于检索缓存结果只考虑函数调用签名和输入值,如果底层函数体被改变,那么CellBench将检索过时的结果。

谨慎:由于每个结果都保存到磁盘,因此要小心产生大量输出并需要在许多不同输入上运行的缓存函数。

set_cellbench_cache_path(".CellBenchCache") methods <- list(method1 = cache_method(method1), method2 = cache_method(method2))

函数缓存可以使用clear_cellbench_cache ().只有当缓存在清除缓存的同一会话中设置时,这才有效。否则,需要手动查找并删除缓存文件夹。

#清除同一会话中set_cellbench_cache_path()设置的缓存

5.3构造具有参数范围的函数

CellBench提供了一个辅助函数fn_arg_seq要创建具有不同参数值的函数列表,以便于搜索参数空间。

它以一个函数作为第一个参数,然后是参数值的向量和函数使用的参数的名称。返回一个函数列表,并使用vector中的每个值填充指定的参数。如果给出了多个参数向量,则每个参数值组合返回一个函数向量。

#是三个参数的函数f < - (x, y, z)函数{x + y + z} # f_list是一个列表的函数有两个参数预先填写f_list < - fn_arg_seq (f, y = 1:2, z = 3:4) f_list # > # 4部分功能列表> $ f (y = 1, z = 3) # > $ f (y = 2, z = 3) # > $ f (y = 1, z = 4) # > $ f (y = 2, z = 4)
名字(f_list)[1] # >[1]“f”(y = 1, z = 3) g < - f_list [[1]] g(10) # > 14名(f_list)[1][2] # >[1]“f”(y = 2, z = 3) h < - f_list h (20) # [[2]] > [1] 25

6总结

CellBench提供了一个轻量级且灵活的框架,用于处理具有多个步骤并导致方法应用的组合设计的基准测试。它使用简单透明的R对象,易于理解和操作,使用基本数据和函数列表结构作为输入。生成的表与流行的表兼容dplyr操作和一般鼓励干净的编码风格,易于理解,调试和扩展。