【问题标题】:R - apply to replace for loop with functions [closed]R - 申请用函数替换for循环[关闭]
【发布时间】:2017-01-12 01:11:14
【问题描述】:

我已经看过十几个网站来学习 apply、sapply、lapply,但它们除了教您如何获取行或列的总和或平均值外,没有比这更进一步。我必须使用很多 for 循环,通常是嵌套的。请在下面告诉我如何用 apply 替换 for。

for (i in 1:NTR) {
    X = as.vector(as.matrix(XTR[i,]))
    YZ = FN(W, V, U, X) # return Y and Z
    Y = YZ$Y
    Z = YZ$Z
    for (j in 1:NOUT) {
        F[i+NOUT*(j-1)] = Y[j]-YTR[i,j]
    } # j
} # i

    for (k in 1:NOUT) {
        for (iwt in 1:NWT) {
            m = NHID*(NNLIN+1) 
            if (iwt <= m) { 
                i = (iwt-1) %/% (NNLIN+1) + 1
                j = (iwt-1) %% (NNLIN+1)
                EVZ = V[k,i+1]*Z[i]*(1-Z[i]) 
                if (j>0) EVZ = EVZ*X[j] 
                J[k+(n-1)*NOUT,iwt] = EVZ
            }
        } # iwt     
    } # k

非常感谢您的任何回复。

【问题讨论】:

  • 您的示例可能对您有意义,但如果没有上下文,它们可能对其他人没有意义。代码的输入和预期输出是什么?

标签: r for-loop apply nested-loops


【解决方案1】:

将循环转换为 lapply 的最简单方法是将 for 循环中的内容插入函数中。让我们从你的内部循环开始。

foo_inner <- function(j) {
    F[i+NOUT*(j-1)] = Y[j]-YTR[i,j]
}

这里有一个问题:你的函数应该返回一个值而不是分配它。在这种情况下,您想返回Y[j]-YTR[i,j]

foo_inner <- function(j) {
    return(Y[j]-YTR[i,j])
}

然后您可以简单地将这个函数应用于1:NOUT。在这种情况下,似乎(尽管我无法确定您的帖子中缺少信息)此返回值只是一个数字,因此您可以直接创建向量而不是列表。在这种情况下,最好使用sapply 而不是lapply('s' 表示简化(到向量),而'l' 表示列表):

sapply(1:NOUT, foo_inner)

现在必须在您的外循环中分配这个新向量。从您的代码中,您似乎想将此向量分配给F[i+NOUT*(1:NOUT-1)]。我只是用 1:NOUT 替换了您的 j 值,这是您的迭代器。因此,您的代码 sn-p 可以这样修改:

for (i in 1:NTR) {
    X = as.vector(as.matrix(XTR[i,]))
    YZ = FN(W, V, U, X) # return Y and Z
    Y = YZ$Y
    Z = YZ$Z
    F[i+NOUT*(1:NOUT-1)] <- sapply(1:NOUT, foo_inner)
} # i

现在让我们处理外循环。和以前一样,我们可以将里面的内容插入一个函数中:

foo_outer <- function(i) {
    X = as.vector(as.matrix(XTR[i,]))
    YZ = FN(W, V, U, X) # return Y and Z
    Y = YZ$Y
    Z = YZ$Z
    F[i+NOUT*(1:NOUT-1)] <- sapply(1:NOUT, foo_inner)
}

这里有两个问题需要克服:首先,您的foo_inner 函数将i 作为参数。但是,foo_innerfoo_outer 之外定义,它将始终使用在您的环境中定义的i,而不是foo_outer 的参数。有两种解决方案:要么在 foo_outer 内定义 foo_inner

foo_outer <- function(i) {
    X = as.vector(as.matrix(XTR[i,]))
    YZ = FN(W, V, U, X) # return Y and Z
    Y = YZ$Y
    Z = YZ$Z
    foo_inner <- function(j) {
        return(Y[j]-YTR[i,j])
    }
    F[i+NOUT*(1:NOUT-1)] <- sapply(1:NOUT, foo_inner)
}

或者,修改您的foo_inner 函数,使其将i 作为参数并返回正确的函数。然后,您将在内部 sapply 中应用函数 foo_inner(i) 而不是 foo_inner

