2010-11-05 14 views
97

¿Cuál es el mejor recurso para aprender a escribir el código C para usar con R? Conozco la sección system and foreign language interfaces de las extensiones R, pero me parece bastante difícil. ¿Cuáles son los buenos recursos (tanto en línea como fuera de línea) para escribir el código C para usar con R?¿Dónde puedo aprender a escribir el código C para acelerar las funciones R lentas?

Para aclarar, no quiero aprender a escribir el código C, quiero aprender cómo integrar mejor R y C. Por ejemplo, cómo convertir de un vector entero C a un vector entero R (o viceversa) o de un escalar C a un vector R?

Respuesta

63

Bueno, está el buen viejo Use la fuente, Luke! --- R tiene mucho código C (muy eficiente) que uno puede estudiar, y CRAN tiene cientos de paquetes, algunos de autores en los que confía. Eso proporciona ejemplos reales y probados para estudiar y adaptarse.

Pero como Josh sospechaba, me inclino más hacia C++ y por lo tanto a Rcpp. También tiene muchos ejemplos.

Editar: Había dos libros que he encontrado útil:

  • El primero es Venables y Ripley "S Programación" a pesar de que cada vez es más largo en el diente (y ha habido rumores de una 2da edición por años). En ese momento simplemente no había nada más.
  • El segundo en el "Software " de Chambers para Análisis de Datos "que es mucho más reciente y tiene una sensación R-céntrica mucho más agradable - y dos capítulos sobre extender R. Se mencionan tanto C como C++. Además, John me destroza por lo que hice con digest, por lo que solo vale la pena el precio de la entrada.

Dicho esto, John está creciendo aficionado a Rcpp (y contribuye) en que se encuentre el partido entre objetos R y objetos C++ (a través de Rcpp) a ser muy natural - y ReferenceClasses ayudar allí.

Edición 2: Con cuestión reenfocado de Hadley, que muy fuertemente exhortamos a considerar C++. Hay tantas tonterías repetitivas que tiene que ver con C --- muy tedioso y muy evitable. Eche un vistazo al Rcpp-introduction vignette. Otro ejemplo simple es this blog post donde muestro que en lugar de preocuparme por las diferencias del 10% (en uno de los ejemplos de Radford Neal) podemos obtener ochenta y uno aumenta con C++ (en lo que es, por supuesto, un ejemplo artificial).

Editar 3: Existe la complejidad en que puede encontrarse con errores de C++ que son, para decirlo suavemente, difíciles de asimilar. Pero para solo use Rcpp en lugar de extenderlo, casi nunca lo necesitará. Y aunque este cuesta es innegable, está eclipsado por el beneficio de código más simple, menos repetitivo, no PROTEGER/DESPRENDER, no hay administración de memoria, etc. pp. Doug Bates declaró ayer que encuentra que C++ y Rcpp son mucho más como escribir R que escribir C++. YMMV y todo eso.

+0

Esperaba que obtuviera una respuesta de "use Rcpp";) Sería realmente útil si pudiera deletrear las desventajas de usar C++ en lugar de C.Uno de los principales parece ser que C++ es mucho más complejo que C, ¿esto hace que sea más difícil de usar? (O, en la práctica, ¿puedes escribir código C++ que sea muy similar a C?) También agradecería más material de referencia dirigido a usuarios nuevos que no están familiarizados con la API C existente. – hadley

+2

See * Edit 3 * and * yes, you can *. Meyers llama a C++ un lenguaje de "cuatro paradigmas" y no tiene que usar los cuatro. Usarlo como 'simplemente una mejor C' y usar Rcpp como pegamento para R está perfectamente bien. Nadie te obliga a un estilo, esto no es Java ;-) –

+0

@Dirk: thx para la elaboración. Planteó la pregunta en nuestra oficina antes, ya que C se usa comúnmente aquí en lugar de C++. ¿Cuándo sería beneficioso el uso de C sobre C++, o simplemente dices "nunca C, siempre C++"? –

