中级R用户的技能

CSAMA 2014
作者:Martin Morgan (mtmorgan@fhcrc.org.)
日期:2014年6月23日

内容

复习:处理数据

案例研究:所有表型数据

本案例研究服务器作为基本输入和数据操作的复习。

输入一个包含ALL(急性淋巴细胞白血病)患者信息的文件

fname < -  file.choose()##“allphenodata.tsv”stopifnot(file.exists(fname))pdata < -  read.delim(fname)

查看帮助页面?read.delim对于输入选项,并探索你创建的对象的基本属性,例如…

类(pdata)
## [1]“data.frame”
colnames (pdata)
# #[1]“id”“诊断”“性”“年龄”# #[5]“缓解”“CR”“BT日期。t.4.11。"“t.9.22。””阶段。n或mal" "citog" ## [13] "mol.biol" "fusion.protein" "mdr" "kinet" ## [17] "ccr" "relapse" "transplant" "f.u" ## [21] "date.last.seen"
暗(pdata)
21 .【答案】b
头(pdata)
## ID诊断性别时代BT REFISCE CR DATE.CR T.4.11。T.9.22。## 1 1005 5/21/1997 M 53 B2 CR CR 8/6/1997 FALSE TRUE ## 2 1010 3/29/2000 M 19 B2 CR CR 6/27/2000 FALSE FALSE ## 3 3002 6/24 /1998 F 52 B4 CR CR 8/17/1998 NA NA ## 4 4006 7/17/1997 M 38 B1 CR CR 9/8/1997真假## 5 4007 7/22/1997 M 57 B2 CR CR 9 /17/1997假假## 4008 7/30/197 M 17 B1 CR CR 9/27/1997假假## Cyto.Normal Citog Mol.Biol Fusion.Protein MDR Kinet CCR ## 1假T(9; 22)BCR / ABL P210 NEG Dypoid False ## 2错误简单Alt。neg  pos dypoid false ## 3 na  bcr / abl p190 neg dypoid false ## 4 false t(4; 11)All1 / AF4  Neg Dypoid False ## 5假del(6q)neg  ncon dypoid false ## 6误复杂Alt。neg  neg hyperd。FALSE ## relapse transplant f.u date.last.seen ## 1 FALSE TRUE BMT / DEATH IN CR  ## 2 TRUE FALSE REL 8/28/2000 ## 3 TRUE FALSE REL 10/15/1999 ## 4 TRUE FALSE REL 1/23/1998 ## 5 TRUE FALSE REL 11/4/1997 ## 6 TRUE FALSE REL 12/15/1997
总结(pdata性美元)
## f m na's ## 42 83 2
摘要(pdata $ cy.normal)
## Mode FALSE TRUE NA的## logical 69 24 34

提醒自己关于子集和访问data.frame列的各种方法

