featured image

En la actualidad existen varias herramientas para hacer análisis de datos sobre distintos lenguajes de programación e incluso plataformas web y de escritorio que exploran esas posibilidades haciendo implementaciones poderosas que permiten su uso a través de una interfaz sencilla y fácil de aprender incluso para personas que no son expertas en algún lenguaje de programación. ¡Vamos a explorar con Erlang/Elixir!

Erlang aún no ha explorado esta característica, aunque con la creciente ola de desarrollos de código abierto sobre Elixir en los últimos años no me queda duda que esto está muy cerca. Herramientas como GenStage o Flow son solo los primeros pasos.

Aún así hemos decidido explorar SCI usando Python donde está probada su eficacia y eficiencia, conectando Erlang/Elixir para que sea el orquestador desde la BEAM.

¿Cómo esta construído JUN?

Nos vamos a concentrar en describir el proceso de arquitectura que ha seguido JUN, una biblioteca poderosa que ha sido ya probada en ambientes de producción y que denota la gran flexibilidad de Erlang/Elixir al momento de hacer desarrollos mucho más estructurados que solo usando Python (aunque con esto no me refiero a que Python no lo pueda ejecutar, Erlang tiene ciertas ventajas).

JUN se basa en procesos simples, que al ser iniciados son ligados a un proceso de Python en el Sistema Operativo y que pueden intercambiar información entre Erlang y Python, asi de simple es la función de un proceso trabajador, el cual permite la libre ejecución de funciones hacia Python.

Un proceso en JUN puede recibir datos de forma binaria para expresar datos que no es posible visualizarlos en Erlang directamente, como: dataframes, series, etc. Esto es posible debido al uso de ErlPort que permite el paso de datos entre ambos lenguajes a tráves de un simple puerto.

¿Cómo usar JUN?

Pero vamos a la práctica, después de clonar el repositorio desde github vamos a generar un "release" para poder jugar un poco con la biblioteca:

make rel

Una vez ejecutado lo anterior sobre la biblioteca, ésta nos va a generar el "release" para poder usar el ambiente completo. Iniciaremos la consola de Erlang:

make live

Ahora dentro de la consola, vamos a generar un trabajador, un simple proceso que nos permitirá manejar todas las funciones (al menos las soportadas por ahora) dentro de la consola de Erlang:

(jun@MacBook-Pro-de-Jorge)1> {ok, MiProceso} = jun_worker:start_link(). 
16:18:57.041 [info] initialized default modules for py pid <0.390.0>
{ok,<0.389.0>}

Como podemos ver la variable MiProceso sostiene el ambiente de Python y sobre él podemos ejecutar las funciones necesarias para realizar nuestro análisis de datos.

Vamos a cargar el siguiente CSV sencillo para hacer algunos cálculos y observar el comportamiento de la biblioteca.

nombre,tipo,raza,edad
Ziggy,Perro,Bulldog,2
Toto,Ave,Cockatiel,1
Tutti,Gato,Calico,6

Procedemos a cargar el CSV como un dataframe y almacenarlo en una variable de Erlang:

(jun@MacBook-Pro-de-Jorge)7> {ok, {_, Dataframe}} = jun_pandas:read_csv(MiProceso, '/Users/zgbjgg/mascotas.csv', []).
{ok,{'pandas.core.frame.DataFrame',{'$erlport.opaque',python,
                                                      <<128,2,99,112,97,110,100,97,115,46,99,111,114,101,46,
                                                        102,114,97,109,101,10,68,...>>}}}

Podemos observar que JUN retorna el dataframe como un binario, el cual es usado para posteriores análisis. Analicemos un poco nuestro dataframe y contestemos las siguientes preguntas:

  • ¿Cuál es la mayor edad presente en todas las mascotas?
  • ¿Cuántas mascotas tenemos en total?

Para contestar la primera pregunta simplemente ejecutaremos la función max/4 que nos determinará que edad es la mayor de todas las existentes:

(jun@MacBook-Pro-de-Jorge)8> jun_pandas:max(MiProceso, Dataframe, 'edad', []).
{ok,6}

Enviamos como primer argumento nuestro proceso, en segundo lugar nuestro dataframe y por último enviamos el nombre de la columna de la cual deseamos obtener el valor máximo.

Similar a lo anterior vamos a contestar la pregunta dos, para ello usaremos la función count/4:

(jun@MacBook-Pro-de-Jorge)9> jun_pandas:count(MiProceso, Dataframe, 'nombre', []).
{ok,3}

¡Bien!, sabemos ahora que tenemos 3 mascotas registradas en nuestro dataframe.

Usos avanzados

Los usos antes mencionados son bastante sencillos pero da ejemplo de lo poderosa que es la biblioteca incluso sin explorar funciones más complejas como agrupaciones, aplicación directa de lambdas o incluso realizar gráficos usando matplotlib. La documentación de las funciones soportadas esta disponible desde aquí.

En mi experiencia usando esta herramienta para ambientes productivos ha sido de gran utilidad ya que en conjunto con capas de caché y memorización que desde Erlang/Elixir es muy sencillo aplicarlas resulta en un producto final bastante consistente.

Actualmente procesamos dataframes de casi 15 mil registros en tiempo real usando solo JUN y con un consumo bastante bajo en CPU de apenas ~5%, usando el 95% de funciones con las que cuenta actualmente la biblioteca.

Conclusiones

Esta demostración pretende aportar un poco al ecosistema de Erlang en un campo aún desconocido pero bastante atractivo y con un muy buen desempeño.

¿Te atreverías a usar JUN en ambientes de producción?

¿Consideras que puede usarse para aumentar la productividad con la que ya cuenta Python? ¡Déjanos tu comentario!