我生成了一个示例数据集,以尝试进行一些基准比较以进行比较。 (如果它不能充分代表您的实际数据,请告诉我,我会再试一次。我不确定这是否重要,因为 pmin 和 pmax 往往与千分之一1。)
new_rank2 <- function(rnk,penalty,min_rnk,max_rnk){
rnk2 = max(min(rnk + penalty, max_rnk), min_rnk)
return(rnk2)
}
set.seed(1)
n <- 20000
step4 <- data.frame(rnk = runif(n), penalty = runif(n),
min_rnk = runif(n), max_rnk = runif(n))
step4b <- step4c <- step4d <- step4
三种方法的基本性能。首先,你的迭代方法:
system.time(
for(i in 1:nrow(step4b)){
step4b$rnk2[i] <- new_rank2(step4b$rnk[i], step4b$penalty[i],
step4b$min_rnk[i], step4b$max_rnk[i])
}
)
## user system elapsed
## 3.40 0.00 3.41
二、矢量化方法:
system.time(
step4c$rnk2 <- with(step4, pmax(pmin(rnk + penalty, max_rnk), min_rnk))
)
## user system elapsed
## 0.02 0.00 0.02
三、利用Hadley Wickham的dplyr的方法:
library(dplyr)
system.time(
step4d <- step4 %>%
mutate(rnk2 = pmax(pmin(rnk + penalty, max_rnk), min_rnk))
)
## user system elapsed
## 0 0 0
虽然对于 20k 条记录,我与您的 120 秒记录相差无几,但我猜测计算量比 rnk2 计算量要多。 (顺便说一句:我的测试计算机是 2 年以上的 i7 2.8GHz,具有 8GB RAM,运行 R-3.1.1 和 cough cough win7。)
这些方法都产生相同的结果:
identical(step4b, step4c)
## [1] TRUE
identical(step4b, step4d)
## [1] TRUE
由于不应将单次运行视为绝对基准,因此进行更严格的比较可能会更有洞察力。
library(microbenchmark)
microbenchmark(
iterative = {
for(i in 1:nrow(step4b)){
step4b$rnk2[i] <- new_rank2(step4b$rnk[i], step4b$penalty[i],
step4b$min_rnk[i], step4b$max_rnk[i])
}
},
vectorized = {
step4c$rnk2 <- with(step4, pmax(pmin(rnk + penalty, max_rnk), min_rnk))
},
dplyr = {
step4d <- step4 %>%
mutate(rnk2 = pmax(pmin(rnk + penalty, max_rnk), min_rnk))
}
)
## Unit: milliseconds
## expr min lq median uq max neval
## iterative 3151.235603 3226.225834 3257.488366 3286.452867 3504.440315 100
## vectorized 1.098110 1.159931 1.195153 1.247251 3.051811 100
## dplyr 1.350165 1.418957 1.524622 1.604054 3.255437 100
仅在这个循环中,测试用例就有超过三个数量级的差异,并且使用了 20k 条记录。选择使用矢量化代码还是 Hadley 的 dplyr 是个人选择,很大程度上受代码复杂性的影响;在这种情况下,我很难不使用矢量化代码,但这只是我和这个例子。
对于您的第二批代码,首先请注意,在您的第二个 if 语句中,您应该将 == 替换为单个 = 或(有些人可能会认为“更好”)<-。改变这个:
if (data$V5[i] == "MED") data$V3[i] == 'XL'
到
if (data$V5[i] == "MED") data$V3[i] <- 'XL'
否则,条件的“then”部分 data$V3[i] == 'XL' 会缩减为 FALSE,而不是将 'XL' 分配到 data$V3[i] 数组元素中。
您可以使用以下方式矢量化您的 for 循环:
data$V3 <- NA
data$V3 <- ifelse(data$V1 %in% c('A', 'B', 'E'),
data$V4,
ifelse(data$V5 == 'MED',
'XL',
data$V3))
我在这里首先将$V3 设置为NA 主要是因为我不知道其他地方发生了什么;实际上,我假设它已经设置为一个合理的值,并且您正在有条件地更改它。使用嵌套的ifelse,这仍然有些可读性,但我会防止嵌套更多。如果需要更多条件,您可能会通过以下方式获得更好的可读性(也许还有性能):
idx <- data$V1 %in% c('A', 'B', 'E')
data$V3[idx] <- data$V4[idx]
idx <- (data$V5 == 'MED')
data$V3[idx] <- 'XL'
## ...
...尽管如果任何测试允许数据多次匹配,您需要小心,并且比较的顺序会影响更新。
脚注:
- 为了清楚起见,我并不是说以千分之一或千分之一进行数学运算是等价的,只是比较。乘法(例如 1e8 之类的数字)的效率使其比除法(相同 OOM)稍微快一点,但比较本身是等价的。