Haciendo un Dashboard con Streamlit en Python

Muchas veces, después de hacer un cálculo, queremos mostrar de forma sencilla algún resultado de tal forma que cualquiera lo pueda visualizar. A veces, el resultado no es algo sencillo y queremos proporcionar herramientas para que otros puedan toquetear los datos y que vean actualizaciones de forma interactiva.

Opciones

Para hacer lo anterior tenemos varias opciones dentro del mundo Python:

La que te voy a enseñar hoy se llama streamlit y consiste en una forma muy sencilla de crear un prototipo de forma rápida y con fácil integración de muchos componentes del stack científico de Python.

Primeros pasos

Considero que tienes conda instalado por lo que abrimos un terminal (bash, cmd,…, dependiendo del sistema operativo en el que te encuentres) y escribimos lo siguiente para crear un entorno virtual conda con nombre streamlit:

conda create -n streamlit -y

Una vez creado el entorno virtual lo puedes inicializar usando el siguiente comando:

conda activate streamlit

Con el entorno activo puedes instalar streamlit usando pip (en este caso, streamlit no está en ningún canal de conda):

pip install streamlit

Y deberías estar lista para empezar a trabajar…

¿Qué es streamlit?

Es una biblioteca que hace fácil crear aplicaciones web para mostrar resultados de tus análisis de datos.

Esto está muy bien pero con este tipo de herramientas pasa que nos tenemos que adaptar nosotros a su forma de hacer las cosas. Si su forma de hacer las cosas se ajusta a nuestras necesidades será perfecto. Si no es así hará que salirse de la norma nos ponga las cosas muy complicadas o las haga imposible. Sabiendo esto de partida veamos las posibilidades.

¿Cómo lo hacemos?

La idea es escribir un script con código Python y ejecutarlo desde la línea de comandos (con el entorno streamlit acttivado) usando:

streamlit run mi_programa.py [-- otros posibles argumentos]

En cuanto se ejecute lo anterior se creará un servidor local que mostrará lo que haya decidido mostrar mediante el código incluido en el script mi_programa.py.

