У меня есть привычка объединять похожие задачи в одну строку. Например, если мне нужно отфильтровать a
, b
и c
в таблице данных, я положу их вместе в одном[]
с Андами. Вчера я заметил, что в моем конкретном случае это было невероятно медленно, и вместо этого проверил фильтры цепочки. Я включил пример ниже.
Сначала я запускаю генератор случайных чисел, загружаю 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 по пакету представитель (v0.3.0)
В этом случае сцепление сокращает время выполнения примерно на 70%. Почему это так? Я имею в виду, что происходит под капотом в таблице данных? Я не видел никаких предупреждений против использования &
, поэтому я был удивлен, что разница настолько велика. В обоих случаях они оценивают одни и те же условия, поэтому разницы не должно быть. В случае И,&
это быстрый оператор, и тогда он должен фильтровать таблицу данных только один раз (т. Е. Используя логический вектор, полученный из 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
для этих срезов).