2010-12-10 13 views
31

Las preguntas recientes sobre el uso de require versus :: plantearon la pregunta sobre qué estilos de programación se usan al programar en R, y cuáles son sus ventajas/desventajas. Navegando por el código fuente o navegando en la red, verá una gran cantidad de estilos diferentes que se muestran.Práctica de codificación en R: ¿Cuáles son las ventajas y desventajas de diferentes estilos?

Las principales tendencias en mi código:

  • que juegan mucho con los índices y los índices (anidados), que se traduce en código bastante oscura a veces, pero generalmente es mucho más rápido que otras soluciones. por ejemplo: x[x < 5] <- 0 en lugar de x <- ifelse(x < 5, x, 0)

  • tiendo a anidar funciones para evitar la sobrecarga de la memoria con objetos temporales que tengo que limpiar. Especialmente con funciones que manipulan grandes conjuntos de datos, esto puede ser una carga real. por ejemplo: y <- cbind(x,as.numeric(factor(x))) en lugar de y <- as.numeric(factor(x)) ; z <- cbind(x,y)

  • Escribo muchas funciones personalizadas, incluso si uso el código solo una vez en, por ejemplo. una sapply. Creo que lo mantiene más legible sin crear objetos que puedan permanecer por ahí.

  • que evitar bucles a toda costa, ya que considero vectorización ser mucho más limpio (y más rápido)

Sin embargo, me he dado cuenta de que las opiniones sobre este difieren, y algunas personas tienden a respaldar lejos de lo que llamarían mi forma de programación "Perl" (o incluso "Lisp", con todos esos corchetes volando en mi código. Sin embargo, no llegaría tan lejos).

¿Qué considera una buena práctica de codificación en R?

¿Cuál es su estilo de programación y cómo ve sus ventajas y desventajas?

Respuesta

20

Lo que hago dependerá de por qué estoy escribiendo el código. Si estoy escribiendo un script de análisis de datos para mi investigación (trabajo diario), quiero algo que funcione pero que sea legible y comprensible meses o incluso años después. No me preocupo demasiado por los tiempos de cálculo. Vectorizar con lapply y col. puede conducir a la ofuscación, lo que me gustaría evitar.

En tales casos, utilizaría bucles para un proceso repetitivo si lapply me hiciera saltar por los aros para construir la función anónima adecuada, por ejemplo. Usaría el ifelse() en su primera viñeta porque, en mi opinión, al menos, la intención de esa llamada es más fácil de comprender que la versión subconjunto + reemplazo. Con mi análisis de datos estoy más preocupado por hacer las cosas correctas que necesariamente con el tiempo de cálculo --- siempre hay fines de semana y noches cuando no estoy en la oficina cuando puedo hacer grandes trabajos.

Para sus otras balas; Tendría que atender no para hacer llamadas en línea/anidar a menos que fueran muy triviales. Si explico los pasos de forma explícita, el código me resulta más fácil de leer y, por lo tanto, es menos probable que contenga errores.

Escribo funciones personalizadas todo el tiempo, especialmente si voy a llamar al código equivalente de la función repetidamente en un bucle o similar. De esa manera he encapsulado el código fuera del script principal de análisis de datos en su propio archivo .R que ayuda a mantener la intención del análisis separada de cómo se realiza el análisis.Y si la función es útil, la tengo para usar en otros proyectos, etc.

Si estoy escribiendo código para un paquete, podría comenzar con la misma actitud que mi análisis de datos (familiaridad) para obtener algo que sé que funciona, y solo entonces ve por la optimización si quiero mejorar los tiempos de cálculo.

Lo único que trato de evitar es demasiado inteligente cuando codigo, sea lo que sea que estoy codificando. En última instancia, nunca soy tan inteligente como creo que soy a veces y si lo hago de forma simple, tiendo a no caerme de bruces tan a menudo como podría si estuviera tratando de ser inteligente.

+6

+1 por ser demasiado listo. Aunque, me acostumbré a usar índices que puedo leer con bastante facilidad el código y ver qué hace. Pero estoy de acuerdo en que eso no siempre es tan obvio para el que viene detrás de mí. –

+0

¿No es eso para lo que son los comentarios? – naught101

+0

Bueno, sí, pero un código R puede ser muy críptico, así que, aunque sé lo que hace algo, cómo lo hace puede ser más fácil de adivinar si el código es legible y comprensible también. –

10

Escribo funciones (en archivos independientes .R) para varios trozos de código que conceptualmente hacen una cosa. Esto mantiene las cosas cortas y dulces. Encontré la depuración algo más fácil, porque traceback() le da qué función produjo un error.

Yo también tiendo a evitar los bucles, excepto cuando es absolutamente necesario. Me siento algo sucio si uso un loop for(). :) Intento realmente hacer todo vectorizado o con la familia de aplicaciones. Esta no es siempre la mejor práctica, especialmente si necesita explicar el código a otra persona que no es tan fluida en aplicar o vectorizar.

