R语言整洁编码的12大原则

好的编码风格就像正确使用标点符号一样,可以让我们写的代码更容易阅读。要知道,代码很多时候写出来,是给人看的,可能是自己,也可能是他人。

对于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)致力于打造中国人的云上生物信息基础设施。大量在线工具免费使用。无需安装,用完即走。活跃的用户社区,随时交流使用心得。
*

生信培训

简说基因的生信培训班,荣获学员的一致好评。如果你也对生物信息学感兴趣,欢迎来跟简说基因,学真生信

生信分析

我们能够承接所有 NGS 组学数据分析业务,包括但不限于 WGS / WES / RNA-seq 等。基因组组装、注释,以及各种重测序业务都可以与简说基因合作。

相关推荐
海阔天空_20131 分钟前
Python pyautogui库:自动化操作的强大工具
运维·开发语言·python·青少年编程·自动化
天下皆白_唯我独黑8 分钟前
php 使用qrcode制作二维码图片
开发语言·php
夜雨翦春韭12 分钟前
Java中的动态代理
java·开发语言·aop·动态代理
小远yyds14 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
何曾参静谧26 分钟前
「C/C++」C/C++ 之 变量作用域详解
c语言·开发语言·c++
q567315231 小时前
在 Bash 中获取 Python 模块变量列
开发语言·python·bash
许野平1 小时前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
也无晴也无风雨1 小时前
在JS中, 0 == [0] 吗
开发语言·javascript
狂奔solar1 小时前
yelp数据集上识别潜在的热门商家
开发语言·python
blammmp2 小时前
Java:数据结构-枚举
java·开发语言·数据结构