Objetivo: construir un dashboard con Streamlit (interfaz), pandas (datos) y plotly (gráficas).
Público: personas con nociones básicas de programación.

0) Preparación rápida

Descarga los archivos

  1. Haz un carpeta del taller y abre una terminal en esa carpeta:
cd C:\Users\...\Documentos\Taller Dashboard\
python -m pip install --upgrade pip
python -m pip install streamlit
python -m pip install pandas
python -m pip install plotly
python -m pip install numpy
  1. Estructura esperada:
workshop_dashboard_python/
├─ dashboard.py
├─ data/
│  ├─ ventas_2024.csv
│  └─ sensores_semana.csv
├─ assets/
│  ├─ uabc_banner.svg
│  └─ uabc_theme.css

Si aparece un error de rutas en Windows (unicodeescape), usa r"ruta\con\backslashes" o /.

🧭 Intro rápida a pandas + plotly

En esta sección aprenderás lo mínimo indispensable para cargar datos con pandas y graficarlos con Plotly. La usaremos para confirmar que todo está bien antes de construir el dashboard.

¿Qué es pandas? Librería para tablas (DataFrames): leer CSV, filtrar, agrupar, resumir y limpiar datos.
¿Qué es Plotly? Librería para gráficas interactivas (zoom, hover, descarga de imagen) que funciona muy bien con pandas y Streamlit.

Haz archivo pruebapandas.py y ejecuta.

# 1) Importar librerías
from pathlib import Path
import pandas as pd
import plotly.express as px

# 2) Leer CSV (ajusta la ruta si tu proyecto usa otra carpeta)
BASE = Path(__file__).parent
df_ventas = pd.read_csv(BASE / "data" / "ventas_2024.csv", parse_dates=["fecha"])
df_sensores = pd.read_csv(BASE / "data" / "sensores_semana.csv", parse_dates=["timestamp"])

# 3) Explorar datos rápidamente
print(df_ventas.head())
print(df_sensores.head())

print("\nInformación de df_ventas:")
print(df_ventas.info())

print("\nDescripción estadística de df_ventas:")
print(df_ventas.describe(include='all'))

Primer gráfico interactivo (Plotly)

Vamos a construir una tendencia de ingreso por fecha con groupby y px.line:

serie_ingreso = df_ventas.groupby("fecha", as_index=False)["ingreso"].sum()
fig = px.line(serie_ingreso, x="fecha", y="ingreso", markers=True, title="Ingreso por fecha")
fig.show()

1) Explora los datos con pandas(opcional, para entender el dataset)

Haz archivo pruebaplotly.py.

# Importamos pandas, la librería estándar para manipular datos en Python
import pandas as pd
from pathlib import Path

# Leemos los CSV. 'parse_dates' convierte esas columnas a tipo fecha automáticamente.
BASE = Path(__file__).parent
ventas = pd.read_csv(BASE / "data" / "ventas_2024.csv", parse_dates=["fecha"])
sensores = pd.read_csv(BASE / "data" / "sensores_semana.csv", parse_dates=["timestamp"])

# .head() muestra las primeras filas; sirve para verificar que cargó bien
ventas.head(), sensores.head()
# Creamos una columna 'mes' para agrupar ventas por mes calendario
ventas['mes'] = ventas['fecha'].dt.to_period('M').dt.to_timestamp()

# 'groupby' + 'sum' acumula el 'ingreso' por mes
agg_mensual = ventas.groupby('mes', as_index=False)['ingreso'].sum()

# También calculamos el total por producto para ver 'top' productos
top_producto = (ventas.groupby('producto', as_index=False)['ingreso']
.sum()
.sort_values('ingreso', ascending=False))

print(agg_mensual.tail(), top_producto)

2) Construye dashboard.py — copia/pega exacto con explicación

Abre dashboard.py en tu editor y sigue en orden.

2.1 Encabezado + Configuración (pegar al inicio del archivo)

Qué hace: importa librerías, define la ruta base y configura la página.