En cuanto al uso de require frente a ::, tiendo a usar ambos. Si solo necesito una función de un determinado paquete, la utilizo a través del ::, pero si necesito varias funciones, cargo todo el paquete. Si hay un conflicto en los nombres de las funciones entre los paquetes, intento recordar y usar ::.

Intento encontrar una función para cada tarea que estoy tratando de lograr. Creo que alguien antes que yo lo ha pensado e hizo una función que funciona mejor que cualquier otra cosa que se me ocurra. Esto a veces funciona, a veces no tanto.

Intento escribir mi código para que pueda entenderlo. Esto significa que comento mucho y construyo trozos de código para que de alguna manera sigan la idea de lo que estoy tratando de lograr. A menudo sobrescribo objetos a medida que avanza la función. Creo que esto mantiene la transparencia de la tarea, especialmente si se refiere a estos objetos más adelante en la función. Pienso en la velocidad cuando el tiempo de cálculo excede mi paciencia. Si una función tarda tanto en terminar que empiezo a navegar SO, veo si puedo mejorarla.

Descubrí que un buen editor de sintaxis con doblado de código y coloreado de sintaxis (uso Eclipse + StatET) me ha ahorrado muchos dolores de cabeza.

Basado en la publicación de VitoshKa, agrego que uso palabras en mayúscula (sensu Java) para los nombres de las funciones y fullstop.delimited para las variables. Veo que podría tener otro estilo para los argumentos de función.

+1

+1 para el plegado del código en el editor. Eso es algo que debería entender en Tinn-R también. Es posible, si supiera cómo ... Pero me gustaría enfatizar que la familia de aplicaciones también es una estructura de bucle, aunque con diferentes efectos secundarios (vea la discusión en esta pregunta: http://stackoverflow.com/ preguntas/2275896/is-rs-apply-family-more-than-syntactic-sugar). –

+2

De acuerdo, aplicar es básicamente "para cada elemento en", que es exactamente como para el ciclo. La única diferencia es que cuando te acostumbras a postularte, no tienes que pensar en ciertos detalles (como seleccionar por columnas, filas, elementos de lista) y el código puede ser muy legible. Pero tal vez solo soy yo. –

+0

También me encuentro con situaciones diarias en las que reemplazar un bucle for con un buen sapply/lapply ha acelerado la ejecución por un orden de magnitud al menos, y no ha disminuido la legibilidad. Entonces yo (casi) nunca más "para". –

4

Para el malabarismo de datos trato de usar tanto SQL como sea posible, al menos para las cosas básicas como los promedios de GROUP BY. Me gusta mucho R pero a veces no solo es divertido darse cuenta de que su estrategia de investigación no fue lo suficientemente buena como para encontrar otra función escondida en otro paquete. Para mis casos, los dialectos SQL no difieren mucho y el código es realmente transparente. La mayoría de las veces, el umbral (cuándo comenzar a usar la sintaxis R) es bastante intuitivo de descubrir. p.ej.

require(RMySQL) 
# selection of variables alongside conditions in SQL is really transparent 
# even if conditional variables are not part of the selection 
statement = "SELECT id,v1,v2,v3,v4,v5 FROM mytable 
      WHERE this=5 
      AND that != 6" 
mydf <- dbGetQuery(con,statement) 
# some simple things get really tricky (at least in MySQL), but simple in R 
# standard deviation of table rows 
dframe$rowsd <- sd(t(dframe)) 

Considero que es una buena práctica y realmente recomiendo usar una base de datos SQL para sus datos en la mayoría de los casos de uso. También estoy investigando TSdbi y guardando series de tiempo en la base de datos relacional, pero realmente no puedo juzgar eso todavía.

+1

+1 para usar SQL, dado el hecho de que estamos hablando de enormes conjuntos de datos. (+100000 filas de muchas variables). En conjuntos de datos más pequeños que parece demasiado exagerado. –

+0

Véase la reciente charla de John Chambers sobre 'Multilingüismo y R'. –

+0

@Dirk: ¿quieres compartir un enlace? –

7

Las convenciones de nomenclatura son extremadamente importantes para la legibilidad del código.Inspirado en el estilo interno S4 de R aquí es lo que yo uso:

  • camelCase de funciones y objetos globales (como doSomething, getXyyy, UPPERLIMIT)
  • las funciones comienzan con un verbo
  • no exportan y funciones de ayuda siempre comienzan con "."
  • las variables y funciones locales están todas en minúsculas y en la sintaxis "_" (do_something, get_xyyy), hace que sea fácil distinguir local vs global y por lo tanto conduce a un código más limpio.
Cuestiones relacionadas