R语言入门学习笔记(五)
文章目录
前言
通过先前的学习,我们有了对R语言的初步了解及相关基础知识储备。现在,是时候来编写一些用于解决问题的程序了,在这一节,将介绍关于编写R程序的相关知识。
一、R程序编写基本策略
在面对一个复杂的编程任务的时候,可能需要复杂的算法才能够完成。利用一些的策略可以简化复杂的编程任务:
- 将复杂任务分解成一些简单的子任务
- 使用实例
- 使用自然语言描述解决方案,然后将其转换成R代码
1.有序步骤和同类情况
当进行子任务分解的时候,通常可以将任务分解为两类情况:有序步骤和同类情况。
有序步骤就是指有着先后顺序的步骤,因此可以按照这些先后顺序编写相应的执行步骤即可。比如想要对一组体检数据进行处理,其中想要提取其中的体重数据计算平均值,这就牵扯到一个有序步骤即,提取数据和计算均值。
同类情况指的是识别出某任务具有几种类似的特性的情况。针对不同的情况要采取不同的算法。如果可以识别出这些情况,就可以一次性设计出较为通用的算法。比如,分析体检数据时,对于身高体重值,我们要通过计算BMI指数,然后对于不同范围的人群给予不同的分数,这就需要将计算得到的BMI值进行分类后处理。
2.条件语句
在处理同类情况的时候会使用到条件语句。 它的基本结构是:
if(this){
that
}
其中this为一个逻辑测试,如果测试结果是TRUE
那么将执行that的代码,否则将跳过。
另外还有:
if(this){
PlanA
}else{
PlanB
}
这将会给出this的测试结果为FALSE
情况下的另一种执行情况,将会执行else后的PlanB区域代码。
另外还有一种形式:
if(caseA){
PlanA
}else if(caseB){
PlanB
}else{
PlanC
}
很容易理解,这是将caseA的结果为FALSE
的进行细分,符合caseB的执行PlanB,否则执行PlanC。类似这样的语句还可以继续嵌套下去以满足各种互斥的分类需求。
这里介绍一些可能会用到的一些函数:1.trunc函数,它接受一个数值作为输入,返回小数点左侧的部分。2.unique函数,它返回一个向量中的非重复值(唯一值)。
3.查找表
R中许多极简的解决方案都会用到取子集的方法。简单来讲查找表就是一个R对象,它包含了具有名称属性的一系列元素。举个例子,比如我们开办一个自助餐厅,提供不同的套餐以供选择,不同的套餐对应不同的金额,我们可以将这些存储进一个R对象中作为一个查找表:
cost <- c(A = 15, B = 20, C = 30, D = 40, V = 100)
cost
#> A B C D V
#> 15 20 30 40 100
现在,通过取子集的方法可以轻松得到各套餐的价格:
cost["A"]
#> A
#> 15
unname(cost["A"])
#> [1] 15
可以通过unname函数去除name属性只获得价格数额。
注意查找所用的"A"
是一个字符元素,那就意味着我们从其他地方获得的字符元素都可以用于这个价格的查找。
con <- c("A", "B", "D")
cost[con[1]]
#> A
#> 15
那么如果我们有一个统计表格,记录了一些人套餐的选择:
table <- data.frame(name = c("P1", "P2", "P3"), met = c("A", "C", "V"))
那么,可以通过查找表它们的金额进行填写:
table$cost <- cost[table$met]
table
#> name met cost
#> 1 P1 A 15
#> 2 P2 C 30
#> 3 P3 V 100
还可以用于查找某个人的用餐金额:
# 查询P1
cost[table$met[table$name == "P1"]]
#> A
#> 15
# 可以使用unname(cost[table$met[table$name == "P1"]])仅返回数值
到此为止,看起来这种方法可能并不简单。但是想象一下,如果是有成百上千个人,查找表的优势就很明显了。请注意查找表本质上是R对象,注意其使用的本质是通过name属性返回数值,本质上还是[]
的应用。
另外,查找表在一定情况下可以用于替代if语句使用。比如在进行赋值时,明确需要赋的值后,将各种可以写成if语句的情况作为选择依据然后根据情况来使用查找表赋值。但是如果if语句种每个分支执行的功能不同,则无法替代。
善于使用查找表是R编程提速的好策略,应为对于R来说查找表的使用([]
的应用)是一种向量化的方法,这也是R最快的向量化编程方式,后续我们在代码提速的部分会重点介绍。
4.注释
注释在所有的编程都十分常见,R种用的是#
来作为注释符号使用。适当使用注释,可以提高代码的可读性。另外在后续调整代码时,也更加方便快捷。
二、调试R代码
在编写R代码的过程中,可能会遇到一些报错,这是很正常的现象。R自带了一些简单的调试工具,可以更好的帮助我们处理这些报错信息。
1.traceback调试
traceback函数可以帮我们精准定位错误。在R的实际应用中,可能会遇到函数的嵌套,在调用一个函数报错时,我们可能并不清楚内部具体的报错位置。
举个简单例子:
first <- function() second()
second <- function() third()
third <- function() fourth()
fourth <- function() fifth()
fifth <- function() bug()
运行first函数时就会顺序调用下面的函数,最后出现报错信息:
first()
Error in bug() : could not find function "bug"
报错信息告诉我们,错误出现在bug()
这里,是由于不存在bug函数。此时我们可以在命令行中键入traceback()
来查看发生错误前的函数调用路径。
traceback()
#> 5: fifth() at #1
#> 4: fourth() at #1
#> 3: third() at #1
#> 2: second() at #1
#> 1: first()
返回值的最顶层就是出错的最底层函数。
另外RStudio在报错时提供了快捷按键来进行traceback,效果如下:
2.browser
利用browser()
函数,可以让R在运行过程中暂停,使得在啊命令行中键入新命令。此时键入的命令,其活动环境将是暂停状态函数的运行时环境。因此,可以查看该函数正在使用的对象,利用与该函数相同的作用域规则查看这些对象的取值。并在该函数所处的环境下运行一些代码。这是发现函数代码错误的最佳手段。
想要使用该函数,只需要将browser()
添加至函数体的对应位置,然后保存该函数即可。当调用该函数运行至该位置时就会暂停,进入调试内部:
在这个界面时,命令提示符变为Browse[1]>
,左上显示当前函数的源代码,并突出显示当前暂停位置。右上环境面板会显示当前运行时环境下的所有对象。右下traceback面板显示的是traceback信息。
此时可以在命令行输入对象名查看对象。其次可以在命令行窗口手动键入或使用上侧快捷键:Next(下一步),用于运行下一行代码;Continue(继续),运行剩余所有代码,完成后退出该模式;Stop(停止),立即退出该模式。这些按钮可以在命令行输入n
,c
,Q
实现。那么如果有对象刚好是这个名字,查看时就需要用get("n")
等命令进行查看,另外cont是c的同义词,where会显示整个调用栈,有同名对象也需要使用get()
来查看。
另外,RStudio提供了报错时,快捷进入调试模式的快捷键:
3.断点标记
在RStudio中,有一种可视化操作更加方便的进入调试模式。可以在R脚本里,在行号的左侧进行一下单击,就会出现一个红色的点,这就是断点,点击Source按钮运行脚本时,到此位置就会进入调试模式。此处的红点就变成了绿色箭头,代表代码运行到此处。想要移除断点,只需要在R脚本中再次单击,红点就会去掉。
4.debug
如果想要调试一个已经存在的函数,可以使用debug()
,它将会在一个已存在的函数的开头添加上browser()
语句。之后R运行这个函数会立即进入调试模式下的窗口。直到你使用undebug()
移除browser功能。
另外,可以使用isdebugge()
查看一个函数是否处于debug状态。如果只想要进行一次调试,可以使用debugonce()
。
在学习browser函数的RStudio快捷键时,它的逻辑其实是,R会在报错后再一次运行R函数,且在命令前加上了debugonce函数。
5.trace
trace可以在某个函数的某个位置加上browser函数:
trace("sample", browser, at = 4)
另外,可以使用trace函数在某个函数内插入其他R函数(不仅仅是browser函数),但请谨慎操作。还有,可以对某个函数运行trace而不插入任何表达式,之后每次R运行该函数时都会在命令行显示trace:<the function>
。
对某个函数使用完trace后可以用untrace()
将其恢复正常。
6.recover
它结合了traceback所生成的调用栈以及browser触发的调试模式的特点。使用它的方法与browser一样,都是将其直接插入到某个函数代码中,以fifth函数为例:
fifth <- function() recover()
调用first函数时产生逐级调用,当运行到recover函数时,会暂停显示调用栈信息,并提供了进入这些函数的调用模式的选项。
first()
#>
#> Enter a frame number, or 0 to exit
#>
#> 1: first()
#> 2: #1: second()
#> 3: #1: third()
#> 4: #1: fourth()
#> 5: #1: fifth()
#>
#> Selection:
它将最后出错的放在了最下面,并且可以输入函数的编码来进入对应函数的调试模式中。如果不想进入任何层级的函数,可以键入数字退出调试。
这是一个强大的工具,但是将recover添加到R函数中有时显得非常累赘。所以可以将其设置为全局选项:
options(error = recover)
这是个全局设置,R每次报错都会自动运行recover命令。直到关闭当前R会话或者输入:
options(error = NULL)
总结
通过本章的学习,我们将对R编程有进一步的了解。关于R编程,可以将复杂的想法分解后分为有序步骤和同类情况来针对编程,关于同类情况可以使用条件语句和查找表来实现。其中查找表满足了向量化编程的需求,是一种方便快捷的方法来对同类情况进行赋值操作。最后,我们学习了很多调试R程序的方法。当遇到报错的时候不要怕,微笑地去调试它,运用学到的这些工具就可以让我们更加方便快捷的调整R程序。