我对以下代码的结果感到惊讶。我希望(0,10,5,0)。
w <- numeric(4)
subw <- c(2,3,2) # these would have been picked at random with replacement
w[subw] <- w[subw] + 5
它产生(0,5,5,0)。我曾希望R会绕过这三个指数。这个例子是我真正想要做的一个简化的例子。子样本将由样本函数生成(替换这就是索引可能重复的原因)并且w的长度会更长。这将是蒙特卡罗模拟运行多次的一部分,所以我希望它快速,从而避免for循环。
This stackoverflow post似乎解释了为什么重复索引似乎被忽略。我希望有人会建议一个有效和明确的实施(也许是一个适用)来实现我的目标。我发现这有效,但它很难看:
w<-numeric(4)
subw <- c(2,3,2)
tbl <- table(subw)
w[as.numeric(names(tbl))]<-w[as.numeric(names(tbl))]+as.numeric(tbl)*5
事实证明,for循环for(i in samp) w[i]<-w[i]+wt.incr
比使用表函数快得多。
答案 0 :(得分:2)
这将很快
w = w + tabulate(subw, length(w)) * 5
但需要稍微考虑一下所需操作所暗示的交换/关联关系。当subw很长时,它胜过简单的for ()
循环。
以下是作为功能的解决方案
f1 = function(x, s, incr = 5) {
for (i in s)
x[i] = x[i] + incr
x
}
f2 = function(x, s, incr = 5)
x + tabulate(s, length(x)) * incr
add5 <- function(vec, i, incr = 5) { vec[i] <- vec[i] + incr ; vec ; }
f3 = function(x, s, incr = 5)
Reduce(add5, s, init = x)
一些正确性测试
identical(f1(w, subw), f2(w, subw))
identical(f1(w, subw), f3(w, subw))
和一些速度测试
> library(microbenchmark)
> microbenchmark(f1(w, subw), f2(w, subw), f3(w, subw))
Unit: microseconds
expr min lq mean median uq max neval cld
f1(w, subw) 1.777 1.9860 2.22398 2.0665 2.2240 12.491 100 a
f2(w, subw) 4.429 4.6470 5.05318 4.8060 5.0635 14.447 100 a
f3(w, subw) 10.087 10.7365 32.88477 11.0870 11.4360 2186.267 100 a
> subw = rep(subw, 100); microbenchmark(f1(w, subw), f2(w, subw), f3(w, subw))
Unit: microseconds
expr min lq mean median uq max neval cld
f1(w, subw) 64.109 64.6135 69.06132 65.0020 66.8465 136.782 100 b
f2(w, subw) 8.385 9.2055 10.29200 9.9430 10.7445 27.038 100 a
f3(w, subw) 498.359 502.5645 531.55586 510.8075 528.6180 922.741 100 c
> subw = rep(subw, 100); microbenchmark(f1(w, subw), f2(w, subw), f3(w, subw))
Unit: microseconds
expr min lq mean median uq max neval
f1(w, subw) 6109.118 6179.5460 6360.9743 6336.36 6464.728 7172.804 100
f2(w, subw) 362.895 378.0825 396.5647 387.67 399.590 693.424 100
f3(w, subw) 48699.123 51214.5500 53320.6088 52772.97 54681.484 68083.120 100
cld
b
a
c
> w = rep(w, 100); microbenchmark(f1(w, subw), f2(w, subw), f3(w, subw))
Unit: microseconds
expr min lq mean median uq max
f1(w, subw) 6107.856 6218.161 6318.051 6312.1125 6397.8395 6653.964
f2(w, subw) 362.744 374.898 388.536 388.7945 398.7475 437.099
f3(w, subw) 67727.781 68851.986 72846.097 69514.9865 70518.8100 194103.885
neval cld
100 b
100 a
100 c
> w = rep(w, 100); microbenchmark(f1(w, subw), f2(w, subw))
Unit: microseconds
expr min lq mean median uq max neval cld
f1(w, subw) 6202.629 6271.900 6504.5917 6387.843 6521.6990 10911.398 100 b
f2(w, subw) 686.987 792.672 839.5853 799.350 822.1955 3842.472 100 a
当然,正确性和速度并不是一切,显然相对性能取决于问题的(未指明的)大小。
答案 1 :(得分:1)
您经常需要这种索引行为,特别是在“字典查找”之类的场景中,您希望查找一次,然后单独保存。这是一个穷人的“加入”或“合并”操作:
df <- data.frame(i=1:5, k=c('a','b','c','a','c'))
dictionary <- c(a=11,b=22,c=33,d=44,e=55)
df$v <- dictionary[ df$k ]
df
# i k v
# 1 1 a 11
# 2 2 b 22
# 3 3 c 33
# 4 4 a 11
# 5 5 c 33
不幸的是,您需要找到一种方法来迭代每个值并使其工作完成。
有人可能试图尝试sapply
或其中一个朋友,但是一次计算的状态不会执行:每次调用函数(sapply
的第二个参数)时,无论是什么以前回来的时间不详。
所以你需要做各种各样的滚动。您可能可以使用zoo::rollapply
,但另一种技术是“减少”它,前一步的返回值是此迭代的输入。我们将初始条件设置为原始零向量w
,并在每个subw
上“迭代”:
add5 <- function(vec, i) { vec[i] <- vec[i] + 5 ; vec ; }
Reduce(add5, subw, init=w)
# [1] 0 10 5 0
这实际上是在呼叫
vec <- w
(vec <- add5(vec, subw[1]))
# [1] 0 5 0 0
(vec <- add5(vec, subw[2]))
# [1] 0 5 5 0
(vec <- add5(vec, subw[3]))
# [1] 0 10 5 0
您可以将其用于教学目的:
Reduce(function(vec,i) { vec[i] <- vec[i] + 5; vec }, subw, init=w, accumulate=TRUE)
# [[1]]
# [1] 0 0 0 0
# [[2]]
# [1] 0 5 0 0
# [[3]]
# [1] 0 5 5 0
# [[4]]
# [1] 0 10 5 0
(顺便说一下:引擎盖下,Reduce
实际上是在使用for
循环,但我更喜欢使用它,因为它清楚地表明(至少对我来说)发生了什么。 。)