Gestión de paquetes en R-Project

Logotipo de R-project distribuido bajo la licencia CC-BY-SA 4.0

Con el paso del tiempo he ido alejándome de algunas cosas que en su momento eran parte integral de mi día a día. Una de ellas es R project. Cuando estaba trabajando en mi tesis lo utilizaba constantemente para hacer análisis o programar algoritmos. Gran parte de los resultados que obtuve fueron gracias a código desarrollado y ejecutado en la plataforma.

Aunque ya no lo utilizo con asiduidad, me siguen consultando cada vez que hay una solicitud de soporte de algún usuario relacionada con R. Soy el (in)experto de R. Esto obliga a estar un poco al tanto del desarrollo y evolución.

Una de las cosas interesantes es la gran cantidad de paquetes disponibles que implementan algoritmos o cálculos de todo tipo. Aunque su instalación no es complicada, existe algunos trucos que pueden ser de interés.

Un poco de historia

Cuando se empezaron a utilizar los ordenadores para realizar cálculos uno de los primeros lenguajes empleados fue Fortran, un lenguaje de programación diseñado por IBM en para el IBM 704, el primer equipo que incorporaba aritmética en coma flotante y que se fabricó masivamente. A pesar de la reticencia inicial a usar un lenguaje de alto nivel en lugar de ensamblador no tardó demasiado en convertirse en el lenguaje de referencia durante muchos años. Tanto que aún hoy en día se sigue empleando con ese propósito.

El principal rival de Fortran surge a en el seno de los laboratorios Bell. John Chambers, Rick Becker y Allan Wilks desarrollan un conjunto de macros Fortran para simplificar el uso que denominaron S de Statistical, siguiendo la moda de nombrar a los lenguajes con una única letra. Una década después se reescribió en C y empezó a evolucionar como un lenguaje independiente llegando a aparecer su primera versión comercial (S-PLUS) .

Robert Gentleman
Robert Gentleman
Ross Ihaka
Ross Ihaka
Los creadores de R.

En , Robert Gentleman (Universidad de Waterloo, Canadá) está en la Universidad de Auckland (Nueva Zelanda) donde coincide con Ross Ihaka (Departamento de Estadística de la Universidad de Auckland). Juntos crean un subdialecto de S con influencias de Scheme, que denominan R (en parte por ser las iniciales de sus nombres y en parte jugando con el lenguaje S). Aunque la Universidad de Auckland eligió este nuevo lenguaje para su docencia, su desarrollo no se hizo público hasta , mediante un mensaje a la lista S-news.

En estos primeros años, los propios usuarios eran los que contribuyen al desarrollo. Estos usuarios se unen a los creadores conformando lo que sería el R Core Team. La lista de miembros originales del grupo aparece en el README de R 0.60, la primera versión bajo el auspicio del proyecto GNU.

Since mid-1997 there has been a core group who can modify the R source code CVS archive. The group currently consists of Peter Dalgaard, Robert Gentleman, Kurt Hornik, Ross Ihaka, Thomas Lumley, Martin Maechler, Paul Murrell, Heiner Schwarte and Luke Tierney.

Kurt Hornik and Friedrich Leisch (Universidad Técnica de Viena), dos de los componentes del recién formado R Core Team, anunciaron la creación de CRAN. Comprehensive R Archive Network (CRAN) se definió un conjunto de sitios que aglutinan todo el material relacionado con R. Se convertiría en repositorio central de información con copias distribuidas por todo el mundo.

La publicación de la primera versión estable (R-1.0.0) cierra la fase de desarrollo inicial de R considerándolo apto para producción.

Su licencia libre1 bajo el auspicio del proyecto GNU y la disponibilidad han hecho que se convierta en uno de los lenguajes más utilizados en la comunidad científica.

Paquetes

El núcleo de R sólo implementa el intérprete del lenguaje y las funcionalidades básicas. Una de las características implementadas es la posibilidad de extender la funcionalidad mediante paquetes que pueden contener funciones reutilizables (en R o en otros lenguajes compilados) junto con su documentación, datos, demostraciones… Esta capacidad de especialización mediante reutilización es clave en el desarrollo y uso de R.

