Hadley Wickham的R语言编写规范

 
  最近要重写16年的R脚本,一查R语言编写规范(多源自谷歌),发现中文版和英文版的居然是相反的😂当年的规范也和现在不太一样了,为了解决其他语言用户认为的R语言太随便难度懂的问题,最近几年R编写规范更加趋向于和其他语言一致,而中文翻译的可能都是很多年前的规范,因而有所出入。

  所以本文翻译自R语言大神Hadley Wickham整理的tidyverse的R语言编写风格指南(注:非直译,有意译、补充和省略),希望能给中文用户提供一些帮助。当然,建议有条件的朋友直接阅读一手英文版,毕竟翻译水平有限,而且大神的风趣幽默俺是翻不出灵魂的~

1 文件名 Files

1.1 R脚本的命名 Names

  文件名最好用“.R”结尾,文件名尽量简洁且见名知意,用字母,数字,英文连字符“-”和英文下划线“_”组成。

# Good
fit_models.R
utility_functions.R

# Bad
fit models.R
foo.r
stuff.r

  如果需要按照顺序运行R脚本(比如写shiny app,app会自动按照R文件夹里的文件顺序运行脚本),可以加数字做前缀。请注意文件名不要区分大小写,最好统一使用小写字母,因为有些系统是不区分大小写的。

00_download.R
01_explore.R
...
09_model.R
10_visualize.R

1.2 R脚本内部结构 Internal structure

  在注释里使用一串英文连字符“-”或者“=”来区分不同功能的代码块,Rstudio可以识别这种注释,可扩展或隐藏注释之间的代码块,方便阅读。

# Load data ---------------------------

# Plot data ---------------------------

  此外,如果使用到R包,建议在文件的起始使用library()一次性导入,不建议把导入包的命令散布在不同的文件里或者加载隐藏的环境变量如.Rprofile进行包的加载。

2 R语法 Syntax

2.1 对象名 Object names

  变量名和函数名均建议统一使用小写字母、数字、英文下划线“_”组成(即所谓的蛇形命名法,snake case)。

# Good
day_one
day_1

# Bad
DayOne
dayone

