使用嵌套的套用函数而不是嵌套的循环

时间:2018-08-01 15:30:46

标签: r apply nested-loops

我的目标是迭代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函数来实现同一目的?

1 个答案:

答案 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