pdata [1:5,3:4]
##性爱时代## 1 M 53 ## 2 M 19 ## 3 F 52 ## 4 M 38 ## 5 M 57
pdata [1:5]
## ID诊断性别时代BT REFISCE CR DATE.CR T.4.11。T.9.22。## 1 1005 5/21/1997 M 53 B2 CR CR 8/6/1997 FALSE TRUE ## 2 1010 3/29/2000 M 19 B2 CR CR 6/27/2000 FALSE FALSE ## 3 3002 6/24 /1998 F 52 B4 CR CR 8/17/1998 NA NA ## 4 4006 7/17/1997 M 38 B1 CR CR 9/8/1997真假## 5 4007 7/22/1997 M 57 B2 CR CR 9 /17/1997错误假## cyto.normal citog mol.biol fusion.prictin mdr电池ccr ## 1 false t(9; 22)bcr / abl p210 neg dypoid false ## 2假简单的Alt。neg  pos dypoid false ## 3 na  bcr / abl p190 neg dypoid false ## 4 false t(4; 11)All1 / AF4  Neg Dypoid False ## 5假del(6q)neg  NEG dyploid FALSE ## relapse transplant f.u date.last.seen ## 1 FALSE TRUE BMT / DEATH IN CR  ## 2 TRUE FALSE REL 8/28/2000 ## 3 TRUE FALSE REL 10/15/1999 ## 4 TRUE FALSE REL 1/23/1998 ## 5 TRUE FALSE REL 11/4/1997
头(pdata [3:5])
# #性别年龄BT # # 1米53 B2 # # 2米19 B2 # # 3 F 52 B4 # # 4 M 38 B1 # # 5米57 B2 # # 6米17 B1
尾(pdata [3:5] 3)
BT # # 125 # #性别年龄19 T2 30 T3 # # # # 126 29 127 T2
头(pdata时代美元)
## [1] 53 19 52 38 57 17
头(pdata性美元)
## [1] M M F M M M M ##级别:F M
头(pdata [pdata $ > 21岁])
## ID诊断性别时代BT REFISCE CR DATE.CR T.4.11。T.9.22。## 1 1005 5/21/1997 M 53 B2 CR CR 8/6/1997 FALSE ## 3 3002 6/24/1998 F 52 B4 CR CR 8/17/1998 NA NA ## 4006 7/17 /1997 M 38 B1 CR 9/8/1997真假## 5 4007 7/22/1997 M 57 B2 CR CR 9/1997假假## 10 8001 1/15/1997 M 40 B2 CR CR 3 /26/1997假假## 11 8011 8/21/1998 M 33 B3 Cr Cr 10/8/1998假假## cyto.normal citog mol.biol fusion.protein mdr kinet ccr ## 1假t(9; 22)BCR / ABL P210 NEC Dypoid FALSE ## 3 NA  BCR / ABL P190 NEG Dypoid FALSE ## 4假T(4; 11)ALL1 / AF4  NEG DYPOID FALSE ## 5假DEL(6Q)NEG NEG dyploid FALSE ## 10 FALSE del(p15) BCR/ABL p190 NEG  FALSE ## 11 FALSE del(p15/p16) BCR/ABL p190/p210 NEG dyploid FALSE ## relapse transplant f.u date.last.seen ## 1 FALSE TRUE BMT / DEATH IN CR  ## 3 TRUE FALSE REL 10/15/1999 ## 4 TRUE FALSE REL 1/23/1998 ## 5 TRUE FALSE REL 11/4/1997 ## 10 TRUE FALSE REL 7/11/1997 ## 11 FALSE TRUE BMT / DEATH IN CR 

从下面似乎有17例在数据集中超过40个,但在子设置时pdata.为了只包含这些个体,选择了19行。为什么?我们能做些什么来纠正这一点呢?

$sex == "F" & pdata$age > 40 table(idx)
## idx ## false true ## 108 17
昏暗(pdata [idx,])
## [1] 19 21

使用mol.biol列将数据子集中用于包含“BCR / ABL”或“NEG”的个人,例如,

bcrabl < - pdata [pdata $摩尔。% c("BCR/ABL", "NEG"),]

mol.biol列是一个因素,即使在子集之后也保留所有级别。您如何降低未使用的因子级别?

bcrabl美元摩尔。杂志< -因子(bcrabl mol.biol美元)

英国电信柱是描述B-和T细胞亚型的因素

水平(bcrabl BT美元)
# #[1]“B”“B1”“B2”“B3”“B4”“T”“T1”“T2”“T3”“T4”

如何将B1 B2。。分解为单一的B类型,同理,T1 T2。。所以只有两个子类型,B和T

表(bcrabl BT美元)
## ## b b1 b2 b3 b4 t t1 t2 t3 t4 ## 4 9 35 22 9 4 1 15 9 2
级别(bcrabl $ bt)< -  substring(级别(bcrabl $ bt),1,1)表(bcrabl $ bt)
#### B T#79 31