50

Hadley,

Definitivamente, usted puede escribir código C++ que es similar al código C.

Entiendo lo que dices acerca de que C++ es más complicado que C. Esto es si quieres dominar todo: objetos, plantillas, STL, meta programación de plantillas, etc. La mayoría de la gente no necesita estas cosas y puede solo confía en otros para eso. La implementación de Rcpp es muy complicada, pero solo porque no sabes cómo funciona tu nevera, no significa que no puedas abrir la puerta y tomar leche fresca ...

De tus muchas contribuciones a R, ¿qué ataca? es que encuentras R algo tedioso (manipulación de datos, gráficos, manipulación de cadenas, etc.). Bueno, prepárate para muchas más sorpresas con el C API interno de R. Esto es muy tedioso.

De vez en cuando, leo los manuales R-exts o R-ints. Esto ayuda. Pero la mayoría de las veces, cuando realmente quiero saber algo, ingreso a la fuente R y también a la fuente de los paquetes escritos, por ejemplo, Simon (generalmente hay mucho que aprender allí).

Rcpp está diseñado para hacer que estos tediosos aspectos de API desaparezcan.

Puede juzgar por sí mismo lo que le parece más complicado, ofuscado, etc., basado en algunos ejemplos. Esta función crea un vector de caracteres mediante la API C:

SEXP foobar(){ 
    SEXP ab; 
    PROTECT(ab = allocVector(STRSXP, 2)); 
    SET_STRING_ELT(ab, 0, mkChar("foo")); 
    SET_STRING_ELT(ab, 1, mkChar("bar")); 
    UNPROTECT(1); 
} 

Usando RCPP, se puede escribir la misma función que:

SEXP foobar(){ 
    return Rcpp::CharacterVector::create("foo", "bar") ; 
} 

o:

SEXP foobar(){ 
    Rcpp::CharacterVector res(2) ; 
    res[0] = "foo" ; 
    res[1] = "bar" ; 
    return res ; 
} 

Como dijo Dirk, hay hay otros ejemplos en varias viñetas. También usualmente señalamos a las personas hacia nuestras pruebas unitarias porque cada una de ellas prueba una parte muy específica del código y se explican por sí mismas.

Obviamente, estoy predispuesto aquí, pero recomendaría familiarizarme con Rcpp en lugar de aprender C API de R, y luego llegar a la lista de correo si algo no está claro o no parece factible con Rcpp.

De todos modos, fin del argumento de venta.

Supongo que todo depende del tipo de código que desee escribir con el tiempo.

Romain

+2

"Rcpp está diseñado para que desaparezcan estos tediosos aspectos de la API" = exactamente lo que estoy buscando. ¡Gracias! Lo que sería realmente útil sería un v. Breve manual de C++ para alguien que esté familiarizado con C y quiera usar Rcpp. – hadley

+0

bueno, ese pequeño ejemplo de Rcpp me consiguió vender. Asumo allocXX y UNPROTECT (1) se maneja de manera similar a cómo los punteros inteligentes manejan el recurso. es decir RAII. ¿Hay alguna penalización de rendimiento notable al usar Rcpp sobre C api vainilla? – jbremnant

+0

Abordamos eso en la introducción de Rcpp con un ejemplo de referencia (que también está en el paquete de fuentes/instalado). En resumen, sin penalización en absoluto. –

17

@jbremnant: Así es. Las clases Rcpp implementan algo parecido al patrón RAII. Cuando se crea un objeto Rcpp, el constructor toma las medidas adecuadas para garantizar que el objeto R subyacente (SEXP) esté protegido del recolector de elementos no utilizados. El destructor retira la protección. Esto se explica en la viñeta Rcpp-intrduction. La implementación subyacente se basa en las funciones de la API R R_PreserveObject y R_ReleaseObject

