我一直在尝试创建一个非常简单的功能。基本上我希望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)
但它给了我一些数字。我觉得我可能会犯一些愚蠢的错误,但我无法理解。
答案 0 :(得分:10)
tl; dr 此操作可以进行矢量化。假设您希望保留仅0
或NA
的值,则可以使用以下方法。
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()
语句中的相同索引。
另请注意,您尚未考虑0
或v
中可能出现的任何其他值(如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抓住我的计算监督。
希望这一切都有道理。有时候我不会用解释和技术术语做得好。如果有任何问题,请发表评论。