from pathlib import Path   # Manejo de rutas multiplataforma
import streamlit as st # Framework para apps web de datos (UI)
import pandas as pd # Manipulación de datos (tablas)
import plotly.express as px # Gráficas interactivas sencillas

# BASE_DIR apunta a la carpeta donde está este archivo .py.
# Así, las rutas a 'data/' y 'assets/' funcionan aunque ejecutes desde otro directorio.
BASE_DIR = Path(__file__).parent

# Configuramos el título de la pestaña y el layout "wide" (ancho) para aprovechar la pantalla
st.set_page_config(page_title="UABC · Dashboard en Python desde Cero", layout="wide")

2.2 Tema UABC y Banner (pegar debajo del bloque anterior)

Qué hace: aplica estilos visuales y muestra el banner si existe.

# Cargamos CSS personalizado (colores UABC, estilos de KPIs, etc.)
theme_css = BASE_DIR / "assets" / "uabc_theme.css"
if theme_css.exists():
# st.markdown permite inyectar HTML/CSS; 'unsafe_allow_html' es necesario para CSS
css_text = theme_css.read_text(encoding="utf-8")
st.markdown(f"<style>{css_text}</style>", unsafe_allow_html=True)

# Mostramos banner si está disponible (SVG). 'use_container_width=True' usa todo el ancho disponible.
banner_svg = BASE_DIR / "assets" / "uabc_banner.svg"
if banner_svg.exists():
st.image(str(banner_svg), use_container_width=True)
else:
st.title("UABC · Taller de Dashboard en Python desde Cero") # Título alterno si no hay banner

2.3 Carga de datos con caché (pegar debajo del banner)

Qué hace: lee los CSV y guarda el resultado en caché para que no se recarguen en cada interacción.

@st.cache_data  # Decorador: si los archivos no cambian, reutiliza los datos
def load_data():
    # Usamos BASE_DIR para formar rutas absolutas seguras
    ventas = pd.read_csv(BASE_DIR / "data" / "ventas_2024.csv", parse_dates=["fecha"])
    sensores = pd.read_csv(BASE_DIR / "data" / "sensores_semana.csv", parse_dates=["timestamp"])
    return ventas, sensores

# Cargamos los DataFrames
ventas, sensores = load_data()

# Creamos dos pestañas: 'Ventas' (negocio) y 'Sensores' (operaciones)
st.markdown("### 📊 Dashboard — Práctica guiada (2 horas)")
tab1, tab2 = st.tabs(["📈 Ventas", "🛠️ Sensores"])

2.4 Pestaña Ventas — filtros y KPIs (pegar debajo del bloque anterior)

Qué hace: aplica filtros por región, producto y fechas. Calcula KPIs básicos.

with tab1:
    st.subheader("Panel Comercial")

    # Tres columnas para filtros
    colf1, colf2, colf3 = st.columns(3)
    with colf1:
        # Lista múltiple para seleccionar regiones
        region = st.multiselect("Región",
                                ventas["region"].unique().tolist(),
                                default=ventas["region"].unique().tolist())
    with colf2:
        # Lista múltiple para seleccionar productos
        producto = st.multiselect("Producto",
                                  ventas["producto"].unique().tolist(),
                                  default=ventas["producto"].unique().tolist())
    with colf3:
        # Selector de rango de fechas basado en los datos
        rango = st.date_input("Rango de fechas",
                              value=(ventas["fecha"].min(), ventas["fecha"].max()))

    # Filtramos según las selecciones
    df = ventas[(ventas["region"].isin(region)) & (ventas["producto"].isin(producto))]
    if isinstance(rango, tuple) and len(rango) == 2:
        df = df[(df["fecha"] >= pd.to_datetime(rango[0])) & (df["fecha"] <= pd.to_datetime(rango[1]))]

    # Calculamos KPIs principales
    col1, col2, col3, col4 = st.columns(4)
    ingreso = df["ingreso"].sum()              # Suma de ingresos
    unidades = df["unidades"].sum()            # Total de unidades vendidas
    precio_prom = df["precio"].mean()          # Precio promedio
    descuento_prom = df["descuento_pct"].mean()# Descuento (%) promedio

    # Mostramos los KPIs como "tarjetas"
    col1.metric("Ingreso total", f"$ {ingreso:,.0f}")
    col2.metric("Unidades", f"{unidades:,.0f}")
    col3.metric("Precio prom.", f"$ {precio_prom:,.2f}")
    col4.metric("Desc. prom. (%)", f"{descuento_prom:,.1f}%")

