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

时间:2017-12-11 18:37:43

标签: r for-loop dataframe vectorization populate

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

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)

我使用嵌套的for循环按列填充T_modelled ,基于以下公式:

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”的方式获得相同的结果?

4 个答案:

答案 0 :(得分:2)

我相信这样做。
在创建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,但这是次要的。)

答案 1 :(得分:2)

就像您之前接受的问题solution一样,只需使用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

答案 2 :(得分:1)

Rui当然是正确的,我只想在编写这样的循环时建议一种推理方式。

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

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

不需要这样的东西

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

也就是说,不需要在R中循环。当然,循环也会发生,它只发生在底层的C,Fortran等代码中,它可以更有效地完成。这通常是我们调用函数向量化时的意思:循环发生在“引擎盖下”。因此,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)

答案 3 :(得分:0)

我希望将数据放在长格式中,将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))