2015-12-29 4 views
6

Каков самый быстрый способ выполнить несколько логических сравнений в R?Каков самый быстрый способ выполнить несколько логических сравнений в R?

Рассмотрим, например, вектор x

set.seed(14) 
x = sample(LETTERS[1:4], size=10, replace=TRUE) 

Я хочу проверить, если каждый элемент x является либо "A" или "B" (а не что-нибудь еще). Следующие работы

x == "A" | x == "B" 
[1] TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 

Вышеупомянутый код трижды проходит по всей длине вектора. Есть ли способ R в цикле только один раз и проверить для каждого элемента, удовлетворяет ли он тому или иному условию?

+3

Как насчет: 'x% in% c (" A "," B ")' – MrFlick

+2

@MrFlick Я играл с этими двумя вариантами, и на самом деле я не вижу большой разницы. И масштаб все на наносекундах, даже когда я делаю все это больше, поэтому я немного потерял. – joran

+3

@joran Да, я не представляю, что вы увидите большой прирост скорости (и я очень сомневаюсь, что это где-то узкое место производительности), но это просто синтаксическая альтернатива. – MrFlick

ответ

12

Если ваша цель просто сделать один проход, что довольно просто писать в Rcpp, даже если у вас нет большого опыта работы с C++:

#include <Rcpp.h> 

// [[Rcpp::export]] 
Rcpp::LogicalVector single_pass(Rcpp::CharacterVector x, Rcpp::String a, Rcpp::String b) { 
    R_xlen_t i = 0, n = x.size(); 
    Rcpp::LogicalVector result(n); 

    for (; i < n; i++) { 
    result[i] = (x[i] == a || x[i] == b); 
    } 

    return result; 
} 

Для такой небольшой объект как используемый в вашем примере, с небольшим накладных .Call (предположительно) маскирует скорость версии Rcpp,

r_fun <- function(X) X == "A" | X == "B" 
## 
cpp_fun <- function(X) single_pass(X, "A", "B") 
## 
all.equal(r_fun(x), cpp_fun(x)) 
#[1] TRUE 
microbenchmark::microbenchmark(
    r_fun(x), cpp_fun(x), times = 1000L) 
#Unit: microseconds 
#expr   min lq  mean median  uq max neval 
#r_fun(x) 1.499 1.584 1.974156 1.6795 1.8535 37.903 1000 
#cpp_fun(x) 1.860 2.334 3.042671 2.7450 3.1140 51.870 1000 

Но для больших векторов (я предполагаю, что это у наше реальное намерение), это значительно быстрее:

x2 <- sample(LETTERS, 10E5, replace = TRUE) 
## 
all.equal(r_fun(x2), cpp_fun(x2)) 
# [1] TRUE 
microbenchmark::microbenchmark(
    r_fun(x2), cpp_fun(x2), times = 200L) 
#Unit: milliseconds 
#expr    min  lq  mean median  uq  max neval 
#r_fun(x2) 78.044518 79.344465 83.741901 80.999538 86.368627 149.5106 200 
#cpp_fun(x2) 7.104929 7.201296 7.797983 7.605039 8.184628 10.7250 200 

Вот quick attempt на обобщая выше, если у вас есть какие-либо использовать для этого.