我的目标是迭代df
中的每一列,然后针对每一列向下迭代每一行并执行一个函数。在这种情况下,特定功能将NA
值替换为最后一列中的相应值,但是所需功能的详细信息与此处的问题无关。我使用两个嵌套的for循环获得了所需的结果:
for (j in 1:ncol(df.i)) {
for (i in 1:nrow(df.i)) {
df.i[i,j] <- ifelse(is.na(df.i[i,j]), df.i[i,39], df.i[i,j])
}
}
但是,我相信应该使用嵌套在apply(df.i, 1, function)
中的apply(df.i, 2, function)
来做到这一点,但是我并不完全确定这是可行的或如何做到的。有谁知道如何嵌套使用apply
函数来实现同一目的?
答案 0 :(得分:2)
有四种方法可以执行内部指令的工作。
首先,是一个数据集示例。
set.seed(5345) # Make the results reproducible
df.i <- matrix(1:400, ncol = 40)
is.na(df.i) <- sample(400, 50)
现在,@ Dave2e的评论:仅一个for
循环就将最里面的一个向量化。
df.i2 <- df.i3 <- df.i1 <- df.i # Work with copies
for (j in 1:ncol(df.i1)) {
df.i1[,j] <- ifelse(is.na(df.i1[, j]), df.i1[, 39], df.i1[, j])
}
然后完全矢量化,无循环。
df.i2 <- ifelse(is.na(df.i), df.i[, 39], df.i)
由@Gregor在评论中进行矢量化的另一个方法要好得多,因为已知ifelse
相对较慢。
df.i3[is.na(df.i3)] <- df.i3[row(df.i3)[is.na(df.i3)], 39]
以及您的解决方案,如问题中所述。
for (j in 1:ncol(df.i)) {
for (i in 1:nrow(df.i)) {
df.i[i,j] <- ifelse(is.na(df.i[i,j]), df.i[i,39], df.i[i,j])
}
}
比较结果。
identical(df.i, df.i1)
#[1] TRUE
identical(df.i, df.i2)
#[1] TRUE
identical(df.i, df.i3)
#[1] TRUE
基准。
@Gregor发表评论后,我决定对这4个解决方案进行基准测试。不出所料,每次优化都会产生明显的渗漏,而他的完全矢量化解决方案是最快的。
f <- function(df.i){
for (j in 1:ncol(df.i)) {
for (i in 1:nrow(df.i)) {
df.i[i,j] <- ifelse(is.na(df.i[i,j]), df.i[i,39], df.i[i,j])
}
}
df.i
}
f1 <- function(df.i1){
for (j in 1:ncol(df.i1)) {
df.i1[,j] <- ifelse(is.na(df.i1[, j]), df.i1[, 39], df.i1[, j])
}
df.i1
}
f2 <- function(df.i2){
df.i2 <- ifelse(is.na(df.i2), df.i2[, 39], df.i2)
df.i2
}
f3 <- function(df.i3){
df.i3[is.na(df.i3)] <- df.i3[row(df.i3)[is.na(df.i3)], 39]
df.i3
}
microbenchmark::microbenchmark(
two_loops = f(df.i),
one_loop = f1(df.i1),
ifelse = f2(df.i2),
vectorized = f3(df.i3)
)
#Unit: microseconds
# expr min lq mean median uq max neval
# two_loops 1125.017 1143.4995 1226.93089 1152.5665 1190.599 5209.431 100
# one_loop 492.945 500.7045 518.73060 504.9435 516.638 678.951 100
# ifelse 42.269 45.7770 50.55519 48.4140 50.470 198.533 100
#vectorized 12.626 14.5520 16.21975 15.6380 17.663 27.525 100