
Deja de escribir «R a la C» y aprovecha la verdadera velocidad de R
Resumen
Para escribir código veloz en R tomamos en cuenta tres cosas:
- Pruebas lógicas
- Subconjuntos
- Ejecuciones simultaneas de elementos de un objeto
El código que las utilice en lugar de ciclos for con condiciones if será por lo general más rápido.
Esto pasa por que R, a diferencia de lenguajes como C, no compila antes de ejecutar. Durante la compilación, la computadora optimiza el uso de la memoria para los ciclos for, es por eso que corren rápido en C, pero en R, debemos hacer uso de las Pruebas lógicas, Subconjuntos y Ejecuciones simultaneas de elementos para sacar provecho de sus características y así obtener el mayor beneficio (la mayoría de las veces).
Se optimizaron las funciones del paquete R Qurra, obteniendo una mejora notoria en tiempo de ejecución, mediante el uso de estos tres principios. También se obtienen los primeros 1000 valores de la función Fi de Euler en tan solo veinte segundos (en un equipo del 2009).
Introducción
En la entrada anterior compartimos un pequeño paquete de funciones de R, resultado de un ejercicio de iniciación realizado después de repasar la sintaxis y conceptos fundamentales. Aunque las funciones cumplieron su cometido, estábamos cayendo en una mala costumbre típica de programadores que se mudan de lenguajes cómo C a R.
G. Grolemund, autor de Hands-On R Programming, libro excelente que comparto a continuación:
https://rstudio-education.github.io/hopr/
diría que estábamos «hablando R con acento de C»…
Es decir, utilizando un montón de ciclos for con condiciones if dentro (sin tomar en cuenta que R no compila) en lugar de aprovechar las características que hacen a R tan poderoso, en una palabra, la vectorización. Se re-escribió el código del paquetito de funciones pero ahora sí, «a la R», e hicimos luchar las distintas versiones de funciones chkprime para verificar que se haya conseguido una mejora en tiempo de ejecución.
Procedimiento
Se escribió una segunda versión del paquete de funciones R Qurra, la cual se comparte a continuación:
intentando evitar ciclos for con condiciones if dentro y se trató de implementar todo mediante pruebas lógicas, subconjuntos y ejecución simultánea de elementos.
Se escribió la función fight para hacer competir a la familia de funciones chkprime, tomando el tiempo que demoran en hallar los primos en los intervalos:
[1,1000] , [1,10000] y [1,100000]
y observar las mejoras en tiempo de ejecución.
La función regresa un data frame listo para utilizarse para graficar en ggplot (paquete de R).
se escribió la función phi que recibe n natural como argumento y regresa phi(n) donde phi es la función Fi de Euler, función de suma importancia en teoría de números y el sistema de cifrado RSA.
Resultados
En este video se muestra como correr el script y utilizar las funciones, así como resultados y conclusiones:
Gráfica de comparación de velocidad de familia de funciones chkprime* (realizada con ggplot2 y data frame que regresa la función fight), observe que a pesar de que el tiempo de ejecución de la familia es muy similar para una longitud de 1000 (punto de partida), a medida que seguimos avanzando observamos que la función chkprime «a la C» (en rojo) se empieza a quedar atrás y es rebasada incluso por la chkprime.r (en verde) que está muy poco optimizada (por no decir otra cosa).

Con ayuda de la función testrlpr reloaded, la cual revisa si un par de números son primos relativos entre si, se diseñó la función phi, que regresa el valor de la función Fi de Euler, es decir, le pasas un natural como argumento y te regresa la cantidad o cardinalidad del conjunto de primos relativos de ese número que sean menores que el mismo. Se obtuvieron los primeros diez mil valores de la función para realizar la gráfica siguiente.
Gráfica de primeros 10000 valores de función Fi de Euler, creada con ggplot.
(obtenidos en aprox. 110 minutos con la función phi en cpu Intel i5 2.4Ghz con 4 GB de RAM)

Conclusiones
A pesar de todo, no hay que pensar que es malo usar ciclos for en R, pero recordar este par de consejos podría ser útil:
- Si observas un ciclo for con un if dentro, lo mas seguro es que puedas remplazarlo con operaciones de subconjuntos y condiciones lógicas y este será mas veloz.
- Si es necesario utilizar un ciclo for, trata de minimizar su bloque de código para que se realice lo menos posible dentro del mismo.
En R la mayoría de las funciones están vectorizadas, por lo que al aplicarlas a un vector se hace a todos los elementos del vector al mismo tiempo, evitando demoras, mientras un loop for lo haría uno por uno (y sin el boost que le daría la compilación), esta es la causa por la cual es preferible evitarlos de ser posible, en la mayoría de los casos.
Una parte muy importante de la programación en cualquier lenguaje es la reutilización de código. En la ciencia de datos, es prudente aprovechar también cualquier información ya existente antes de generarla uno mismo, así se logra completar proyectos en menor tiempo. Por ejemplo, seguro en algún lugar en la red podemos encontrar los primeros diez mil (o más) valores de la función Fi de Euler. Nosotros hubieramos podido ahorrar los 110 minutos que se tardó la función phi en hallar los primeros diez mil valores que utilizamos para la gráfica. También pudimos haber utilizado alguna función para checar primos disponible en la red en lugar de perder tiempo escribiendo las nuestras… Pero la idea de escribir estos pequeños paquetes de funciones, es que nos sirva de ejercicio para: acostumbrarnos a la sintaxis de R, entender el manejo de las estructuras de control que ofrece R para después poder comprender, modificar y unir cualquier código que hallemos y que el tiempo que perdimos en escribir estos «paquetitos de funciones» se recupere a la hora de solucionar las tareas de código del futuro. Lo que queremos decir es; si todavía no te sientes como un experto de R, es mejor que escribas unas cuantas funciones por ti mismo antes de querer reutilizar código ya existente. Así cómo crear algunos vectores, matrices, listas y marcos de datos para acostumbrarse a su manejo y filtrado.
¡Los mejores deseos para todos ustedes!
– Atl Tlachinolli
