接触 R 语言快两年时间了,多少也算是对 R 有了个囫囵了解,平日里没事爱倒腾个数据来分析一下,期间自然踩过不少坑,有些还是深坑巨壑。两年来 R 基础这块的报错基本见了个遍,所以每当有新手小白问我问题帮他们解决后就一口一个大神大佬大咖的,唬得我全身不自在。等到人都散去,坐到电脑边打开 R 语言,深感恐惧,于是奋而用心学习。之前少年志气,不知天高地厚,而今对数据科学越发深入学习,越感自己积贫积弱。貌似扯歪了,下面言归正传。
建了公众号之后时常会有学习 R 语言的朋友要求加我微信,同意好友申请之后咣当一个文件扔过来然后说了句让我帮他看看代码哪里不对,怀着万分复杂的心情打开了他的代码文件,各种乱码无注释中英文标点混用让我瞬间抓狂,看完也导致情绪不稳定了。没注释其实还可以理解,毕竟大家都懒,小编也经常在写代码的时候放飞自我不加任何注释,但这样的代码我是不敢给别人看的。但是很多同学在写 R 代码时也忒不认真了,中英文标点和括号混用的风生水起还一个劲的问我代码怎么可能错。小编在此之前虽然对代码规范性不加重视,但也不敢如此造次。今天小编就跟大家认真谈论一下怎样才能写出规范整洁的 R 语言代码。
注释
注释是一门编程语言的基本要素,更是 R 语言用户的自我修养。抱着对自己代码负责任的态度,养成良好的注释习惯,对你自己和对大家都有好处。至于怎么写注释,如何把 R 注释写得清新脱俗那就需要一番讲究了,一般的 R 代码中注释包括文件注释、代码块的注释以及具体重要单行代码的注释。
文件注释比较正式,像小编这么不正式的人自个儿玩的时候从来不写文件注释,所谓文件注释,就是要在你这个文件代码开始前需要声明的一些内容,比如说什么环境下运行本代码、使用哪个版本的 RStudio、约定编码方式(通常是 utf-8)以及这个代码文件主要是用来干嘛的。通常文件注释开始可以用两个#。
文件注释
## !/user/bin/env RStudio 1.0153
## -*- coding: utf-8 -*-
## exploratory data analysis of nba shooting data
代码块注释顾名思义就是对某个代码块的一个注释,在 R 中为了提高代码效率,我们通常喜欢把函数模块化以避免代码大面积重复,这时候来一行代码块注释可能会更搭配。代码块注释通常也是使用两个#。
代码块注释
## define MyStyle function for ggplot2.boxplot
MyStyle <- function(xName, yName, groupName){
ggplot2.boxplot(data = doc, xName, yName, groupName,
showLegend = FALSE, na.rm = TRUE)
}
boxplot1 <- MyStyle('team_expert', 'reservations', 'team_expert') +
scale_fill_brewer(palette = "Paired")
最后是单行代码注释,也可以叫短注释,通常放在一行代码的后面,在代码较长时也可以另起一行进行注释,但要尽量保证每行代码要对齐,不然代码加注释混一起乱糟糟的一团很影响心情。
命名规范
与注释一样, R语言中规范地对代码中的变量、函数和文件名进行命名也是一项 R 语言用户的基本操守。我知道身边大伙都很随性,字里行间通常笔走龙蛇不拘一格,但到了 R 语言这里,还是麻烦大伙儿规规矩矩的来。对 R 文件的命名应尽可能以体现文件内容为准,比如说这个文件代码是用来分析 NBA 球员投篮数据的,那么文件可以命名为 analysis_nba_data.R,千万不要信马由缰随便命名,日后坑的都是你自己。
函数和变量的命名则需要尤其小心,在 R 环境对于大小写是极其敏感的,变量名应该都使用小写字母,而函数名则可以在首字母使用大写,另一点需要注意的是变量和函数命名时应尽量避免与 R 环境中本身存在的一些函数或者变量重名,不然系统也会混乱弄不清的。不同单词间可以用 . 或者 _ 来连接,看个人习惯,但貌似谷歌的 R语言代码规范上要求使用 . 来连接。而命名函数则尽量不要使用下划线或者点连接符,在单词选择上也最好能体现函数的动作,以动词来命名函数。且看下面例子:
变量
正例: shooting_distance shooting.distance
反例: ShootingDistance
函数
正例: GetShootingEff
反例: getshootingeff
代码组织
有组织有层次的 R 代码通常会出现在正式的项目中,其实在代码组织上我觉得Python就做的非常好,时常去 GitHub 上观摩一番别人的代码感觉也是赏心悦目。在正式的 R 语言项目里,以下内容是必不可少的:
版权声明
编码和环境声明
作者注释
文件说明
项目目的
输入与输出说明
函数定义说明
其他
在日常的 R 代码训练时,这些就有点过于苛求了。像小编这么懒的人连个编码都懒得声明一下的,还是平时多养成多声明的习惯的好,不然等着正式接项目了必定手足无措漏洞百出。
R 编程的一般约定
除了上述我们不能违背的一些较为明显的规定之外,在 R 中通常还包括一些约定俗成的规则,小编在之前的学习中就不大注意这些规矩,以致于写的代码被内行人一眼看上去就知道是很业余的 code 。那这些约定俗成的规定具体包括哪些呢?小编去认真查找了一下 Google 的 R 代码规范手册,且看慢慢道来。
1. 每行代码最长不超过 80 个字符
合理,一行代码过程效率既低又不美观。
2. 使用空格键空两格进行缩进,尽量不要用 Tab 键
实际测试了下,感觉差不多。但可能是我没发现其中奥妙。
3. 使用 <- 符号进行赋值,而不是 = 号。使用 <- 符号进行赋值,而不是 = 号。使用 <- 符号进行赋值,而不是 = 号。(重要的事情说三遍!不要跟我说二者没差别啊什么的,函数内参数指定除外)
4. 在使用二次运算符时(+ - = <-)两端都需要空一格。
非常简单的建议,保证代码不挤在一起一团糟。
5. 逗号。逗号前不用空格,但逗号后一定要空一格。同第 4 条,很实用的建议。
6. 分号。无特殊情况不要使用分号。
7. 花括号。通常在循环语句或者自定义函数中需要用到。左花括号不换行写,右花括号独占一行写。
8. 尽量少用 attach 函数。我喜欢称之为绑定函数。这一点还不能体会很深,我用到现在还没出现过大问题,可能是 R 环境中绑来绑去容易把变量弄混吧。
9. 小括号。在前括号前加一个空格,但调用函数时除外,特指 if 等循环命令。
10. 全部代码约定应保持一致。不能一会儿有空格一会儿又挤在一起,当然我们要求是全部有空格。
正例
#自定义函数对ggplot.box进行封装
MyStyle <- function(xName, yName, groupName){
ggplot2.boxplot(data = doc, xName, yName, groupName,
showLegend = FALSE, na.rm = TRUE)
}
反例
MyStyle<-function(xName,yName,groupName){
ggplot2.boxplot(data=doc,xName,yName,groupName,
showLegend=FALSE,na.rm=TRUE)}
测试效率并优化代码
实际上这部分并不属于 R 代码规范的必备内容。但还是有必要强调一下。无论新手老手,都不可能把程序写的完美无缺,当你写完一段代码之后,你需要测试这段代码的效率并对其进行不断的优化。RGui 中没有测试代码的工具,但 RStudio 完美的解决了这一问题,在用 Debug 检测完你的代码正确与否之后,你还可以使用 Profile来检测你的代码效率如何,这一点无论是对 R 新手用户还是老用户都不容易。
常用的优化代码效率的方式包括函数模块化、向量化运算以及多使用 R 内置函数。前两天在在网上找到谢益辉老大的一个报告,主题叫做论 R 用户的自我修养,其中的一个要点就是函数模块化。通常在重复调用一些含有较多参数的函数时,我们的代码会看起来很臃肿,一眼看去就有很多冗余。比如 ggplot 函数通常会包括众多参数,我们在重复调用时可以自定义一个调用函数,将我们想利用的图形格式参数都模块化到自定义函数模块中去,这样会大大提升代码效率。
少用循环而多用 R 自带的向量化运算。自打入坑以来就曾被人告诫 R 的循环效率极低,能不用则不用。前段时间曾写了个关于 apply 函数族的用法心得,R 语言向量化运算精髓即在于此,有兴趣的同学可以自行去翻看。最后一点则是要尽可能地利用 R 中已经封装好了的函数,不要求个平均值还要自己去编个循环,其实一个 mean 函数就可以搞定。重复造轮子的事情在目前情况下尽量少做吧,一句话,都是为了效率。
参考资料:
Google 的R 语言代码规范
一个数据科学践行者的学习日记