¿Cómo organizar grandes programas de R?

161

Cuando emprendo un proyecto R de cualquier complejidad, mis guiones rápidamente se vuelven largos y confusos.

¿Cuáles son algunas prácticas que puedo adoptar para que sea un placer trabajar con mi código? Estoy pensando en cosas como

  • Colocación de funciones en archivos fuente
  • Cuándo dividir algo en otro archivo fuente
  • Lo que debería estar en el archivo maestro
  • Usar funciones como unidades organizativas (si esto vale la pena dado que R dificulta el acceso al estado global)
  • Prácticas de sangría / salto de línea.
    • Tratar (como {?
    • Poner cosas como)} en 1 o 2 líneas?

Básicamente, ¿cuáles son sus reglas generales para organizar grandes scripts R?

Dan Goldstein
fuente
11
debería ser wiki comunitario
SilentGhost
También es posible que desee ver el ProjectTemplatepaquete.
ctbrown

Respuestas:

71

La respuesta estándar es usar paquetes: consulte el manual de Extensiones de Writing R , así como diferentes tutoriales en la web.

Te lo dá

  • Una forma casi automática de organizar su código por tema
  • lo alienta encarecidamente a escribir un archivo de ayuda, haciéndole pensar en la interfaz
  • muchos controles de cordura a través de R CMD check
  • una oportunidad para agregar pruebas de regresión
  • así como un medio para espacios de nombres.

Solo corriendo source() el código funciona para fragmentos realmente cortos. Todo lo demás debe estar en un paquete, incluso si no planea publicarlo, ya que puede escribir paquetes internos para repositorios internos.

En cuanto a la parte 'cómo editar', el manual R Internals tiene excelentes estándares de codificación R en la Sección 6. De lo contrario, tiendo a usar valores predeterminados en el modo ESS de Emacs .

Actualización 2008-Ago-13: David Smith acaba de blogs sobre la Guía de estilo de Google R .

Dirk Eddelbuettel
fuente
8
Si está haciendo crecer su árbol fuente / análisis "orgánicamente", ¿no le resulta difícil / engorroso? Si nota un error en su código (común al explorar un nuevo espacio problemático), debe (i) corregir la fuente; (ii) reinstalar el paquete; (iii) volver a cargarlo en su espacio de trabajo? ¿Hay alguna forma de llamar a la biblioteca (...) para volver a cargar un paquete que ya está cargado (paso iii anterior)? ¿No tiene que matar su espacio de trabajo, reiniciar R y luego volver a cargar su biblioteca / paquete para ver si es correcto?
Steve Lianoglou
1
Intentando buscar en Google el estilo de codificación R.
hadley
3
@SteveLianoglou Sé que esto es bastante antiguo, pero el paquete devtools de Hadley hace que la recarga de todo su código sea muy fácil.
Dason
1
Esta publicación de blog ofrece (mi opinión) un tutorial rápido realmente bueno para crear un primer paquete básico
panterasBox
1
Aquí está el enlace de trabajo a la Guía de estilo de Google R
Denis Rasulev
51

Me gusta poner diferentes funcionalidades en sus propios archivos.

Pero no me gusta el sistema de paquetes de R. Es bastante difícil de usar.

Prefiero una alternativa ligera, para colocar las funciones de un archivo dentro de un entorno (lo que cualquier otro idioma llama un "espacio de nombres") y adjuntarlo. Por ejemplo, hice un grupo de 'util' de funciones así:

util = new.env()

util$bgrep = function [...]

util$timeit = function [...]

while("util" %in% search())
  detach("util")
attach(util)

Todo esto está en un archivo util.R . Cuando lo obtiene, obtiene el entorno 'util' para que pueda llamar util$bgrep()y tal; pero, además, la attach()llamada lo hace tan justo bgrep()y funciona directamente. Si no pusiera todas esas funciones en su propio entorno, contaminarían el espacio de nombres de nivel superior del intérprete (el que se ls()muestra).

Estaba tratando de simular el sistema de Python, donde cada archivo es un módulo. Sería mejor tenerlo, pero parece estar bien.