使用xtabs ()(交叉表格)将BCR / ABL和NEG组中的B和T细胞类型计算样本数量

xtabs(~ BT + mol.biol, bcrabl)
## BCR/ABL NEG ## b37 42 ## T 0 31

使用总计的()计算BCR/ABL和NEG治疗组男女平均年龄。

总数(年龄~ mol.biol +性别,bcrabl,平均)
## mol.biol性爱时代## 1 bcr / abl f 39.94 ## 2 neg f 30.42 ## 3 bcr / abl m 40.50 ## 4 neg m 27.21

使用t.test()比较BCR/ABL组和NEG组的年龄;使用以下方法将结果可视化箱形图()。在这两种情况下,使用公式接口。参考帮助页面t.test ?假设两组中的年龄的变化是相同的,并且重新进行测试。测试输出的哪些部分变化?

T.Test(年龄〜mol.biol,bcrabl)
## ## Welch两个样本t-test ## ##数据:摩尔## T = 4.817,df = 68.53,p值= 8.401e-06 ##替代假设:意味着的真实差异不是等于0 ## 95%置信区间:## 7.135 17.224 ##样本估计:##在BCR / ABL中的均值在Group Neg ## 40.25 28.07
Boxplot(年龄〜mol.biol,bcrabl)

块状的情节

案例研究:重要的事情

本案例研究是对基本数据操作和可视化技能的第二次演练。我们使用的数据来自美国疾病控制中心的行为风险因素监测系统(BRFSS.)年度调查。查看网页了解更多信息。我们正在使用该数据的一小部分,包括来自1990年和2010年的每次10000观测的随机样本。

使用输入数据read.csv(),创建一个变量brfss持有它。使用file.choose ()定位数据文件BRFSS-subset.csv

fname < -  file.choose()## brfss-subset.csv stopifnot(file.exists(fname))brfss < -  read.csv(fname)

练习:基本绘图函数

  1. 探索数据使用类(),暗淡(),头(),概括()等等。使用xtabs ()总结一下研究中的男性和女性的数量,两年中的每一年。

  2. 使用总计的()总结每年的平均重量。

  3. 创建显示体重和身高的平方根之间关系的散点图,使用阴谋()函数和主要诠释绘图的论点。注意变换的y轴。实验不同的绘图符号(尝试命令示例(分)查看不同的点)。

    绘图(SQRT(重量)〜高度,BRFS,Main =“全年,两性”)

    块brfss-simple图

  4. 女性和男性的颜色不同。要做到这一点,使用上校争论阴谋()。提供对该参数的价值,颜色矢量颜色,子集美元brfss性

  5. 创建一个只包含2010年观察数据的子集。

    BRFSS2010 < -  BRFSS [BRFSS $年==“2010”,]
  6. 创建下图(一个图中包含两个面板)。使用票面价值()用来的功能mfcol论点之前调用阴谋()。可能在向函数提供数据时,还需要创建另外两个数据子集阴谋

    OPAR < -  PAR(MFCOL = C(1,2))绘图(SQRT(重量)〜高度,BRFSS2010 [BRFSS2010 $性==“女性”,],MAIN =“2010,女性”)绘图(SQRT(重量)〜Height,BRFSS2010 [BRFSS2010 $性==“男性”,],MAIN =“2010,男性”)

    块的块BRFSS-对图

    票面价值(mfcol = c (1,1))
  7. 绘制大量点意味着它们通常过度绘制,可能模糊了重要的模式。对争论进行实验阴谋()解决过度策划的问题,例如:pch ='。'orgydF4y2Baα=。4。试着使用smoothScatter ()函数(数据必须以xy,而不是配方)。尝试添加hexbin库到你的R会话(使用图书馆()),并创建Hexbinplot()

练习:格子图形和晶格包裹

