持续更新R语言编程中的一些小技巧,包括一些易错点。但是,需要注意的,一旦需要使用一些非常稀奇古怪的技巧时,可以尝试反思是否是自己数据结构或者算法出了问题。
if
爱你,else
就要和你在一起,又要给你一些距离if (con){
expr
} else{
expr2
}
if...else..
结构中,}
与else
必须在同一行。
简易形式(不推荐):
# else不能与expr在同一行
if (con) expr
else expr2
建议将所有if ... else ...
结构写成第一种形式,如果没有else
,用else {}
代替。
NULL
是空,NULL
是气,NULL
在R中就是空气。如果表达式(expression)或者函数(function)的值没有定义,那么将返回NULL
。在R中,NULL
与很多对象合并在一起时,都被忽略掉。比如以下例子:
# NULL is neglected in vector
> c(1, NA, NULL, FALSE)
[1] 1 NA 0
# also in matrix
> matrix(c(1, NA, NULL, 2), c(2, 2))
[,1] [,2]
[1,] 1 2
[2,] NA 1
警告信息:
In matrix(c(1, NA, NULL, 2), c(2, 2)) : 数据长度[3]不是矩阵行数[2]的整倍
# also in list
> tmp <- list(1, 2)
> tmp[1] <- NULL
> tmp
[[1]]
[1] 2
> tmp[[1]] <- NULL
> tmp
list()
如果一定要将列表中的某个个元素赋值为NULL,可以使用list(NULL),比如:
> tmp <- list()
> tmp[1] <- list(NULL)
> tmp
[[1]]
NULL
> is.null(tmp[[1]])
[1] TRUE
列表(list)在R中是个全能选手、无所不“包”;list还是金箍棒,别看它丫的开始长度(length()
)短,之后想变长就变长,多长都行。
这暗示list可以这样用:在循环开始时,我们不知道会产生多少个元素,可以事先规定输出结果为a <- list()
,之后赋值;甚至可以按照名字赋值,比如a$add <- exprs
。
如果要创建一个长度为n的、每个元素都为
NULL
的list,可以使用vector("list", n)
。
如果要在list中插入元素,可以使用
append(x, values, after = length(x))
函数。append()
函数原本是对vector设计的,但是list也能使用。
如果要创建一个空的对象(对象的长度是0)很容易,比如:
numeric()
或者numeric(0)
character()
或者character(0)
integer()
或者integer(0)
可以起到同样的效果
factor()
创建一个长度为0的因子注意:
factor(0)
的长度是1
matrix(0, 0, 0)
注意:
array()
创建的对象长度为1。
list()
data.frame()
R中一些对象转换要格外小心,特别是牵扯到data.frame类型。以下是几个常见的陷阱:
需要先转成字符串向量过渡一下:
as.numeric(as.character(MyFactor))
每一列是按照字符型因子保存。留意表面上是数值的一列,它非常危险,特别是排序。一个例子:
> a1 <- as.factor(as.character(a1))
> a1
[1] 5 6 7 8 9 10 11 12 13 14 15
Levels: 10 11 12 13 14 15 5 6 7 8 9
> sort(a1)
[1] 10 11 12 13 14 15 5 6 7 8 9
Levels: 10 11 12 13 14 15 5 6 7 8 9
else
/if
别担心,switch()
函数来帮助如果需要在不同情况下,对变量进行不同的处理,自然会联想到使用多个多个else
/if
。如果else
/if
是用来处理的简单的赋值,可以使用switch(EXPR, ...)
函数。其中,EXPR
是一个表达式,生成一个数字或者字符串;接下来,使用生成的数字和字符串匹配...
中的内容。
所以,我们很容易想到,如果EXPR
生成的数字,则对应匹配...
的第几个元素,此时...
中的变量可以没有名字;如果是字符串,就会严格地匹配...
中相同名字的变量;如果EXPR
生成是字符串,...
中没有相匹配的变量名,则返回NULL
;如果...
中有一个“无名”变量(即没有对变量赋值),上一种情况下返回该无名变量,相当于最后一个else
。比如:
> switch(1, a = 3, b = 2)
[1] 3
> switch(2, aa = 'red', bb = 'blue', cc = 'yellow', 'No value')
[1] "blue"
> switch('aa', aa = 'red', bb = 'blue', cc = 'yellow', 'No value')
[1] "red"
> switch('dd', aa = 'red', bb = 'blue', cc = 'yellow', 'No value')
[1] "No value"
> switch('bb', aa = 'red', bb = , cc = 'yellow')
[1] "yellow"
> switch(2, aa = 'red', bb = , cc = 'yellow')
Error: empty alternative in numeric switch
> switch('dd', aa = 'red', bb = , cc = 'yellow', 'no', 'value')
Error: duplicate switch defaults: '"no"' and '"value"'
注意:
如果EXPR
是数字,范围不能超过...
的长度。同时,虽然此时...
变量可以名字,但是有名字的变量必须有数值,不能学习以上例子中倒数第二种情况,否则会报错。
如果EXPR
是字符串,那么会进行自动继承,即前者继承后者。
虽然EXPR
是字符串情况可以自动继承,甚至继承一个无名变量,但是无名变量只能有一个。
最后将EXPR
明确写出,为了R的版本兼容。
总结:
swich()
函数是一个好玩却又危险的函数,一个没有歧义的使用方法是:EXPR
明确写出,且返回字符串,...
中每一个变量都有确切的值,无名变量只有一个,比如:switch(EXPR='dd', aa = 'red', bb = 'blue', cc = 'yellow', 'no value')
。
drop
选项巧帮助当对象是一个矩阵的时候,通常我们要获取其中一些元素。如果取出的正好是一行或者一列,此时,R会返回给我们一个向量,而不是矩阵。这会带来很大的麻烦,因为我们必须判断返回对象是向量,还是矩阵,才能继续下面的操作。此时,drop
可以帮助我们,如果设置drop = FALSE
,就会保持原样返回结果。
> b <- matrix(1:6, nrow = 2, ncol = 3)
> b
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
> b[, 3]
[1] 5 6
> b[, 3, drop = FALSE]
[,1]
[1,] 5
[2,] 6
> b[1, , drop = FALSE]
[,1] [,2] [,3]
[1,] 1 3 5
|
和 ||
有区别,返回向量与单值R中的逻辑运算符号有两组,|
或者||
(&
或者&&
),看起来觉得有些多余。其实,这是为了R独特的“向量运算”考虑的。|
或者&
首先考虑返回一个向量,而||
和&&
则只返回第一个比较结果。比如:
> c(FALSE, TRUE) | c(FALSE, FALSE)
[1] FALSE TRUE
> c(FALSE, TRUE) || c(FALSE, FALSE)
[1] FALSE
R在解释代码时,不支持尾递归优化,所以在R中不要写递归。
0
和NA
来代替在R绘图的图例中,通常需要“线条”和“点”分开单独表示,这里用到0
和NA
的小技巧。如果在legend()
函数函数中,pch = NA
表示没有点,lty=0
表示没有线条。
eval()
/parse()
很随意如果我们有一个字符字符串,现在需要将其转为可执行代码。方法如下:
> test1 = '1:3'
> test1
[1] "1:3"
> eval(parse(text = test1))
[1] 1 2 3
c()
函数有妙招如果我们需要对一个factor的标签(level)进行转换,并且返回一个向量,有两种方法可以使用:
c()
函数> tmp1 <- factor(rep(1:3, each = 3))
> tmp1
[1] 1 1 1 2 2 2 3 3 3
Levels: 1 2 3
> c(letters[1:3])[tmp1]
[1] "a" "a" "a" "b" "b" "b" "c" "c" "c"
as.character()
函数。> tmp2 <- factor(tmp1, labels = letters[1:3])
> tmp2
[1] a a a b b b c c c
Levels: a b c
> as.character(tmp2)
[1] "a" "a" "a" "b" "b" "b" "c" "c" "c"
同时,配合使用droplevels()
(去除多余level)、relevel()
(以某个level为基准)和ordered(.., levels)
(调整level顺序)等操作factor的level。
> "+"(1, 2)
[1] 3
> '>'(1, 2)
[1] FALSE
> tmp1 = list()
> for(i in 1:10)
tmp1[[i]] = i:(i+1)
> tmp1
[[1]]
[1] 1 2
[[2]]
[1] 2 3
[[3]]
[1] 3 4
[[4]]
[1] 4 5
[[5]]
[1] 5 6
[[6]]
[1] 6 7
[[7]]
[1] 7 8
[[8]]
[1] 8 9
[[9]]
[1] 9 10
[[10]]
[1] 10 11
> sapply(tmp1, '[[', 2)
[1] 2 3 4 5 6 7 8 9 10 11
最后一个例子中,使用了[[
,巧妙地取得了第二个元素。
quote
参数来帮忙在使用write.table()
命令输入一个data.frame对象时候,输入文件会加引号。这时,设定quote
参数,write.table(..., quote = FALSE)
。
identical()
do.call()
do.call(rbind, list)
split()
> test1 <- rep(c('a', 'b'), c(3, 4))
> test1
[1] "a" "a" "a" "b" "b" "b" "b"
> split(test1, factor(test1))
$a
[1] "a" "a" "a"
$b
[1] "b" "b" "b" "b"
sink()
sink()
可以将R的对象输出到外部文档中,第一遍可能包括了系统信息,需要输出第二遍。一个典型的流程如下:
> sink(filename)
> test1
> sink()
R中for循环中,需要指定一个计数器,比如i in 1:10之类。R可以直接将一个向量用于计数,返回向量中对应的值,比如:
> tmp1 <- letters[1:10]
for (i in tmp1) {
print(i)
}
> [1] "a"
[1] "b"
[1] "c"
[1] "d"
[1] "e"
[1] "f"
[1] "g"
[1] "h"
[1] "i"
[1] "j"
Reduce()
> Reduce(union, list(letters[1:3], letters[1:4], letters[1:5]))
[1] "a" "b" "c" "d" "e"
source_url
需要安装和调用devtools包
> library(devtools)
> source_url('https://raw.githubusercontent.com/YulongNiu/FunFunc/master/test_two_proportion.R')
2022年09月06日