2015-12-31 7 views
0

Предположим, у меня есть вектор [2,4,6,8,10], и мне нужно удалить 2-й и 4-й элемент из этот вектор. Желаемый результирующий вектор должен быть [2,6,10]. Это очень легко реализовать в R:Rcpp/RcppArmadillo: удаление несмежных элементов из вектора на основе позиций

v1 <- c(2,4,6,8,10) 
v1[-c(2,4)] 

Но как я могу осуществить это в Rcpp/RcppArmadillo? Я могу вычислить смежный случай (т. Е. Удалить второй через 4-й элемент) с помощью функции .erase(), но несмежный случай для меня не кажется таким очевидным, поскольку .erase, похоже, не принимает тип векторов uvec , Скорость может быть связана с тем, что v1 может быть довольно большим в моем приложении.

EDIT: Реализация Rcpp или Armadillo в порядке, поскольку я использую оба.

+1

Это должно помочь: http://gallery.rcpp.org/articles/armadillo-subsetting/ –

+0

Вы говорите 'Rcpp/RcppArmadillo', но Rcpp векторы и Armadillo векторы имеют различные интерфейсы. Просьба уточнить это. – nrussell

+0

@DirkEddelbuettel: Я действительно прочитал эту страницу, прежде чем задавать этот вопрос. Возможно, я что-то пропустил, но до сих пор не могу понять, как удалить элементы вместо получения элементов, определенных 'uvec' или' umat'. Не могли бы вы рассказать? – aenima

ответ

0

Вот один из возможных подходов:

#include <Rcpp.h> 

Rcpp::LogicalVector logical_index(Rcpp::IntegerVector idx, R_xlen_t n) { 
    bool invert = false; 
    Rcpp::LogicalVector result(n, false); 

    for (R_xlen_t i = 0; i < idx.size(); i++) { 
    if (!invert && idx[i] < 0) invert = true; 
    result[std::abs(idx[i])] = true; 
    } 

    if (!invert) return result; 
    return !result; 
} 


// [[Rcpp::export]] 
Rcpp::NumericVector 
Subset(Rcpp::NumericVector x, Rcpp::IntegerVector idx) { 
    return x[logical_index(idx, x.size())]; 
} 

x <- seq(2, 10, 2) 

x[c(2, 4)] 
#[1] 4 8 
Subset(x, c(1, 3)) 
#[1] 4 8 

x[-c(2, 4)] 
#[1] 2 6 10 
Subset(x, -c(1, 3)) 
#[1] 2 6 10 

Обратите внимание, что индексы для функции Rcpp являются 0 на основе, так как они обрабатываются в C++.

Я абстрагировать логику Подменит в свою собственную функцию, logical_index, который преобразовывает IntegerVector к LogicalVector для того, чтобы иметь возможность «принять решение», следует ли удалить или сохранить указанные элементы (например, путем инвертирования результата). Я полагаю, что это можно было бы сделать и с подмножеством на основе целых чисел, но это не должно иметь значения в любом случае.

Как векторное подмножество в R, вектор все отрицательные индексы означают падение соответствующих элементов; тогда как вектор всех положительных индексов указывает на сохранение элементов. Я не проверял смешанные случаи, которые, вероятно, должны были бы исключить, как это сделает R.


Что касается моего последнего комментария, вероятно, было бы более разумно полагаться на родные перегрузках Rcpp для обыкновенных Подменят, и имеет специальную функцию для инверсного подмножества (R в x[-c(...)] конструкта), а не смешивая функциональность, как описано выше. Существуют ранее существовавшие выражения сахара для создания такой функции, например.

#include <Rcpp.h> 

template <int RTYPE> 
inline Rcpp::Vector<RTYPE> 
anti_subset(const Rcpp::Vector<RTYPE>& x, Rcpp::IntegerVector idx) { 
    Rcpp::IntegerVector xi = Rcpp::seq(0, x.size() - 1); 
    return x[Rcpp::setdiff(xi, idx)]; 
} 

// [[Rcpp::export]] 
Rcpp::NumericVector 
AntiSubset(Rcpp::NumericVector x, Rcpp::IntegerVector idx) { 
    return anti_subset(x, idx); 
} 

/*** R 

x <- seq(2, 10, 2) 

x[-c(2, 4)] 
#[1] 2 6 10 

AntiSubset(x, c(1, 3)) 
#[1] 2 6 10 

*/ 
+0

Большое вам спасибо! Я внес некоторые изменения в ваш код, чтобы лучше отразить 1-индексацию R, так что вход 'idx' может быть таким же, как R. Конечно, это чисто личное предпочтение. Мне также пришлось изменить строку 'LogicalVector result (n, false);' потому что компилятор будет жаловаться на то, что он неоднозначен. Я также добавил результаты сравнительного анализа. Эта реализация медленнее, чем R, но если вектор или 'idx' очень велик, разница должна быть тривиальной. Что заставляет меня задаться вопросом, как это делает R ... – aenima

+0

Ну, я оставил его как индексирование на основе 0 на предпосылке, что вам нужно было вызвать функцию из C++. И да, называть это из R, скорее всего, будет медленнее, чем собственное индексирование, потому что это очень примитивная операция в R, преимущественно обрабатываемая кодом C. Но опять же, если вы * нуждаетесь в *, чтобы называть это из C++ напрямую, нет смысла сравнивать его с чем-то, выполненным в R, потому что подмножество R не будет доступно вам из вашего кода на C++. – nrussell

+0

Я согласен с вами в том, что бенчмаркинг против R, вероятно, не нужен, поскольку я буду называть его в C++. Еще раз спасибо. – aenima

0

Вот функция, которую я написал, которая выполняет эту задачу. Не использовать отрицательные индексы, а через вызов функции. Это немного медленнее, чем функция R на моих тестах (небольшие векторы). Возможно, кто-то может опираться на это, я не проверял код nrussel, так что это может быть хуже. Изменить - если вы передаете вектор R, содержащий индексы для удаления, измените инструкцию if из «inds (j) == i» to «inds (j) -1 == i» (я считаю).

Примечание. Производительность может быть повышена путем установки нижнего предела внутреннего контура на основе того, какие индексы найдены. Предполагая, что индексный вектор сортируется в порядке возрастания.

arma::uvec rmIndices(unsigned int vecsize, arma::uvec inds){ 
    unsigned int negInds = 0, p = inds.size(); 
    bool foundMatch = false; 
    arma::uvec neg_inds(vecsize - p); 

    for(unsigned int i = 0; i < vecsize; ++i){ 

    for(unsigned int j = 0; j < p; ++j){ 
     if(inds(j) == i){//Check if we have a match between the index and the specified value 
     foundMatch = true; 
     } 
    }//End inner loop 
    if(!foundMatch){ 
     neg_inds(negInds) = i; 
     negInds = negInds + 1;//We have a match so, go to next position. 
    } 
    foundMatch = false; 
    } 

    return(neg_inds); 
} 
Смежные вопросы