R有许多附加的绘图工具,它们都是“基础”分发包和用户贡献包的一部分。的晶格封装采用特定的哲学对数据的演示,并且可以是一个非常有效的可视化工具。

  1. 使用图书馆()加载晶格包中。

    库(晶格)
  2. 使用以下内容创建下图xyplot ()函数与公式BRFSS2010数据。公式是sqrt(体重)~身高|性别,这可以解读为体重与身高的平方根,条件是性别。

    XYPLOT(SQRT(重量)〜高度|性爱,BRFSS2010)

    块格条件作用图

  3. 使用参数向每个面板添加背景网格和回归线type = c('p','g','r');更改宽度(lwd)颜色(col.line.)回归线。对于其他参数的一些提示,请参阅帮助页面xyplot ?(用于情节的整体结构)和panel.xyplot ?(每个“面板”数据是如何绘制的)。

  4. 创建下图。使用densityplot ()功能与公式~ sqrt(重量)。的组=性函数参数为每个性别创建单独的行。一定要使用plot.points = FALSE避免在图的基础上的点数。你能添加一个密钥(图例)吗?

    密度图(~sqrt(Weight), brfss2010, group=Sex, plot.points=FALSE)

    块格密度图

  5. 属性创建下面的图bwplot函数。公式要求一年被强迫因素,因子(年)

    bwplot(sqrt(Weight) ~ factor(Year) | Sex, brfss)

    块状格子Bwplot的情节

  6. 创建下图,a小提琴情节,使用bwplot()面板参数设置为panel.violin。从?BWPLOT.我们了解到,面板是确定每个面板如何绘制的函数;在帮助页面上描述了控制小提琴面板图的详细信息?panel.violin.

    BWPLOT(SQRT(重量)〜因子(年)|性别,BRFSS,Panel = Panel.Violin)

    块格小提琴图

  7. (高级)我们可以写我们自己的面板争论晶格影响每个面板显示方式的函数。这里我们在年龄和体重的中位数上加了一个点。

    XYPLOT(SQRT(重量)〜高度|性爱,BRFSS2010,Panel =函数(x,y,...){buare.xyplot(x,y,...)panel.points(中位数(x,na.rm =真实),中位数(y,na.rm = true),cex = 2,pch = 20,col =“红色”)},type = c(“p”,“g”,“r”),lwd = 2,col.line =“红色”,xlim = c(120,210))

    块格面板图

取得进展:类、方法和包

本节侧重于类,方法和包,目标是学习浏览帮助系统和互动发现设施。

动机

序列分析专门化

额外的注意事项

解决方案:使用定义良好的代表复杂数据;方法操作类来执行有用的函数。类和方法放在一起并作为这样我们都可以从艰苦的工作和测试代码中获益。

案例研究:IRanges和GRanges

IRangesPackage定义了一个用于指定整数范围的重要类,例如:

图书馆(绞喉)
##加载所需包:parallel ## ##附加包:'BiocGenerics' ## ##以下对象被'package:parallel'屏蔽:## ## clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, ## clusterExport, clusterMap, parApply, parCapply, parApply, ## parapplylb, parRapply, parSapply, parSapplyLB ## ##以下对象被'package:stats'屏蔽:## ## xtabs ## ##以下对象被'package:base'屏蔽:## ## Filter, Find, Map, Position, Reduce, anyduplicate, append, ## as.data.frame, as。向量,cbind,冒号,do。Call, ## duplicate, eval, evalq, get, intersect, is。Unsorted, lapply, ## mapply, match, mget, order, paste, pmax, pmax.int, pmin, ## pmin.int, rank, rbind, rep.int, rownames, sapply, setdiff, ## sort, table, tapply, union, unique, unlist
ir <- IRanges(start=c(10,20,30), width=5
## [1] 10 14 5 ## [2] 20 24 5 ## [3] 30 34

在范围内有许多有趣的操作,例如,侧翼()标识相邻的范围

侧面(ir, 3)
## [1] 7 9 3 ## [2] 17 19 3 ## [3] 27 29 3

咨询侧翼的帮助页面,?旁边,并探索其他基于范围的操作。

IRanges类是类层次结构的一部分。要看这个,问R类IR.,以及课堂定义IRanges班级

类(红外)
# #[1]“IRanges”# # attr(“包”)# #[1]“IRanges”
getClassDef(类(ir))
##类“讽刺”[包“讽刺”] ## ##插槽:## ##名称:开始宽度名称元素##类:整数整数字符符号## ##名称:ElementMetadata元数据##类:DataTableDATA列表####扩展:##类“范围”,直接##类“Integerlist”,按类别“范围”,距离2 ##类“范围”,按类别“范围”,距离2 ##类“原子列表”,按类别“范围”,距离3 ##类“列表”,按类别“范围”,距离4 ##类“向量”,按类别“范围”,距离5 ##类“注释”,按类“范围”范围“,距离6 ######已知的子类:”普通Arranges“

请注意,IRanges扩展了范围班级。现在尝试进入?“侧翼,,在那里意味着按Tab键询问选项卡完成(可能在RStudio中可能不需要)。您可以看到多个不同类别有帮助页面。标签完成

?”旁边,Ranges-method”

并验证您是否在描述与其相关的方法的页面上IRanges实例。

GenomicRanges包装扩展了范围的概念,包括在序列分析中施加与范围的应用相关的特征,特别是将范围与序列名称(例如,染色体)和股线相关联的能力。创建一个农庄实例,基于IRanges实例,如下

库(GenomicRanges)
##加载所需包:Genomeinfodb
gr < -农庄(c(“chr1”、“chr1”,“chr2”),红外光谱、链= c ("+", "-", "+")) gr
## GRanges有3个范围和0个元数据列:## seqnames ranges strand ##    ## [1] chr1 [10, 14] + ## [2] chr1 [20,24] - ## [3] chr2[30,34] + ##—## seqlength# # chr1 chr2 ## NA NA

侧翼序列的概念在生物学中具有更细微的含义。特别是我们可能期望侧翼序列+链在区间之前,负链在区间之后。验证侧面应用于农庄对象具有此行为。

侧面(gr, 3)
## GRanges 3 ranges和0 metadata column: ## seqnames ranges strand ##    ## [1] chr1 [7,9] + ## [2] chr1 [25,27] - ## [3] chr2[27,29] + ##—## seqlength# # chr1 chr2 ## NA NA

发现什么课程农庄扩展,找到记录行为的帮助页面侧面当应用于Granges对象时,并验证帮助页面记录我们刚刚观察到的行为。

班级(GR)
# #[1]“农庄”# # attr(“包”)# #[1]“GenomicRanges”
getclassdef(类(gr))
## Class“GRanges”[package“genome ranges”]## ## Slots: ## ## Name: seqnames ranges strand elementMetadata ## Class: Rle IRanges Rle DataFrame ## ## Name: seqinfo metadata ## Class: seqinfo list ## ## Extends:直接# # # #“GenomicRanges”类,类“向量”,由“GenomicRanges”类,距离2 # #类“GenomicRangesORmissing”,由“GenomicRanges”类,距离2 # #类“GenomicRangesORGRangesList”,由“GenomicRanges”类,距离2 # #类“注释”,由“GenomicRanges”类,距离3
?”旁边,GenomicRanges-method”

请注意可用的侧翼()类中定义的方法对方法进行了扩充GenomicRanges包中。

似乎有很多有用的方法可以用于基因组范围的研究;我们可以从命令行中发现其中的一些方法,指出这些方法应该位于当前search ()路径

showMethods (class = "农庄”,=搜索())

使用帮助()列表中的帮助页GenomicRanges包,小插曲()查看和访问可用的小插图;这些也可以在Rstudio的“帮助”标签中找到。

帮助(包=“genomicranges”)小插图(包=“Genomicranges”)Vignette(包=“Genomicranges”,“Genomicrangeshowtos”)

序列分析套餐旅游

这个非常开放的话题指向一些最著名的生物导体序列分析软件包。利用这个实验的机会来探索下面高亮显示的软件包小插图和帮助页面;欧洲杯2021体育彩票许多材料将在随后的实验室和讲座中更详细地涵盖。

基础知识

具体域分析 - 探索以下两种或三个包装的着陆页面,小插图和参考手册。

使用序列、对齐、常见的web文件格式和原始数据;这些软件包非常依赖于IRanges/GenomicRanges我们后面会讲到的基础设施。

注释:Bioconductor提供了对“注释”资源的广泛访问(参见AnnotationDataBIOCVIEWS层次结构);这些在星期四的实验室中更详细地介绍,但在此实验室期间探索一些有趣的例子包括:

避免致命的错误:高效的代码

本节的目标是学习如何编写正确、健壮、简单和高效的R代码。我们通过一些案例研究来做到这一点。

优先级

  1. 正确:与手工制作的例子一致
  2. 健壮的:支持真实的输入,例如,0长度的向量,NA值,…
  3. 简单:下个月容易理解;很容易向同事描述它的作用;容易发现逻辑错误;容易提高。
  4. 快,或者至少从现代计算机的速度来看是合理的。

策略

  1. 轮廓
  2. 向量化——对向量进行操作,而不是显式循环

    x < -  1:10 log(x)##不是for(i在seq_along中)x [i] < -  log(x [i])
    ## [1] 0.0000 0.6931 1.0986 1.3863 1.6094 1.7918 1.9459 2.0794 2.1972 2.3026
  3. 预先分配内存,然后填充结果

    Result [i] <- runif(1): Result [i] <- runif(1) * Result [i - 1] Result
    ## [9] 0.05750 0.02301 ## [9] 0.05750 0.02301 ## [9] 0.05750 0.02301
  4. 将公共子表达式提升到重复计算之外,以便子表达式只计算一次

  5. 重新使用现有,测试代码

  6. 重新思考如何解决这个问题

  7. 使用字节编译器编译脚本

  8. 使用并行评估

  9. 用舌头 - “外国”语言如C,Fortran

案例研究:从迭代到矢量化

这是一个明显低效的函数:

f0 < - 函数(n,a = 2){## stopfnot(是.integer(n)&&(长度(n)== 1)&& ##!是.na(n)&&(n> 0))结果< -  numeric()for(i在seq_len(n)中)结果[[i]] < -  a * log(i)结果}

使用system.time调查该算法如何衡量n,专注于经过的时间。

System.time(F0(10000))
##用户系统运行## 0.256 0.003 0.258
n < - 1000 * seq(2) 1, 20日t < -酸式焦磷酸钠(n,函数(i) system.time (f0(我)[[3]])情节(t ~ n、类型=“b”)

块系统时间的绘图

记住当前的“正确”值,以及近似的时间

N <- 10000系统。预计时间(< - f0 (n))
##用户系统运行## 0.252 0.003 0.254
头(预期)
## [1] 0.000 1.386 2.197 2.773 3.219 3.584

修改提升公共乘数的函数,一个,不知情。确保“优化”结果与原始计算结果相同。使用Microbenchmark.包来比较两个版本

f1 < - 函数(n,a = 2){for(i在seq_len(n)中)的结果< -  numeric()结果[[i]] < -  log(i)a *结果}相同(预期,f1(n)))
# # [1]
microbenchmark(f0(n), f1(n), times=5)
## f0(n) 231.6 234.1 235.2 349.7 352.2 5 ## f1(n) 229.6 232.3 348.2 348.8 357.9 5 ## f0(n) 231.6 234.1 235.2 349.7 352.2

采用“预先分配和填补”策略

F2 <- function(n, a=2) {result <- numeric(n) for (i in seq_len(n)) result[[i]] <- log(i) a * result} identical(expected, F2 (n))
# # [1]
Microbenchmark(F0(N),F2(N),时间= 5)
##单位:毫秒## expr min lq中位数UQ max neval ## f0(n)235.23 236.08 236.31 351.28 363.17 5 ## F2(n)16.68 16.81 17.15 17.91 18.37 5

用A.*申请()避免必须明确地预先分配的功能,并使矢量化更加明显。

F3 <- function(n, a=2) a * sapply(seq_len(n), log) identical(expected, F3 (n))
# # [1]
微基准测试(f0(n), f2(n), f3(n), times=10)
## f0(n) 234.50 237.66 298.54 359.67 365.47 10 ## f2(n) 16.84 17.24 17.32 18.30 21.26 10 ## f3(n) 20.37 21.04 22.21 22.55 27.46 10

现在代码显示在一行中,很明显它可以很容易地向量化。抓住机会将其矢量化:

F4 <- function(n, a=2) a * log(seq_len(n)) identical(expected, F4 (n))
# # [1]
Microbenchmark (f0(n), f3(n), f4(n), times=10)
## f3(n) 20219.3 20356.3 20679 21235.9 22307.4 10 ## f4(n) 473.4 474.3 488 488.7 527.9 10

回到显式迭代f2 ()在这些情况下,将代码编译为更有效的表示有助于。使用编译器包执行此操作。

f2c <- cmpfun(f2) n <- 10000相同的(f2(n), f2c(n))
# # [1]
微基准测试(f2 (n), f2c (n),时间= 10)
##单位:毫秒:MILLIN LQ中位数UQ MAX NEVAL ## F2(N)17.214 17.214 17.670 18.173 19.025 22.358 10 ## F2C(n)7.272 7.374 7.533 7.835 9.165

F4()绝对是赢家。它是如何缩放的n吗?(重复几次)

t <- sapply(N, function(i) system.time(f4(i))[[3]]) plot(t ~ N, type="b")

块的矢量比例图

对不同响应模式的解释?

得到教训:

  1. 向量化在迭代中提供了巨大的改进
  2. 当需要显式迭代时,预分配和填充非常有用。
  3. *申请()函数有助于避免显式预分配的需要,并使向量化的机会更加明显。这可能会以较小的性能成本为代价,但通常是值得的
  4. 吊装常用子表达式和使用方法编译器当需要显式迭代时,包可以有助于提高性能。

案例研究:选择算法

从抽象的意义上解释算法,理解操作如何扩展是非常有帮助的。这是一个有趣的问题StackOverflow假设有一个很长的排序向量

VEC < -  C(SEQ(-100,-1,length.out.out = 1e6),rep(0,20),seq(1,100,length.out = 1e6))

简单的目标是识别小于零的值的数量。最初的帖子和许多回复建议,扫描向量的值小于零,然后求和

F0 <- function(v) sum(v < 0)

的每个元素进行比较vec到零,创建一个逻辑向量,长度(v)。然后对这个逻辑向量进行扫描计算满足条件的元素个数。

问题:

  1. 有多少长度的向量v需要分配给这个算法吗?
  2. 根据需要执行的比较次数,您希望该算法如何随长度进行伸缩v吗?用一个简单的数字验证这个。
n < -  seq(1,11,2)* 1e6时间< -  sapply(n,function(n){v < -  sort(rnorm(n))system.time(f0(v))[[3]]})绘图(时间〜n,type =“b”)

块算法扫描时间图

是否有更好的算法,即,一种到达相同答案的方法,但需要更少的时间(和/或空间)?矢量被排序,我们可以通过做一个二分查找。算法出奇地简单:创建最小(第一个)元素和最大(最后一个)元素的索引。检查中间的元素是否大于等于零。如果是,将最大索引移动到该点。否则,就把这个点设为新的最小值。重复此过程,直到最小索引不再小于最大索引。

f3 <- function(x, threshold=0) {imin <- 1L imax <- length(x) while (imax >= imin) {imid <- as.integer(imin + (imax - imin) / 2) if (x[imid] >= threshold) imax <- imid - 1L else imin <- imid + 1L} imax}

每次迭代都丢弃大约一半可能的值,因此我们预计平均地将在最后到达log2(长度(v))迭代 - 算法缩放了长度的日志v,而不是与长度v,不创建长向量。随着长度的增加,这些差异变得越来越重要v就长了。

问题:

  1. 用简单的数据验证F3()f0 ()导致完全相同的()的答案。
  2. 比较如何定时F3()向量长度的缩放。
## f0((-2):2), f3((-2):2)
# # [1]
相同的(f0 (2:4), f3 (2:4))
# # [1]
相同的(f0 (- (2)), f3(-(2节)))
# # [1]
相同(F0(VEC),F3(VEC))
# # [1]
## Scale N < -  10 ^(1:7)时间< -  sapply(n,function(n){v 

块algo-二进制表的情节

  1. 使用Microbenchmark.包的性能比较f0 ()F3()有了原始数据,vec
  2. 可以编译R代码,并且编译有助于执行非矢量化操作的最多F3()。使用编译器:: cmpfun()编译F3(),并使用Microbenchmark比较结果。
##相对时间库(MicroBenchmark)Microbenchmark(F0(VEC),F3(VEC))
##单位:microseconds ## expr min lq median uq max neval ## f0(vec) 26688.44 27822.29 27924.6 28301.45 32728.0 100 ## f3(vec) 52.44 55.19 72.3 79.73 121.5 100
f3c <- cmpfun(f3)微基准测试(f3(vec), f3c(vec))
##单位:microseconds ## expr min lq median uq max neval ## f3(vec) 23.11 23.85 24.83 25.71 35.18 100

我们很可能通过用C编写二进制搜索算法来获得额外的速度,但是我们已经很满意性能的提高,所以我们不会再那样做了!

问丢失的是有用的F3()相比f0 ()。例如,算法是否在字符向量上工作?当向量包含时呢?NA价值观?如何治疗联系?

FindInterval()可能是解决原始问题的一种更好的内置方法,并适用于其他情况。我们的想法是把我们感兴趣的问题,0,并找到指定的间隔vec它发生的地方。

f4 <- function(v, query=0)Eps, v) identical(f0(vec), f4(vec))
# # [1]
Microbenchmark(F0(VEC),F3(VEC),F4(VEC))
##单位:microseconds ## expr min lq median uq max neval ## f0(vec) 26911.29 27841.17 27956.62 28343.8 31953.2 100 ## f3(vec) 52.59 58.17 70.79 78.8 137.9 100 ## f4(vec) 21972.92 22035.44 22062.42 22158.6 23855.8 100

事实上,它是灵活的和良好的测试意味着它往往是首选F3(),尽管速度较慢。例如,比较查询10000个不同的点所花费的时间f3和迭代,与findInterval和向量化。

Threshold <- rnorm(10000)相同(sapply(Threshold, f3, x=vec), f4(vec, Threshold))
# # [1]
微基准测试(sapply(x, f3), f4(vec, x))
##单位:微秒## expr min lq中位数UQ max neval ## Sapply(x,f3)128.2 138.2 181.7 257.9 291.2 100 ## F4(VEC,x)22000.2 22037.3 22091.4 22308.3 23871.0 100

一些实现有效算法的功能是sort ()(包括基数排序),比赛()(哈希表查找),和表格();这些在您自己的代码中可能很有用。

得到教训:

  1. 算法的选择非常重要
  2. 实现经典算法(如二分搜索)可能是一个有益的学习经验,即使在一天结束时,使用现有的函数可能会更好。
  3. 实现高效算法的内置R函数对于更复杂的代码来说是重要的构建块。