内容

1目标

1.1派对技巧

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

x < -  runif(5)x
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))]
0.8932159 0.5110514 0.2235770

整洁的,是吗?命令向量XO.是否可以以一种对原始数据来说可能比较困难的方式进行转换,例如寻找有序值之间的差异

差异(c(0,xo))[订单(order(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(实际上,10,000's)基因的“基因表达”。这个想法是“基线”表达遵循某种指数分配

set.seed(123)n_genes < -  1000基础< -  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 < -  RELHAPE :: MELT(表达)
##用作id变量
ggplot(df,aes(x =值,color = fings(变量)))+ geom_dentions()

少数假设,例如RNA-SEQ,是大多数基因的表达不变,并且分布之间的差异是由于技术伪影。在这种假设下,我们预计在上面的第二个数字中的分布是相同的。所以我们可以尝试正常化样品,以便分布每个样品看起来都差不多。分位数正常化是这种正常化的极端形式,并使分布完全相同。

  1. 按顺序放置每列

    x < - 表达式$ x xo < -  x [订购(x)] y < - 表达式$ y yo < -  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(order(x))], y = row_mean[order(order(y))])))

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

GGPLOT(归一化,AES(x,y))+ geom_point()

但是现在是相同的分布

DF < -  RELHAPE :: MELT(标准化)
##用作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 < - 应用(表达式,2,功能(v)v [order(v)])m
# # 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高效的

申请()是一个迭代- 每个元素都调用一次函数m。我们可以使用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相同”。检查输入是否符合合同,极大地简化了我们的功能,并通常为用户提供有用的指示事情总是以一种神秘的方式出错。所以看起来我们应该尽快验证函数的输入。

standile_normalize < -  function(x){##只要输入可以被调给到矩阵... x <--as.matrix(x)##并且可以验证以符合我们的合同...停止(是.nu​​meric(x),!Anyna(x))## ...我们有信心我们将满足返回值m < -  apply(x,2,function(v)v [stord(v)])row_mean <-  Rowmeans(m)应用(x,2,函数(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 ()为了计算表达式,但是一个稍微复杂一点的方法复制每个时间,以平均与我们的算法无关的差异。

图书馆(MicroBenchmark)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 < - 矩阵(RNORMES * 10000),NCOR = 10000)次< -  Microbenchmark(Smareile_Normalize(G10),SmileIre_Normalize(G100),Smianile_Normalize(G1000),Smientile_Normalize(G10000),时间= 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
绘图(次数,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限制和方向

代码中有几个明显的问题

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

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

图书馆(Tidyverse)
##已注册的S3方法被“r升”覆盖:##来自## read_xml.response XML2的方法
# #──附加包────────────────────────────────────tidyverse 1.2.1──
## ## 1.3.1 - 1 - 1 ## # 2 - 1
# #──冲突───────────────────────────────────────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(变量)%mutate(o = Order(Value))整洁
# # #一个宠物猫: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"
## # A tibble: 8 x 4 ## # Groups: variable [2] ## variable value o m ##     ## 1 x 1 1 1.5 ## 2 x 2 2 2.5 ## 3 x 4 3 4.5 ## 4 x 7 4 7.5 ## 5 y 3 2 2.5 ## 6 y 2 1 1.5 ## 7 y 5 3 4.5 ## 8 y 8 4 7.5

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

tidy_quantile_normalize < - 函数(tbl){##需要验证... ## stantile归一化整理(变量)%group_by(变量)%mutate(o = order(value))stantile_mean < - 整数%>%group_by(o)%>%概述(m =均值))left_join(整洁,stantile_mean)%>%##准备结果选择(变量,值= m)%>%ungroup()}

实际上,我们做到了

TBL <- melt(expression) %>% as_tibble() tidy_quantile_normalize(TBL)
###缩放:8 x 2 ##变量##   ## 1 x 1.5 ## 2 x 2.5 ## 3 x 4.5 ## 4 x 7.5 ## 5 y 2.5 ## 6 y1.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