En efecto, existe penalización en el rendimiento debido a la encapsulación C++. Tratamos de mantener esto al mínimo con la creación de líneas, etc. La penalización es pequeña, y cuando se toma en cuenta la ganancia en términos de tiempo que lleva escribir y mantener el código, no es tan relevante.

Llamar a funciones R de la clase Rcpp La función es más lenta que llamar directamente a eval con la API C. Esto se debe a que tomamos precauciones y ajustamos la llamada a la función en un bloque tryCatch para que capturemos errores R y los promovamos a excepciones C++ para que puedan ser tratados usando el try/catch estándar en C++.

La mayoría de las personas quiere usar vectores (especialmente NumericVector), y la penalización es muy pequeña con esta clase. El directorio examples/ConvolveBenchmarks contiene varias variantes de la notoria función de convolución de R-exts y la viñeta tiene resultados de referencia. Resulta que Rcpp lo hace más rápido que el código de referencia que usa la API R.

26

@hadley: desafortunadamente, no tengo recursos específicos en mente para ayudarlo a comenzar con C++. Lo recogí de los libros de Scott Meyers (Effective C++, C++ más efectivo, etc.) pero estos no son realmente lo que se podría llamar introductorio.

Utilizamos casi exclusivamente la interfaz .Call para llamar al código C++. La regla es bastante fácil:

  • La función C++ debe devolver un objeto R. Todos los objetos R son SEXP.
  • función
  • El C++ tarda entre 0 y 65 R objetos como entrada (de nuevo SEXP)
  • debe (no realmente, pero podemos guardar para más tarde) la declaración posterior de vinculación C, ya sea con extern "C" o el RcppExport alias que define Rcpp.

lo tanto una función .Llame se declaró como este en algún archivo de cabecera:

#include <Rcpp.h> 

RcppExport SEXP foo(SEXP x1, SEXP x2) ; 

e implementado como esto en un archivo .cpp:

SEXP foo(SEXP x1, SEXP x2){ 
    ... 
} 

no hay mucho más para saber acerca de la API R para usar Rcpp.

La mayoría de la gente solo quiere tratar con vectores numéricos en Rcpp. Lo haces con la clase NumericVector. Hay varias maneras de crear un vector numérico:

De un objeto existente que se pasa por debajo de R:

SEXP foo(SEXP x_) { 
    Rcpp::NumericVector x(x_) ; 
    ... 
} 

Con valores dados utilizando el :: create función estática:

Rcpp::NumericVector x = Rcpp::NumericVector::create(1.0, 2.0, 3.0) ; 
Rcpp::NumericVector x = Rcpp::NumericVector::create( 
    _["a"] = 1.0, 
    _["b"] = 2.0, 
    _["c"] = 3 
) ; 

De un tamaño dado:

Rcpp::NumericVector x(10) ;  // filled with 0.0 
Rcpp::NumericVector x(10, 2.0) ; // filled with 2.0 

Luego, una vez que tienes un vector, lo más útil es extraer un elemento de él. Esto se hace con el operador [], con la indexación base 0, por lo que, por ejemplo, sumando los valores de un vector numérico es algo como esto:

SEXP sum(SEXP x_){ 
    Rcpp::NumericVector x(x_) ; 
    double res = 0.0 ; 
    for(int i=0; i<x.size(), i++){ 
     res += x[i] ; 
    } 
    return Rcpp::wrap(res) ; 
} 

Pero con el azúcar RCPP podemos hacer esto mucho más agradable ahora:

using namespace Rcpp ; 
SEXP sum(SEXP x_){ 
    NumericVector x(x_) ; 
    double res = sum(x) ; 
    return wrap(res) ; 
} 

Como dije antes, todo depende del tipo de código que desea escribir. Mire lo que hace la gente en paquetes que dependen de Rcpp, revise las viñetas, las pruebas de unidades, vuelva a consultarnos en la lista de correo. Siempre estamos felices de ayudar.

Cuestiones relacionadas