好的编码风格就像正确使用标点符号一样,可以让我们写的代码更容易阅读。要知道,代码很多时候写出来,是给人看的,可能是自己,也可能是他人。
对于R语言新手,也建议大家规范编码。刚开始可能不习惯,但如果坚持练习,很快就能适应并成为你的第二天性,看着不规范的代码就难受,代码完美主义技能 get。
本文我们就来学习一下R语言整洁编码的12大原则。
命名
R语言变量的命名,应仅使用小写字母(a-z)和数字(0-9)组成,可用下划线( _ )分隔名称中的单词。如:
go
# 提倡:
short_flights <- flights |> filter(air_time < 60)
# 避免:
SHORTFLIGHTS <- flights |> filter(air_time < 60)
原则一:根据一般经验,变量名尽量有意义,可以使用长而有描述性的名称,不应使用短的没意义的名称。但是一些特殊场合可以使用短名称,如代表循环计数的 i,大家普遍采用,使用短名称更显简洁。
原则二:如果有一系列相关变量,可以给它们一个相同的前缀,而不是相同的后缀,因为相同的前缀在开发环境中自动变量补全时具有优势。
空白
我见过新手写的R语言代码,行内无空格,行之间无空行,看着让人非常压抑。比如:
go
flights|>filter(dest=="IAH")|>group_by(year,month,day)|>summarize(n=n(),
delay=mean(arr_delay,na.rm=TRUE))|>filter(n>10)
flights|>filter(carrier=="UA",dest%in%c("IAH","HOU"),sched_dep_time>
0900,sched_arr_time<2000)|>group_by(flight)|>summarize(delay=mean(
arr_delay,na.rm=TRUE),cancelled=sum(is.na(arr_delay)),n=n())|>filter(n>10)
相信很多人都拿到过类似的代码。留白非常重要,适时空格和空行,让代码看起来更具美感,画家作画还要留白呢,是吧?
原则三:在赋值运算符两侧加空格。
原则四:在数学运算符两侧加空格(指数运算符^除外)。
go
# 提倡
z <- (a + b)^2 / d
# 避免
z<-( a + b ) ^ 2/d
原则五:常规函数调用时,函数名与括号之间不要有空格。总是在逗号后面紧跟一个空格,就像标准英语一样。
go
# 提倡
mean(x, na.rm = TRUE)
# 避免
mean (x ,na.rm=TRUE)
原则六:为了代码整洁美观,可以增加必要的空格,如:
go
flights |>
mutate(
speed = distance / air_time,
dep_hour = dep_time %/% 100,
dep_minute = dep_time %% 100
)
管道
原则七:管道操作符 |> 的前面应该始终有一个空格,并且 |> 应该位于一行的末尾。
go
# 提倡
flights |>
filter(!is.na(arr_delay), !is.na(tailnum)) |>
count(dest)
# 避免
flights|>filter(!is.na(arr_delay), !is.na(tailnum))|>count(dest)
这样做的好处显而易见。比如可以更轻松地添加新的步骤、重新排列现有步骤、修改步骤中的元素,以及通过浏览左侧的动词就可以推测代码的要完成的功能。
原则八:管道中的函数如果有命名参数,可以将每个参数放在独立的行上。如果函数没有命名参数,可以将所有内容放在一行中,确有必要时也可以分多行。
go
# 提倡
flights |>
group_by(tailnum) |>
summarize(
delay = mean(arr_delay, na.rm = TRUE),
n = n()
)
# 避免
flights |>
group_by(
tailnum
) |>
summarize(delay = mean(arr_delay, na.rm = TRUE), n = n())
原则九:管道第一步之后,每行缩进两个空格。如果要将参数放在自己的行上,再缩进两个空格。确保 ) 位于自己的行上,并且与函数的第一个字符保持对齐。
go
# 提倡
flights |>
group_by(tailnum) |>
summarize(
delay = mean(arr_delay, na.rm = TRUE),
n = n()
)
# 避免
flights|>
group_by(tailnum) |>
summarize(
delay = mean(arr_delay, na.rm = TRUE),
n = n()
)
# 避免
flights|>
group_by(tailnum) |>
summarize(
delay = mean(arr_delay, na.rm = TRUE),
n = n()
)
如果你的管道很适合写在一行,也可以回避上述一些规则。但根据经验,短语句变长是很常见的,因此从长远来看,一开始就选择垂直书写方式是有益的,这往往会节省许多时间。
go
# 一行看着很紧凑
df |> mutate(y = x + 1)
# 尽管这种写法占了4行,但这在未来很容易扩展到更多变量和步骤。
df |>
mutate(
y = x + 1
)
当然这并没有一定之规,我个人的话,还是倾向于第一种写法。当后续需要扩展时,再换成第二种也不迟。一开始就写第二种,稍显繁琐了。
原则十:要避免写很长的管道,比如超过10-15行。尝试将长管道分解为小的子任务。这更有助于调试,查看中间结果,并且更容易检查结果是否符合预期。
Ggplot2
原则十一:适用于管道的规则也适用于 ggplot2,只需要以对待 |> 相同的方式对待 + 即可。
go
flights |>
group_by(month) |>
summarize(
delay = mean(arr_delay, na.rm = TRUE)
) |>
ggplot(aes(x = month, y = delay)) +
geom_point() +
geom_line()
同样,如果你无法将函数的所有行都放在一行中,请将每个参数放在自己的行上:
go
flights |>
group_by(dest) |>
summarize(
distance = mean(distance),
speed = mean(distance / air_time, na.rm = TRUE)
) |>
ggplot(aes(x = distance, y = speed)) +
geom_smooth(
method = "loess",
span = 0.5,
se = FALSE,
color = "white",
linewidth = 4
) +
geom_point()
分段注释
原则十二:随着脚本的长度变长,你可以使用分段注释将文件分解为可管理的部分:
go
# Load data --------------------------------------
# Plot data --------------------------------------
小结
可以看到,遵循良好的规范写出来的代码让人赏心悦目,而糟糕代码则让人看不下去。最后,推荐一个R包:styler,它可以整理现有代码,使其符合编码规范,是拯救已有糟糕代码的神器。
- 本文内容主要来源于《R数据科学》第二版。我们正在以该书为教材,进行R语言入门培训,课程采用录播 + 微信群答疑的形式,学员反映效果还不错。以下是学员的评价。
- 用R把数据整理好了,上传到 Galaxy 平台,一键分析,简直完美,这是生信未来的形式。
关于简说基因
生信平台
Galaxy中国(UseGalaxy.cn)致力于打造中国人的云上生物信息基础设施。大量在线工具免费使用。无需安装,用完即走。活跃的用户社区,随时交流使用心得。
*