У меня проблема с R, потому что мне нужно выполнить некоторые операции со списком и создать новый фрейм данных с этими значениями из списка. Если я использую цикл for, это занимает очень много времени. И не знаю, как избежать этого цикла for и как сделать это «if + case_when» без цикла for.
В приведенном ниже коде есть комментарии, объясняющие, что я делаю и что происходит.
Большое спасибо!!
#search in all rows of list "total"
for(i in 1:nrow(total)) {
#Take with total$Cad[[i]] a value from another list
val1 <- posdi[posdi$cad == str_to_upper(total$Cad[[i]]),]
#Check if "font" value from val1 is equal to "Taake" and take the value
val2 <- val1[val1$font == "Taake",]
#Format date value
thedate <- as.numeric(format(as.Date(total$TheDate[[i]], format = "%Y-%m-%d"), '%Y%m%d'))
#And here comes where I can't continue easily. I want to do an IF and make a different
#case_when if the result is between 1 and 5 or between 6 and 7
if (total$dia[[i]] >= 1 & total$dia[[i]] <= 5) {
fran = case_when(
total$secs[[i]]>=0 & total$secs[[i]]<1.5 ~ 1,
total$secs[[i]]>=1.5 & total$secs[[i]]<4 ~ 2,
total$secs[[i]]>=4 & total$secs[[i]]<8 ~ 3,
total$secs[[i]]>=8 & total$secs[[i]]<10 ~ 4)
} else {
fran = case_when(
total$secs[[i]]>=0 & total$secs[[i]]<1.5 ~ 5,
total$secs[[i]]>=1.5 & total$secs[[i]]<4 ~ 6,
total$secs[[i]]>=4 & total$secs[[i]]<8 ~ 7,
total$secs[[i]]>=8 & total$secs[[i]]<10 ~ 8)
}
#and finally, add that "fran" value, those three from the beggining and some from total list to a new dataframe
datosTel[nrow(datosTel) + 1,] = c(val2$cad, str_to_upper(total$Camp[[i]]), total$numsem[[i]], thedate, total$diasem[[i]], fran, 0)
}
#It works with the "for" loop, but it take so much time (it goes one by one and the list has more than 200K rows).
#How can I do it without that for loop and make the "if + case_when" correctly?
Еще раз спасибо и хорошего дня
Как было сказано ранее, мои проблемы связаны с циклом FOR и теми IF и CASE_WHEN внутри FOR, потому что я не знаю, как это сделать, если цикла нет.
Вы уверены, что у вас есть список, а не data.frame? Если это data.frame, вы можете взглянуть на dplyr
и функции, упомянутые Конрадом.
Код внутри вашего цикла касается только текущего элемента ([[i]]
), и все операции, которые вы выполняете, по умолчанию векторизованы (за исключением if
, но мы можем заменить его непосредственно на if_else
).
Таким образом, вы можете заменить весь цикл оператором mutate
или transmute
(они делают то же самое, transmute
просто не сохраняет существующие столбцы, поэтому в вашем случае он кажется более подходящим).
Кроме того, вы можете упростить if
, объединив две ветви и добавив смещение, зависящее от total$dia
.
Наконец, ваше выражение case_when
может быть выражено как выражение findInterval
.
Далее я предполагаю, что datosTel
— это пустая таблица перед вашим циклом, а также делаю некоторые предположения об именах столбцов, которые вам, возможно, потребуется изменить.
datosTel = total %>%
transmute(
cad = posdi$cad[posdi$cad == str_to_upper(Cad) & posdi$font == "Taake"],
Camp = str_to_upper(Camp),
numsem = numsem,
thedate = as.numeric(format(as.Date(TheDate, format = "%Y-%m-%d"), '%Y%m%d')),
diasem = diasem,
offset = if_else(dia >= 1 & dia <= 5, 0, 4),
fran = offset + findInterval(secs, c(0, 1.5, 4, 8, 10, Inf)),
LAST_COLUMN = 0
) %>%
select(-offset)
(Замените LAST_COLUMN
фактическим именем столбца.)
Вызов findInterval
эквивалентен:
case_when(
secs >= 0 & secs < 1.5 ~ 1,
secs >= 1.5 & secs < 4 ~ 2,
secs >= 4 & secs < 8 ~ 3,
secs >= 8 & secs < 10 ~ 4
)
Кстати, вычисление thedate
выглядит очень хитро, но я только что скопировал сюда ваш код.
Спасибо, @konrad-rudolph, все работает!! Как вы видели, я новичок с R С уважением
Взгляните на упаковку
purrr
.