foo_inner <- function(i) {
    function(j) {
        return(Y[j]-YTR[i,j])
    }
}
foo_outer <- function(i) {
    X = as.vector(as.matrix(XTR[i,]))
    YZ = FN(W, V, U, X) # return Y and Z
    Y = YZ$Y
    Z = YZ$Z
    F[i+NOUT*(1:NOUT-1)] <- sapply(1:NOUT, foo_inner(i))
}

接下来要做的修改是确保您想要返回一个值,而不是在函数内部进行赋值:

foo_inner <- function(i) {
    function(j) {
        return(Y[j]-YTR[i,j])
    }
}
foo_outer <- function(i) {
    X = as.vector(as.matrix(XTR[i,]))
    YZ = FN(W, V, U, X) # return Y and Z
    Y = YZ$Y
    Z = YZ$Z
    return(sapply(1:NOUT, foo_inner(i)))
}

您现在可以应用此功能:

lapply(1:NTR, foo_outer)

在这种情况下,我使用了lapply,因为返回的每个元素都是一个向量,所以我宁愿返回一个列表并在之后折叠它,因为我不确定sapply 在这种情况下是否这样做(暂时懒得去查,如果有人能确认我会更正)。

所以你现在想要一个大向量,所以我将把它全部折叠起来。

do.call(c, lapply(1:NTR, foo_outer))

然后我可以直接将此值分配给F

F <- do.call(c, lapply(1:NTR, foo_outer))

显然,如果不知道 FYTRY 以及您的所有输入是什么,我无法确保这正是您想要的。但我希望它能让你走上正确的道路!

编辑:我认为我为您的最终向量创建了错误的顺序:使用上面的内容,它会将所有“j”值放入 i= 1,然后将所有“j”值放入 i = 2... 但回头看在您的F,看起来您想要的顺序是j=1 所有“i”值...为此,您只需重新排列第二个lapply 的输出。这应该可行。

我想创建一个从列表中获取所有j 元素的函数:

LL <- lapply(1:NTR, foo_outer)
get_j_values <- function(j) {
    sapply(LL, function(x) x[[j]])
}

对于任何j 值,get_j_value 在向量中返回LL 的所有jth 元素。通过将此函数应用于所有可能的 j 值,它会返回一个包含第一个元素的列表:j=1 的所有“i”值然后对于第二个元素,j=2 的所有“i”值...

LL2 <- lapply(1:NOUT, get_j_values)

然后我可以将这个列表折叠成一个大向量并分配它。

F <- do.call(c, LL2)

编辑 2:虽然可以使用 apply 函数来重新创建你的 for 循环,但这可能是 for 循环实际上更好的时候之一:没有结果的累积,所以 apply 方法不应该更快,我相信 for 循环会更清晰。当您循环使用多个不同对象的索引时,通常会出现这种情况,因此您不能直接在任何特定对象上使用apply,但需要将函数应用于索引向量......只是我的两美分...

【讨论】:

  • 这太棒了,比任何教程都向我展示的要多。我仍然需要了解函数定义中的函数是如何工作的,但我可以阅读。 F 和 Y 是向量,YTR 是一个矩阵(全是数字),所以 Y[j]-YTR[i,j] 是一个数字。它只需要放在向量 F 中的正确位置。FN 是矩阵 W、V、U 和向量 X 的函数。(XTR 是一个数据框,因为我从带有 read.csv 的文件中读取它。)我不明白最后一部分 - LL 和 LL2。我们根本没有创建任何列表。只有 YZ 是一个列表,因为我想从 FN 返回两个矩阵。再次感谢您的解释。
  • 使用foo_inner &lt;- function(i) function(j) paste("i=", i, "j=", j)并运行它,然后比较第一种方法创建的F和第二种方法创建的F,这应该可以区分LLLL2更清晰。我在这里使用一个列表只是为了确保我将正确的值放在正确的位置。
  • 很高兴您发现它很有用。
  • @user6439024,您可以通过单击 Choubi 答案旁边的灰色阴影刻度线来接受 Choubi 的答案,您还可以通过单击刻度线上方的向上箭头给他的答案“+1”标记
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多