你遇到过最大的R语言陷阱是什么?

58

你有没有遇到过某些R语言的坑让你感到惊讶?我认为我们都可以从分享这些经验中受益。

下面是我的经历:在列表索引中,my.list[[1]]不等于my.list[1]。我在开始学习R的早期就了解了这一点。


9
在《R地狱》http://www.burns-stat.com/pages/Tutor/R_inferno.pdf中,有许多更多的陷阱,大小不一。 - Patrick Burns
4
if-else语句中的空格很重要。如果在if语句的大括号后面换行,然后在else语句前面加上一个空格,就会出现错误:"else"意外出现。具体表现为:if { ... } \n else { ... } - Tomas
choose 函数。choose(n, k) 不是 n 元素集合中 k 元素子集的数量。例如,choose(-4,2) == 10 - Flounderer
29个回答

43
< p>[Hadley在评论中指出了这一点。]< /p> < p>在迭代时,如果使用序列作为索引,最好使用seq_along()函数,而不是像1:length(x)这样的东西。

< p>在这里,我创建了一个向量,两种方法都返回相同的结果:

> x <- 1:10
> 1:length(x)
 [1]  1  2  3  4  5  6  7  8  9 10
> seq_along(x)
 [1]  1  2  3  4  5  6  7  8  9 10

现在将向量设为NULL

> x <- NULL
> seq_along(x) # returns an empty integer; good behavior
integer(0)
> 1:length(x) # wraps around and returns a sequence; this is bad
[1] 1 0

这可能会在循环中引起一些混淆:

> for(i in 1:length(x)) print(i)
[1] 1
[1] 0
> for(i in seq_along(x)) print(i)
>

36

当您加载数据时自动创建因子。 您可能会不假思索地将数据框中的一列视为字符,并且在尝试将值更改为不是级别的值时,这通常效果良好。 这会生成警告,但会将数据框中的值设置为NA ...

如果您的R脚本出现意外错误,请检查因子是否有问题。


7
没问题 - 但您可以在启动文件中使用options("stringsAsFactors"=FALSE)来更改这一点,从而避免将字符串作为因子处理。 - Dirk Eddelbuettel
11
@Dirk,一切都很好,直到你把代码发送给一个使用不同的.Rprofile文件的人(这个情况发生在我这周;))。 - baptiste
1
иҝҷе®һйҷ…дёҠдёҚд»…еҸ‘з”ҹеңЁиҜ»еҸ–ж–Ү件时пјҢиҝҳеҸ‘з”ҹеңЁдҪҝз”Ёdata.frameжһ„йҖ еҮҪж•°ж—¶гҖӮиҝҷд№ҹжӣҫз»ҸеӨҡж¬Ўи®©жҲ‘жҺӘжүӢдёҚеҸҠгҖӮ - prabhasp

32

在将矩阵子集减少到单个维度时,忘记使用drop=FALSE参数,从而同时丢失对象类:

R> X <- matrix(1:4,2)
R> X
     [,1] [,2]
[1,]    1    3
[2,]    2    4
R> class(X)
[1] "matrix"
R> X[,1]
[1] 1 2
R> class(X[,1])
[1] "integer"
R> X[,1, drop=FALSE]
     [,1]
[1,]    1
[2,]    2
R> class(X[,1, drop=FALSE])
[1] "matrix"
R> 

32

删除数据框中的行会导致添加了非唯一命名的行,进而出现错误:

> a<-data.frame(c(1,2,3,4),c(4,3,2,1))
> a<-a[-3,]
> a
  c.1..2..3..4. c.4..3..2..1.
