R语言入门学习笔记(七)
文章目录
前言
截至目前,我们已经学习了R的基本知识,也会使用R来编写一些程序。但是在我们平时学习中,可能会遇到要重复完成某个任务,为了实现这个需求,我们来学习循环的用法。循环很好的解决了一些问题,但是不同的写法可能影响代码的运行速度,最后我们来学习一些关于R中特有的代码提速方式来优化我们的代码。
一、循环
循环时R用来重复完成某个任务的方法,非常实用。下面将介绍常用的循环函数。
1.expand.grid
R中的expand.grid
函数可以方便地写出n个向量元素的所有组合。比如:
a <- c(1, 2, 3)
expand.grid(a,a)
#> Var1 Var2
#> 1 1 1
#> 2 2 1
#> 3 3 1
#> 4 1 2
#> 5 2 2
#> 6 3 2
#> 7 1 3
#> 8 2 3
#> 9 3 3
2.for循环
for循环可以重复运行某段代码一定的次数,次数取决于循环的输入中有多少个元素。
for(value in that){
this
}
这里that是一个对象集合,可以是包含字符串也可以是数值的向量。对于that的每个值,for循环都会运行一遍大括号内的代码。代码运行时,for会从that中的第一个值开始赋给value,知道穷尽that中的值,循环结束后查看value的值,会是that集合中的最后一个值。
需要注意的是:value是循环符号,可以选择任何名称,但是要小心选择,因为它会覆盖当前活动环境中的同名对象(for内部循环是在当前活动环境进行的)。
另外for循环不会返回输出结果,要使用循环内的数据,要在循环中明确保存想要的对象。为了实现这个,可以在for循环前创建一个空的向量或列表来获取循环结果用。
results <- vector(length = 3)
for(i in 1:3){
results[i] <- i
}
results
#> [1] 1 2 3
3.while循环
while循环的特征是,只要某个条件为真,就会重复运行某段代码。
while(condition){
code
}
while会在每次循环前运行condition,这是个逻辑测试。如果condition的结果为TRUE
while就会运行code代码,如果结果为FALSE
while就会结束循环。
while的结果怎么会从TRUE
变为FALSE
,这就可能是code代码在一次次运行时改变了测试的结果。如果code与condition无关,代码会一直运行下取,直到强制停止运行。因此,编写while循环时要小心,如果想要停止while循环,可以按键盘的Esc键,或者单击RStudio控制台的停止键。
它与for一样返回任何结果。
4.repeat循环
repeat循环会一直运行某段代码,直到终止循环或者遇到break命令。这通常回合if语句联合使用来进行循环。
repeat{
code
if(condition){
break
}
}
二、代码提速
相比于其他语言,R语言中的循环运行速度较慢。因此我们要学习使用向量化代码来编写R程序,利用好R的优势。
1.向量化代码
所谓向量化代码就是指代码可以接受一个含有多个值的向量作为输入,并且同时操作向量中的每一个元素。R代码中经常用到的向量化代码操作有:逻辑测试、取子集和元素方式执行。
下面给一个具体例子来展示向量化代码,我们对比两段程序,它们都是为了计算数值型向量中所有元素的绝对值:
# 非向量化:使用for循环
abs_for <- function(num){
for(i in 1:length(num)){
if(num[i] < 0){
num[i] <- -num[i]
}
}
num
}
# 向量化编程
abs_vec <- function(num){
num[num < 0] <- -num[num < 0]
num
}
第二个函数使用了逻辑测试,取子集和元素方式执行,要比第一个快很多。我们可以生成一个数据来进行测试。
num <- rep(c(-1, 1), 100000000)
#> system.time(abs_for(num))
#> 用户 系统 流逝
#> 13.08 0.07 13.16
#> system.time(abs_vec(num))
#> 用户 系统 流逝
#> 2.06 0.58 2.64
system.time报告了两个函数在计算机执行时用户、系统和实际所耗费的时间。随着R的优化迭代,循环处理速度已经有所提升,但是像这样20000万长度的向量时,向量化编程速度快于循环5倍左右,这就是向量化编程的优势。
许多现有R函数都进行了向量化和优化,因此速度很快,可以试一下R自带的abs函数的速度:
#> system.time(abs(long))
#> 用户 系统 流逝
#> 0.29 0.11 0.41
2.编写向量化代码
R中编写向量化代码并不难,因为许多函数都已经向量化了。基于这些函数,要编写向量化代码,可以从下面两个方面入手:
(1)对于程序中有序步骤使用向量化函数完成
(2)对于同类情况,使用逻辑值取子集的方式处理。尝试一次性处理完一类情况中的所有元素
另外,在R中往往if与for的组合出现的地方有可能就有优化空间。它们同时操作将不同情况的结果输出到整个向量中去。这样的组合往往可以使用逻辑值取子集的方式代替。(可以用到查找表)
3.for循环提速的几个细节
当然,也不能盲目避免使用for循环,因为许多需求是替代不了的。对于for循环有两个细节可以提升运行效率。首先是,能放在循环外的就要避免放在循环内。其次是在循环开始前确保循环输出结果的对象具备足够的容量以容纳结果。比如:
system.time({
output <- rep(NA, 10000000)
for(i in 1:10000000){
output[i] <- i
}
})
#> 用户 系统 流逝
#> 0.36 0.02 0.37
system.time({
output <- c()
for(i in 1:10000000){
output[i] <- i
}
})
#> 用户 系统 流逝
#> 2.03 0.17 2.21
可以看到效率的差距,如果输出的数据更多更大,这将更加明显。
总结
通过这节的学习,我们学到了循环在R编程中的运用。善于使用这些循环可以解决很多事情。但是有些循环可以被R中的向量化代码所代替,这样会大大发挥R的优势,使得代码运行速度大大加快。R向量化编程的精髓就是,将不同的情况用逻辑测试表示出来,然后将不同逻辑测试的结果串联起来作为筛选条件对结果进行向量化操作。但是在对代码进行向量化的时候,要注意不要一味追求代码向量化程度,有时将普通代码转换成向量化代码所花费的时间可能比运行一个普通代码还要长很多,这可能就没有必要了。
截至到此,我们关于R语言的入门学习系列就结束了。我们通过学习了解了R语言的大概样子,接下来,我们将会在此基础上,进一步介绍利用R进行数据分析方面的知识。