Brendan OConnor
fuente
Gracias Brendan. Eso es muy util. ¿Qué pasa con el ciclo while? ¿Qué tiene de malo si (! ("Util"% en% search ())) attach (util)
Dan Goldstein
2
para que pueda hacer la fuente ("util.R") una y otra vez si desea modificarlo y tal.
Brendan OConnor
realmente no necesita un ciclo while; todo lo que necesita es desconectar (util). No recuerdo si da un error o no, si aún no está cargado, pero esto es más seguro y funciona. Sugerencias bienvenidas.
Brendan OConnor
1
Crear entornos específicos de funciones y adjuntarlos es el camino a seguir para mí. Otro método para lograr lo mismo de una manera diferente (con más modularidad) es el uso de sys.source: MyEnv <- attach(NULL, name=s_env); sys.source(file, MyEnv). Incluso declaro (en su propio entorno) al inicio una función sys.source2que buscará si ya existe un entorno con el mismo nombre y alimenta este en lugar de crear uno nuevo. Hace que agregar funciones personales sea rápido, fácil y un poco organizado :-)
Antoine Lizée
55
Acabo de ver esto hoy y se aplica a una alternativa de paquete: github.com/klmr/modules
ctbrown
34

Esto puede sonar un poco obvio, especialmente si eres un programador, pero así es como pienso en las unidades lógicas y físicas de código.

No sé si este es su caso, pero cuando estoy trabajando en R, rara vez comienzo con un gran programa complejo en mente. Normalmente comienzo en un script y separo el código en unidades lógicamente separables, a menudo usando funciones. La manipulación de datos y el código de visualización se colocan en sus propias funciones, etc. Y estas funciones se agrupan en una sección del archivo (manipulación de datos en la parte superior, luego visualización, etc.). En última instancia, debe pensar en cómo facilitarle el mantenimiento de su secuencia de comandos y reducir la tasa de defectos.

El grado de precisión de sus funciones variará y existen varias reglas generales: por ejemplo, 15 líneas de código, o "una función debe ser responsable de realizar una tarea que se identifica por su nombre", etc. Su kilometraje variará . Dado que R no admite llamadas por referencia, por lo general varío para hacer que mis funciones sean demasiado finas cuando se trata de pasar marcos de datos o estructuras similares. Pero esto puede ser una sobrecompensación por algunos errores tontos de rendimiento cuando comencé con R.

¿Cuándo extraer unidades lógicas en sus propias unidades físicas (como archivos fuente y agrupaciones más grandes como paquetes)? Tengo dos casos Primero, si el archivo se hace demasiado grande y desplazarse entre las unidades lógicamente no relacionadas es una molestia. Segundo, si tengo funciones que pueden ser reutilizadas por otros programas. Normalmente empiezo colocando alguna unidad agrupada, digamos funciones de manipulación de datos, en un archivo separado. Entonces puedo obtener este archivo de cualquier otro script.

Si va a implementar sus funciones, debe comenzar a pensar en los paquetes. No despliegue el código R en producción o para su reutilización por otros por varias razones (brevemente: la cultura de la organización prefiere otros idiomas, preocupaciones sobre el rendimiento, GPL, etc.). Además, tiendo a refinar y agregar constantemente a mis colecciones de archivos de origen, y prefiero no tratar con paquetes cuando hago un cambio. Por lo tanto, debe consultar las otras respuestas relacionadas con el paquete, como Dirk, para obtener más detalles sobre este frente.

Finalmente, creo que su pregunta no es necesariamente particular para R. Realmente recomendaría leer Code Complete de Steve McConnell, que contiene mucha sabiduría sobre tales problemas y prácticas de codificación en general.

ars
fuente
3
Comentario muy útil, ars, gracias. Soy programador, pero es bueno consultar con otros. Cuando dice "Dado que R no admite llamadas por referencia, por lo general soy cauteloso de hacer que mis funciones sean demasiado finas", te escucho. Estoy acostumbrado a escribir funciones como ReadData (); CleanData (); Analizar datos(); GraphData (); y R lo hace engorroso. Me estoy despertando a la idea de que necesito usar "fuente" de la misma manera que uso funciones en otros idiomas.
Dan Goldstein
2
Tienes razón, Dan. Me encuentro usando "fuente" de esa manera para las tareas de preparación de conjuntos de datos, por lo que puedo usar un marco de datos preparado en otros scripts donde se realiza el análisis real. Nunca estuve seguro de si esto era una buena práctica porque simplemente se siente extraño en relación con otros lenguajes, más como scripts de shell realmente. Es bueno comparar notas. :)
ars
Buena respuesta y comentarios. Creo que esto es particularmente molesto en R, porque a menudo trabajas con datos que están en un formato particular, para el cual es realmente difícil escribir funciones reutilizables. Entonces, ¿escribe un código funcional agradable, a pesar de que sabe que nunca se reutilizará, o simplemente escribe un código de procedimiento rápido y desagradable para obtener esa eficiencia extra con objetos grandes? Un pequeño dilema ... R sería absolutamente increíble con una llamada por referencia ...
naught101
Así, se puede hacer, más o menos , pero no hay aumento de la eficiencia ...
naught101
19