2.5 Pestaña Ventas — gráficas y tabla (pegar debajo del bloque anterior)

Qué hace: línea de tendencia mensual, barra de top productos y tabla de detalle.

    # Dos columnas: 2/3 para la línea, 1/3 para la barra
    c1, c2 = st.columns((2,1))

    with c1:
        st.markdown("**Tendencia mensual**")
        # Agrupamos por fecha (mensual en este dataset) y sumamos ingresos
        serie = df.groupby("fecha", as_index=False)["ingreso"].sum()
        fig = px.line(serie, x="fecha", y="ingreso", markers=True)
        st.plotly_chart(fig, use_container_width=True)

    with c2:
        st.markdown("**Top productos por ingreso**")
        top_prod = (df.groupby("producto", as_index=False)["ingreso"]
                      .sum()
                      .sort_values("ingreso", ascending=False))
        fig2 = px.bar(top_prod, x="producto", y="ingreso")
        st.plotly_chart(fig2, use_container_width=True)

    st.markdown("**Tabla de detalle**")
    st.dataframe(df.sort_values("fecha", ascending=False), use_container_width=True, hide_index=True)

2.6 Pestaña Sensores — filtros, KPIs y gráficas (pegar al final, antes del footer)

Qué hace: filtra por máquina y fechas, muestra KPIs y gráficas (temperatura y vibración), y tabla de alertas.

with tab2:
    st.subheader("Panel Operaciones")

    # Filtros
    colA, colB = st.columns(2)
    with colA:
        maq = st.multiselect("Máquina",
                             sensores["maquina"].unique().tolist(),
                             default=sensores["maquina"].unique().tolist())
    with colB:
        fecha_s = st.date_input("Rango de fechas",
                                 value=(sensores["timestamp"].min().date(), sensores["timestamp"].max().date()))

    # Aplicamos filtros
    df2 = sensores[sensores["maquina"].isin(maq)]
    if isinstance(fecha_s, tuple) and len(fecha_s) == 2:
        df2 = df2[(df2["timestamp"] >= pd.to_datetime(fecha_s[0])) & (df2["timestamp"] <= pd.to_datetime(fecha_s[1]) + pd.Timedelta(days=1))]

    # KPIs operativos
    colk1, colk2, colk3 = st.columns(3)
    colk1.metric("Registros", f"{len(df2):,}")
    colk2.metric("Temp. media (°C)", f"{df2['temperatura_c'].mean():.2f}")
    colk3.metric("Alertas", f"{(df2['estado']=='ALERTA').sum():,}")

    # Gráficas
    st.markdown("**Temperatura (línea)**")
    fig3 = px.line(df2, x="timestamp", y="temperatura_c", color="maquina")
    st.plotly_chart(fig3, use_container_width=True)

    st.markdown("**Vibración por máquina (boxplot)**")
    fig4 = px.box(df2, x="maquina", y="vibracion_g", color="maquina")
    st.plotly_chart(fig4, use_container_width=True)

    # Tabla de eventos en alerta
    st.markdown("**Eventos**")
    st.dataframe(df2[df2['estado']=='ALERTA'].sort_values('timestamp'), use_container_width=True, hide_index=True)

Qué hace: agrega un pie de página con nota de uso académico UABC.

st.markdown('<div class="footer">© UABC · Uso académico (Taller Dashboard en Python desde Cero, FCIAS).</div>', unsafe_allow_html=True)

3) Mejoras opcionales (para sumar puntos)

Pega estos bloques después de sus respectivas secciones.

3.1 Botón para descargar CSV (Ventas)