Los paquetes se distribuyen en CRAN desde prácticamente los inicios de R. CRAN está abierto a incorporar nuevos paquetes2 distribuyéndolos a toda la comunidad. Esto ha permitido pasar de los 12 paquetes inicialmente publicados en su primera versión () a los más de 18 000 paquetes disponibles 24 años después ().

Aunque CRAN es la referencia oficial, existen otros repositorios de paquetes como Bioconductor o R-forge.

Instalación

La instalación de paquetes en R es muy sencilla. Basta con ejecutar en una sesión de R:

install.packages("<nombre>")

También es posible instalar los paquetes desde cualquier consola:

R CMD INSTALL "<nombre>"

Una vez instalado, el paquete puede utilizarse en cualquier sesión o script simplemente cargándolo:

library("<nombre>")

A partir de ese momento se puede utilizar su contenido como si fuera nativo.

Rutas de instalación

Por omisión, la instalación se intenta de forma global en el sistema en el repositorio de librerías que está en la ruta de instalación de R. Al ser el comportamiento por omisión es muy habitual empezar instalando todos los paquetes bajo la instalación de R utilizada.

Para poder instalar en la ubicación global, se necesitan permisos de administración. En equipos personales no suele ser ningún problema pero cuando se trabaja en sistemas de cómputo compartidos no es habitual disponer de estos privilegios. Si no se dispone, el sistema automáticamente pregunta si se desea crear un repositorio propio (en ~/R/) donde instalarlos.

El comportamiento de estas rutas se puede controlar con la variable de entorno R_LIBS. Definiendo esta variable se establece las rutas en las que se buscan los paquetes. La primera ruta que aparezca será la que se utiliza para instalar los paquetes lo que permite definir rutas con conjuntos de paquetes personalizados para cada tarea. Sería el equivalente a un entorno virtual en otros lenguajes o al uso de renv.

Gestionando paquetes

La instalación básica manual es lo más habitual cuando se empieza con R. Sin embargo, en cuanto se trabaja en equipo o se quiere recuperar un análisis que uno mismo hizo hace años en otro equipo empiezan a surgir problemas ¿Qué paquetes utilizaba para esto? ¿Qué versiones? ¿De dónde lo saqué?

Es necesario gestionar de alguna forma los paquetes utilizados. Y R no proporciona ningún mecanismo nativo de gestión de estos paquetes.

README

El primer sistema de gestión que se suele utilizar es crear un fichero de texto junto al código en el que se describe lo que hace falta para utilizar el código.

Aunque tener el fichero es una buena práctica y tiene muchísima utilidad, se tienen los paquetes en la documentación y es necesario un proceso de instalación manual aparte que puede llegar a errores.

El método naïve

Una de las primeras ideas que se ocurren es instalar los paquetes necesarios antes de su carga.

install.packages("<pkg1>"); library("<pkg1>")
install.packages("<pkg2>"); library("<pkg2>")
install.packages("<pkg3>"); library("<pkg3>")
install.packages("<...>");  library("<...>")
install.packages("<pkgN>"); library("<pkgN>")

Este mecanismo hace que se instalen todos los paquetes en cada ejecución. Es posible mejorarlo ligeramente:

## needed packages
packages <- c(
    "<pkg1>",
    "<pkg2>",
    "<pkg3>",
    "<...>",
    "<pkgN>",
)

## check which packages are not installed
missing.packages = packages[!(packages %in% rownames(installed.packages()))]

## install missing packages
if (length(missing.packages) > 0) {
    install.packages(missing.packages)
}

## load packages
lapply(packages, library, character.only = TRUE)

Usando este fragmento de código se asegura que todos los paquetes necesarios están instalados y cargados al utilizar.

Este método tiene un par de inconvenientes:

  • El método installed.packages puede ser muy pesado dependiendo del número de paquetes instalados en la distribución. Cuando se tienen cientos de paquetes puede suponer un retardo importante simplemente para comprobar si están o no instalados.

  • Únicamente permite instalar paquetes distribuidos mediante CRAN.

    Para utilizar paquetes de otras fuentes habría que invocar a sus correspondientes instaladores (BiocManager::install en el caso de Bioconductor o remotes::install_* en el caso de repositorios) que deberian estar instalados previamente. Esto obligaría a triplicar el código para saber qué paquetes proceden de qué fuente y usar las llamadas adecuadas para cada uno o crear una función que lo hiciera.

