Data Analytics en aplicaciones Rails con Jupyter Notebook y IRuby

ruby abr. 23, 2021

A diferencia de en su par interpretado Python, las herramientas para análisis de datos en Ruby son prácticamente inexistentes. En Platanus hacemos todos nuestros proyectos en Rails, por lo que realizar análisis de los datos sacados directamente de  nuestras apps en producción es una tarea poco amigable. Hemos tenido que usar algunos workarounds, como conectar nuestras bases de datos a google data studio o generar .csvs para abrirlos con Python.

Recientemente empezamos a explorar nuevas formas de poder hacer estos análisis sin tener que salirnos de nuestro confortable y poderoso ambiente Rails. La solución a la que llegamos es montar Jupyter notebooks con kernels de Ruby con acceso ambiente Rails... what? significa que tenemos un notebook con acceso a todos los modelos, helpers, servicios, de una app Rails. Podemos hacer cosas así:

🤯

En esta guía te voy a enseñar cómo lograr esto en tu aplicación Rails, junto con algunos recursos y tips para sacarle el mayor provecho posible, incluyendo cómo graficar los resultados de nuestras consultas. Los ejemplos que mostraré son de un notebook de prueba que hicimos en nuestro producto Mute, una aplicación web para hacer reuniones más eficientes, e incluso silenciosas 🤫.

Set up

El setup requiere los siguientes pasos, los cuales iremos viendo en detalle:

  1. Instalación Jupyter Notebook.
  2. Instalación de los requisitos de IRuby.
  3. Agregar las gemas necesarias a tu aplicación Rails.

1. Instalar Jupyter

El primer paso es tener Jupyter instalado. Si ya tienes python (si no entonces debes instalarlo primero!) en general basta con correr pip install jupyter. Una vez instalado asegúrate de que puedes levantar correctamente un notebook ejecutando jupyter notebook.

2. Requisitos IRuby

Ahora debemos seguir las instrucciones instalar las dependencias IRuby. Principalmente necesitamos instalar ZeroMQ:

#ubuntu
sudo apt install libtool libffi-dev ruby ruby-dev make
sudo apt install libzmq3-dev libczmq-dev

#mac
brew install automake gmp libtool wget
brew install zeromq --HEAD
brew install czmq --HEAD

Si se te cae alguno de los comandos en Mac puede ser que tengas que updatear las command line tools.

Si es que tienes algún otro problema, en el repositorio de IRuby hay instrucciones más detalladas. Ojo que no es necesario instalar IRuby propiamente tal, con instalar ZeroMQ debería bastar, ya que IRuby se instalará automáticamente como dependencia de una de las gemas que agregaremos a la app rails en el próximo paso.

3. Agregar las gemas a tu aplicación

Para poder correr jupyter dentro de rails usaremos la gema jupyter_on_rails, para eso agregamos al Gemfile de la app, en el ambiente de development:

group :development do 
  gem 'jupyter_on_rails'
  gem 'ffi-rzmq'
  # ...tus_otras_gemas
end

E instalamos:

bundle install

Ahora deberías poder hacer rails jupyter:notebook, para levantar un notebook con kernels ruby y ambiente rails. Si al clickear en “New” te aparece el kernel de la aplicación y el kernel sandbox estás listo. Deberías ver algo así pero con el nombre de tu app:

Ahora le das click a cualquiera de los dos kernels (la diferencia es que en el sandbox todas las consultas se juntan en una transaction a la cual se le hace rollback cuando apagas el kernel) y se creará un notebook en Ruby con todas las magias de Rails y con acceso a los modelos y datos de tu aplicación.

Ojo: Si piensas subir estos cambios a tu repositorio es importante que agregues al .gitignore todos los archivos auxiliares que genera jupyter y que no queremos en el repo:

#.gitignore
.ipynb_checkpoints
.ipython

DataFrames con el poder de ActiveRecord

IRuby al igual que IPython, permite mostrar sets de datos como tablas dentro del notebook, la mejor forma de hacer esto es mediante la gema de dataframes en Ruby Daru. La gema Jupyter_on_rails ya la incluye en sus dependencias y además nos da un método to_df para pasar objetos de ActiveRecord directamente a DataFrames, por ejemplo podemos ver los primeros 5 usuarios con id y username fácilmente ejecutando algo así:

User.select(:id,:username).limit(5).to_df

Lo que nos da una tabla que podemos visualizar de manera super cómoda en el notebook.

Consultas más complejas, como joins o agregaciones (que no retornan objetos de ActiveRecord) no tienen el metodo .to_df, pero se pueden transformar en Dataframes o en Vectors usando Daru::Vector.new. Por ejemplo, en Mute tenemos los modelos Announcements y Documents en que un anuncio pertenece a un documento. Podemos hacer fácilmente una serie de la cantidad de anuncios hechos por documento y mostrarlos según las fechas de cada documento. Algo así:

Daru::Vector.new(
  Announcement
  .joins(section: :document)
  .group('documents.created_at')
  .order('documents.created_at').count
)

Las distintas formas que da Daru para construir un Dataframe nos dan mucha libertad para formatear los datos, por ejemplo podemos hacer una consulta para una columna y otra para el índice de la tabla (el índice no puede tener duplicados).

counts = Document.last.sections.map { |x| x.announcements.count }
sections = Document.last.sections.map(&:title)
announcements_per_section = Daru::DataFrame.new({ 'announcements'=>counts }, index: sections)

Daru tiene varias funcionalidades para hacer análisis en un data frame por ejemplo .summary(), similar al describe() de pandas:

announcements_per_section.summary

En el repositorio de la gema puedes encontrar más funcionalidades  y algunos notebooks de ejemplo.

Graficando con Ruby

Existen varias gemas para hacer gráficos en Ruby pero ninguna tiene mucho soporte de la comunidad ni muchas funcionalidades. Lamentablemente al trabajar con ruby es difícil acercarse a lo que se puede lograr con matplotlib o seaborn, pero de todas formas algo podemos hacer. Para ello usaremos la gema Gruff. Gruff permite graficar con muy poco código y los resultados se pueden ver directamente en el notebook. Gruff utiliza ImageMagick para generar los gráficos, varias gemas usadas en aplicaciones rails para procesar imágenes utilizan ImageMagick así que es probable que ya lo tengas instalado en tu máquina. Si no, puedes seguir las instrucciones para instalar la gema rmagick, que es el wrapper en ruby de ImageMagick. Teniendo eso resuelto basta que agreguemos al Gemfile:

group :development do 
  gem 'gruff'
  ...tus_otras_gemas
end

E instalamos:

bundle install

Y con eso ya estamos listos para jugar. La forma más simple de graficar es usando la interfaz que provee Daru. Para eso primero le decimos a Daru que queremos usar gruff para graficar y luego usamos el método .plot para graficar:

Daru.plotting_library = :gruff
announcements_per_section['announcements'].plot type: :pie

Nos queda algo así:

Con este método podemos configurar el gráfico pasándole un bloque, pero en general no es ni tan amigable y ni tan flexible. Si queremos modificar mucho el gráfico default es mejor graficar usando directamente Gruff. Por ejemplo podemos hacer un line chart a partir de la serie de tiempo de anuncios que construimos más arriba, le ponemos título al gráfico, cambiamos el tamaño de la fuente de los ejes y formateamos los labels para que se vean bien las fechas, todo usando código ruby común y corriente.

g = Gruff::Line.new(500)
g.title = 'Stats por documento'
g.labels = data.index.each_with_index.map{|x, index| [index, x.strftime('%d-%m')]}.to_h
g.data('Anuncios', announcement_vector)
g.marker_font_size = 12
g

Nos queda así:

Gruff permite hacer varios tipos de gráficos distintos y cada uno con varios parámetros configurables. La documentación no es tan buena (link acá) pero leyéndola con cuidado y también viendo los ejemplos en los tests unitarios se pueden lograr hartas cosas.

Lo que hemos visto hasta ahora es suficientemente poderoso para hacer análisis rápidos de la base de datos y para construir pseudo dashboards/reportes en el mismo notebook.

Con eso termina este tutorial, si te sirvió compártelo y asegúrate de suscribirte a nuestro newsletter: Plata.news

¡Genial! Te has suscrito con éxito.
¡Genial! Ahora, completa el checkout para tener acceso completo.
¡Bienvenido de nuevo! Has iniciado sesión con éxito.
Éxito! Su cuenta está totalmente activada, ahora tienes acceso a todo el contenido.