Dónde pegar: al final de la pestaña Ventas, debajo de la tabla.

    # --- Botón de descarga del dataframe filtrado ---
    csv = df.to_csv(index=False).encode("utf-8-sig")
    st.download_button("⬇️ Descargar CSV filtrado", data=csv, file_name="ventas_filtrado.csv", mime="text/csv")

3.2 Barras apiladas región×producto (Ventas)

Dónde pegar: en la pestaña Ventas, debajo del ‘Top productos’.

    st.markdown("**Ingreso por región y producto (barras apiladas)**")
    stacked = df.groupby(['region','producto'], as_index=False)['ingreso'].sum()
    fig_stack = px.bar(stacked, x='region', y='ingreso', color='producto')
    st.plotly_chart(fig_stack, use_container_width=True)

3.3 Treemap o Pie de participación (Ventas)

Dónde pegar: en la pestaña Ventas, debajo de las barras apiladas.

    st.markdown("**Participación por producto (treemap)**")
    part = df.groupby('producto', as_index=False)['ingreso'].sum()
    fig_tree = px.treemap(part, path=['producto'], values='ingreso')
    st.plotly_chart(fig_tree, use_container_width=True)

3.4 Detección de anomalías con IQR (Sensores)

Dónde pegar: en la pestaña Sensores, debajo del boxplot de vibración.

Qué hace: marca como anomalías los valores que superan Q3 + 1.5*IQR por máquina.

    # --- Anomalías por IQR ---
    import pandas as pd
    def iqr_flags(group, col):
        q1 = group[col].quantile(0.25)
        q3 = group[col].quantile(0.75)
        iqr = q3 - q1
        thresh = q3 + 1.5*iqr
        return group[col] > thresh

    df2 = df2.copy()
    df2['anom_temp'] = df2.groupby('maquina', group_keys=False).apply(lambda g: iqr_flags(g, 'temperatura_c'))
    df2['anom_vib']  = df2.groupby('maquina', group_keys=False).apply(lambda g: iqr_flags(g, 'vibracion_g'))

    st.markdown("**Anomalías detectadas (IQR)**")
    resumen = (df2.assign(dia=df2['timestamp'].dt.date)
                  .melt(id_vars=['timestamp','maquina','dia'], value_vars=['anom_temp','anom_vib'], var_name='tipo', value_name='es_anom')
                  .query('es_anom')
                  .groupby(['dia','maquina','tipo']).size().reset_index(name='conteo'))
    st.dataframe(resumen.sort_values(['dia','maquina']), use_container_width=True, hide_index=True)

4) Código completo de dashboard.py 

Puedes reemplazar el archivo por este contenido tal cual si prefieres.

import streamlit as st
import pandas as pd
import plotly.express as px
from pathlib import Path

st.set_page_config(page_title="UABC · Dashboard en Python desde Cero", layout="wide")

# Theme injection
theme_css = Path("assets/uabc_theme.css")
if theme_css.exists():
css_text = theme_css.read_text(encoding="utf-8")
st.markdown(f"<style>{css_text}</style>", unsafe_allow_html=True)

# Header banner (SVG)
banner_svg = Path("assets/uabc_banner.svg")
if banner_svg.exists():
st.image(str(banner_svg), use_container_width=True)
else:
st.title("UABC · Taller de Dashboards en Python")

@st.cache_data
def load_data():
ventas = pd.read_csv("data/ventas_2024.csv", parse_dates=["fecha"])
sensores = pd.read_csv("data/sensores_semana.csv", parse_dates=["timestamp"])
return ventas, sensores

ventas, sensores = load_data()

st.markdown("### 📊 Dashboard — Práctica guiada")

tab1, tab2 = st.tabs(["📈 Ventas", "🛠️ Sensores"])

