内容

1目标

1.1党技巧

一个有趣的派对诀窍是拍摄矢量并使用它订单()

x < -  runif(5)x
## [1] 0.7809753 0.5210077 0.8932159 0.5110514 0.2235770
Xo <- x[order(x)] Xo
## [1] 0.2235770 0.5110514 0.5210077 0.7809753 0.8932159

原始订单可以通过应用来检索订单()两次,用结果来排序xo

xo [订单(订单(x))]
## [1] 0.7809753 0.5210077 0.8932159 0.5110514 0.2235770

整洁的,是吗?命令向量xo可以以可能难以原始数据的方式转换,例如在有序值之间找到差异

diff (c (0, xo))(订单(订单(x)))
## [1] 0.259967575 0.009956273 0.112240639 0.287474455 0.223576978

或将数值转换为字母,使的最小值x给出的是第一个字母等。

字母[SEQ_ALONG(XO)] [订购(ORDER(x))]
## [1] " d " " c " " e " " b " " a "

1.2分位数正常化

创建一个矩阵,用两列表示两个样本中100个(实际上是10000个)基因的“基因表达”。这个想法是,“基线”表达式遵循某种指数分布

n_genes <- 1000 base <- 2 + rexp(n_genes)

并且在每个样本中观察到的表达是基线加上一些(通常分布的)误差。以下假设测量误差的尺寸随着基因表达的增加而降低;样本y是否(例如,由于样本处理)一个持续比样本强的信号x

表达式< -  data.frame(x =碱基+ rnorm(n_genes,0.3)/碱基,y = 1.1 *底座+ rnorm(n_genes,0.4)/底座)

将两个样本中每个基因的表达可视化…

库(GGPLOT2)GGPLOT(表达式,AES(x,y))+ geom_point()

以及所有基因中表达值的分布

df < -重塑::融化(表达式)
##使用id变量
ggplot(df,aes(x =值,color = fings(变量)))+ geom_dentions()

一个常见的假设是,例如RNA-seq,大多数基因的表达是不变的,而分布之间的差异是由于技术因素造成的。在这个假设下,我们期望上面第二幅图中的分布是相同的。所以我们可以试着正常化样品,以便分布每个样本看起来都是一样的。分位数正常化是这种正常化的极端形式,并使分布完全相同的。

  1. 将每一列按顺序排列

    y <- expression$y y <- y[order(y)]

    对于100的样本,x [1]是第一个量级,x [2]第二,等等。

  2. 计算每个分位数的平均值(或更强大,例如,中位数)统计。我会通过使用cbind ()创建一个两列矩阵,然后应用()遍历每一行来计算平均值

    <- cbind(xo, yo) row_mean <- apply(Mo, 1, mean)
  3. 使用原始向量中每个分位数的平均值

    归一化< -  data.frame(x = row_mean [订单(order(x))],y = row_mean [订单(订单(y))])

可视化标准化数据——两个样本中表达式之间相同的整体关系

Ggplot (normalized, aes(x, y)) + geom_point()

但是现在是相同的分布

df < -重塑:融化(归一化)
##使用id变量
ggplot(df,aes(x =值,color = fings(变量)))+ geom_dentions()

2从一次性脚本到可重用功能

2.1功能

我们可以编写一个脚本,每次我们想要使用它时进行复制/粘贴

x < - 表达式$ x xo < -  x [order(x)] y < - 表达式$ y yo < -  Y [ORDER(Y)] MO < -  CBIND(XO,YO)ROW_MEAN < -  MO,1,平均值)归一化< -  data.frame(x = row_mean [订单(order(x))],y = row_mean [订单(订单(y))])

最好是写一个函数

standile_normalize <函数(表达式){x < - 表达式$ x xo < -  x [order(x)] y < - 表达式$ y yo < -  Y [ORDER(Y)] MO < -  CBIND(XO,YO)row_mean< - 应用(mo,1,均值)data.frame(x = row_mean [订单(order(x))],y = row_mean [订单(order(y))])}

用在不同的数据集上。

标准化< -  standile_normalize(表达式)

2.2测试