1             1             4
2             2             3
4             4             1
> a[4,1]<-1
> a
Error in data.frame(c.1..2..3..4. = c("1", "2", "4", "1"), c.4..3..2..1. = c(" 4",  : 
  duplicate row.names: 4

这里的情况是:

  1. 创建一个四行数据框,所以行名为c(1,2,3,4)

  2. 删除第三行,所以行名变成了c(1,2,4)

  3. 添加了第四行,R自动将行名设置为索引即4,因此行名为c(1,2,4,4)。这是非法的,因为行名应该是唯一的。我不明白为什么R允许这种类型的行为。在我看来,R应该提供一个唯一的行名。


2
有趣。我自1988年以来一直在使用R及其S前身,但我从未见过这样的东西! - Rob Hyndman
11
这里正在发生的事情是:
  1. 创建了一个四行数据框,所以行名为c(1,2,3,4)。
  2. 删除了第三行,所以行名变成了c(1,2,4)。
  3. 添加了第四行,R会自动将行名设置为索引值,即4,因此行名为c(1,2,4,4)。这是不合法的,因为行名应该是唯一的。
我不明白为什么R要允许这种行为。在我看来,R应该提供一个唯一的行名。
- Ian Fellows
1
非常有趣。两个想法:(1)从长远来看,编辑您的答案并在那里添加解释可能更清晰;(2)您是否考虑将此邮件发送到r-devel邮件列表? - Shane
5
请注意,这是print.data.frame的错误。否则代码将正常运行(带有警告)。 - Eduardo Leoni
1
似乎在R 3.3.3中已经修复。最后一行现在是4.1 1 NA - Nick Kennedy
显示剩余7条评论

25

首先,我要说我理解用二进制系统表示数字的基本问题。然而,我认为可以很容易地改进一个问题,那就是当十进制值超出R的典型表示范围时,数字的表示方式。

x <- 10.2 * 100
x
1020
as.integer(x)
1019

如果结果可以表示为整数,那么我不介意将其表示为整数。例如,如果值确实为1020,则将其打印为x是可以的。但是,在此情况下在打印x时加上像1020.0这样简单的内容会更明显地表明该值不是整数且不能表示为整数。当存在极小的小数部分未显示时,R应默认显示某种指示。


1
我理解你的困境,但这确实很难把握。除了始终将所有内容打印为完整精度外,你能否举一个做得更好的语言的例子呢?(这是一个真正的问题,而不是一个修辞手法...) - Ben Bolker
1
我认为我使用的任何语言都无法很好地处理这个问题,但我认为R目前的方法是最糟糕的,因为它显示浮点数时就像整数一样。简单地显示某种浮点数至少会更好,但这可能会需要更多的小数位。实际上,将上述内容显示为1.0199e3会很有帮助。或者,在另一种情况下,像81.00000001这样的数字呈现8.10e1作为一个令人惊讶的结果,可能暗示着还有更多的小数位。有很多更好的方法,但更差的方法则较少。 - John
1
哦,大多数解释型编程语言会尽可能地尝试打印整个数字。它们至少会将浮点数显示为浮点数。 - John
2
我认为这就是 R 作为交互式数据分析平台的遗产所带来的问题。尝试限制显示的数字位数是有道理的,但很难看出如何实现。也许一个过于聪明的替代方案(永远不会被实现)是始终使用至少一个小数点打印浮点值,即 1.000000000001 将打印为 1.;另一种选择是在整数后面打印显式的 L,但那样会很丑。 - Ben Bolker
我认为如果数字确实可以表示为int,那么最好还是像现在一样以int的形式呈现。但是,如果它非常接近int,但不完全相等,则要在数字后加上小数点。R已经能够解决数字的四舍五入问题。它已经可以知道数字不是一个精确的整数,并将其呈现为精确的整数。因此,在数字后面指示它是否真的精确也不需要太多额外的请求。 - John

20

必须考虑到NANaNInf的组合可能会让人感到烦恼。它们的行为有所不同,对其中一个进行测试并不一定适用于其他的情况。

> x <- c(NA,NaN,Inf)
> is.na(x)
[1]  TRUE  TRUE FALSE
> is.nan(x)
[1] FALSE  TRUE FALSE
> is.infinite(x)
[1] FALSE FALSE  TRUE

然而,测试这些麻烦制造者最安全的方法是:

> is.finite(x)
[1] FALSE FALSE FALSE

8
有趣...我一直把NA理解为“我还不知道”,但我的解释与is.infinite(NA)is.finite(NA)返回的“FALSE”不一致:我原本预期的是NA - mariotomo

18

始终测试当您有一个 NA 时会发生什么!

在许多痛苦的经历之后,我始终需要特别注意的一件事情就是 NA 值。R函数易于使用,但编程方式无法克服数据问题。

例如,任何带有 NA 的向量操作都等于 NA。这在表面上看似乎有些“令人惊讶”:

> x <- c(1,1,2,NA)
> 1 + NA
[1] NA
> sum(x)
[1] NA
> mean(x)
[1] NA

这会被推广到其他更高级的函数中。

换句话说,默认情况下,缺失值通常与测量值一样重要。许多函数具有na.rm=TRUE/FALSE默认值;值得花些时间决定如何解释这些默认设置。

编辑1:Marek提出了一个很好的观点。 NA 值还会在索引中导致混乱的行为。例如:

> TRUE && NA
[1] NA
> FALSE && NA
[1] FALSE
> TRUE || NA
[1] TRUE
> FALSE || NA
[1] NA

当你尝试创建条件表达式(用于if语句)时,这也是正确的:

> any(c(TRUE, NA))
[1] TRUE
> any(c(FALSE, NA))
[1] NA
> all(c(TRUE, NA))
[1] NA

当这些 NA 值成为您的向量索引时,可能会出现许多意想不到的情况。对于 R 来说,这都是良好的行为,因为这意味着您必须小心处理缺失值。但是,这可能会在开始时引起重大头痛。


2
下标操作很痛苦,例如(1:3)[c(TRUE,FALSE,NA)]会得到1,NA。当你在一个包含NA的向量上创建逻辑向量(1:3)[c(1,2,NA)<2]时,很容易陷入这种情况。 - Marek

13

round 函数总是向最接近的偶数舍入。

> round(3.5)
[1] 4  

> round(4.5)
[1] 4

3
我很确定这是浮点数陷阱,而不是R语言陷阱。 - Marek
1
维基百科上有一些关于这种舍入方式的有用信息,这归功于IEEE 754浮点规范。 - Iterator

13

忘记了 strptime() 和相关函数返回的是 POSIXt POSIXlt,其中 length() 总是九 -- 转换为 POSIXct 会有所帮助:

R> length(strptime("2009-10-07 20:21:22", "%Y-%m-%d %H:%M:%S"))
[1] 9
R> length(as.POSIXct(strptime("2009-10-07 20:21:22", "%Y-%m-%d %H:%M:%S")))
[1] 1
R> 

2
...现在在R 2.14.0版本(以及可能一些早期版本)中返回长度为1... - Tommy

12

整数的数学运算与双精度浮点数(有时复数也很奇怪)的运算略有不同。

更新:在 R 2.15 中修复了一些问题。

1^NA      # 1
1L^NA     # NA
(1+0i)^NA # NA 

0L %/% 0L # 0L  (NA from R 2.15)
0 %/% 0   # NaN
4L %/% 0L # 0L  (NA from R 2.15)
4 %/% 0   # Inf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接