¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. Data Scientist y lenguaje R
  3. Full Stack R
Extrait - Data Scientist y lenguaje R Autoformación en los aspectos básicos de la inteligencia artificial en el universo... (2a edición)
Extractos del libro
Data Scientist y lenguaje R Autoformación en los aspectos básicos de la inteligencia artificial en el universo... (2a edición) Volver a la página de compra del libro

Full Stack R

¿Por qué este capítulo?

Después de la primera edición de este libro, recibimos muchas preguntas a las que respondimos con entusiasmo. Mirando hacia atrás, estas preguntas a menudo giraban en torno a problemas de implementación, algunas veces un poco técnicos. Esperamos que este capítulo satisfaga estas expectativas tan «pragmáticas» de nuestros lectores.

El título de este capítulo, Full Stack R, evoca la idea de repasar los aspectos del ecosistema R que le permitirán hacer un código sólido y anidado en su entorno en términos de resiliencia, datos, espacio de memoria, procesadores, interfaz con otras aplicaciones en un contexto web. También es un guiño al hecho de que algunos desarrolladores se presentan como «full stack developer».

Programación funcional y/o defensiva

R no es un lenguaje diseñado para los aficionados a la programación funcional, aunque permite limitar ciertos efectos secundarios generados al implementar conceptos propios de este tipo de programación, con un estilo parecido a esta (pero menos compacto que los lenguajes que se especializan en este enfoque). En cierto sentido, esto se caracteriza por codificar usando funciones que evalúan de la misma manera que las funciones en el ámbito de las matemáticas clásicas. Por lo tanto, encapsulamos las estructuras de control en estas funciones.

Este estilo de escritura es muy útil en la parte relacionada con la transformación de datos (que es reproducible sobre otros agregados de datos diferentes a aquel sobre el que se desarrolló el programa) y en la implementación de algoritmos descritos con una formalización matemática clásica (es decir, sin asignación).

Por ejemplo, hemos visto con anterioridad en el libro que en R es posible crear fácilmente funciones que reciben otras funciones como argumentos y generan nuevas funciones.

Las diferentes posibilidades que ofrece R se listan a continuación y son típicas de este estilo de codificación que facilita la implementación de programas que se adecúan a sus requisitos (es decir, se puede verificar la conformidad algorítmica, limitando los efectos secundarios si se compara con las expresiones matemáticas):

  • El lenguaje está vectorizado, lo que permite manipular entidades como vector, matrix y array con una formulación parecida a cómo se manipulan vectores, matrices y tensores en matemáticas.

  • Podemos limitar las operaciones de asignación (a <- b) y, de esta manera, reducir los efectos secundarios (en la programación funcional real, no se permite ninguna operación de asignación).

  • Es posible utilizar funciones anónimas, que se aplican de manera local sin riesgo de salirse del contexto. En algunos lenguajes, las funciones anónimas se llaman funciones λ (lambda).

  • Es fácil crear cierres, más conocidas como closures en inglés, funciones generadas por otras funciones que son fácilmente manipulables (parecido a las variables).

  • Además, las funciones pueden ser polimórficas, podemos gestionarlas en listas...

Persistencia, bases de datos y R

El objetivo de esta sección no es proporcionar una descripción general completa de este tema, sino ayudarle a empezar presentando algunas habilidades que se pueden reproducir en varios entornos.

R permite guardar y volver a leer objetos sin ninguna transformación, es decir, en el estado correspondiente a su representación de memoria. Puede ser una variable, un objeto RC, un modelo, un dataframe, una función, etc. Este modo de registro es muy útil para preservar la memoria de su programa o para gestionar un proceso de construcción de modelos: estas funciones son muy rápidas de codificar y leer, lo que permite que pueda eliminar de su entorno los objetos que consumen más memoria después de haberlos guardo, y restaurarlos en el futuro cuando sea necesario.

En el siguiente ejemplo, haremos una copia de seguridad y restauraremos de dos maneras diferentes un dataframe que contiene un calendario. La función que crea este calendario no tiene nada que ver con el objetivo de este capítulo, pero le sugerimos que la conserve porque un calendario de este tipo es muy útil para asociar eventos o contextos a diferentes fechas. En cierta manera, gracias al uso de esta técnica de agregación, los autores de este libro fueron premiados durante un hackaton en el que era necesario crear modelos mixtos que incluyeran series temporales y varias features para transformar y analizar, en función del tipo de día (por ejemplo, el tipo de actividad de gran parte de la población no es el mismo los domingos y los lunes).