Base R(R的基础包)在函数名(contrib.url()和变量类型名 (data.frame)中使用点(dots),但是最好只在S3对象中使用点,因为在S3对象中,方法被命名为function.class,如果你在函数名或者类名中使用点,在我们调用方法时就会有歧义,如as.data.frame.data.frame()

还有如果需要命名一系列变量,比如model_2018model_2019model_2020,建议使用list或者data frame。

总的来说,变量名最好是名词,函数名最好是动词,这样我们看到名字就能区分了。

# Good
day_one

# Bad
first_day_of_the_month
djm1

如果可能,尽量不要复用咱们已存在的变量名和和函数名(比如咱们base包里已经有的函数,如sum,mean等),会造成混淆。

# Bad
T <- FALSE
c <- 10
mean <- function(x) sum(x)

2.2 插入空格 Spacing

2.2.1 逗号 Commas

所有逗号前不加空格,逗号后统一空一格,就像通常写英语一样。

# Good
x[, 1]

# Bad
x[,1]
x[ ,1]
x[ , 1]

2.2.2 括号 Parentheses

请勿在常规的函数调用括号两边加空格。

# Good
mean(x, na.rm = TRUE)

# Bad
mean (x, na.rm = TRUE)
mean( x, na.rm = TRUE )

在条件判断即 iffor, or while的前和后括号都加一个空格

# Good
if (debug) {
  show(x)
}

# Bad
if(debug){
  show(x)
}

在所有的函数定义的右括号后加空格(左括号前不加空格)

# Good
function(x) {}

# Bad
function (x) {}
function(x){}

2.2.3 包容运算符 embracing operator

包容运算符{{ }}里使用空格来进行强调。

# Good
max_by <- function(data, var, by) {
  data %>%
    group_by({{ by }}) %>%
    summarise(maximum = max({{ var }}, na.rm = TRUE))
}

# Bad
max_by <- function(data, var, by) {
  data %>%
    group_by({{by}}) %>%
    summarise(maximum = max({{var}}, na.rm = TRUE))
}

2.2.4 中缀运算符 Infix operators

中缀运算符如==+-<-,前后都加空格。

# Good
height <- (feet * 12) + inches
mean(x, na.rm = TRUE)

# Bad
height<-feet*12+inches
mean(x, na.rm=TRUE)

但是也存在例外:

· 高优先级的运算符不加空格::::::$@[[[^, unary -, unary +, and :

# Good
sqrt(x^2 + y^2)
df$z
x <- 1:10

# Bad
sqrt(x ^ 2 + y ^ 2)
df $ z
x <- 1 : 10

· 单个运算符右侧直接连接变量名:

# Good
~foo
tribble(
  ~col1, ~col2,
  "a",   "b"
)

# Bad
~ foo
tribble(
  ~ col1, ~ col2,
  "a", "b"
)

但是如果公式比较复杂还是需要空格:

# Good
~ .x + .y

# Bad
~.x + .y

· 使用 !! 和 !!! 命令时,因为优先级高于其他运算符所以不加空格:

# Good
call(!!xyz)

# Bad
call(!! xyz)
call( !! xyz)
call(! !xyz)

· 帮助运算符

# Good
package?stats
?mean

# Bad
package ? stats
? mean

2.2.5 额外的空格 Extra spaces

在定义变量的如列表时候可以为了整齐美观在 或 <- 前加额外的空格,但是切记不能加空格的地方还是不能加。

# Good
list(
  total = a + b + c,
  mean  = (a + b + c) / n
)

# Also fine
list(
  total = a + b + c,
  mean = (a + b + c) / n
)

2.3 函数的调用 Function calls

2.3.1 命名参数的使用 Named arguments

  R函数的参数通常分为输入数据以及控制参数,当调用一个函数,我们通常会习惯性地省略输入数据的参数名称,不过如果我们需要赋予控制参数缺省值以外的值,建议写上参数名称,而且写完整的名称,不要使用部分匹配。(R输入参数时有部分匹配的功能,比如要输入nrow参数可以用nr或者r。)

# Good
mean(1:10, na.rm = TRUE)

# Bad
mean(x = 1:10, , FALSE)
mean(, TRUE, x = c(1:10, NA))

2.3.2 赋值 Assignment

避免对调用函数进行赋值:

# Good
x <- complicated_function()
if (nzchar(x) < 1) {
  # do something
}

# Bad
if (nzchar(x <- complicated_function()) < 1) {
  # do something
}

唯一例外是赋值捕获返回函数副作用的情况:

output <- capture.output(x <- f())

2.4 控制流 Control flow

2.4.1 代码块 Code blocks

大括号 {} 决定了R代码最重要的层级结构。为了方便阅读:

· { 左括号应位于句末,尽量和相关的代码处于同一行(例如,一个 if 条件语句,一个函数声明,尾部的逗号等)

· 括号里的内容应缩进2个空格

· } 右括号应该处于句首

# Good
if (y < 0 && debug) {
  message("y is negative")
}

if (y == 0) {
  if (x > 0) {
    log(x)
  } else {
    message("x is negative or zero")
  }
} else {
  y^x
}

test_that("call1 returns an ordered factor", {
  expect_s3_class(call1(x, y), c("factor", "ordered"))
})

tryCatch(
  {
    x <- scan()
    cat("Total: ", sum(x), "\n", sep = "")
  },
  interrupt = function(e) {
    message("Aborted by user")
  }
)

# Bad
if (y < 0 && debug) {
message("Y is negative")
}

if (y == 0)
{
    if (x > 0) {
      log(x)
    } else {
  message("x is negative or zero")
    }
} else { y ^ x }

(未完)

评论

此博客中的热门博文

R包编写详细教程

RMarkdown中文报错的问题【解决】