The tidyverse style guide_Part I
最近群里契卡大佬分享了一本薄薄的小册子:《The tidyverse style guide 》,是Hadley Wickham 自己写的关于Tidyverse的编程范式。看了第一部分觉得甚好,在这里随便写下翻译,作为自己的cheatsheet。(第二部分是关于R包里面的范式,暂时还用不到)
书来源:
The tidyverse style guide
前言:
- 有两个R包支持Tidyverse的编程范式引导:
styler和lintr。
Charpter 1:Files
-
文件的命名要避免特殊符号,坚持用数字、字母、
-、_。# Good fit_models.R utility_functions.R # Bad fit models.R foo.r stuff.r -
如果文件是有顺序的,在文件名之前带上数字前缀。如果你有超过10个文件,左边留一个0.
00_download.R 01_explore.R ... 09_model.R 10_visualize.R -
如果中间遗留了什么步骤,可以尝试用
02a或者02b这种。但还是建议重新命名 -
注意不同操作系统之间的大小写敏感问题。最好文件名都是小写的,永远不要有两个文件的名字只是大小写不一样。
-
用
-和=的注释行来把你的文件分成一个个块# Load data --------------------------- # Plot data --------------------------- -
如果你的脚本需要附加包,在一开始就载入他们。这会比你在整个文件中到处加载代码或者在启动文件(比如说
.Rprofile)中隐藏地加载要好。
Charpter 2:Syntax
-
变量和函数的名字应该只用小写字母,数字和
_。# Good day_one day_1 # Bad DayOne dayone -
基本的R会用在函数名(
contrib.url())或者类名(data.frame) 中用到.,但最好只为S3系统保留点这个符号。 -
变量名最好是名词而函数名最好是动词。
# Good day_one # Bad first_day_of_the_month djm1 -
避免重复使用常见的函数和变量
# Bad T <- FALSE c <- 10 mean <- function(x) sum(x) -
像平常的英语语法一样,经常在逗号后面放一个空格,而不是在逗号前面
# Good x[, 1] # Bad x[,1] x[ ,1] x[ , 1] -
对于常规函数的调用,不要在圆括号的里面或者外面放空格
# Good mean(x, na.rm = TRUE) # Bad mean (x, na.rm = TRUE) mean( x, na.rm = TRUE ) -
当使用
if,for, 或者while的时候,在()的前面和后面放空格# Good if (debug) { show(x) } # Bad if(debug){ show(x) } -
Place a space after
()used for function arguments:# Good function(x) {} # Bad function (x) {} function(x){} -
大部分中缀操作符 (
==,+,-,<-, etc.) 的前后都应该有空格# Good height <- (feet * 12) + inches mean(x, na.rm = 10) # Bad height<-feet*12+inches mean(x, na.rm=10) -
但也有些例外
-
有高优先权的一些操作符:
::,:::,$,@,[,[[,^, unary-, unary+, and:。# Good sqrt(x^2 + y^2) df$z x <- 1:10 # Bad sqrt(x ^ 2 + y ^ 2) df $ z x <- 1 : 10unary指的是一元运算符,unary - 指的应该是 -2,-3这种负号。
-
当右侧的表达式是单个identifier,单边的公式
# Good ~foo tribble( ~col1, ~col2, "a", "b" ) # Bad ~ foo tribble( ~ col1, ~ col2, "a", "b" ) -
当右边的表达式很复杂的时候,单边公式应当有个空格
# Good ~ .x + .y # Bad ~.x + .y -
有tidy计算符的时候,比如
!!和!!!# Good call(!!xyz) # Bad call(!! xyz) call( !! xyz) call(! !xyz) -
帮助符
# Good package?stats ?mean # Bad package ? stats ? mean
-
-
为了对齐
=和<-,可以加一些额外的空格# Good list( total = a + b + c, mean = (a + b + c) / n ) # Also fine list( total = a + b + c, mean = (a + b + c) / n ) -
一个函数的参数往往分成两大类, 一种是用来放要计算的数据的,另一种是控制计算的细节的。当调用一个函数的使用,你可以选择性地忽略掉数据参数的名字,因为他们很常用。
# Good mean(1:10, na.rm = TRUE) # Bad mean(x = 1:10, , FALSE) mean(, TRUE, x = c(1:10, NA))避免参数的部分匹配
-
对于花括号的使用有一些准则
-
{应该是行的最后一个字符。一些相关的代码(e.g., anifclause, a function declaration, a trailing comma, …) 必须和{在同一行 - 缩进应该是两个空格
-
}应该是行的第一个字符
# 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 } -
-
如果函数在一行的话,也可以不加花括号。只要其没有副作用(但像print,stop这种似乎是由副作用的)
# Good y <- 10 x <- if (y < 20) "Too low" else "Too high" -
会影响控制流(like
return(),stop()orcontinue)的函数调用应该在其自己的 {} 块内# Good if (y < 0) { stop("Y is negative") } find_abs <- function(x) { if (x > 0) { return(x) } x * -1 } # Bad if (y < 0) stop("Y is negative") if (y < 0) stop("Y is negative") find_abs <- function(x) { if (x > 0) return(x) x * -1 } -
每行的代码应该控制在80个字符左右,这样才方便后来的打印。要学会断行。
# Good do_something_very_complicated( something = "that", requires = many, arguments = "some of which may be long" ) # Bad do_something_very_complicated("that", requires, many, arguments, "some of which may be long" ) -
对于一些很常见的参数,你可以不写参数名字。如果不写参数名的参数很短,可以放在一行里面。
map(x, f, extra_argument_a = 10, extra_argument_b = c(1, 43, 390, 210209) ) -
如果几个参数很相关,那么就可以放在一行里面,比如说
paste或者stop这种。# Good paste0( "Requirement: ", requires, "\n", "Result: ", result, "\n" ) # Bad paste0( "Requirement: ", requires, "\n", "Result: ", result, "\n") -
在变量赋值的时候使用
<-而不是=# Good x <- 5 # Bad x = 5 -
不要用
;……(个人觉得) -
在用引号包字符串的时候,用
""而不是''。唯一的例外是你字符串里面有双括号,而没有单括号。# Good "Text" 'Text with "quotes"' '<a href="http://style.tidyverse.org">A link</a>' # Bad 'Text' 'Text with "double" and \'single\' quotes' -
在写代码的时候,注释是用来标记重要的发现和决定的。如果你需要注释来解释你的代码在干什么,那么就要考虑重写你的代码,从而让你的代码更加清楚。如果你发现你的注释比代码多了,考虑用Rmarkdown。
Charpter 3:Functions
-
和之前说的一样,函数名应该用动词
# Good add_row() permute() # Bad row_adder() permutation() -
函数里面的参数要对齐
# Good long_function_name <- function(a = "a long argument", b = "another argument", c = "another long argument") { # As usual code is indented by two spaces. } # Bad long_function_name <- function(a = "a long argument", b = "another argument", c = "another long argument") { # Here it's hard to spot where the definition ends and the # code begins } -
return只用在早期的返回值上,后面的返回值依赖于R的自动返回。
# Good find_abs <- function(x) { if (x > 0) { return(x) } x * -1 } add_two <- function(x, y) { x + y } # Bad add_two <- function(x, y) { return(x + y) } -
return应该在其自己那行
# Good find_abs <- function(x) { if (x > 0) { return(x) } x * -1 } # Bad find_abs <- function(x) { if (x > 0) return(x) x * -1 } -
如果你的函数主要是为了其副作用(打印,画图或者保存到硬盘里面),其应该让第一个参数隐式。这可以让这个函数作为管道的一部分。
print函数应该经常是这样的。一个来自 httr 的例子print.url <- function(x, ...) { cat("Url: ", build_url(x), "\n", sep = "") invisible(x) } -
在代码中,注释应该是解释为什么,而非你要做什么或者怎么做。每个注释都应该以注释符号# 开头,再加上一个空格
# Good # Objects like data frames are treated as leaves x <- map_if(x, is_bare_list, recurse) # Bad # Recurse only with bare lists x <- map_if(x, is_bare_list, recurse) -
Comments should be in sentence case, and only end with a full stop if they contain at least two sentences:
# Good # Objects like data frames are treated as leaves x <- map_if(x, is_bare_list, recurse) # Do not use `is.list()`. Objects like data frames must be treated # as leaves. x <- map_if(x, is_bare_list, recurse) # Bad # objects like data frames are treated as leaves x <- map_if(x, is_bare_list, recurse) # Objects like data frames are treated as leaves. x <- map_if(x, is_bare_list, recurse)