Переработка векторов с помощью Rcpp

#c #r #vector #vectorization #rcpp

#c #r #векторный #векторизация #rcpp

Вопрос:

Я пытаюсь заставить переработку векторов работать в Rcpp.

 > recycle_and_add <- Rcpp::cppFunction("
  NumericVector recycle_and_add(NumericVector x, NumericVector y) {
      return x   y;
  }")
> recycle_and_add(42, 1:5)
[1] 43
  

Я ожидаю, что он вернет что-то вроде

 > 42   1:5
[1] 43 44 45 46 47
  

После некоторого анализа я выяснил, что это x.size() есть 1 и y.size() находится 5 внутри функции Rcpp, поэтому очевидно, что векторная переработка не работает «из коробки».

Хотя я могу вручную найти самый длинный из x и y и переработать более короткий, в реальном приложении есть 3 или 4 аргумента, требующих переработки, поэтому я могу представить, что ручная прокрутка приведет к множеству переменных, указывающих на разные векторы, и превратит код в кучу спагетти.

Имеет ли Rcpp какую-либо встроенную поддержку для переработки векторов, например, с добавлением сахара?

Комментарии:

1. Разверните свои векторы в R перед вызовом на C . Самый простой способ сделать это — вызвать data.frame .

2. Перечитайте некоторую документацию. Вы ошибочно полагаете, что Rcpp волшебным образом переработается за вас. Когда вы передаете 42 в NumericVector вы получаете именно это: одноэлементный числовой вектор, содержащий 42.

3. Вы можете использовать rep_len() , как в R.

4. @HongOoi Спасибо, это звучит просто, но я не уверен, что снижение производительности незначительно. Также просто для записи, вы предлагаете мне использовать df <- data.frame(arg1=arg1, arg2=arg2) , а затем .Call("_myPackage_myFunction", df$arg1, df$arg2) ?

Ответ №1:

С точки зрения стратегии почти всегда проще переработать в R, а затем перейти на C .

Если это должно выполняться на C , то должен работать следующий шаблон проектирования:

 #include <Rcpp.h>

// [[Rcpp::export]]
Rcpp::NumericVector recycle_vector(Rcpp::NumericVector x, 
                             Rcpp::NumericVector y) {

    // Obtain vector sizes
    int n_x = x.size();
    int n_y = y.size(); 

    // Check both vectors have elements
    if(n_x <= 0 || n_y <= 0) {
        Rcpp::stop("Both `x` and `y` vectors must have at least 1 element.");
    }

    // Compare the three cases that lead to recycling... 
    if(n_x == n_y) {
        return x   y;
    } else if (n_x > n_y) {
        return Rcpp::rep_len(y, n_x)   x;
    }

    return Rcpp::rep_len(x, n_y)   y; 
}
  

Тестовые примеры:

 recycle_vector(1:3, 1:3)
# [1] 2 4 6
recycle_vector(4, 1:3)
# [1] 5 6 7
recycle_vector(10:12, -2:-1)
# [1] 8 10 10