Estoy de acuerdo con el consejo de Dirk! En mi humilde opinión, organizar sus programas desde scripts simples hasta paquetes documentados es, para la programación en R, como cambiar de Word a TeX / LaTeX para escribir. Recomiendo echar un vistazo a la muy útil Creación de paquetes R: un tutorial de Friedrich Leisch.

Paolo
fuente
66
Los paquetes se ven atractivos. Sin embargo, me preocupaba que pudieran ser excesivos. No estoy escribiendo código de uso general. La mayor parte de lo que estoy haciendo es probar esta hipótesis, probar esa hipótesis, trazar esto, ajustar los parámetros de la trama, trazar eso, remodelar los datos, trazar eso. Estoy haciendo cosas que, una vez terminado, probablemente nunca se volverán a ejecutar.
Dan Goldstein
1
En ese caso, deberías echar un vistazo a Sweave. Combina el código R con LaTeX. Entonces tiene el análisis y la fuente del informe juntos.
Thierry
15

Mi respuesta concisa:

  1. Escriba sus funciones cuidadosamente, identificando salidas y entradas suficientemente generales;
  2. Limite el uso de variables globales;
  3. Utilice objetos S3 y, cuando corresponda, objetos S4;
  4. Coloque las funciones en paquetes, especialmente cuando sus funciones están llamando a C / Fortran.

Creo que R se usa cada vez más en la producción, por lo que la necesidad de código reutilizable es mayor que antes. El intérprete me parece mucho más robusto que antes. No hay duda de que R es 100-300x más lento que C, pero generalmente el cuello de botella se concentra alrededor de unas pocas líneas de código, que se pueden delegar a C / C ++. Creo que sería un error delegar las fortalezas de R en la manipulación de datos y el análisis estadístico a otro idioma. En estos casos, la penalización de rendimiento es baja y, en cualquier caso, vale la pena ahorrar en el esfuerzo de desarrollo. Si el tiempo de ejecución solo fuera el problema, todos estaríamos escribiendo ensamblador.

alegre
fuente
11

He tenido la intención de descubrir cómo escribir paquetes, pero no he invertido el tiempo. Para cada uno de mis miniproyectos, mantengo todas mis funciones de bajo nivel en una carpeta llamada 'funciones /', y las obtengo en un espacio de nombres separado que creo explícitamente.

Las siguientes líneas de código crearán un entorno llamado "myfuncs" en la ruta de búsqueda si aún no existe (usando adjuntar), y lo completarán con las funciones contenidas en los archivos .r en mi directorio 'funciones /' (usando sys.source). Normalmente pongo estas líneas en la parte superior de mi script principal destinado a la "interfaz de usuario" desde la que se llaman las funciones de alto nivel (invocando las funciones de bajo nivel).

if( length(grep("^myfuncs$",search()))==0 )
  attach("myfuncs",pos=2)
for( f in list.files("functions","\\.r$",full=TRUE) )
  sys.source(f,pos.to.env(grep("^myfuncs$",search())))

Cuando realiza cambios, siempre puede volver a obtenerlos con las mismas líneas, o usar algo como

evalq(f <- function(x) x * 2, pos.to.env(grep("^myfuncs$",search())))

para evaluar adiciones / modificaciones en el entorno que creó.

Es un error, lo sé, pero evita tener que ser demasiado formal al respecto (pero si tienes la oportunidad, aliento el sistema de paquetes; con suerte, migraré de esa manera en el futuro).

En cuanto a las convenciones de codificación, esto es lo único que he visto con respecto a la estética (me gustan y las sigo libremente pero no uso demasiadas llaves en R):

http://www1.maths.lth.se/help/R/RCC/

Hay otras "convenciones" con respecto al uso de [, drop = FALSE] y <- como lo sugirió el operador de asignación en varias presentaciones (generalmente keynote) en useR! conferencias, pero no creo que ninguna de estas sea estricta (aunque [, drop = FALSE] es útil para programas en los que no está seguro de la entrada que espera).

hatmatrix
fuente
6

