Избежание цикла при заполнении фреймов данных в R

У меня пустой фрейм данных 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)

я заполнил 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-подобным" образом?

4 ответа

Решение

Руи, конечно, прав, я просто хочу предложить способ рассуждения при написании такого цикла.

У вас есть два числовых вектора. Функции для чисел в 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)

Как и решение вашего предыдущего вопроса, которое вы приняли, подумайте просто 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

Я верю, что это делает это.
Запустите эту первую инструкцию сразу после создания 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 напрямую, но это имеет второстепенное значение.)

Я бы предпочел поместить данные в длинный формат со всеми комбинациями z а также t_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))
Другие вопросы по тегам