8.1 幽灵(no.61~no70)

8.1 幽灵(no.61~no70)

8.1.61 错误的迭代(I)

for循环的迭代可以适用于任何向量。这使循环比其他语言更加通用,但是也会使一些人困惑:

nums <- seq(-1, 1, by=.01)
ans <- NULL
for(i in nums) ans[i] <- i^2

有两处错误。你应该意识到我们已经在第二轮回遇到过了(但失败了),ans的索引并不是我们期望的那样。最好这样做:

nums <- seq(-1, 1, by=.01)
ans <- numeric(length(nums))
for(i in seq(along=nums)) ans[i] <- nums[i]^2

再进一步,当然,应该避免循环。在这个示例中是这样的,在真实的应用中就不一定了。

8.1.62 错误的迭代(II)

这样一个循环:

for(i in 0:9) {
this.x <- x[i]
...

不会按照预期那样。尽管C和其他一些语言的索引从0开始,R是从1开始的。幸运的是在这个案例中,索引为0是允许的,但是没有做我们想要的操作。

8.1.63 错误的迭代(III)

nam <- c(4, 7)
vec <- rep(0, length(nam))
names(vec) <- nam
for(i in nam) vec[i] <- 31
vec
4  7
0  0  NA  31  NA  NA  31

8.1.64 迭代神圣不可侵犯

下述循环中有两个”i”。

> for(i in 1:3) 
{
  cat("i is", i, "\n")
  i <- rpois(1, lambda=100)
  cat("end iteration", i, "\n")
}
i is 1
end iteration 93
i is 2
end iteration 91
i is 3
end iteration 101

循环体中所创建的i在迭代的过程中被使用,但是并没有改变下一次迭代的迭代控制的i。这和其他很多语言不同(包括S+)。

这是R很难被搞混的证据,但是这样的代码毫无疑问会迷惑人类。所以避免吧。

8.1.65 错误的序列

> seq(0:10)
[1] 1 2 3 4 5 6 7 8 9 10 11
> 0:10
[1] 0 1 2 3 4 5 6 7 8 9 10
> seq(0, 10)
[1] 0 1 2 3 4 5 6 7 8 9 10

我想说的并不是第二个或者第三个命令,但是将它们混在一起将使你得到错误的结果。

8.1.66 空的字符串

不要混淆:

character(0)

""

第一个是一个长度为0的向量,如果有的话,它的元素是字符型的。第二个是一个长度为1的向量,它的元素是一个空的字符串。

函数nchar()作用在第一个对象上是一个长度为0的数值型向量,作用在第二个上是0—意思是长度为1的向量它的第一个且仅有的元素是0.

> nchar(character(0))
numeric(0)
> nchar("")
[1] 0

8.1.67 NA 字符串

字符型的数据中会有缺失值。在正常输出的缺失值是:NA;但是当没有使用引号时,会这样输出:<NA>。和"NA"区分。

> cna <- c(’missing value’=NA, ’real string’=’NA’)
> cna
missing value  real string
NA             "NA"
> noquote(cna)
missing value  real string
<NA>            NA

NA作为一个字符串确实是存在的。比如金融行业中的Nabisco,地理中的North America,甚至化学中的sodium。尤其是将数据读入R时,字符串的NA将会变为缺失值。有一个代表着缺失值的名字确实是已经令人不愉快的经历。

如果在字符向量中有缺失值,当对这个向量进行操作时,你或许需要采取一些避免措施。

> people <- c(’Alice’, NA, ’Eve’)
> paste(’hello’, people)
[1] "hello Alice"  "hello NA"  "hello Eve"
> ifelse(is.na(people), people, paste(’hello’, people))
[1] "hello Alice"  NA  "hello Eve"

8.1.68 大写

有些人面对R的大小写敏感时,总是很难适应。大小写敏感是意见好的事情。The case of letters REALLy doEs MakE a diFFerencE.

8.1.69 作用域(I)

作用域问题在R中不是很常见因为R使用了对几乎所有情况都很直观的作用域规则。作用域问题经常在将S+代码移植到R中发生。

或许你想知道作用域是什么意思。在评估器中,如果在某一点需要一个有确定名字的对象比如z,然后我们需要直到到哪去寻找z。作用域就是去哪寻找的规则集合。

这是一个简单的例子:

> z <- ’global’
> myTopFun
function ()
{
  subfun <- function() 
  {
    paste(’used:’, z)
  }
  z <- ’inside myTopFun’
  subfun()
}
> myTopFun()
[1] "used: inside myTopFun"

在函数中被使用的z。让我们考虑一下什么不会发生。在函数subfun()被定义的那一个时间点,仅存的z是那个全局环境中的。什么时候对象被赋值不重要。什么地方对象被赋值才重要。同等重要的是当这个函数执行的时候的相关环境。

8.1.70 作用域(II)

最可能出现作用域问题的地方是建模函数。
我们分析一下一下示例。

> scope1
function ()
{
  sub1 <- function(form) coef(lm(form))
  xx <- rnorm(12)
  yy <- rnorm(12, xx)
  form1 <- yy ~ xx
  sub1(form1)
}
> scope1()

(Intercept)  xx
-0.07609548  1.33319273


> scope2
function ()
{
  sub2 <- function() 
  {
    form2 <- yy ~ xx
    coef(lm(form2))
  }
  xx <- rnorm(12)
  yy <- rnorm(12, xx)
  sub2()
}
> scope2()
(Intercept)  xx
-0.1544372   0.2896239

scope1()和scope2()作用相同。但是scope3()不同—它跳出了自然嵌套的环境。

> sub3
function () 
{
  form3 <- yy ~ xx
  coef(lm(form3))
}
> scope3
function () 
{
  xx <- rnorm(12)
  yy <- rnorm(12, xx)
  sub3()
}
> scope3()
Error in eval(expr, envir, enclos) : Object "yy" not found

这里的一个教训是调用函数没有找到。(技术层面,这是一个动态调用而不是R使用的词典调用。)

当然有解决方案。scope4()通过告诉在哪里寻找代码涉及的数据来解决这个问题。

> sub4
function (data) 
{
  form4 <- yy ~ xx
  coef(lm(form4, data=data))
}
> scope4
function () 
{
  xx <- rnorm(12)
  yy <- rnorm(12, xx)
  sub4(sys.nframe())
}
> scope4()
(Intercept)  xx
0.6303816    1.0930864

另一种可能的方法是改变代码的环境:

> sub5
function (data) 
{
  form5 <- eval(substitute(yy ~ xx), envir=data)
  coef(lm(form5))
}
> scope5
function () 
{
  xx <- rnorm(12)
  yy <- rnorm(12, xx)
  sub5(sys.nframe())
}
> scope5()
(Intercept)   xx
0.1889312     1.4208295

解决方案的应该注意一些事情—并不是所有的建模函数遵循参数的规则。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值