Segundo en una breve serie sobre el desarrollo de paneles de datos utilizando las últimas herramientas de desarrollo de GUI basadas en Python, Streamlit, Gradioy Taipy.
El conjunto de datos de origen para cada tablero será el mismo, pero se almacenará en diferentes formatos. En la medida de lo posible, también intentaré hacer que los diseños reales del tablero para cada herramienta se parezcan entre sí y tendrán la misma funcionalidad.
En la primera parte de esta serie, creé una versión de transmisión del tablero que recupera sus datos de una base de datos local PostgreSQL. Puedes ver ese artículo aquí.
Esta vez, estamos explorando el uso de la Biblioteca Gradio.
Los datos para este tablero se encuentran en un archivo CSV local, y Pandas será nuestro principal motor de procesamiento de datos.
Si desea ver una demostración rápida de la aplicación, la he desplegado en espacios faciales para abrazar. Puede ejecutarlo usando el siguiente enlace, pero tenga en cuenta que las dos ventanas emergentes de selección de fecha de entrada no funcionan debido a un error conocido en el entorno de la cara abrazada. Este es solo el caso de las aplicaciones implementadas en HF, aún puede cambiar las fechas manualmente. Ejecutar la aplicación localmente funciona bien y no tiene este problema.
Demostración del tablero en Huggingface
¿Qué es Gradio?
Gradio es un paquete Python de código abierto que simplifica el proceso de construcción de demostraciones o aplicaciones web para modelos de aprendizaje automático, API o cualquier función de Python. Con él, puede crear demostraciones o aplicaciones web sin necesidad de JavaScript, CSS o experiencia de alojamiento web. Al escribir solo unas pocas líneas de código Python, puede desbloquear el poder de Gradio y mostrar sin problemas sus modelos de aprendizaje automático a una audiencia más amplia.
Gradio simplifica el proceso de desarrollo al proporcionar un marco intuitivo que elimina las complejidades asociadas con la construcción de interfaces de usuario desde cero. Ya sea que sea un desarrollador de aprendizaje automático, investigador o entusiasta, Gradio le permite crear demostraciones hermosas e interactivas que mejoren la comprensión y la accesibilidad de sus modelos de aprendizaje automático.
Este paquete Python de código abierto lo ayuda a cerrar la brecha entre su experiencia en aprendizaje automático y una audiencia más amplia, lo que hace que sus modelos sean accesibles y procesables.
Lo que desarrollaremos
Estamos desarrollando un tablero de datos. Nuestros datos de origen serán un solo archivo CSV que contenga 100,000 registros de ventas sintéticas.
La fuente real de los datos no eso importante. Podría ser con la misma facilidad un archivo de texto, un archivo de Excel, SQLite o cualquier base de datos a la que pueda conectarse.
Así es como se verá nuestro tablero final.
Hay cuatro secciones principales.
- La fila superior permite al usuario seleccionar fechas de inicio y finalización específicas y/o categorías de productos utilizando recolectores de fecha y una lista desplegable, respectivamente.
- La segunda fila, las métricas clave, muestra un resumen de nivel superior de los datos elegidos.
- La sección de visualización permite al usuario seleccionar uno de los tres gráficos para mostrar el conjunto de datos de entrada.
- La sección de datos sin procesar es precisamente lo que dice ser. Esta representación tabular de los datos elegidos muestra efectivamente una instantánea del archivo de datos CSV subyacente.
Usar el tablero es fácil. Inicialmente, se muestran estadísticas para todo el conjunto de datos. El usuario puede reducir el enfoque de datos utilizando los tres campos de filtro en la parte superior de la pantalla. Los gráficos, las métricas clave y las secciones de datos sin procesar se actualizan dinámicamente para reflejar las opciones del usuario en los campos de filtro.
Los datos subyacentes
Como se mencionó, los datos de origen del tablero están contenidos en un solo archivo de valores separados por comas (CSV). Los datos constan de 100,000 registros sintéticos relacionados con las ventas. Estos son los primeros diez registros del archivo para darle una idea de cómo se ve.
+----------+------------+------------+----------------+------------+---------------+------------+----------+-------+--------------------+
| order_id | order_date | customer_id| customer_name | product_id | product_names | categories | quantity | price | total |
+----------+------------+------------+----------------+------------+---------------+------------+----------+-------+--------------------+
| 0 | 01/08/2022 | 245 | Customer_884 | 201 | Smartphone | Electronics| 3 | 90.02 | 270.06 |
| 1 | 19/02/2022 | 701 | Customer_1672 | 205 | Printer | Electronics| 6 | 12.74 | 76.44 |
| 2 | 01/01/2017 | 184 | Customer_21720 | 208 | Notebook | Stationery | 8 | 48.35 | 386.8 |
| 3 | 09/03/2013 | 275 | Customer_23770 | 200 | Laptop | Electronics| 3 | 74.85 | 224.55 |
| 4 | 23/04/2022 | 960 | Customer_23790 | 210 | Cabinet | Office | 6 | 53.77 | 322.62 |
| 5 | 10/07/2019 | 197 | Customer_25587 | 202 | Desk | Office | 3 | 47.17 | 141.51 |
| 6 | 12/11/2014 | 510 | Customer_6912 | 204 | Monitor | Electronics| 5 | 22.5 | 112.5 |
| 7 | 12/07/2016 | 150 | Customer_17761 | 200 | Laptop | Electronics| 9 | 49.33 | 443.97 |
| 8 | 12/11/2016 | 997 | Customer_23801 | 209 | Coffee Maker | Electronics| 7 | 47.22 | 330.54 |
| 9 | 23/01/2017 | 151 | Customer_30325 | 207 | Pen | Stationery | 6 | 3.5 | 21 |
+----------+------------+------------+----------------+------------+---------------+------------+----------+-------+--------------------+
Y aquí hay algún código de Python que puede usar para generar un conjunto de datos similar. Asegúrese de que las bibliotecas Numpy y Pandas se instalen primero.
# generate the 100K record CSV file
#
import polars as pl
import numpy as np
from datetime import datetime, timedelta
def generate(nrows: int, filename: str):
names = np.asarray(
[
"Laptop",
"Smartphone",
"Desk",
"Chair",
"Monitor",
"Printer",
"Paper",
"Pen",
"Notebook",
"Coffee Maker",
"Cabinet",
"Plastic Cups",
]
)
categories = np.asarray(
[
"Electronics",
"Electronics",
"Office",
"Office",
"Electronics",
"Electronics",
"Stationery",
"Stationery",
"Stationery",
"Electronics",
"Office",
"Sundry",
]
)
product_id = np.random.randint(len(names), size=nrows)
quantity = np.random.randint(1, 11, size=nrows)
price = np.random.randint(199, 10000, size=nrows) / 100
# Generate random dates between 2010-01-01 and 2023-12-31
start_date = datetime(2010, 1, 1)
end_date = datetime(2023, 12, 31)
date_range = (end_date - start_date).days
# Create random dates as np.array and convert to string format
order_dates = np.array([(start_date + timedelta(days=np.random.randint(0, date_range))).strftime('%Y-%m-%d') for _ in range(nrows)])
# Define columns
columns = {
"order_id": np.arange(nrows),
"order_date": order_dates,
"customer_id": np.random.randint(100, 1000, size=nrows),
"customer_name": [f"Customer_{i}" for i in np.random.randint(2**15, size=nrows)],
"product_id": product_id + 200,
"product_names": names[product_id],
"categories": categories[product_id],
"quantity": quantity,
"price": price,
"total": price * quantity,
}
# Create Polars DataFrame and write to CSV with explicit delimiter
df = pl.DataFrame(columns)
df.write_csv(filename, separator=',',include_header=True) # Ensure comma is used as the delimiter
# Generate 100,000 rows of data with random order_date and save to CSV
generate(100_000, "/mnt/d/sales_data/sales_data.csv")
Instalación y uso de Gradio
Instalar gradio es fácil de usar pepitapero para la codificación, la mejor práctica es configurar un entorno de Python separado para todo su trabajo. Utilizo Miniconda para ese propósito, pero no me siento libre de usar cualquier método que se adapte a su práctica laboral.
Si desea ir por la ruta de condena y aún no la tiene, debe instalar Miniconda (recomendado) o Anaconda primero.
Tenga en cuenta que, al momento de escribir, Gradio necesita al menos Python 3.8 instalado para funcionar correctamente.
Una vez que se crea el entorno, cambie a él utilizando el ‘activar’ comando y luego ejecutar ‘PIP Instalar’ a Instale nuestras bibliotecas Python requeridas.
#create our test environment
(base) C:\Users\thoma>conda create -n gradio_dashboard python=3.12 -y
# Now activate it
(base) C:\Users\thoma>conda activate gradio_dashboard
# Install python libraries, etc ...
(gradio_dashboard) C:\Users\thoma>pip install gradio pandas matplotlib cachetools
Diferencias clave entre Streamlit y Gradio
Como demostraré en este artículo, es posible producir paneles de datos muy similares usando Streamlit y Gradio. Sin embargo, su ethos difiere de varias maneras clave.
Enfocar
- Gradio se especializa en la creación de interfaces para modelos de aprendizaje automático, mientras que Strewlit está más diseñado para aplicaciones y visualizaciones de datos de uso general.
Facilidad de uso
- Gradio es conocido por su simplicidad y capacidades rápidas de prototipos, lo que facilita que los principiantes usen. Streamlit ofrece características más avanzadas y opciones de personalización, que pueden requerir una curva de aprendizaje más pronunciada.
Interactividad
- Racionalización usa un reactivo Programación Modelo donde cualquier cambio de entrada desencadena una repetición completa de script, actualizando todos los componentes de inmediato. Gradio, de forma predeterminada, se actualiza solo cuando un usuario hace clic en un botón Enviar, aunque se puede configurar para actualizaciones en vivo.
Personalización
- Gradio se centra en los componentes previos a la construcción para demostrar rápidamente modelos de IA. Streamlit proporciona opciones de personalización más extensas y flexibilidad para proyectos complejos.
Despliegue
- Habiendo implementado una aplicación de racionalización y una aplicación de Gradio, diría que es más fácil implementar una aplicación de transmisión que una aplicación de Gradio. En Strewlit, la implementación se puede hacer con un solo clic a través de la nube de la comunidad de optimización. Esta funcionalidad está integrada en cualquier aplicación de transmisión que cree. Gradio ofrece implementación utilizando espacios faciales para abrazos, pero implica más trabajo. Sin embargo, ninguno de los métodos es particularmente complejo.
Casos de uso
Apreciado sobresale en la creación de aplicaciones centradas en datos y paneles interactivos para proyectos complejos. Gradio es ideal para exhibir rápidamente modelos de aprendizaje automático y construir aplicaciones más simples.
El código del tablero de Gradio
Desglosaré el código en secciones y explicaré cada una a medida que avanzamos.
Comenzamos importando las bibliotecas externas requeridas y cargando el conjunto de datos completo desde el archivo CSV en un Pandas DataFrame.
import gradio as gr
import pandas as pd
import matplotlib.pyplot as plt
import datetime
import warnings
import os
import tempfile
from cachetools import cached, TTLCache
warnings.filterwarnings("ignore", category=FutureWarning, module="seaborn")
# ------------------------------------------------------------------
# 1) Load CSV data once
# ------------------------------------------------------------------
csv_data = None
def load_csv_data():
global csv_data
# Optional: specify column dtypes if known; adjust as necessary
dtype_dict = {
"order_id": "Int64",
"customer_id": "Int64",
"product_id": "Int64",
"quantity": "Int64",
"price": "float",
"total": "float",
"customer_name": "string",
"product_names": "string",
"categories": "string"
}
csv_data = pd.read_csv(
"d:/sales_data/sales_data.csv",
parse_dates=["order_date"],
dayfirst=True, # if your dates are DD/MM/YYYY format
low_memory=False,
dtype=dtype_dict
)
load_csv_data()
A continuación, configuramos un caché de tiempo para vivir con un máximo de 128 elementos y un vencimiento de 300 segundos. Esto se utiliza para almacenar los resultados de las costosas llamadas de funciones y acelerar las búsquedas repetidas
El get_unique_categories La función devuelve una lista de categorías únicas, limpias (capitalizadas) del marco de datos `csv_data`, almacenando en caché el resultado para un acceso más rápido.
El get_date_range La función devuelve las fechas de pedido mínima y máxima del conjunto de datos, o ninguno si los datos no están disponibles.
El Filter_data La función filtra el cuadro de datos CSV_DATA basado en un rango de fecha especificado y una categoría opcional, devolviendo el marcado de datos filtrado.
El get_dashboard_stats La función recupera métricas resumidas (ingresos totales, pedidos totales, valor de pedido promedio y categoría superior) para los filtros dados. Internamente usa filter_data() Para alcanzar el conjunto de datos y luego calcule estas estadísticas clave.
El get_data_for_table FLa unción devuelve un marco de datos detallado de los datos de ventas filtrados, ordenados por orden_id y order_dateincluyendo ingresos adicionales para cada venta.
El get_plot_data Los datos de formatos de función para generar un gráfico sumando ingresos con el tiempo, agrupados por fecha.
El get_revenue_by_category La función agregue y devuelve los ingresos por categoría, ordenados por ingresos, dentro del rango de fechas especificados y la categoría.
El get_top_products La función devuelve los 10 productos principales por ingresos, filtrados por rango de fechas y categoría.
Basado en el argumento de orientación, el create_matplotlib_figure La función genera una gráfica de barra a partir de los datos y lo guarda como un archivo de imagen, ya sea vertical u horizontal.
cache = TTLCache(maxsize=128, ttl=300)
@cached(cache)
def get_unique_categories():
global csv_data
if csv_data is None:
return []
cats = sorted(csv_data['categories'].dropna().unique().tolist())
cats = [cat.capitalize() for cat in cats]
return cats
def get_date_range():
global csv_data
if csv_data is None or csv_data.empty:
return None, None
return csv_data['order_date'].min(), csv_data['order_date'].max()
def filter_data(start_date, end_date, category):
global csv_data
if isinstance(start_date, str):
start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d').date()
if isinstance(end_date, str):
end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d').date()
df = csv_data.loc[
(csv_data['order_date'] >= pd.to_datetime(start_date)) &
(csv_data['order_date'] <= pd.to_datetime(end_date))
].copy()
if category != "All Categories":
df = df.loc[df['categories'].str.capitalize() == category].copy()
return df
def get_dashboard_stats(start_date, end_date, category):
df = filter_data(start_date, end_date, category)
if df.empty:
return (0, 0, 0, "N/A")
df['revenue'] = df['price'] * df['quantity']
total_revenue = df['revenue'].sum()
total_orders = df['order_id'].nunique()
avg_order_value = total_revenue / total_orders if total_orders else 0
cat_revenues = df.groupby('categories')['revenue'].sum().sort_values(ascending=False)
top_category = cat_revenues.index[0] if not cat_revenues.empty else "N/A"
return (total_revenue, total_orders, avg_order_value, top_category.capitalize())
def get_data_for_table(start_date, end_date, category):
df = filter_data(start_date, end_date, category)
if df.empty:
return pd.DataFrame()
df = df.sort_values(by=["order_id", "order_date"], ascending=[True, False]).copy()
columns_order = [
"order_id", "order_date", "customer_id", "customer_name",
"product_id", "product_names", "categories", "quantity",
"price", "total"
]
columns_order = [col for col in columns_order if col in df.columns]
df = df[columns_order].copy()
df['revenue'] = df['price'] * df['quantity']
return df
def get_plot_data(start_date, end_date, category):
df = filter_data(start_date, end_date, category)
if df.empty:
return pd.DataFrame()
df['revenue'] = df['price'] * df['quantity']
plot_data = df.groupby(df['order_date'].dt.date)['revenue'].sum().reset_index()
plot_data.rename(columns={'order_date': 'date'}, inplace=True)
return plot_data
def get_revenue_by_category(start_date, end_date, category):
df = filter_data(start_date, end_date, category)
if df.empty:
return pd.DataFrame()
df['revenue'] = df['price'] * df['quantity']
cat_data = df.groupby('categories')['revenue'].sum().reset_index()
cat_data = cat_data.sort_values(by='revenue', ascending=False)
return cat_data
def get_top_products(start_date, end_date, category):
df = filter_data(start_date, end_date, category)
if df.empty:
return pd.DataFrame()
df['revenue'] = df['price'] * df['quantity']
prod_data = df.groupby('product_names')['revenue'].sum().reset_index()
prod_data = prod_data.sort_values(by='revenue', ascending=False).head(10)
return prod_data
def create_matplotlib_figure(data, x_col, y_col, title, xlabel, ylabel, orientation='v'):
plt.figure(figsize=(10, 6))
if data.empty:
plt.text(0.5, 0.5, 'No data available', ha='center', va='center')
else:
if orientation == 'v':
plt.bar(data[x_col], data[y_col])
plt.xticks(rotation=45, ha='right')
else:
plt.barh(data[x_col], data[y_col])
plt.gca().invert_yaxis()
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.tight_layout()
with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmpfile:
plt.savefig(tmpfile.name)
plt.close()
return tmpfile.name
El update_Dashboard La función recupera estadísticas clave de ventas (ingresos totales, pedidos totales, valor de pedido promedio y categoría superior) llamando a laget_dashboard_stats función. Se reúne datos para tres visualizaciones distintas (ingresos a lo largo del tiempo, ingresos por categoría y productos superiores), luego utiliza create_matplotlib_figure para generar gráficos. Prepara y devuelve una tabla de datos (a través del get_data_for_table() función) junto con todas las gráficas y estadísticas generadas para que puedan mostrarse en el tablero.
El create_dashboard La función establece los límites de fecha (fechas mínimas y máximas) y establece los valores iniciales de filtro predeterminado. Utiliza Gradio para construir una interfaz de usuario (UI) con recolectores de fecha, desplegables de categoría, pantallas métricas clave, pestañas de gráficos y una tabla de datos. Luego altera los filtros para que cambiar cualquiera de ellos desencadena una llamada al update_Dashboard Función, asegurando que las imágenes y las métricas del tablero siempre estén sincronizadas con los filtros seleccionados. Finalmente, devuelve la interfaz de Gradio ensamblada lanzada como una aplicación web.
def update_dashboard(start_date, end_date, category):
total_revenue, total_orders, avg_order_value, top_category = get_dashboard_stats(start_date, end_date, category)
# Generate plots
revenue_data = get_plot_data(start_date, end_date, category)
category_data = get_revenue_by_category(start_date, end_date, category)
top_products_data = get_top_products(start_date, end_date, category)
revenue_over_time_path = create_matplotlib_figure(
revenue_data, 'date', 'revenue',
"Revenue Over Time", "Date", "Revenue"
)
revenue_by_category_path = create_matplotlib_figure(
category_data, 'categories', 'revenue',
"Revenue by Category", "Category", "Revenue"
)
top_products_path = create_matplotlib_figure(
top_products_data, 'product_names', 'revenue',
"Top Products", "Revenue", "Product Name", orientation='h'
)
# Data table
table_data = get_data_for_table(start_date, end_date, category)
return (
revenue_over_time_path,
revenue_by_category_path,
top_products_path,
table_data,
total_revenue,
total_orders,
avg_order_value,
top_category
)
def create_dashboard():
min_date, max_date = get_date_range()
if min_date is None or max_date is None:
min_date = datetime.datetime.now()
max_date = datetime.datetime.now()
default_start_date = min_date
default_end_date = max_date
with gr.Blocks(css="""
footer {display: none !important;}
.tabs {border: none !important;}
.gr-plot {border: none !important; box-shadow: none !important;}
""") as dashboard:
gr.Markdown("# Sales Performance Dashboard")
# Filters row
with gr.Row():
start_date = gr.DateTime(
label="Start Date",
value=default_start_date.strftime('%Y-%m-%d'),
include_time=False,
type="datetime"
)
end_date = gr.DateTime(
label="End Date",
value=default_end_date.strftime('%Y-%m-%d'),
include_time=False,
type="datetime"
)
category_filter = gr.Dropdown(
choices=["All Categories"] + get_unique_categories(),
label="Category",
value="All Categories"
)
gr.Markdown("# Key Metrics")
# Stats row
with gr.Row():
total_revenue = gr.Number(label="Total Revenue", value=0)
total_orders = gr.Number(label="Total Orders", value=0)
avg_order_value = gr.Number(label="Average Order Value", value=0)
top_category = gr.Textbox(label="Top Category", value="N/A")
gr.Markdown("# Visualisations")
# Tabs for Plots
with gr.Tabs():
with gr.Tab("Revenue Over Time"):
revenue_over_time_image = gr.Image(label="Revenue Over Time", container=False)
with gr.Tab("Revenue by Category"):
revenue_by_category_image = gr.Image(label="Revenue by Category", container=False)
with gr.Tab("Top Products"):
top_products_image = gr.Image(label="Top Products", container=False)
gr.Markdown("# Raw Data")
# Data Table (below the plots)
data_table = gr.DataFrame(
label="Sales Data",
type="pandas",
interactive=False
)
# When filters change, update everything
for f in [start_date, end_date, category_filter]:
f.change(
fn=lambda s, e, c: update_dashboard(s, e, c),
inputs=[start_date, end_date, category_filter],
outputs=[
revenue_over_time_image,
revenue_by_category_image,
top_products_image,
data_table,
total_revenue,
total_orders,
avg_order_value,
top_category
]
)
# Initial load
dashboard.load(
fn=lambda: update_dashboard(default_start_date, default_end_date, "All Categories"),
outputs=[
revenue_over_time_image,
revenue_by_category_image,
top_products_image,
data_table,
total_revenue,
total_orders,
avg_order_value,
top_category
]
)
return dashboard
if __name__ == "__main__":
dashboard = create_dashboard()
dashboard.launch(share=False)
Ejecutando el programa
Crear un Pitón Archivo, por ejemplo, gradio_test.py, e inserte todos los fragmentos de código anteriores. Guárdelo y ejecutarlo así,
(gradio_dashboard) $ python gradio_test.py
* Running on local URL: http://127.0.0.1:7860
To create a public link, set `share=True` in `launch()`.
Haga clic en la URL local que se muestra y el tablero abrirá la pantalla completa en su navegador.
Resumen
Este artículo proporciona una guía integral para construir un panel de rendimiento de ventas interactiva utilizando Gradio y un archivo CSV como datos de origen.
Gradio es un marco moderno de código abierto basado en Python que simplifica la creación de paneles de datos y aplicaciones GUI. El tablero que desarrollé permite a los usuarios filtrar rangos de datos por fecha y categorías de productos, ver métricas clave, como ingresos totales y categorías de alto rendimiento, explorar visualizaciones como tendencias de ingresos y productos superiores, y navegar a través de datos sin procesar con paginación.
También mencioné algunas diferencias clave entre el desarrollo de herramientas de visualización utilizando Gradio y Streamlit, otra biblioteca de Python frontal popular.
Esta guía proporciona una implementación integral de un panel de datos de Gradio, que cubre todo el proceso desde la creación de datos de muestra hasta el desarrollo de funciones de Python para consultar datos, generar gráficos y manejar la entrada del usuario. Este enfoque paso a paso demuestra cómo aprovechar las capacidades de Gradio para crear paneles de uso de usuarios y dinámicos, lo que lo hace ideal para ingenieros de datos y científicos que desean construir aplicaciones de datos interactivas.
Aunque utilicé un archivo CSV para mis datos, modificar el código para usar otra fuente de datos, como un sistema de gestión de bases de datos relacionales (RDBMS) como SQLite, debe ser sencillo. Por ejemplo, en mi otro artículo de esta serie sobre la creación de un tablero similar que usa Streamlit, la fuente de datos es una base de datos PostgreSQL.