Finanzas Personales con Streamlit #3: Elementos interactivos
Esta semana volvemos con un nuevo capítulo de la serie “"Finanzas Personales con Streamlit”
En el primer artículo de la serie se introdujo el proceso CRISP-DM y como Streamlit puede jugar un papel fundamental en él. Además, comenzamos a crear una app con esta herramienta e introdujimos algunos de los conceptos fundamentales.En el segundo artículo añadimos un sistema de autenticación a la aplicación y también conectamos una base de datos. Gracias a la combinación de ambas conseguimos elevar considerablemente los casos de uso que permite la herramienta.
Hoy veremos nuevos elementos como formularios, tablas y visualizaciones interactivas.
Formularios
Comencemos por los formularios. Un formulario en Streamlit es una forma de obtener información del usuario de manera interactiva. Puedes usarlos para solicitar información necesaria para hacer cálculos o para filtrar datos. Para crear un formulario, basta con usar la función st.form
.
import streamlit as st
with st.form("my_form"):
name = st.text_input("Por favor, introduce tu nombre")
submit_button = st.form_submit_button(label='Enviar')
if submit_button:
st.write(f'Hola {name}!')
En este ejemplo, hemos creado un formulario usando st.form
y hemos añadido un campo de entrada de texto con st.text_input
. Streamlit también ofrece otros tipos de campos que se pueden incluir en un formulario. Algunos de ellos son:
st.slider
: control deslizante que permite al usuario seleccionar un valor numérico dentro de un rango determinado.st.selectbox
,st.checkbox
yst.radio
: para la selección de opciones dada una lista.st.date_input
yst.time_input
: campos de entrada para seleccionar fechas y horas.st.file_uploader
: un campo que permite al usuario cargar archivos.
Lógicamente, un formulario puede estar formado por varios tipos de campos.
Tablas interacivas con Ag-Grid
La librería streamlit-aggrid
es una extensión de Streamlit que permite la visualización de datos en forma de tablas interactivas utilizando el popular plugin de JavaScript "Ag-Grid".
Ag Grid es una biblioteca de código abierto en JavaScript para la creación de tablas de datos interactivas.
Algunas de las características básicas que ofrecen la librería son:
Ordenar y filtrar y buscar datos por columna
Selección de filas y columnas
Paginación para navegar más facilmente por los datos
Algunas características más avanzadas son:
Edición de valores de la tabla
Agrupación, agregación y fijación de columnas
Expansión y contracción filas anidadas
Personalización de la apariencia y el comportamiento de las columnas y filas.
Interacción con otros componentes de Streamlit.
Para comenzar con Streamlit lo primero es importar la clase AgGrid
de la librería.
import streamlit as st
import pandas as pd
from st_aggrid import AgGrid
@st.cache_data()
def load_data():
data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')
return data
data = load_data()
st.title("Tabla con Ag-Grid")
AgGrid(data, height=400)
Opciones de configuración
Ahora, vamos a añadir opciones de configuración del grid, utilizando gridOptionsBuilder
from st_aggrid import GridOptionsBuilder, JsCode, AgGrid
data["date"] = data["year"].astype(str) + "-12-31"
gb = GridOptionsBuilder()
Primero, se crean algunos parámetros predeterminados para las columnas, que permiten a los usuarios redimensionar, filtrar y ordenar las columnas en la tabla.
gb.configure_default_column(
resizable=True,
filterable=True,
sortable=True,
editable=False,
)
Luego, usando el método gb.configure_column()
, se seleccionan y configuran las columnas que se incluirán en la tabla,
Por ejemplo, para la columna country
, se agrega un tooltip y se ajusta el ancho de la columna para que se ajuste al espacio disponible en la tabla.
gb.configure_column(
field="country",
flex=1,
tooltipField="país",
header_name="País",
)
Para la columna "date
", se añade una función de representación de celda que dará formato a los valores de la celda en un formato de fecha legible JsCode(cell_render)
, y se ajusta el ancho de la columna.
cell_render = """
function(params) {
var date = new Date(params.value);
console.log(date);
var options = {
year: "numeric",
month: "long",
day: "numeric"
};
return date.toLocaleDateString("es-ES", options);
};
"""
gb.configure_column(
field="date",
header_name="Año",
width=150,
cellRenderer=JsCode(cell_render),
)
Finalmente, para la columna pop
, se especifica que la columna es de tipo numérico.
gb.configure_column(
field="pop",
header_name="Población",
width=150,
type=["numericColumn"],
)
Cuando acabemos de configurar las columnas que se quieren añadir al grid, usamos el método build()
para guardar las caractéristicas del grid. Por último usamos la clase AgGrid()
para construir la tabla.
go = gb.build()
AgGrid(data, gridOptions=go, height=400, allow_unsafe_jscode=True)
Agregación y estilos
El punto fuerte de Ag-Grid no es la selección de columnas con un formato dado sino, la agrupación de datos y la personalización del estilo de las celdas.
A continuación, se muestra cómo aplicar estas opciones para obtener la media del PIB per cápita y la esperanza de vida agrupando los datos por año, continente y país.
Primero, se deben seleccionar los campos por los que se quiere agrupar y añadir el argumento rowGroup=True
a cada una de las columnas correspondientes:
gb.configure_column(
field="year",
flex=1,
tooltipField="Año",
header_name="Año",
rowGroup=True,
hide=True
)
gb.configure_column(
field="continent",
flex=1,
tooltipField="Continente",
header_name="Continente",
rowGroup=True,
hide=True
)
gb.configure_column(
field="country",
flex=1,
tooltipField="País",
header_name="País"
)
Después, se debe especificar la forma en que se van a agrupar las columnas. En este caso, se hará por la media:
gb.configure_column(
field="gdpPercap",
header_name="PIB per cápita",
width=150,
aggFunc="avg",
type=["numericColumn"],
)
gb.configure_column(
field="lifeExp",
header_name="Esperanza de vida",
width=200,
aggFunc="avg",
type=["numericColumn"],
cellStyle=JsCode(cellstyle),
)
Lo siguiente es añadir estilo a la columna lifeExp
. Concretamente queremos que tenga un gradiente de color en función del valor. Para ello necesitamos crear una función en JavaScript que siga esta lógica:
cellstyle = """
function(params) {
if (params.value < 30) {
return {
'backgroundColor': 'rgb(255, 0, 0)'
}
} else if (params.value < 50) {
return {
'backgroundColor': 'rgb(255, 128, 0)'
}
} else if (params.value < 65) {
return {
'backgroundColor': 'rgb(255, 255, 0)'
}
} else if (params.value < 80) {
return {
'backgroundColor': 'rgb(128, 255, 0)'
}
} else {
return {
'backgroundColor': 'rgb(0, 255, 0)'
}
}
};
"""
A continuación, hacemos como antes.
go = gb.build()
AgGrid(data, gridOptions=go, height=400, allow_unsafe_jscode=True)
Es imposible cubrir todas las características de Ag-Grid en un solo artículo, pero esperamos haber dado una idea general de lo que es posible hacer con esta herramienta. Si quieres profundizar más en Ag-Grid, te invito a explorar su documentación y a perderte entre las infinitas posibilidades
Visualizaciones
¿Qué sería de nuestras aplicaciones si solo tuviesen tablas y texto? ¡No me lo quiero ni imaginar!
Streamlit permite crear visualizaciones interactivas usando bibliotecas populares como matplotlib
, plotly
y bokeh
. El código en Python se crea de igual forma que si no estuviésemos utilizando Streamlit y por último se llama al objeto creado. Un sencillo ejemplo con plotly
:
import streamlit as st
import plotly.express as px
import pandas as pd
data = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv")
st.write('### Población vs. Esperanza de vida en el mundo')
fig = px.scatter(data_frame=data, x="gdpPercap", y="lifeExp", animation_frame="year",
size="pop", color="continent", log_x=True, range_x=[100, 100000], range_y=[20, 90])
st.plotly_chart(fig)
Todo junto sabe mejor
Por último, vamos a hacer una aplicación en la que se mezclan todos los elementos que hemos explicado hoy.
Visualizar datos previamente cargados.
Interactuar con la gráfica haciendo
Formulario para añadir y eliminar información.
Botones para modificar la forma en la que queremos visualizar la información (tipo de gráfico, colores, una serie u otra, ejes etc).
Tabla de Ag-Grid para editar o eliminar elementos.
Parte 1: Carga de datos y construcción del GridOptionsBuilder
import streamlit as st
import pandas as pd
from st_aggrid import GridOptionsBuilder, JsCode, AgGrid
import plotly.express as px
try:
data = pd.read_csv("datos.csv")
except:
data = pd.DataFrame({
"Año":[1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010],
"Categoría":["Facturas","Ocio","Ocio","Facturas","Ocio","Ocio","Facturas","Ocio","Ocio","Facturas","Ocio","Ocio",
"Facturas","Ocio","Ocio","Facturas"],
"Cantidad":[25,11,12,27,14,15,15,17,18,23,20,21,30,23,24,25]
})
gb = GridOptionsBuilder()
gb.configure_default_column(
filterable=True,
sortable=True,
editable=False,
)
gb.configure_column(
field="Año",
flex=1,
tooltipField="Año",
header_name="Año",
)
gb.configure_column(
field="Categoría",
flex=1,
tooltipField="Categoría",
header_name="Categoría",
)
euro_cellstyle = """
function(params) {
return params.value.toFixed(2) + ' €';
}
"""
gb.configure_column(
field="Cantidad",
flex=1,
tooltipField="Cantidad
header_name="Cantidad",
editable=True,
cellRenderer=JsCode(euro_cellstyle),
)
gb.configure_selection("multiple", use_checkbox=True, header_checkbox=True, suppressRowClickSelection=True)
gb.configure_pagination(paginationAutoPageSize=False, paginationPageSize=15)
go = gb.build()
En la primera parte del código se carga un archivo csv llamado datos.csv
con la función read_csv
de Pandas, si el archivo no existe, se crea un DataFrame de ejemplo con los siguientes campos: "Año", "Categoría" y "Cantidad".
A continuación hacemos uso del objeto GridOptionsBuilder
para personalizar los campos de la tabla (como ya hemos visto antes)
En el caso de la columna "Cantidad", se le añade una función cellRenderer
que permite modificar el valor que se muestra en la tabla, en este caso, se le añade el símbolo del euro al final del valor y se limita a dos decimales.
Por último, se configura la selección de filas y la paginación con configure_selection
y configure_pagination
, respectivamente. Todo esto se guarda en el objeto go
para su posterior uso en la tabla.
Parte 2: Formulario para añadir datos
with st.expander("Añadir datos"):
with st.form("my_form"):
year = st.date_input("Seleccione la fecha").year
categoria = st.selectbox("Seleccionar categoría", ["Facturas","Ocio","Viajes"])
cantidad = st.number_input("Cantidad (€)", min_value=0)
submit_button = st.form_submit_button(label='Añadir registro')
if submit_button:
data_new = pd.DataFrame([[year, categoria,cantidad]], columns=["Año","Categoría","Cantidad"])
data = pd.concat([data, data_new])
data = data.groupby(["Año","Categoría"]).sum().reset_index()
data.to_csv("datos.csv", index=False)
En el siguiente bloque de código, se crea un formulario con el objeto st.form()
que permitirá al usuario añadir nuevos datos a la tabla. Se utiliza un st.date_input()
para seleccionar la fecha, un st.selectbox()
para elegir la categoría y un st.number_input()
para introducir la cantidad en euros. Al hacer clic en el botón "Añadir registro", añadiremos los datos a la tabla original.
Parte 3: Interacción con Ag-Grid
Hasta ahora hemos visto cómo Ag-Grid permite ver la información en la tabla, agrupada, sin agrupar, con formato condicional, etc. Pero también permite seleccionar información e interactuar con ella.
En el siguiente fragmento, al crear el objeto grid_response
obtenemos un diccionario con la información de la tabla y con información sobre cómo el usuario interactúa con la tabla… como si fuesen las cookies🍪. Con esto podemos saber donde ha clickado el usuario y por ejemplo, eliminar la fila o campo seleccionado.
with st.expander("Ver tabla"):
grid_response = AgGrid(data, gridOptions=go, height=400, allow_unsafe_jscode=True)
index_selected = [int(i["_selectedRowNodeInfo"]["nodeId"]) for i in grid_response["selected_rows"]]
delete = st.button("Eliminar registro(s)", key="movements_del")
if delete:
if len(index_selected) == 0:
st.warning("No ha seleccionado ningún registro, seleccione antes de eliminar")
else:
data = data.drop(index_selected, axis=0)
data.to_csv("datos.csv", index=False)
st.experimental_rerun()
Parte 4: Visualización de los datos
only_checks = st.checkbox("Graficar unicamente lo seleccionado")
data_plot = data.iloc[index_selected]
if not only_checks:
data_plot = data.copy()
fig = px.line(data_plot, x="Año", y="Cantidad", color='Categoría')
st.plotly_chart(fig, use_container_width=True)
En el último bloque de código, se utiliza un st.checkbox()
para permitir al usuario seleccionar si desea visualizar únicamente los datos seleccionados en la tabla Ag-Grid. Y por último, pintamos la gráfica.
¡Hasta aquí el capítulo de hoy! Estamos cada vez más cerca del final de esta serie sobre Streamlit y de empezar a usarlo para crear modelos predictivos con IA
Nos vemos la semana que viene con las #NoticiasDelMes, donde hablaremos, entre otras cosas, de lo que ha presentado hoy Microsoft.
¡MUCHAS GRACIAS!
Si el artículo te ha gustado, dejar un ❤️ y compartirlo con alguien a quién creas que pueda servirle sería la MEJOR FORMA DE VALORAR MI TRABAJO
Cada semana recibirás en tu bandeja de entrada algo nuevo que aprender sobre el mundo de los datos.