Эффективный способ фильтрации одного фрейма данных по диапазонам в другом

Предположим, у меня есть кадр данных, содержащий кучу данных и столбец даты / времени, указывающий, когда была собрана каждая точка данных. У меня есть другой фрейм данных, в котором перечислены промежутки времени, где столбец «Старт» указывает дату / время начала каждого промежутка, а столбец «Конец» указывает дату / время окончания каждого интервала.

Я создал фиктивный пример ниже, используя упрощенные данные:

main_data = data.frame(Day=c(1:30)) spans_to_filter = data.frame(Span_number = c(1:6), Start = c(2,7,1,15,12,23), End = c(5,10,4,18,15,26)) 

Я поиграл с несколькими путями решения этой проблемы и получил следующее решение:

 require(dplyr) filtered.main_data = main_data %>% rowwise() %>% mutate(present = any(Day >= spans_to_filter$Start & Day % filter(present) %>% data.frame() 

Это работает отлично, но я заметил, что может потребоваться некоторое время для обработки, если у меня много данных (я полагаю, потому что я выполняю сравнение по ряду строк). Я все еще изучаю все возможности R, и мне было интересно, есть ли более эффективный способ выполнения этой операции, предпочтительно используя dplyr / tidyr?

Вот функция, которую вы можете запустить в dplyr чтобы найти даты в заданном диапазоне, используя функцию from (from dplyr ). Для каждого значения Day mapply выполняется between каждой из пар дат Start и End и функция использует rowSums для возврата TRUE если Day находится между хотя бы одним из них. Я не уверен, что это самый эффективный подход, но это приводит к почти четвертому улучшению скорости.

 test.overlap = function(vals) { rowSums(mapply(function(a,b) between(vals, a, b), spans_to_filter$Start, spans_to_filter$End)) > 0 } main_data %>% filter(test.overlap(Day)) 

Если вы работаете с датами (а не с датами), может быть еще эффективнее создать вектор конкретных дат и проверить членство (это может быть лучшим подходом даже с датами):

 filt.vals = as.vector(apply(spans_to_filter, 1, function(a) a["Start"]:a["End"])) main_data %>% filter(Day %in% filt.vals) 

Теперь сравните скорости выполнения. Я сократил ваш код, чтобы потребовать только операцию фильтрации:

 library(microbenchmark) microbenchmark( OP=main_data %>% rowwise() %>% filter(any(Day >= spans_to_filter$Start & Day <= spans_to_filter$End)), eipi10 = main_data %>% filter(test.overlap(Day)), eipi10_2 = main_data %>% filter(Day %in% filt.vals) ) Unit: microseconds expr min lq mean median uq max neval cld OP 2496.019 2618.994 2875.0402 2701.8810 2954.774 4741.481 100 c eipi10 658.941 686.933 782.8840 714.4440 770.679 2474.941 100 b eipi10_2 579.338 601.355 655.1451 619.2595 672.535 1032.145 100 a 

ОБНОВЛЕНИЕ: Ниже приведен тест с гораздо большим кадром данных и несколькими дополнительными диапазонами дат, чтобы они соответствовали (спасибо @Frank за то, что вы предлагаете это в своем комментарии). Оказывается, что в этом случае прирост скорости намного больше (примерно в 200 раз для mapply/between методом и намного больше для второго метода).

 main_data = data.frame(Day=c(1:100000)) spans_to_filter = data.frame(Span_number = c(1:9), Start = c(2,7,1,15,12,23,90,9000,50000), End = c(5,10,4,18,15,26,100,9100,50100)) microbenchmark( OP=main_data %>% rowwise() %>% filter(any(Day >= spans_to_filter$Start & Day <= spans_to_filter$End)), eipi10 = main_data %>% filter(test.overlap(Day)), eipi10_2 = { filt.vals = unlist(apply(spans_to_filter, 1, function(a) a["Start"]:a["End"])) main_data %>% filter(Day %in% filt.vals)}, times=10 ) Unit: milliseconds expr min lq mean median uq max neval cld OP 5130.903866 5137.847177 5201.989501 5216.840039 5246.961077 5276.856648 10 b eipi10 24.209111 25.434856 29.526571 26.455813 32.051920 48.277326 10 a eipi10_2 2.505509 2.618668 4.037414 2.892234 6.222845 8.266612 10 a 

В пакете data.table, начиная с v1.9.8, были реализованы соединения non-equi . При этом я создал функцию-оболочку inrange() для точно таких операций, где задача включает в себя поиск, если точка лежит в любом из предоставленных интервалов, и если так возвращать TRUE , else FALSE .

 require(data.table) # v>=1.9.8 setDT(main_data)[Day %inrange% spans_to_filter[, 2:3]] # inclusive bounds # Day # 1: 1 # 2: 2 # 3: 3 # 4: 4 # 5: 5 # 6: 7 # 7: 8 # 8: 9 # 9: 10 # 10: 12 # 11: 13 # 12: 14 # 13: 15 # 14: 16 # 15: 17 # 16: 18 # 17: 23 # 18: 24 # 19: 25 # 20: 26 

См. ?inrange для больше.

Использование Base R:

 main_data[unlist(lapply(main_data$Day, function(x) any(x >= spans_to_filter$Start & x <= spans_to_filter$End))),] 
  • Управление порядком отображения легенды ggplot2
  • Лучший IDE / TextEditor для R
  • Как сопоставить вектор значений другому вектору с моей собственной пользовательской картой в R
  • Уникальная комбинация всех элементов из двух (или более) векторов
  • Чтение больших данных с фиксированной шириной
  • Соединение по отсутствующим значениям с помощью geom_line
  • R Заseleniumные сетки долготы - широта сетки на карте
  • Можно ли определить «средний» диапазон в scale_fill_gradient2 ()?
  • do.call (rbind, list) для нечетного числа столбцов
  • Избегайте сортировки ggplot по оси x при построении geom_bar ()
  • Участок с осью 2 y, одна ось y слева и другая ось y справа
  • Interesting Posts

    Рекурсивно сохраняя веб-страницы

    Можно ли использовать SQL для построения таблицы данных Excel из других файлов Excel?

    Сбросить значение textarea после отправки формы

    Не удается найти файл конфигурации в «/ etc / ansible /» в Mac OS X

    Как вы определяете локальный var / val в основном конструкторе в Scala?

    Локальный кеш для NAS или сетевой папки

    Имеет ли C конструкцию цикла foreach?

    Как получить список текущих переменных из шаблона Jinja 2?

    Некоторые электрические удары от моего собственного нового ноутбука

    Принудительная загрузка файла на веб-сервере – ASP.NET C #

    Определение даты создания файла в Java

    Как читать содержимое файла из внутреннего хранилища – приложение для Android

    Производите случайное число в диапазоне, используя C #

    Как разблокировать файлы chm – кнопка разблокирования отсутствует

    Как разрешить внутренний MVC Web Api с внешнего сайта за пределами сети

    Давайте будем гением компьютера.