2009-11-12 14 views
14

Qual è il modo migliore per utilizzare una libreria C++ in R, con la speranza di preservare le strutture di dati C++. Non sono affatto un utente C++, quindi non sono chiaro sui meriti relativi degli approcci disponibili. Il manuale R-ext sembra suggerire di avvolgere ogni funzione C++ in C. Tuttavia, esistono almeno quattro o cinque altri modi per incorporare C++.Utilizzo di librerie C++ in un pacchetto R

Due modi sono pacchetti con un lignaggio simile, Rcpp (gestito dal prolifico overflow Dirk Eddelbuettel) e pacchetti RcppTemplate (entrambi su CRAN), quali sono le differenze tra i due?

Un altro pacchetto, rcppbind disponibile, su R forge che afferma di adottare un approccio diverso per l'associazione C++ e R (non sono a conoscenza).

Il pacchetto inline disponibile su CRAN, afferma di consentire C in linea C/C++ Non sono sicuro che questo differisca dalle funzionalità incorporate, a parte per consentire al codice di essere in linea con R /.

E, infine, RSwig che sembra essere in the wild ma non è chiaro come sia supportato, poiché lo author's page non è stato aggiornato da anni.

La mia domanda è, quali sono i meriti relativi di questi diversi approcci. Quali sono i più portatili e robusti, che sono i più facili da implementare. Se stavi progettando di distribuire un pacchetto su CRAN quale dei metodi useresti?

risposta

15

Prima di tutto, una dichiarazione di non responsabilità: io uso Rcpp tutto il tempo. Infatti, quando (essendo stato rinominato dal momento in cui Rcpp) RcppTemplate era già rimasto orfano e senza aggiornamenti per due anni, ho iniziato a mantenerlo sotto il suo nome iniziale di Rcpp (sotto il quale era stato contribuito a RQuantLib). È stato circa un anno fa e ho apportato un paio di modifiche incrementali che puoi trovare documentate nel ChangeLog.

Ora RcppTemplate è tornato molto recentemente dopo trentacinque mesi senza alcun aggiornamento o correzione. Contiene un nuovo codice interessante, ma sembra che non sia retrocompatibile quindi non lo userò dove ho già usato Rcpp.

Rcppbind non è stato mantenuto molto attivamente ogni volta che ho controllato. Whit Armstrong ha anche un pacchetto di interfaccia basato su modelli chiamato rabstraction.

Inline è qualcosa di completamente diverso: facilita il ciclo di compilazione/collegamento "incorporando" il programma come stringa di caratteri R che viene quindi compilata, collegata e caricata. Ho parlato con Oleg di avere un supporto in linea Rcpp che sarebbe bello.

Swig è anche interessante. Joe Wang ha fatto un ottimo lavoro lì e ha avvolto tutto QuantLib per R. Ma quando l'ho provato l'ultima volta, non funzionava più a causa di alcuni cambiamenti in R internals. Secondo qualcuno del team di Swig, Joe potrebbe ancora lavorarci su. L'obiettivo di Swig è comunque librerie più grandi. Questo progetto potrebbe probabilmente fare con un risveglio, ma non è senza sfide tecniche.

Un'altra menzione dovrebbe andare a RInside che funziona con Rcpp e consente di incorporare R all'interno di applicazioni C++.

Quindi, per riassumere: Rcpp funziona bene per me, soprattutto per i piccoli progetti di esplorazione in cui si desidera semplicemente aggiungere una funzione o due. Il suo obiettivo è la facilità d'uso, e ti permette di "nascondere" alcuni dei R interni che non sono sempre divertenti da usare. Conosco un certo numero di altri utenti che ho aiutato su e via email. Quindi direi di andare per questo.

Le esercitazioni di "Intro to HPC with R" presentano alcuni esempi di Rcpp, RInside e inline.

Modifica: Quindi diamo un'occhiata ad un esempio concreto (tratto dalle diapositive 'HPC con R Intro' e preso in prestito da Stephen Milborrow che lo ha preso da Venables and Ripley). Il compito è enumerare tutte le possibili combinazioni del determinante di una matrice 2x2 contenente solo cifre singole in ciascuna posizione. Questo può essere fatto in modi Vectorised intelligenti (come vedremo nelle diapositive di tutorial) o con la forza bruta come segue:

#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 utilizzare

PKG_CPPFLAGS=`Rscript -e 'Rcpp:::CxxFlags()'` \ 
    PKG_LIBS=`Rscript -e 'Rcpp:::LdFlags()'` \ 
    R CMD SHLIB dd.rcpp.cpp 

per creare una libreria condivisa. Utilizziamo Rscript (o r) per chiedere a Rcpp l'intestazione e le posizioni delle librerie. 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 '.

Edit 2 (circa cinque + anni più tardi):

Quindi questa risposta appena ricevuto un upvote e quindi gorgogliare nella mia coda. A il lotto di tempo è trascorso da quando l'ho scritto e Rcpp ha ottenuto un sacco più ricco di funzionalità. Così ho molto rapidamente scritto 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:

  • più semplice costruire: basta sourceCpp() esso; anche esegue R codice di prova all'estremità
  • pieno titolo IntegerVector tipo
  • di gestione delle eccezioni involucro aggiunto automaticamente dal generatore di codice sourceCpp()
+0

Grazie per la risposta! Potresti chiarire cosa intendi con "codice nuovo interessante" in RcppTemplate. Come si differenzia il pacchetto da Rcpp? Non conosco quasi nulla di C++, come funzionano questi pacchetti? – Peter

+2

Dirk, forse è il momento per la vignetta rapida parlata su come far funzionare C++ con R ... :(! – knguyen

+0

Non è facile spiegare come differiscono così com'è ... nel C++ che differiscono –