Cuenta conmigo como otra persona a favor de los paquetes. Admitiré que soy bastante pobre en escribir páginas de manual y viñetas hasta que tenga que hacerlo (es decir, ser liberado), pero es una forma realmente útil de agrupar la fuente doe. Además, si te tomas en serio el mantenimiento de tu código, los puntos que menciona Dirk entran en juego.

geoffjentry
fuente
4

Yo tambien estoy de acuerdo. Use la función package.skeleton () para comenzar. Incluso si cree que su código puede no volver a ejecutarse nuevamente, puede ayudarlo a motivarlo a crear un código más general que pueda ahorrarle tiempo más adelante.

En cuanto al acceso al entorno global, eso es fácil con el operador << -, aunque no se recomienda.

cameron.bracken
fuente
3

Como todavía no he aprendido a escribir paquetes, siempre me he organizado buscando sub scripts. Es similar a las clases de escritura pero no tan complicado. No es programáticamente elegante, pero creo que construyo análisis con el tiempo. Una vez que tengo una gran sección que funciona, a menudo la muevo a un script diferente y la obtengo, ya que usará los objetos del espacio de trabajo. Tal vez necesito importar datos de varias fuentes, ordenarlos todos y encontrar las intersecciones. Podría poner esa sección en un script adicional. Sin embargo, si desea distribuir su "aplicación" para otras personas, o si utiliza alguna entrada interactiva, un paquete es probablemente una buena ruta. Como investigador, rara vez necesito distribuir mi código de análisis, pero A MENUDO necesito aumentarlo o modificarlo.

kpierce8
fuente
Utilicé este método pero desde entonces me di cuenta de que las funciones y los paquetes son mejores que la fuente ("next_script.R"). Escribí sobre esto aquí: stackoverflow.com/questions/25273166/…
Arthur Yip el
1

También he estado buscando el santo grial del flujo de trabajo adecuado para armar un proyecto R grande. El año pasado encontré este paquete llamado rsuite y, ciertamente, era lo que estaba buscando. Este paquete R se desarrolló explícitamente para la implementación de proyectos R grandes, pero descubrí que se puede usar para proyectos R más pequeños, medianos y grandes. Daré enlaces a ejemplos del mundo real en un minuto (a continuación), pero primero quiero explicar el nuevo paradigma de construir proyectos de R conrsuite .

Nota. No soy el creador o desarrollador de rsuite.

  1. Hemos estado haciendo proyectos mal con RStudio; El objetivo no debe ser la creación de un proyecto o un paquete, sino un alcance mayor. En rsuite crea un superproyecto o proyecto maestro, que contiene los proyectos R estándar y los paquetes R, en todas las combinaciones posibles.

  2. Al tener un superproyecto R, ya no necesita Unix makepara administrar los niveles inferiores de los proyectos R debajo; usa scripts R en la parte superior. Deja que te enseñe. Cuando crea un proyecto maestro rsuite, obtiene esta estructura de carpetas:

ingrese la descripción de la imagen aquí

  1. La carpeta Res donde coloca sus scripts de gestión de proyectos, los que reemplazarán make.

  2. La carpeta packageses la carpeta donde rsuitese encuentran todos los paquetes que componen el superproyecto. También puede copiar y pegar un paquete que no es accesible desde Internet, y rsuite también lo compilará.

  3. la carpeta deploymentes donde rsuiteescribirá todos los binarios de paquetes que se indicaron en los DESCRIPTIONarchivos de paquetes . Entonces, esto hace que, por sí mismo, proyectes un tiempo accros totalmente reproducible.

  4. rsuiteviene con un cliente para todos los sistemas operativos. Los he probado todos. Pero también puede instalarlo como un addinRStudio.

  5. rsuiteTambién le permite construir una condainstalación aislada en su propia carpeta conda. Este no es un entorno sino una instalación física de Python derivada de Anaconda en su máquina. Esto funciona junto con los R SystemRequirements, desde los cuales puede instalar todos los paquetes de Python que desee, desde cualquier canal conda que desee.

  6. También puede crear repositorios locales para extraer paquetes R cuando esté desconectado, o si desea construir todo más rápido.

  7. Si lo desea, también puede construir el proyecto R como un archivo zip y compartirlo con sus colegas. Se ejecutará, siempre que sus colegas tengan instalada la misma versión R.

  8. Otra opción es construir un contenedor de todo el proyecto en Ubuntu, Debian o CentOS. Entonces, en lugar de compartir un archivo zip con la compilación de su proyecto, comparte todo el Dockercontenedor con su proyecto listo para ejecutarse.

