У меня есть приведенный ниже код, где я пытаюсь перейти к каждой строке определенного столбца фрейма данных и проверить это значение в другом фрейме данных, чтобы вставить значение в столбец 4 первого фрейма данных.
for (i in 1:length(DF$Date)){
if (DF$column1[i] %in% DF_2$column_1){
DF$column4[i] <- "YES"
}
}
Код работает нормально, так как у меня миллионы записей, выполнение задачи занимает огромное время.
Было бы полезно, если бы у кого-нибудь был эффективный способ решить эту проблему в короткие сроки.
Здесь вам не нужен цикл for
, так как %in%
векторизован. Простой ifelse
должен работать.
DF$column4 <- ifelse(DF$column1 %in% DF_2$column_1, 'YES', 'NO')
Вы также можете сделать это следующим образом:
DF$column4 <- c('NO', 'YES')[(DF$column1 %in% DF_2$column_1) + 1]
что может быть быстрее на больших наборах данных.
Большое спасибо @Ronak, как всегда помог мне :)
Давайте сравним 3 подхода. Первый — это цикл for
в вашем вопросе. Второй — тот, на который ответил Ронак, используя ifelse()
, что ускоряет операцию. Однако сам оператор %in%
несколько медленный, поэтому, если производительность действительно важна, вы можете получить еще более быстрое решение, используя индексацию с именами.
Например, используя набор данных words
в пакете stringr
:
library(stringr)
DF1 <- data.frame(column1 = sample(words, 700))
DF2 <- data.frame(column1 = sample(words, 700))
Мы можем сравнить эти методы:
bench::mark(for_loop = {
res1 <- character(nrow(DF1))
for (i in seq_len(nrow(DF1))){
if (DF1$column1[i] %in% DF2$column1){
res1[i] <- "YES"
}
}
res1
},
ifelse = {
res2 <- ifelse(DF1$column1 %in% DF2$column1, "YES", "")
},
by_names = {
res3 <- setNames(rep("", nrow(DF1)),
DF1$column1)
res3[intersect(DF1$column1, DF2$column1)] <- "Yes"
},check = FALSE)
# A tibble: 3 x 13
# expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
# <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl>
# 1 for_loop 7.94ms 8.49ms 112. 3.8MB 8.98 50 4
# 2 ifelse 200.6us 214.8us 4494. 60.7KB 4.09 2197 2
# 3 by_names 73.4us 78.2us 12342. 73.9KB 17.5 5628 8
# ... with 5 more variables: total_time <bch:tm>, result <list>, memory <list>,
# time <list>, gc <list>
Как видите, метод ifelse
в 40 раз быстрее, чем цикл for
, а индексация по имени в 3 раза быстрее, чем с ifelse
.
Если метод ifelse
достаточно быстрый, вы должны использовать его, так как его легче читать, но если ваш набор данных слишком велик, выбор по имени может повысить производительность.
NB: три решения дают один и тот же результат, но у третьего метода есть имена, отсюда и аргумент check=FALSE
.
Большое спасибо за упрощенное объяснение @Alexlok
Вы можете повысить эффективность, используя fastmatch
:
library(fastmatch)
DF$column4 <- c("", "YES")[(DF$column1 %fin% DF_2$column1) + 1]
или:
DF$column4 <- ""
x[fmatch(DF_2$column1, DF$column1, 0)] <- "Yes"
Сравнительный анализ (с использованием данных @Alexlok):
library(stringr)
set.seed(42)
DF <- data.frame(column1 = sample(words, 700))
DF_2 <- data.frame(column1 = sample(words, 700))
microbenchmark::microbenchmark(control=list(order = "block")
, Vin = {x <- character(nrow(DF))
for (i in 1:nrow(DF)) {
if (DF$column1[i] %in% DF_2$column1) {
x[i] <- "YES"
}
}
x}
, RonakShah1 = ifelse(DF$column1 %in% DF_2$column1, 'YES', 'NO')
, RonakShah2 = c('NO', 'YES')[(DF$column1 %in% DF_2$column1) + 1]
, Alexlok = {x <- setNames(rep("", nrow(DF)), DF$column1)
x[intersect(DF$column1, DF_2$column1)] <- "Yes"
x}
, fmatch = {x <- character(nrow(DF))
x[fmatch(DF_2$column1, DF$column1, 0)] <- "Yes"
x}
, fin = c('NO', 'YES')[(DF$column1 %fin% DF_2$column1) + 1]
)
#Unit: microseconds
# expr min lq mean median uq max
# Vin 13782.353 20549.5995 21467.31764 21795.3050 22845.0755 36143.621
# RonakShah1 281.118 308.1385 322.57051 316.5560 340.8620 362.097
# RonakShah2 60.906 66.5565 68.17693 67.8925 69.4110 88.363
# Alexlok 162.817 179.6205 184.22998 182.3755 187.8450 257.323
# fmatch 27.949 29.0590 29.90183 29.5275 30.0465 49.612
# fin 30.446 31.0340 31.86467 31.5135 31.9540 49.817
Вам будет легче помочь, если вы включите простой воспроизводимый пример с образцом ввода и желаемым результатом, который можно использовать для тестирования и проверки возможных решений.