我们将对函数进行更新,使其更高效、更通用、更健壮,但我们希望对函数的工作状态有一定的信心。因此,我们将制作一个更小的数据集,我们可以“手工”验证并用作测试用例。

表达式< -  data.frame(x = c(1,2,4,7),y = c(3,2,5,8))smileile_normalize(表达式)
x y # # 1 # # # # 2 2.5 - 1.5 1.5 - 2.5 7.5 - 7.5 4.5 - 4.5 # # 3 # # 4

我们可以使用一个叫做“单元测试”的正式概念来检查我们的代码是否正确

库(testthat) test_that("quantile_normalize() works", {## calculate simple example 'by hand'…x < - c(1、2、4、7)y < - c(3、2、5、8)xo < - x (x))[命令你< - y[秩序(y)]莫< -应用(cbind (xo,哟),1,意味着)预期< - data.frame (x =莫(订单(订单(x))), y =莫(订单(订单(y)))) # #比较函数表达式的结果< - data.frame (x = x, y = y)观察到< - quantile_normalize(表达式)expect_equal(观察,期望)#其他可能的期望})

3.多一点……

3.1一般

我们的函数假设有两个样本,并且这两个样本都有标记xy。让我们试着用应用()要对任意数量的列进行排序,

M <- apply(expression, 2, function(v) v[order(v)])
# # x y # # [1] 1 2 # # [2] 2 3 # # [3] 4 5 # # 7 8 [4]

并应用同样的逻辑来重建量级

standile_normalize < -  function(x){m < -  apply(x,2,function(v)v [ORDER(v)])row_mean < - 应用(m,1,均值)应用(x,2,function(v,row_mean)row_mean [订购(order(v))],row_mean)}

我们的单元测试将失败,因为原始函数返回一个data.frame,而当前函数返回一个矩阵。让我们更新单元测试(我们也可以在返回之前将矩阵强制转换为data.frame)quantile_normalize ())。

test_that(“standile_normalize()works”,{x < -  c(1,2,4,7)y < -  c(3,2,5,8)xo < -  x [order(x)] yo < -  y[订购(y)] mo < -  apply(cbind(xo,yo),1,平均值)预期< -  cbind(x = mo [订单(ourser(x))],y = mo [订单(订单(y))])表达式< -  data.frame(x = x,y = y)观察到< -  standile_normalize(表达式)预期(观察到,预期)})

3.2非常高效。

应用()是一个迭代的每个元素调用一次函数。我们可以使用rowMeans (),它是矢量化的,并仅为整个数据进行一个函数调用。这在r中更有效,因此让我们尝试修改我们的功能

standile_normalize < -  function(x){m < -  apply(x,2,function(v)v [Order(v)])row_mean < -  rowmeans(m)应用(x,2,函数(v,row_mean)row_mean [订单(订单(v))],row_mean)}

我们的单元测试仍然有效

test_that(“standile_normalize()works”,{x < -  c(1,2,4,7)y < -  c(3,2,5,8)xo < -  x [order(x)] yo < -  y[订购(y)] mo < -  apply(cbind(xo,yo),1,平均值)预期< -  cbind(x = mo [订单(ourser(x))],y = mo [订单(订单(y))])表达式< -  data.frame(x = x,y = y)观察到< -  standile_normalize(表达式)预期(观察到,预期)})

所以我们有信心在不改变结果的情况下改变了实施。

3.3健壮的

好的函数提供类似契约的东西——如果提供了特定类型的参数,那么函数将返回特定类型的值。在我们的例子中,我们可以把契约看作是“如果你为我提供一个包含所有数字列的data.frame,或者一个数字矩阵,我将为你提供一个分位数标准化列的矩阵”。契约可能会有额外的约束,例如“输入不能有NA值”或“输入的dimnames与输出的dimnames相同”。检查输入是否符合合同,极大地简化了我们的功能,并通常为用户提供有用的指示事情总是以一种神秘的方式出错。所以看起来我们应该尽快验证函数的输入。

<- function(x){##只要输入可以被强制为一个矩阵…X <- as.matrix(X) ##,可以验证符合我们的合同…stopifnot(is.numeric(x), !anyNA(x)) ##…我们有信心我们将满足返回值m <- apply(x, 2, function(v) v[order(v)])) row_mean <- rowMeans(m) apply(x, 2, function(v, mo) mo[order(v))], mo)}

我们可以检查我们的原始单元测试是否满意,但也添加了标记测试......

test_that(“quantile_normalize()的验证输入",{m < - cbind(字母,字母)expect_error (quantile_normalize (m)) df < - data.frame (x = rnorm (26), y =字母)expect_error (quantile_normalize (df)) m < -矩阵(rnorm (10), nrow = 5) m [1] < - NA expect_error (quantile_normalize (m))})

看起来我们的函数应该在原始对象上保留dimnames。这个函数需要修改,我们可以编写一个新的单元测试

##验证输入x <- as.matrix(x) stopifnot(is.numeric(x), ! anya (x)) ## quantile normalization m <- apply(x, 2, function(v) v[order(v)])) row_mean <- rowMeans(m) result <- apply(x, 2, function(v, row_mean) row_mean[order(v))], row_mean) ## propagate dimnames dimnames(result) <- dimnames(x) result}
test_that("'quantile_normalize()' propagates dimnames", {m <- matrix(rnorm(10), 5, dimnames=list(LETTERS[1:5], LETTERS[1:2])) observed <- quantile_normalize(m) expect_identical(dimnames(observed), dimnames(m))})

我们还可以检查我们的函数对于不太常见的情况是否健壮,比如0行和/或0列的输入。这些测试导致额外的修改-应用()不总是返回一个矩阵!

standile_normalize < -  function(x){##验证输入x <--as.matrix(x)stopifnot(是numeric(x),!Anyna(x))## stantile normalize m < -  apply(x,2,function(v)v [订单(v)])dim(m)< -  dim(x)#应用()并不总是返回矩阵!row_mean < -  reammeans(m)结果< -  apply(x,2,函数(v,row_mean)row_mean [订单(v))],row_mean)dim(结果)< -  dim(x)##传播Dimmnes Dimmnames(结果)< -  dimmames(x)结果}
test_that(“quantile_normalize()的作品与边界情况”,{m < -矩阵(rnorm (5), nrow = 5) expect_identical (m, quantile_normalize (m)) m < -矩阵(rnorm (5), ncol = 5) expect_identical(矩阵(意味着(m), ncol = 5), quantile_normalize (m)) m < -矩阵(0,0,0)expect_identical (m, quantile_normalize (m))})

4表现

我们希望了解我们的算法如何在各种大小的问题中执行。我们可以使用system.time ()为了计算表达式,但是一个稍微复杂一点的方法复制每个时间,以平均与我们的算法无关的差异。

库(微基准测试)n_genes < - 10000 g10 < -矩阵(rnorm (n_genes * 10), ncol = 10) g100 < -矩阵(rnorm (n_genes * 100), ncol = 100) g1000 < -矩阵(rnorm (n_genes * 1000), ncol = 1000) g10000 < -矩阵(rnorm (n_genes * 10000), ncol = 10000) * < -微基准测试(quantile_normalize (g10) quantile_normalize (g100) quantile_normalize (g1000),Quantile_normalize (g10000), times = 10)次
# #单元:# expr min lq mean median ## quantile_normalize(g10) 13.62116 16.41978 35.16609 20.94962 # quantile_normalize(g100) 141.18307 158.67358 209.78565 167.93833 ## quantile_normalize(g1000) 1443.09392 1620.43055 2649.53222 2817.20918 ## quantile_normalize(g10000) 18012.71678 20354.2486 22825.75161 21764.57106 ## uq Max neval ## 26.83651138.9633 10 ## 241.73374 394.6181 10 ## 3298.01893 4772.0140 10 ## 26625.01458 27220.2120 10
Plot (times, log = "y")

结果表明,使用样本的数量线性执行算法的时间。甚至1000个样本只需要3.5秒以正常化。我们的实施对于许多目的,我们的实施是“足够快”。

Rprof ()可以用来查看我们的算法在哪里花费时间

rprof()#开始分析结果< -  standile_normalize(g10000)rprof(null)#停止分析summumentrprof()$ by.total

输出如下所示

总计总时间。pct的自我。时间的自我。pct“quantile_normalize”32.30 100.00 0.00 0.00“应用”31.92 98.82 2.92 9.04“有趣”23.42 72.51 6.22 19.26“秩序”“aperm.default”2.46 7.62 2.46 51.08 17.20 53.25 16.50 7.62“aperm”2.46 7.62 0.00 0.00“unlist”“数组”1.32 4.09 1.32 5.57 1.80 5.57 1.80 4.09 0.34 1.05 0.24 0.74“vapp匹配。arg" 0.34 1.05 0.16 0.50 "rowMeans" 0.22 0.68 0.22 0.68 "anyNA" 0.16 0.50 0.16 0.50 "…Elt“0.16 0.50 0.00 0.00”stopifnot“0.16 0.50 0.00 0.00”正式“0.10 0.31 0.02 0.06”匹配。Fun " 0.08 0.25 0.08 0.25 "eval" 0.08 0.25 0.04 0.12 "系统。命令功能" 0.08 0.25 0.02 0.06 "sys。父“0.06 0.19 0.06 0.19”all“0.02 0.06 0.02 0.06”c“0.02 0.06 0.02 0.06”为。列表“0.02 0.06 0.02 0.06”逻辑“0.02 0.06 0.02 0.06

从上到下的顺序表示“在”每个函数中大约花费的时间。注意里面有很大的不同订单(),与内部aperm ()(我们的代码不使用aperm (),但是我们的代码呼叫的一些代码是......)和self.pct订单()。这表明大约50%的时间用于执行订单(),如果我们能找到避免打电话的方法订单(),或称订单()更有效地(例如,一次,而不是10000次),我们的算法可能会提高速度。这样的聪明通常会增加代码的复杂性,在我们的情况下,追求更快的执行时间没有真正的价值。

5限制和方向

代码中有几个明显的问题

如果它足够重要,这些领域中的每一个都可以成为更详细的努力的主题。

我们的方法使用“base”R功能。一般来说,这需要相当高水平的理解和抽象思维,例如关于应用()而且它是乐趣论点。我们还必须考虑不同的数据结构 - data.frame,矩阵,矢量,......。不同且最近流行的方法是使用“整理”数据。该想法是始终旨在旨在为单一类型的测量(例如,基因表达)占用单个列,其他列用于将数据分组为组;这% > %是一个'管道',它取出左侧的输出并将其用作右侧的输入

库(tidyverse)
##注册的S3方法被'rvest'覆盖:##方法从## read_xml。响应xml2
# #──附加包────────────────────────────────────tidyverse 1.2.1──
##✔Tibble 2.1.1✔UTRR0.3.2 ##✔Tidyr0.8.3✔dipl0.8.0.1 ##✔readr 1.3.1✔stringr 1.4.0 ##✔tibble 2.1.1✔forcats 0.4.0
# #──冲突───────────────────────────────────────tidyverse_conflicts()──# #✖dplyr:过滤()面具统计数据::过滤器()# #✖purrr:: is_null()面具testthat:: is_null() # #✖dplyr:滞后()面具统计数据::滞后()# #✖dplyr:匹配()面具testthat::匹配()
图书馆(重塑)
## ##附加包:“重塑”
##以下对象被“package:dplyr”屏蔽:## ## rename
##以下对象从“包:Tidyr”屏蔽:## ##扩展,史密斯
表达式
## x y ## 1 1 3 ## 2 2 2 ## 4 5 ## 4 7 8
Tidy <- melt(表达式)%>% as_tibble()
##使用id变量
整齐的
1 x 1 ## 2 x 2 ## 3 x 4 ## 4 x 7 ## 5 y 3 ## 6 y 2 ## 7 y 5 ## 8 y 8

简洁的分析强调了一些具有非常刻板行为的“动词”,总是以长形式返回一个“咬”。所以我们可以使用group_by变异()更新我们的资源描述用列表示订单()我们的标准化步骤

group_by(variable) %>% mutate(o = order(value)) Tidy
# # #一个宠物猫:8 x 3 # # #组:变量[2]# # # #阿变量值< fct > <双> < int > # # 1 x 1 1 # # 2 x 2 2 # # 3 x 4 3 # # 4 x 7 # # 5 3 2 # # 6 y 2 1 # # 7 y 5 3 # # 8 y 8 4

分位数可以分组计算概括()

standile_mean < -  tidy%>%group_by(o)%>%sumparize(m =均值))stantile_mean
## # A tibble: 4 x 2 ## m ##   ## 1 1 1.5 ## 2 2.5 ## 3 3 4.5 ## 4 4 7.5

行意味着可以连接到我们的整洁数据,而连接(使用公共列o加入这两种食物)

quantile_mean left_join(整洁)
##连接,by = "o"
# # #一个宠物猫:8 x 4 # # #组:变量[2]# #变量值o m # # < fct > <双> < int > <双> # 1.5 # 1 x 1 1 # # 2.5 # # 3 x 2 x 2 2 4 3 4.5 # # 4 x 7 4 7.5 # # 5 3 2 2.5 # # 6 y 2 1 4.5 1.5 # # 7 y 5 3 # 7.5 # 8 y 8 4

这使我们在列中提供了我们的正常化数据!!作为一个函数,我们可能有

Tidy_quantile_normalize <- function(tbl){##需要验证…## quantile normalize tidy <- tidy %>% group_by(variable) %>% mutate(o = order(value)) quantile_mean <- tidy %>% group_by(o) %>% summarize(m = mean(value)) left_join(tidy, quantile_mean) %>% ## prepare result select(variable, value = m) %>% ungroup()}

实际上,我们做到了

TBL <- melt(expression) %>% as_tibble() tidy_quantile_normalize(TBL)
## # 2 ## 2 ## 3 # 4.5 # 4 # 7.5 # 5 y 2.5 # 6 y 1.5 ## 7 y 4.5 # 8 y 7.5

会话信息

## BLAS: /Users/ka36530_ca/R-stuff/bin/R-devel/lib/libRblas. php: x86_64-apple-darwin17.7.0(64位)/ user /ka36530_ca/R-stuff/bin/R-devel/lib/libRlapack。dylib ## ##随机数生成:## RNG: mersene - twister ## Normal: Inversion ## Sample:舍入## ## locale: ## [1] en_US. utf -8/en_US. utf -8/C/en_US. utf -8/en_US. utf -8。UTF-8 ## ## attached base packages: ## [1] stats graphics grDevices utils datasets methods base ## #### [10] microbenchmark_1.4-6 testthat_2.0.1 ggplot2_3.1.1 ## [13] BiocStyle_2.11.0 ## ##通过命名空间加载(并没有附加):# # # # [1] tidyselect_0.2.5 xfun_0.6 haven_2.1.0 [4] lattice_0.20-38 colorspace_1.4-1 generics_0.0.2 # # [7] htmltools_0.3.6 yaml_2.2.0 utf8_1.1.4 # # [10] rlang_0.3.4 pillar_1.3.1 glue_1.3.1 # # [13] withr_2.1.2 modelr_0.1.4 readxl_1.3.1 # # [16] plyr_1.8.4 munsell_0.5.0 gtable_0.3.0 # # [19] cellranger_1.1.0 rvest_0.3.3 codetools_0.2-16 # # [22]evaluate_0.13 knitr_1.22 fansi_0.4.0 ## [25] broom_0.5.2 Rcpp_1.0.1 scales_1.0.0 ## [28] backports_1.1.4 BiocManager_1.30.4 jsonlite_1.6 ## [31] hms_0.4.2 digest_0.6.18 stringi_1.4.3 # [34] bookdown_0.9 grid_3.7.0 cli_1.1.0 # [37] tools_3.7.0 magrittr_1.5 lazyeval_0.2.2 ## [40] crayon_1.3.4 pkgconfig_2.0.2 xml2_1.2.0 # [43] lubridate_1.7.4assertthat_0.2.1 rmarkdown_1.12 ## [46] httr_1.4.0 rstudioapi_0.10 R6_2.4.0 ## [49] nlme_1 .1-139 compiler_3.7.0