Para mostrar cosas en pantalla en la web, streamlit me proporciona una interfaz amplia (y que se sigue ampliando rápidamente). Algunas de las cosas que podemos usar en nuestro script Python (en los ejemplos siguientes supón que he importado streamlit de la siguiente forma import streamlit as st:

Texto:

  • st.title("Esto sería un título"): Principalmente usado para el título de la webapp. En realidad inserta una etiqueta H1 en HTML.
  • st.header("Esto sería un cabecero"): Esto es un header como una etiqueta H2 en HTML.
  • st.subheader("Esto sería un cabecero menor"): Esto es un header como una etiqueta H3 en HTML.
  • st.text("Esto sería texto"): Inserta texto con letras de ancho constante (como las que se ven en un IDE o en una consola).
  • st.latex("y = x^2"): Permite mostrar latex en pantalla.
  • st.code("a = 1", language="python"): Este es una forma especial del siguiente comando que se ha creado por conveniencia y que permite mostrar código en pantalla con su resaltado gracias a la keyword language que nos permite indicar como queremos resaltar el código.
  • st.markdown("Esto es **texto** usando *Markdown*"): Inserta texto que permite formatearlo como si fuera Markdown.
  • st.write(...): Este comando funciona de comodín y permite escribir muchas cosas y dependiendo de lo que usamos funcionará de una forma u otra. Por ejemplo, podemos usar texto y funcionará mostrando texto. Pero podemos usar un DataFrame de pandas y lo mostrará usando el método especial del objeto _repr_html_. Podemos usar una figura de matplotlib, de altair, de plotly, de bokeh,…

Las dos últimas son un poco las opciones estrella puesto que disponen de una opción, unsafe_allow_html, que permite pasar código HTML (y CSS y JS) y proporciona mucha más libertad. Pero esto lo quieren eliminar por temas de seguiridad ya que inyectar según qué código puede ser peligroso. De la documentación:

That said, we strongly advise against it. It is hard to write secure HTML, so by using this argument you may be compromising your users’ security.

Puedes ver cómo lo está usando/abusando la gente visitando este hilo en su foro de discusión: https://discuss.streamlit.io/t/are-you-using-html-in-markdown-tell-us-why/96

Con lo que ya he mostrado vamos a hacer nuestro Hola Mundo inicial. Guardamos el siguiente código en un fichero que se llame app01.py en la carpeta que quieras:

Desde la carpeta donde he guardado el fichero app01.py y con el entorno streamlit activado escribo en la línea de comandos:

streamlit run app01.py

Y, si no hay errores, mostrará en pantalla algo como:

Y se abrirá una pestaña en el navegador (si no se abre copia http://localhost:8501 y pégalo en la barra de direcciones en una nueva pestaña de tu navegador). Si todo funciona bien veré algo como lo siguiente:

En la parte principal se ve todo lo que hemos escrito en el programa Python. Abajo nos inyecta un pie de página con ‘Made with Streamlit’ y, además, añade un menú que muestro desplegado en la figura anterior.

Entrando en la parte interesante

Lo anterior está muy bien y muestra un poco la mecánica de cómo mostrar cosas en pantalla en tu aplicación web pero de momento no he mostrado nada muy interesante. Ahora voy a hacer algo más interesante deteniéndome mucho menos para que este hilo no se haga eterno. Si queréis más detalle visitad la documentación.

Lo que voy a hacer va a ser convertir lo que hice en este otro artículo en algo más interactivo para que cualquiera se pueda descargar un gráfico para la localización que considere.

Antes de continuar hemos de instalar una serie de cosas en el entorno streamlit para que el ejemplo funcione:

conda install -c conda-forge numpy pandas matplotlib xarray netcdf4 seaborn

En una primera versión solo voy a añadir un par de sliders que permitan elegir la latitud y la longitud y un botón para que calcule la gráfica. Escribo el código y lo explico brevemente. Lo guardaré en un fichero llamado app02.py:

Para que el código anterior funcione debes descargar el fichero con los datos de temperatura en el mismo sitio donde se encuentre el fichero app02.py y se debe llamar NOAA_V5_air_temperature_anomaly.nc.

Como he comentado, me centro en esta parte:

  • xarr = read_noaa("NOAA_V5_air_temperature_anomaly.nc"): Leemos los datos en formato NetCDF.
  • st.title('#ShowYourStripes'): Explicado más arriba.
  • lon = st.slider(...): Añade un control para deslizar entre un rango y con un paso y una posición inicial. Lo mismo se hace con lat. Esto me permite obtener información del usuario y guardarlo en las variables lon y lat que usaré más tarde.
  • if st.button(...): ... else: ...: En este caso añadimos un botón que cuando pulsemos hará lo que aparece en la condición y cuando actualicemos cualquier control mostrará lo del else.
  • st.pyplot(fig): Dentro de la condición uso st.pyplot que me permite mostrar instancias de Figure (matplotlib).
  • st.table(ts): Me permite mostrar un DataFrame o una Series de pandas como una tabla HTML. Puedo hacer algo muy similar usando st.dataframe(ts). La ventaja del segundo es que, por ejemplo, podemos ordenar por los valores de una columna o estilizar la tabla. De momento uso st.table para simplificar.

Para ver la aplicación en acción debemos ejecutar desde una línea de comandos, localizada en la misma carpeta donde hemos guardardo app02.py y los datos y con el entorno streamlit esté activado, lo siguiente:

streamlit run app02.py

Y deberás ver algo parecido a:

Hay muchos más controles útiles que podría usar, checkboxes^, menús drop-down,… Dependiendo de la aplicación y la necesidad tendrá sentido usar unos u otros.

Podemos iterar sobre esta idea. Ahora, para seguir viendo más cosas que podemos hacer con streamlit, voy a añadir un mapa que indique la serie que he seleccionado y voy a meter los controles en el lateral para que la navegación sea más cómoda.

Para mostrar un mapa y que sea algo más interactivo voy a usar folium. Primero habrá que instalarlo en el entorno streamlit. Lo puedes hacer usando:

conda install -c conda-forge folium

Escribo primero el código que guardaré en el fichero app03.py y luego comento las partes nuevas:

Entre las novedades de la versión 3 de nuestra aplicación he incluido:

  • create_geojson_grid: Esta es una función que crea un fichero GeoJson con polígonos de 5×5 grados alrededor de cada nodo donde tenemos datos de temperatura. Este GeoJson lo uso luego en folium.
  • create_map: Esta función me ayuda a crear el mapa que quiero mostrar en pantalla. Crea un mapa, lee el GeoJson creado con la función anterior y añade, además, un marcador en la localización que ha elegido el usuario.

En la parte de los controles he añadido:

  • tile = st.sidebar.selectbox(...): Esto añade un menú drop-down en la barra lateral de controles. Me permite elegit los tiles que usará el mapa de folium.

En la parte de mostrar resultados (el contenedor principal) he añadido:

  • st.markdown(mapa._repr_html_(), unsafe_allow_html=True): De esta forma puedo representar el código HTML del mapa de folium y que se muestre sin problemas. PERO RECUERDA QUE LA OPCIÓN unsafe_allow_html LA QUIEREN ELIMINAR. Supongo que añadirán alguna alternativa pero habrá que ver cómo evoluciona.

Aquí puedes ver cómo quedaría:

Resumen

Esto ha sido solo un ejemplo real de uso de streamlit para crear un cuadro de mando (dashboard) de unos datos reales. Como ves, parece bastante sencillo de usar. Todo es código Python y la parte de la aplicación es muy pequeña, el resto del código es el análisis normal que se haría para obtener el gráfico.

Cosas que me han gustado:

  • Prácticamente no interfiere en el código normal que escribimos. Con un par de funciones y algo de código para crear la capa visual tenemos una aplicación funcional.
  • La capa visual (streamlit) me parece que está muy bien pensada con opciones que con poco permiten hacer mucho.
  • El desarrollo está muy activo y están preguntando a los usuarios lo cual me parece positivo.
  • Permite pasar de la idea a una aplicación usable en muy poco tiempo.

Cosas que no me han gustado:

  • Está muy verde aun y el servidor se ‘rompe’ cada dos por tres.
  • Es muy lento. Existe una opción para cachear cosas pero no creo que sea muy útil en muchos casos.
  • Si te sales del guión será difícil hacer algo muy personalizado y te tocará ir a contracorriente.
  • El desarrollo está muy enfocado a crear un producto/empresa por lo que no sé si acabará siendo positivo o negativo y genera cierta incertidumbre para adoptarlo. Otros modelos como el de plotly parace que han funcionado bien por lo que esto no es necesariamente negativo pero sí que es negativa la incertidumbre.
  • Si se adopta de forma masiva veríamos mcuhas webs muy parecidas. Similar a la bootstrap-ificación de la web en general. Eso resta diversidad visual, de enfoques, de soluciones,…

¿Has usado alguna de las otras opciones para crear un cuadro de mando? Puedes comentar si te gusta la idea detrás de Panel, cómo se hacen las cosas con Voilà, la facilidad de uso de Dash,… O, incluso mejor, nos puedes escribir un artículo contándonos tus impresiones. Estaría genial para poder comparar.

Y eso es todo por hoy.

Saludos.

P.D.: El código y los datos los podéis descargar de este repositorio. He creado también un binder para ejecutar la app pero falla algo con tk y no tengo ni idea de cómo resolverlo. Si alguien sabe cómo resolverlo le agradecería que me echase un cable.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

42 − = thirty three