## creación de un calendario fácil de manejar                 ## 
fabrica_secuencia_dias <- function(inicio = "2000-01-01", 
                                    fin   = "2020-12-31" ){ 
  require(dplyr) 
  num_dias  <- as.Date(fin) - as.Date(inicio) 
  sec_dias <- seq.Date(as.Date(inicio), 
                        by="1...

Paralelización

Existen varias soluciones para distribuir el procesamiento, sin mencionar las características de Hadoop (o Spark), que permiten encapsular los mecanismos de paralelización destinados a los datos masivos. Para distribuir la carga de cálculo, podemos imaginar que es posible lanzar varios procesos R o trabajar con varios procesadores en una sola máquina y, que en un conjunto de máquinas es posible trabajar con clústeres vinculados por mecanismos de bajo nivel o por una distribución trivial de procesamientos (invocando máquinas remotas en modo remote).

Hay varios frameworks para administrar la distribución en varias máquinas: en el entorno R, a menudo usamos snow o nws. Un paquete de más alto nivel encapsula varios mecanismos y no requiere habilidades específicas (si los clústeres están instalados). El siguiente ejemplo presenta el paquete llamado future. El término future se refiere a que la programación paralela consiste en instanciar operaciones de manera paralela, cuyos resultados esperamos más adelante, cuando estén disponibles. Tenga en cuenta que el uso de bucles foreach del paquete homónimo también permite administrar correctamente la división de los futures, además de varios paquetes relacionados con la paralelización.

library(future)                        #...

Recolectar datos externos

Cuando tiene acceso a un sitio web que alberga un archivo que le interesa, la recopilación de datos es trivial porque, para acceder, solo necesita usar la instrucción download.file(url,«mi_archivo»). Además, los diversos proveedores de datos, en particular de series temporales económicas, le informan casi sistemáticamente sobre las sintaxis que se debe utilizar en R. Por ejemplo, los World Bank Data tienen un acceso particularmente fácil y son protagonistas de un paquete R dedicado. (WDI).

El sitio web de Open Data (https://www.quandl.com) ofrece varios modos de acceso. Para mostrarle cómo acceder a la información proporcionada por un API JSON (que es un caso muy común), vamos a utilizar un paquete muy práctico a través de un sencillo ejemplo, que apunta a una de las muchas series temporales del sitio (la cotización del crudo).

require(jsonlite) 
url_api <- "https://www.quandl.com/api/v3/datasets/OPEC/ORB.json" 
retorno_api <- fromJSON(url_api) 
 
s_OPEC <- data.frame(retorno_api$data$data) 
names(s_OPEC) <- c("sec_dias","cotizacion") 
s_OPEC <- s_OPEC[order(s_OPEC$sec_dias),]   # orden creciente 
head(s_OPEC) 

     sec_dias  cotizacion 
3907 2003-01-02 30.05 
3906 2003-01-03 30.83 
3905 2003-01-06...

Crear una API con R

Hasta entonces hemos utilizado las API (funciones y/o datos) de otras personas. Por el contrario, ahora vamos a exponer una API con una función simple (aquí, multiplicar un número entero por 2).

Para hacer esto, primero debe crear la función para exponer. Para que esto se entienda como un API, deberá agregar un decorador en el código. Por lo general, un decorador adopta la forma de una etiqueta integrada en un comentario, que aporta información del entorno a un programa que puede usarla.

# mul2.R 
 
# el siguiente comentario decora la función 
# con el tipo de acción y el nombre de la API 
#* @get /mul2 
mul2 <-   function(x=1){as.integer(x)*2} 

Este código se llamará por la función que lo interpretará en el paquete plumber y después lo expondrá a la API en un puerto determinado.

library(plumber) 
r <- plumb("mul2.R") 
r$run(port=8000) 

En este caso, la API se expone en su servidor local localhost. A continuación, puede acceder a la API, por ejemplo, utilizando la instrucción curl en la línea de comandos de su sistema operativo.

curl "http://localhost:8000/mul2?x=100" 

[200] 

Se trata de un resultado en un formato R. Para serializarlo de otra manera, tenemos más decoradores.

# mul2.R 
 
# el comentario siguiente decora...