with tab1:
st.subheader("Panel Comercial")
colf1, colf2, colf3 = st.columns(3)
with colf1:
region = st.multiselect("Región", ventas["region"].unique().tolist(), default=ventas["region"].unique().tolist())
with colf2:
producto = st.multiselect("Producto", ventas["producto"].unique().tolist(), default=ventas["producto"].unique().tolist())
with colf3:
rango = st.date_input("Rango de fechas", value=(ventas["fecha"].min(), ventas["fecha"].max()))
df = ventas[(ventas["region"].isin(region)) & (ventas["producto"].isin(producto))]
if isinstance(rango, tuple) and len(rango) == 2:
df = df[(df["fecha"] >= pd.to_datetime(rango[0])) & (df["fecha"] <= pd.to_datetime(rango[1]))]

col1, col2, col3, col4 = st.columns(4)
ingreso = df["ingreso"].sum()
unidades = df["unidades"].sum()
precio_prom = df["precio"].mean()
descuento_prom = df["descuento_pct"].mean()
col1.metric("Ingreso total", f"$ {ingreso:,.0f}")
col2.metric("Unidades", f"{unidades:,.0f}")
col3.metric("Precio prom.", f"$ {precio_prom:,.2f}")
col4.metric("Desc. prom. (%)", f"{descuento_prom:,.1f}%")

c1, c2 = st.columns((2,1))
with c1:
st.markdown("**Tendencia mensual**")
serie = df.groupby("fecha", as_index=False)["ingreso"].sum()
fig = px.line(serie, x="fecha", y="ingreso", markers=True)
st.plotly_chart(fig, use_container_width=True)
with c2:
st.markdown("**Top productos por ingreso**")
top_prod = df.groupby("producto", as_index=False)["ingreso"].sum().sort_values("ingreso", ascending=False)
fig2 = px.bar(top_prod, x="producto", y="ingreso")
st.plotly_chart(fig2, use_container_width=True)

st.markdown("**Tabla de detalle**")
st.dataframe(df.sort_values("fecha", ascending=False), use_container_width=True, hide_index=True)

with tab2:
st.subheader("Panel Operaciones")
colA, colB = st.columns(2)
with colA:
maq = st.multiselect("Máquina", sensores["maquina"].unique().tolist(), default=sensores["maquina"].unique().tolist())
with colB:
fecha_s = st.date_input("Rango de fechas", value=(sensores["timestamp"].min().date(), sensores["timestamp"].max().date()))
df2 = sensores[sensores["maquina"].isin(maq)]
if isinstance(fecha_s, tuple) and len(fecha_s) == 2:
df2 = df2[(df2["timestamp"] >= pd.to_datetime(fecha_s[0])) & (df2["timestamp"] <= pd.to_datetime(fecha_s[1]) + pd.Timedelta(days=1))]

colk1, colk2, colk3 = st.columns(3)
colk1.metric("Registros", f"{len(df2):,}")
colk2.metric("Temp. media (°C)", f"{df2['temperatura_c'].mean():.2f}")
colk3.metric("Alertas", f"{(df2['estado']=='ALERTA').sum():,}")

st.markdown("**Temperatura (línea)**")
fig3 = px.line(df2, x="timestamp", y="temperatura_c", color="maquina")
st.plotly_chart(fig3, use_container_width=True)

st.markdown("**Vibración por máquina (boxplot)**")
fig4 = px.box(df2, x="maquina", y="vibracion_g", color="maquina")
st.plotly_chart(fig4, use_container_width=True)

st.markdown("**Eventos**")
st.dataframe(df2[df2["estado"]=="ALERTA"].sort_values("timestamp"), use_container_width=True, hide_index=True)

st.markdown('<div class="footer">© UABC · Uso académico (Taller Dashboard en Python desde Cero, FCIAS).</div>', unsafe_allow_html=True)

5) ¿Cómo ejecutarlo? (y errores comunes)

  1. Desde la carpeta del proyecto:
cd C:\Users\...\Documentos\Taller Dashboard\
streamlit run dashboard.py
  1. FileNotFoundError: data\ventas_2024.csv → no estás en la carpeta correcta; entra a la del proyecto o usa rutas absolutas (como aquí).
  2. unicodeescape en Windows → usa r"..." o /.