He estado experimentando mucho con rsuite buscando la reproducibilidad total y evito depender de los paquetes que uno instala en el entorno global. Esto es incorrecto porque tan pronto como instala una actualización de paquete, el proyecto, la mayoría de las veces, deja de funcionar, especialmente aquellos paquetes con llamadas muy específicas a una función con ciertos parámetros.

Lo primero que comencé a experimentar fue con los bookdownlibros electrónicos. Nunca he tenido la suerte de tener un libro para sobrevivir a la prueba del tiempo por más de seis meses. Entonces, lo que hice fue convertir el proyecto original de bookdown para seguir el rsuitemarco. Ahora, no tengo que preocuparme por actualizar mi entorno global R, porque el proyecto tiene su propio conjunto de paquetes en eldeployment carpeta.

Lo siguiente que hice fue crear proyectos de aprendizaje automático, pero en el rsuitecamino. Un proyecto maestro, orquestador en la parte superior, y todos los subproyectos y paquetes que estén bajo el control del maestro. Realmente cambia la forma en que codifica con R, haciéndolo más productivo.

Después de eso comencé a trabajar en un nuevo paquete mío llamado rTorch. Esto fue posible, en gran parte, debido a rsuite; te permite pensar e ir a lo grande.

Sin embargo, un consejo. Aprender rsuiteno es fácil. Debido a que presenta una nueva forma de crear proyectos R, se siente difícil. No te preocupes por los primeros intentos, continúa subiendo la cuesta hasta que lo hagas. Requiere un conocimiento avanzado de su sistema operativo y de su sistema de archivos.

Espero que algún día RStudionos permita generar proyectos de orquestación comorsuite hace desde el menú. Sería genial.

Enlaces:

RSuite GitHUb repo

bookdown de r4ds

Keras y tutorial brillante

moderno-libro-rsuite

interpretable_ml-rsuite

IntroducciónMachineLearningWithR-rsuite

clark-intro_ml-rsuite

hyndman-bookdown-rsuite

statistic_rethinking-rsuite

fread-benchmarks-rsuite

dataviz-rsuite

retail-segmentation-h2o-tutorial

telco-customer-churn-tutorial

sclerotinia_rsuite

f0nzie
fuente
-7

R está bien para uso interactivo y pequeños scripts, pero no lo usaría para un programa grande. Usaría un lenguaje convencional para la mayoría de la programación y lo envolvería en una interfaz R.

John D. Cook
fuente
1
Hay paquetes muy grandes (es decir, programas) por ahí. ¿Estás sugiriendo seriamente que deberían reescribirse en otro idioma? ¿¿¿Por qué???
Eduardo Leoni
44
Una consideración es la eficiencia. A menudo reescribí el código R como código C ++ y lo hice 100 veces más rápido. Otro es el soporte de herramientas. R no tiene nada comparable a IDEs como Eclipse o Visual Studio. Finalmente, si un programa es muy grande, es probable que esté haciendo tareas no estadísticas para las que R no es adecuado.
John D. Cook
2
Hay un complemento (Stat-ET) disponible que permite que Eclipse interactúe con R. Estoy de acuerdo en que C ++ puede funcionar mucho más rápido que R. Pero, ¿cuánto tiempo necesita para volver a codificar las cosas R en C ++? A menos que pueda reutilizar el código con frecuencia, el beneficio del código más rápido no vale mucho en comparación con el esfuerzo de recodificarlo en C ++.
Thierry
2
Sí, hay una compensación (productividad v rendimiento). Y para el análisis puramente de datos / trabajo estadístico, R a menudo gana. Pero para escribir otras tareas, por ejemplo, GUI, web, etc., no estoy seguro de que sea así. A menudo realizamos prototipos y trabajamos en R, pero implementamos código de producción en Python / C ++. Con este último obtienes rendimiento y bibliotecas / marcos muy maduros y reutilizables para diversas tareas. Pero, esta es una situación fluida y el ecosistema R está en constante evolución.
ars
El uso del Rcpppaquete, incluido el código C ++ en los programas R, se vuelve bastante sencillo. Por lo tanto, reescribir ciertas partes del código R puede integrarse en R con bastante facilidad. Además, la llegada de RStudio ha introducido un IDE para R, aunque quizás aún no sea tan poderoso como Visual Studio.
Paul Hiemstra