Мені властиво з’єднувати подібні завдання разом в один рядок. Наприклад, якщо мені потрібно буде фільтрувати на a
, b
і c
в таблиці даних, я []
з'єднаю їх в одне з AND. Вчора я помітив, що в моєму конкретному випадку це були надзвичайно повільні і перевірені ланцюгові фільтри. Я включив приклад нижче.
По-перше, я закладаю генератор випадкових чисел, завантажую таблицю data.table та створюю фіктивний набір даних.
# Set RNG seed
set.seed(-1)
# Load libraries
library(data.table)
# Create data table
dt <- data.table(a = sample(1:1000, 1e7, replace = TRUE),
b = sample(1:1000, 1e7, replace = TRUE),
c = sample(1:1000, 1e7, replace = TRUE),
d = runif(1e7))
Далі я визначаю свої методи. Перший підхід ланцюги фільтрує разом. Другий І фільтрує разом.
# Chaining method
chain_filter <- function(){
dt[a %between% c(1, 10)
][b %between% c(100, 110)
][c %between% c(750, 760)]
}
# Anding method
and_filter <- function(){
dt[a %between% c(1, 10) & b %between% c(100, 110) & c %between% c(750, 760)]
}
Тут я перевіряю, що вони дають однакові результати.
# Check both give same result
identical(chain_filter(), and_filter())
#> [1] TRUE
Нарешті, я їх орієнтую.
# Benchmark
microbenchmark::microbenchmark(chain_filter(), and_filter())
#> Unit: milliseconds
#> expr min lq mean median uq max
#> chain_filter() 25.17734 31.24489 39.44092 37.53919 43.51588 78.12492
#> and_filter() 92.66411 112.06136 130.92834 127.64009 149.17320 206.61777
#> neval cld
#> 100 a
#> 100 b
Створено 2019-10-25 пакетом reprex (v0.3.0)
У цьому випадку ланцюжок скорочує час роботи приблизно на 70%. Чому це так? Я маю на увазі, що відбувається під кришкою в таблиці даних? Я не бачив жодних попереджень щодо використання &
, тому мене здивувало, що різниця така велика. В обох випадках вони оцінюють однакові умови, так що це не повинно бути різницею. У випадку AND &
- швидкий оператор, і тоді йому потрібно лише один раз відфільтрувати таблицю даних (тобто, використовуючи логічний вектор, отриманий від AND), на відміну від фільтрування тричі у ланцюговому випадку.
Бонусне питання
Чи справедливий цей принцип для операцій з таблицею даних взагалі? Чи завжди модулізація завдань є кращою стратегією?
base
спостереження з векторами, зробивши наступне: chain_vec <- function() { x <- which(a < .001); x[which(b[x] > .999)] }
та and_vec <- function() { which(a < .001 & b > .999) }
. (де a
і b
є вектори однакової довжини від runif
- я використовував n = 1e7
для цих обрізів).