Utilizzando C ++ librerie in un pacchetto R
Domanda
Qual è il modo migliore per fare uso di una libreria C ++ in R, si spera preservando il C ++ strutture di dati. Io non sono affatto un utente C ++, quindi non sono chiare sui meriti relativi degli approcci disponibili. Il manuale R-ext sembra suggerire avvolgere ogni funzione C ++ in C. Tuttavia, almeno quattro o cinque altri mezzi di incorporare C ++ esiste.
Due modi sono pacchetti w / simile lignaggio, il Rcpp (mantenuta dal prolifico overflower Dirk Eddelbuettel) e RcppTemplate pacchetti (sia su CRAN), quali sono le differenze tra i due?
Un altro pacchetto, rcppbind disponibili, sulla fucina R che pretende di adottare un approccio diverso al legame C ++ e R (io non sono esperto per dire).
La linea pacchetto disponibile sul CRAN, sostiene di consentire linea C / C ++ non sono sicuro che questo si differenzia dal sviluppato in funzionalità, a parte per consentire il codice per essere in linea w / R.
E, infine RSwig che sembra essere href="http://www.swig.org/Doc1.3/R.html" in natura ma è chiaro come sostenuto è, come la pagina del autore non è stato aggiornato da anni.
La mia domanda è, quali sono i meriti relativi di questi diversi approcci. Quali sono i più portatile e robusto, che sono le più facili da implementare. Se avete intenzione di distribuire un pacchetto su CRAN quale dei metodi useresti?
Soluzione
Prima di tutto, un disclaimer: io uso Rcpp tutto il tempo. Infatti, quando (essendo stato rinominato per il momento da Rcpp) RcppTemplate era già stato orfano e senza aggiornamenti per due anni, ho iniziato a mantenerlo sotto il suo nome iniziale di Rcpp (in base al quale era stata contribuito a RQuantLib ). E 'stato circa un anno fa, e ho fatto un paio di modifiche incrementali che si può trovare documentati nel changelog.
Ora RcppTemplate è a poco tempo fa tornare dopo ben trentacinque mesi senza alcun aggiornamento o correggere. Contiene nuovo codice interessante, ma sembra che non sia compatibile quindi non voglio usarlo dove ho già usato Rcpp.
Rcppbind non è stata mantenuta molto attivamente ogni volta che ho controllato. Whit Armstrong ha anche un pacchetto di interfaccia su modelli chiamato rabstraction .
Inline è qualcosa di completamente diverso: facilita la compilazione / link ciclo 'incorporando' il tuo programma come una stringa di caratteri R che poi viene compilato, legata, e caricato. Ho parlato con Oleg di avere in linea il supporto Rcpp che sarebbe bello.
Swig è interessante anche. Joe Wang ha fatto grande lavoro lì e avvolto tutti QuantLib per R. Ma quando ho provato l'ultima, non è più funzionato a causa di alcuni cambiamenti nella struttura interna R. Secondo qualcuno della squadra Swig, Joe può ancora lavorare su di esso. L'obiettivo di Swig è librerie più grandi comunque. Questo progetto potrebbe probabilmente fare con un rilancio, ma non è senza sfide tecniche.
Un altro menzione dovrebbe andare a RInside che funziona con Rcpp e consente di incorporare all'interno R di applicazioni C ++.
Quindi, per riassumere: Rcpp funziona bene per me, soprattutto per le piccole esplorativa progetti in cui si desidera solo aggiungere una funzione o due. Il suo obiettivo è la facilità d'uso, e ti permette di 'nascondere' alcuni degli interni R che non sono sempre divertente lavorare con. So di un certo numero di altri utenti i quali mi hanno aiutato in e fuori e via e-mail. Quindi direi andare per questo.
Il mio 'Introduzione a HPC con R' tutorial avere alcuni esempi di Rcpp, RInside e in linea.
Modifica Quindi diamo un'occhiata a un esempio concreto (tratto dal 'HPC con R Intro' diapositive e presa in prestito da Stephen Milborrow che ha preso da Venables e Ripley). Il compito è enumerare tutte le possibili combinazioni del determinante di una matrice 2x2 contenente solo singole cifre in ciascuna posizione. Questo può essere fatto in modi Vectorised intelligenti (come vedremo nelle diapositive di tutorial) o con la forza bruta nel seguente modo:
#include <Rcpp.h>
RcppExport SEXP dd_rcpp(SEXP v) {
SEXP rl = R_NilValue; // Use this when there is nothing to be returned.
char* exceptionMesg = NULL; // msg var in case of error
try {
RcppVector<int> vec(v); // vec parameter viewed as vector of ints
int n = vec.size(), i = 0;
if (n != 10000)
throw std::length_error("Wrong vector size");
for (int a = 0; a < 9; a++)
for (int b = 0; b < 9; b++)
for (int c = 0; c < 9; c++)
for (int d = 0; d < 9; d++)
vec(i++) = a*b - c*d;
RcppResultSet rs; // Build result set to be returned as list to R
rs.add("vec", vec); // vec as named element with name 'vec'
rl = rs.getReturnList(); // Get the list to be returned to R.
} catch(std::exception& ex) {
exceptionMesg = copyMessageToR(ex.what());
} catch(...) {
exceptionMesg = copyMessageToR("unknown reason");
}
if (exceptionMesg != NULL)
Rf_error(exceptionMesg);
return rl;
}
Se si salva questo come, ad esempio, dd.rcpp.cpp
e hanno Rcpp installato, quindi è sufficiente usare
PKG_CPPFLAGS=`Rscript -e 'Rcpp:::CxxFlags()'` \
PKG_LIBS=`Rscript -e 'Rcpp:::LdFlags()'` \
R CMD SHLIB dd.rcpp.cpp
per costruire una libreria condivisa. Usiamo Rscript
(o r
) chiedere Rcpp sulle sue sedi di intestazione e libreria. Una volta costruito, siamo in grado di caricare e utilizzare questo da R come segue:
dyn.load("dd.rcpp.so")
dd.rcpp <- function() {
x <- integer(10000)
res <- .Call("dd_rcpp", x)
tabulate(res$vec)
}
Allo stesso modo, è possibile inviare vettori, Matrics, ... di vari R e C ++ tipi di dati back-end indietro con facilità. Spero che questo aiuti un po '.
Modifica 2 (circa cinque anni + tardi):
Quindi, questa risposta appena ricevuto un upvote e quindi gorgogliare nella mia coda. A molto di tempo è passato da quando l'ho scritto, e Rcpp hcome ottenuto molto più ricco di funzionalità. Così ho scritto molto rapidamente questo
#include <Rcpp.h>
// [[Rcpp::export]]
Rcpp::IntegerVector dd2(Rcpp::IntegerVector vec) {
int n = vec.size(), i = 0;
if (n != 10000)
throw std::length_error("Wrong vector size");
for (int a = 0; a < 9; a++)
for (int b = 0; b < 9; b++)
for (int c = 0; c < 9; c++)
for (int d = 0; d < 9; d++)
vec(i++) = a*b - c*d;
return vec;
}
/*** R
x <- integer(10000)
tabulate( dd2(x) )
*/
che può essere utilizzato nel modo seguente con il codice in un file /tmp/dd.cpp
R> Rcpp::sourceCpp("/tmp/dd.cpp") # on from any other file and path
R> x <- integer(10000)
R> tabulate( dd2(x) )
[1] 87 132 105 155 93 158 91 161 72 104 45 147 41 96
[15] 72 120 36 90 32 87 67 42 26 120 41 36 27 75
[29] 20 62 16 69 19 28 49 45 12 18 11 57 14 48
[43] 10 18 7 12 6 46 23 10 4 10 4 6 3 38
[57] 2 4 2 3 2 2 1 17
R>
Alcune delle differenze principali sono:
- semplice accumulo: basta
sourceCpp()
esso; anche esegue R codice di prova all'estremità - a tutti gli effetti di tipo
IntegerVector
- gestione delle eccezioni involucro aggiunto automaticamente dal generatore di codice
sourceCpp()