简单的功能在r

时间:2015-09-16 23:50:53

标签: r

我一直在尝试创建一个非常简单的功能。基本上我希望t$C中的每个元素都根据我的代码中的if then语句进行更改,而其他元素保持不变。所以这是我的代码:

set.seed(20)
x1=rnorm(100)
x2=rnorm(100)
x3=rnorm(100)
t=data.frame(a=x1,b=x1+x2,c=x1+x2+x3)
fun1=function(multi1,multi2)
{
  v=t$c
  s=c()
  for (i in v)
  {
    if (i<0)
    {
      s[i]=i*multi1
    }
    else if(i>0)
    {
      s[i]=i*multi2
    }
  }

  return(s)
}

fun1(multi1=0.5,multi2=2)

但它给了我一些数字。我觉得我可能会犯一些愚蠢的错误,但我无法理解。

1 个答案:

答案 0 :(得分:10)

tl; dr 此操作可以进行矢量化。假设您希望保留仅0NA的值,则可以使用以下方法。

with(t, c * ifelse(c < 0, 0.5, ifelse(c > 0, 2, 1)))

如果你想将它们包含在一边(例如正面),那就更简单了。

with(t, c * ifelse(c < 0, 0.5, 2))

就你的循环而言,你在那里遇到了一些问题。

首先,您使用十进制值索引s,这可能会导致计算错误。这也是你的结果向量如此之短的原因。当你在循环中建立索引时,索引被移动到整数值,并且由于其中一些被重复,s最终变得很短。

实际的唯一索引长度是这样的 -

length(unique(as.integer(t$c)))
# [1] 9

结果,作为一个简单的例子,你得到了

s[c(1, 2, 1, 1)] <- something

由于重复1,因此只改变了索引1和2。这就是循环中发生的事情。进一步说明为

x <- 1:5
x[1.2]
# [1] 1
x[1.99]
# [1] 1

接下来,请注意我们已经分配了向量s。我们可以这样做,因为我们知道结果向量的长度将与v相同。这是推荐的,更有效的方式,而不是在循环中构建向量。

继续,我将for(i in v)更改为for(i in seq_along(v))以更正此问题。现在我们使用i的序列进行索引。然后我们还需要以相同的方式索引v。最后,我们可以分配s[i] <- if(...而不是分配给if()语句中的相同索引。

另请注意,您尚未考虑0v中可能出现的任何其他值(如NA)。我添加了最终else,我们只留下这些值。根据需要更改。此外,我们可以将它作为一个参数传递给全局环境,而不是去全局环境来获取t$c,而是使这个函数更通用(对于该建议,可归功于@ShawnMehan)。这是修改后的版本:

fun1 <- function(vec, multi1, multi2) {
    s <- vector("numeric", length(vec))
    for (i in seq_along(vec)) {
        s[i] <- if (vec[i] < 0) {
            vec[i] * multi1
        } else if(vec[i] > 0) {
            vec[i] * multi2
        } else {
            vec[i]
        }
    }
    return(s)
}

所以现在我们得到一个长度为100的结果

x <- fun1(t$c, 0.5, 2)
str(x)
# num [1:100] 2.657 -0.949 7.423 -0.749 5.664 ...

我写了这个很长的解释,因为我想你正在学习如何写一个循环。在R中,我们可以对整个操作进行矢量化并将其放入一行代码中。以下行提供与fun1(t$c, 0.5, 2)相同的结果。

with(t, c * ifelse(c < 0, 0.5, ifelse(c > 0, 2, 1)))

感谢@Frank抓住我的计算监督。

希望这一切都有道理。有时候我不会用解释和技术术语做得好。如果有任何问题,请发表评论。