在 R 中填充数据帧时避免循环

Avoiding a loop when populating data frames in R

我有一个包含 2784 列和 150 行的空数据框 T_modelled

T_modelled <- data.frame(matrix(ncol = 2784, nrow = 150))
names(T_modelled) <- paste0("t=", t_sec_ERT)
rownames(T_modelled) <- paste0("z=", seq(from = 0.1, to = 15, by = 0.1))

哪里

t_sec_ERT <- seq(from = -23349600, to = 6706800, by = 10800)
z <- seq(from = 0.1, to = 15, by = 0.1)

我根据公式 T_modelled 按列 填充了嵌套 for 循环:

for (i in 1:ncol(T_modelled)) {
  col_tmp <- colnames(T_modelled)[i]
  for (j in 1:nrow(T_modelled)) {
    z_tmp <- z[j]-0.1
    T_tmp <- MANSRT+As*e^(-z_tmp*(omega/(2*K))^0.5)*sin(omega*t_sec_ERT[i]-((omega/(2*K))^0.5)*z_tmp)
    T_modelled[j ,col_tmp] <- T_tmp
  }
}

哪里

MANSRT <- -2.051185
As <- 11.59375
omega <- (2*pi)/(347.875*24*60*60)
c <- 790
k <- 0.00219
pb <- 2600
K <- (k*1000)/(c*pb)
e <- exp(1)

我确实得到了想要的结果,但我一直认为必须有一种更有效的方法来填充该数据框。循环很慢,对我来说看起来很麻烦。我想有机会利用 R 的矢量化计算方式。我只是看不到自己如何以更简单的方式合并公式来填充 T_modelled.

有人知道如何以更快、更 "R-like" 的方式获得相同的结果吗?

我相信这样做。
运行 创建 T_modelled 后的第一条指令,需要测试结果是否相等。

Tm <- T_modelled

现在运行你的代码然后运行下面的代码。

z_tmp <- z - 0.1
for (i in 1:ncol(Tm)) {
    T_tmp <- MANSRT + As*exp(-z_tmp*(omega/(2*K))^0.5)*sin(omega*t_sec_ERT[i]-((omega/(2*K))^0.5)*z_tmp)
    Tm[ , i] <- T_tmp
}

all.equal(T_modelled, Tm)
#[1] TRUE

您不需要内部循环,这是唯一的区别。
(我也直接用了 exp 但这是次要的。)

我更愿意以长格式放置数据,将 zt_sec_ERT 的所有组合作为两列,以便利用矢量化。虽然我通常更喜欢 tidyr 在长格式和宽格式之间切换,但我试图将其作为基本解决方案:

t_sec_ERT <- seq(from = -23349600, to = 6706800, by = 10800)
z <- seq(from = 0.1, to = 15, by = 0.1)

v <- expand.grid(t_sec_ERT, z) 
names(v) <- c("t_sec_ERT", "z")
v$z_tmp <- v$z-0.1
v$T_tmp <- MANSRT+As*e^(-v$z_tmp*(omega/(2*K))^0.5)*sin(omega*v$t_sec_ERT-((omega/(2*K))^0.5)*v$z_tmp)

T_modelled <- data.frame(matrix(v$T_tmp, nrow = length(z), ncol = length(t_sec_ERT), byrow = TRUE))
names(T_modelled) <- paste0("t=", t_sec_ERT)
rownames(T_modelled) <- paste0("z=", seq(from = 0.1, to = 15, by = 0.1))

Rui当然是对的,我只是想在写这样一个循环时提出一种推理方法。

您有两个数值向量。 R 中的数字函数通常是向量化的。我的意思是你可以做这样的事情

x <- c(1, 6, 3)
sum(x)

不需要这样的东西

x_ <- 0
for (i in x) {
    x_ <- i + x_ 
}
x_

也就是说,不需要在 R 中循环。当然,循环发生的次数越少 none,它只是发生在底层 C、Fortran 等代码中,可以更有效地完成。这通常是我们调用向量化函数时的意思:循环发生 "under the hood" 。因此,Vectorize() 的输出未按此定义严格矢量化。

当您有两个数值向量要循环时,您必须首先查看构成函数是否已向量化,通常是通过阅读文档。

如果是,您将继续构建中心向量化复合函数,并开始使用一个向量和一个标量对其进行测试。在您的情况下,它将是这样的(仅使用 t_sec_ERT 的第一个元素进行测试)。

z_tmp <- z - 0.1
i <- 1

T_tmp <- MANSRT + As * 
         exp(-z_tmp*(omega/(2*K))^0.5) * 
         sin(omega*t_sec_ERT[i] - ((omega/(2*K))^0.5)*z_tmp)

看起来不错。然后你开始循环 t_sec_ERT.

的元素
T_tmp <- matrix(nrow=length(z), ncol=length(t_sec_ERT))

for (i in 1:length(t_sec_ERT)) {
    T_tmp[, i] <- MANSRT + As * 
             exp(-z_tmp*(omega/(2*K))^0.5) * 
             sin(omega*t_sec_ERT[i] - ((omega/(2*K))^0.5)*z_tmp)
}

或者您可以使用 sapply() 来完成,这通常更整洁。

f <- function(x) {
    MANSRT + As * 
    exp(-z_tmp*(omega/(2*K))^0.5) * 
    sin(omega*x - ((omega/(2*K))^0.5)*z_tmp)
}

T_tmp <- sapply(t_sec_ERT, f)

很像您之前接受的问题 ,考虑简单地使用 sapply,遍历向量 t_sec_ERT,这与所需数据框的列数长度相同。但首先将 z 的每个元素调整 0.1。另外,无需事先创建空数据框。

z_adj <- z - 0.1

T_modelled2 <- data.frame(sapply(t_sec_ERT, function(ert)
        MANSRT+As*e^(-z_adj*(omega/(2*K))^0.5)*sin(omega*ert-((omega/(2*K))^0.5)*z_adj)))

colnames(T_modelled2) <- paste0("t=", t_sec_ERT)
rownames(T_modelled2) <- paste0("z=", z)

all.equal(T_modelled, T_modelled2)
# [1] TRUE