Evidentemente, alguien ya se ha enfrentado a estos problemas antes…

Librarian

Librarian implementa la instalación, actualización y carga automática de paquetes con una interfaz única para paquetes de CRAN, Bioconductor o Github que se pueden mezclar en la misma llamada. Además, proporciona algunas funcionalidades extra como la posibilidad de descargar paquetes o que se realicen cargas automáticas en el arranque.

Utilizar librarian es muy simple:

199
200
201
202
203
204
205
206
207
208
209
if (!require("librarian")) install.packages("librarian") ## install librarian with the "naïve" approach
library(librarian)

## load packages with automatic install/updates as needed
shelf(
    <pkg1>,
    <pkg2>,
    <pkg3>,
    <...>,
    <pkgN>,
)

Básicamente, tras instalar librarian (que podría hacerse de forma global eliminando la primera línea del fragmento previo), se utiliza la función self como si se tratara de library y se encargará automáticamente de instalar o actualizar los paquetes según sea necesario. El código se puede ejecutar en cualquier otro equipo de forma trivial, simplemente en la primera ejecución se instalarán los paquetes necesarios automáticamente.

Una de las funcionalidades útiles que proporciona librarian es la posibilidad de cargar en el inicio del entorno paquetes. Esta funcionalidad se puede utilizar para no precisar ni siquiera la carga del propio librarian antes de utilizar la carga de las librerías utilizadas. Simplemente se ejecuta:

199
200
install.packages("librarian") ## install librarian
librarian::lib_startup(librarian, global = TRUE) ## Register to load librarian at startup

A partir de este momento se puede usar self directamente en cualquier script3. El código sería el mismo que con un R vanilla reemplazando library por self y tendría la misma legibilidad que cualquier código R.

Conclusiones

La flexibilidad que proporcionan los paquetes de R es a la vez su punto más fuerte y su mayor debilidad. Es posible instalar conjuntos de paquetes para adaptarlo a las necesidades de un estudio. Pero migrar a otro equipo supone controlar y reinstalar todos los paquetes necesarios en cada momento.

El uso de Librarian simplifica enormemente esta tarea haciendo que el código sea autocontenido. Si se usa adecuadamente junto a la redefinición de R_LIBS se pueden crear entornos personalizados para cada análisis. Además, permite migrar a un entorno de cómputo compartido (como pueda ser un supercomputador) de forma sencilla.

Aparte simplifica la carga de múltiples repositorios pasando de4:

install.packages("dplyr")
remotes::install_github("DesiQuintans/desiderata")
BiocManager::install("phyloseq")
library(dplyr)
library(desiderata)
library(phyloseq)

A simplemente:

shelf(
    dplyr,
    DesiQuintans/desiderata,
    phyloseq,
)

Librarian no es el único paquete que aborda esta problemática, aunque puede que sea uno de los más completos y con algunas funcionalidades (como la autocarga en el inicio de R) diferenciadoras. Existen algunas alternativas como Pacman con soluciones para el problema. La elección es cuestión de gustos.

Referencias


  1. Aunque inicialmente sus creadores pensaron en convertirlo en un producto comercial, Martin Mächler (ETH Zurich) les convenció de utilizar una licencia Open Source. Mächler se unió al equipo inicial de desarrolladores y al primigenio R Core Team↩︎

  2. Para desarrollar un paquete propio es muy adecuado leer R Packages de Hadley Wickham↩︎

  3. No es extrictamente necesario cargar el paquete para usar su contenido. Se puede utilizar la sintaxis librarian::self para usar la función directamente sin necesitar el library(librarian) ↩︎

  4. El ejemplo se ha extraído de Introduction to ‘Librarian’↩︎

Oscar Cubo Medina
Oscar Cubo Medina
Ingeniero en informática

Responsable técnico @ DC Rectorado—UPM. Ciencia, informática, música, libros y nuevas tecnologías.

Relacionado