Dashboards en tiempo real: Best practices
Un dashboard bien diseñado es la diferencia entre datos abrumadores e insights accionables. En este artículo, exploraremos las mejores prácticas para crear dashboards en tiempo real que realmente impulsen decisiones de negocio, evitando los errores más comunes que hacen que los dashboards fallen.
¿Qué es un Dashboard en Tiempo Real?
Un dashboard en tiempo real es una interfaz visual que muestra métricas y KPIs clave actualizados continuamente (o con mínima latencia) para permitir monitoreo y toma de decisiones inmediata.
Características Clave
Actualización Continua:
- Datos refrescados automáticamente
- Latencia mínima (segundos a minutos)
- Reflejan el estado actual del negocio
Visualización Efectiva:
- Información presentada visualmente
- Diseño intuitivo y escaneable
- Foco en métricas prioritarias
Interactividad:
- Filtros y drill-downs
- Exploración de datos
- Personalización por usuario
Accesibilidad:
- Disponible en múltiples dispositivos
- Permisos según roles
- Compartible con stakeholders
Nota: "Tiempo real" puede significar cosas distintas según el contexto. Para trading puede ser milisegundos, para social media pueden ser minutos. Define claramente tus requisitos de latencia.
Principios Fundamentales de Diseño
1. El Principio de la Pirámide Invertida
Organiza la información por importancia decreciente:
┌─────────────────────────────────────┐
│ KPI PRINCIPAL (más visible) │
├─────────────────────────────────────┤
│ Métricas Secundarias │
├─────────────────────────────────────┤
│ Detalles y Contexto │
└─────────────────────────────────────┘
Aplicación práctica:
- Superior izquierdo: Métrica más crítica
- Primera mitad: KPIs principales
- Segunda mitad: Métricas de soporte y detalles
2. La Regla del 5-Segundo
Un usuario debe entender el estado general del negocio en 5 segundos.
Cómo lograrlo:
- Usa indicadores visuales claros (colores, iconos)
- Limita la cantidad de métricas visibles
- Jerarquiza información visualmente
- Elimina elementos decorativos innecesarios
3. Data-Ink Ratio de Tufte
Maximiza la proporción de "tinta" dedicada a representar datos vs. decoración.
Eliminar:
- Bordes innecesarios
- Sombras decorativas
- Efectos 3D sin propósito
- Colores excesivos
- Logos grandes
Mantener:
- Datos y sus representaciones
- Etiquetas claras
- Ejes y escalas necesarias
- Guías visuales útiles
4. Principio de Consistencia
Mantén consistencia en:
- Paleta de colores
- Tipografía
- Posicionamiento de elementos similares
- Formato de números
- Lenguaje y terminología
# Ejemplo: Definir paleta de colores consistente
PALETA_COLORES = {
'primario': '#1E88E5', # Azul para información principal
'exitoso': '#43A047', # Verde para métricas positivas
'advertencia': '#FB8C00', # Naranja para alertas
'critico': '#E53935', # Rojo para problemas
'neutral': '#757575', # Gris para datos neutrales
'fondo': '#FAFAFA', # Gris claro para fondo
'texto': '#212121' # Negro suave para texto
}
# Umbrales consistentes
UMBRALES = {
'excelente': 90,
'bueno': 75,
'aceptable': 60,
'critico': 50
}
def aplicar_color_por_valor(valor, metrica):
if valor >= UMBRALES['excelente']:
return PALETA_COLORES['exitoso']
elif valor >= UMBRALES['bueno']:
return PALETA_COLORES['primario']
elif valor >= UMBRALES['aceptable']:
return PALETA_COLORES['advertencia']
else:
return PALETA_COLORES['critico']
Selección de KPIs Efectivos
Características de Buenos KPIs
Un KPI efectivo debe ser SMART:
S - Specific (Específico):
- "Ingresos" ❌
- "Ingresos mensuales recurrentes (MRR)" ✓
M - Measurable (Medible):
- Cuantificable objetivamente
- Con método de cálculo claro
- Basado en datos disponibles
A - Achievable (Alcanzable):
- Realista según contexto
- Influenciable por el equipo
- Con metas claras
R - Relevant (Relevante):
- Alineado con objetivos de negocio
- Importante para decisiones
- Accionable
T - Time-bound (Temporal):
- Con período de medición definido
- Comparable en el tiempo
- Con frecuencia de revisión clara
KPIs por Tipo de Dashboard
Dashboard de Marketing Digital
KPIs Primarios:
1. Costo por Adquisición (CAC)
- Cálculo: Total Inversión Marketing / Nuevos Clientes
- Umbral: < $50 (ejemplo)
- Actualización: Diaria
2. Retorno de Inversión Publicitaria (ROAS)
- Cálculo: Ingresos por Publicidad / Inversión Publicitaria
- Umbral: > 3.0
- Actualización: Tiempo real
3. Tasa de Conversión
- Cálculo: Conversiones / Visitantes × 100
- Umbral: > 2.5%
- Actualización: Por hora
4. Engagement Rate
- Cálculo: (Likes + Comments + Shares) / Alcance × 100
- Umbral: > 5%
- Actualización: Diaria
KPIs Secundarios:
- Alcance orgánico vs. pagado
- Click-Through Rate (CTR)
- Costo por Click (CPC)
- Tasa de rebote
- Tiempo promedio en sitio
Dashboard de E-commerce
KPIs Primarios:
1. Ventas en Tiempo Real
- Comparación con mismo día semana anterior
- Meta diaria proyectada
- Actualización: Cada minuto
2. Tasa de Abandono de Carrito
- Cálculo: Carritos Abandonados / Carritos Creados × 100
- Umbral: < 70%
- Actualización: Por hora
3. Valor Promedio de Orden (AOV)
- Cálculo: Ingresos Totales / Número de Órdenes
- Meta: Crecimiento mensual > 5%
- Actualización: Tiempo real
4. Tasa de Conversión de Checkout
- Cálculo: Compras Completadas / Inicios de Checkout × 100
- Umbral: > 50%
- Actualización: Por hora
Dashboard de Customer Service
KPIs Primarios:
1. Tiempo Promedio de Respuesta
- Meta: < 2 horas
- Actualización: Cada 15 minutos
2. Tasa de Resolución en Primera Interacción (FCR)
- Cálculo: Casos Resueltos en Primer Contacto / Total Casos × 100
- Umbral: > 70%
- Actualización: Diaria
3. Customer Satisfaction Score (CSAT)
- Escala: 1-5
- Meta: > 4.2
- Actualización: Después de cada encuesta
4. Backlog de Tickets
- Tickets pendientes por prioridad
- Meta: < 50 tickets críticos
- Actualización: Tiempo real
Evita Vanity Metrics
Vanity Metrics son números que se ven bien pero no impulsan decisiones.
Ejemplos de Vanity Metrics:
- Total de seguidores (sin contexto de engagement)
- Pageviews (sin considerar calidad de tráfico)
- Número de descargas de app (sin activación)
- Total de registros (sin usuarios activos)
Cómo convertirlas en métricas accionables:
❌ "10,000 seguidores" ✓ "10,000 seguidores con 5% engagement rate"
❌ "50,000 pageviews mensuales" ✓ "50,000 pageviews con 3.5 min tiempo promedio y 2.5% conversión"
❌ "5,000 descargas de app" ✓ "5,000 descargas con 40% activación en primeras 48 horas"
Tip: Pregúntate siempre: "¿Esta métrica me ayuda a tomar una decisión específica?" Si la respuesta es no, probablemente es una vanity metric.
Tipos de Visualizaciones y Cuándo Usarlas
1. KPI Cards (Tarjetas de Métricas)
Cuándo usar:
- Mostrar un único valor importante
- Comparación rápida con período anterior
- Estado de una métrica crítica
Elementos esenciales:
┌─────────────────────────┐
│ NOMBRE DEL KPI │
│ │
│ $125,430 │ ← Valor principal (grande)
│ ↑ 15.3% │ ← Variación (con indicador)
│ │
│ vs. mes anterior │ ← Contexto
└─────────────────────────┘
Implementación con Python/Plotly:
import plotly.graph_objects as go
def crear_kpi_card(valor, valor_anterior, nombre, prefijo="$", sufijo=""):
# Calcular cambio
cambio = ((valor - valor_anterior) / valor_anterior) * 100
# Determinar color
color = '#43A047' if cambio > 0 else '#E53935'
flecha = '↑' if cambio > 0 else '↓'
fig = go.Figure(go.Indicator(
mode = "number+delta",
value = valor,
title = {'text': nombre, 'font': {'size': 24}},
delta = {
'reference': valor_anterior,
'relative': True,
'valueformat': '.2%',
'font': {'size': 20}
},
number = {'prefix': prefijo, 'suffix': sufijo, 'font': {'size': 48}}
))
fig.update_layout(height=200)
return fig
# Uso
fig = crear_kpi_card(
valor=125430,
valor_anterior=108900,
nombre="Ingresos Mensuales",
prefijo="$"
)
2. Line Charts (Gráficos de Línea)
Cuándo usar:
- Mostrar tendencias a lo largo del tiempo
- Comparar múltiples series temporales
- Identificar patrones estacionales
Best practices:
- Máximo 5 líneas para mantener legibilidad
- Usar colores distintivos
- Destacar la línea más importante
- Incluir marcadores en puntos de datos si hay pocos puntos
import plotly.express as px
import pandas as pd
# Datos de ejemplo
df = pd.DataFrame({
'fecha': pd.date_range('2025-01-01', periods=30),
'ventas': [100 + i*2 + (i%7)*10 for i in range(30)],
'meta': [120] * 30
})
fig = px.line(
df,
x='fecha',
y=['ventas', 'meta'],
labels={'value': 'Monto ($)', 'fecha': 'Fecha'},
title='Ventas Diarias vs. Meta'
)
# Personalizar
fig.update_traces(
line=dict(width=3),
selector=dict(name='ventas')
)
fig.update_layout(
hovermode='x unified',
showlegend=True,
legend=dict(orientation='h', yanchor='bottom', y=1.02)
)
3. Bar Charts (Gráficos de Barras)
Cuándo usar:
- Comparar valores entre categorías
- Rankings y top performers
- Distribuciones discretas
Orientación:
- Horizontal: Mejor para muchas categorías o nombres largos
- Vertical: Mejor para pocas categorías o series temporales
Evitar:
- Gráficos de barras 3D (distorsionan percepción)
- Barras apiladas con muchas categorías
- Escalas que no empiezan en cero (engañoso)
import plotly.graph_objects as go
# Top 10 productos
productos = ['Producto A', 'Producto B', 'Producto C', 'Producto D', 'Producto E']
ventas = [45000, 38000, 32000, 28000, 25000]
fig = go.Figure(go.Bar(
x=ventas,
y=productos,
orientation='h',
marker=dict(
color=ventas,
colorscale='Blues',
showscale=False
),
text=[f'${v:,.0f}' for v in ventas],
textposition='outside'
))
fig.update_layout(
title='Top 5 Productos por Ventas',
xaxis_title='Ventas ($)',
height=400,
margin=dict(l=150) # Espacio para nombres de productos
)
4. Gauge Charts (Indicadores Circulares)
Cuándo usar:
- Mostrar progreso hacia una meta
- Indicar nivel de un KPI crítico
- Estados con umbrales definidos
Mejores prácticas:
- Define claramente zonas (rojo, amarillo, verde)
- Usa con moderación (ocupan mucho espacio)
- Ideal para dashboards ejecutivos
def crear_gauge(valor, maximo, titulo, umbrales):
fig = go.Figure(go.Indicator(
mode = "gauge+number+delta",
value = valor,
title = {'text': titulo},
delta = {'reference': umbrales['meta']},
gauge = {
'axis': {'range': [None, maximo]},
'bar': {'color': "darkblue"},
'steps': [
{'range': [0, umbrales['critico']], 'color': "#FFEBEE"},
{'range': [umbrales['critico'], umbrales['aceptable']], 'color': "#FFF3E0"},
{'range': [umbrales['aceptable'], maximo], 'color': "#E8F5E9"}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': umbrales['meta']
}
}
))
return fig
# Uso
fig = crear_gauge(
valor=78,
maximo=100,
titulo="Customer Satisfaction (%)",
umbrales={'critico': 50, 'aceptable': 70, 'meta': 80}
)
5. Heatmaps (Mapas de Calor)
Cuándo usar:
- Mostrar patrones en matrices de datos
- Análisis de correlaciones
- Distribución temporal (día/hora)
- Comparaciones multidimensionales
import plotly.express as px
import numpy as np
# Datos: ventas por hora y día de la semana
dias = ['Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb', 'Dom']
horas = [f'{h:02d}:00' for h in range(8, 22)]
# Generar datos de ejemplo
np.random.seed(42)
ventas_matrix = np.random.randint(50, 200, size=(len(horas), len(dias)))
# Heatmap
fig = px.imshow(
ventas_matrix,
labels=dict(x="Día", y="Hora", color="Ventas"),
x=dias,
y=horas,
color_continuous_scale='YlOrRd',
aspect='auto'
)
fig.update_layout(
title='Ventas por Día y Hora',
xaxis_title='Día de la Semana',
yaxis_title='Hora del Día'
)
6. Sparklines (Líneas Compactas)
Cuándo usar:
- Mostrar tendencia sin detalles
- Complementar KPI cards
- Ahorrar espacio en tablas
Características:
- Sin ejes ni etiquetas
- Miniatura de tendencia
- Contexto rápido
import plotly.graph_objects as go
def crear_sparkline(datos, color='#1E88E5'):
fig = go.Figure()
fig.add_trace(go.Scatter(
y=datos,
mode='lines',
line=dict(color=color, width=2),
fill='tozeroy',
fillcolor=f'rgba(30, 136, 229, 0.1)'
))
fig.update_layout(
showlegend=False,
height=50,
margin=dict(l=0, r=0, t=0, b=0),
xaxis=dict(visible=False),
yaxis=dict(visible=False),
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)'
)
return fig
# Uso: agregar a una tabla o junto a KPI
tendencia_ventas = [100, 105, 103, 110, 115, 112, 120]
sparkline = crear_sparkline(tendencia_ventas)
Matriz de Selección de Visualización
| Objetivo | Visualización Recomendada |
|---|---|
| Comparar categorías | Barras horizontales |
| Mostrar tendencia temporal | Línea |
| Distribución | Histograma |
| Parte de un todo | Donut (mejor que pie) |
| Relación entre 2 variables | Scatter plot |
| Múltiples dimensiones | Heatmap |
| Progreso hacia meta | Gauge o barra de progreso |
| Valor único destacado | KPI Card |
| Distribución geográfica | Mapa coropletico |
| Cambio a lo largo del tiempo | Línea o área |
Errores Comunes y Cómo Evitarlos
Error 1: Dashboard Sobrecargado
Problema:
- Demasiadas métricas en una sola vista
- Imposible identificar lo importante
- Parálisis de análisis
Solución:
# Principio de priorización
def priorizar_metricas(metricas, limite=5):
"""
Selecciona las métricas más importantes basadas en:
1. Impacto en objetivos de negocio
2. Frecuencia de uso
3. Accionabilidad
"""
metricas_priorizadas = sorted(
metricas,
key=lambda m: (m['impacto'] * m['frecuencia_uso'] * m['accionabilidad']),
reverse=True
)
return metricas_priorizadas[:limite]
# Ejemplo de uso
metricas_disponibles = [
{'nombre': 'Revenue', 'impacto': 10, 'frecuencia_uso': 10, 'accionabilidad': 8},
{'nombre': 'Pageviews', 'impacto': 3, 'frecuencia_uso': 5, 'accionabilidad': 2},
{'nombre': 'Conversion Rate', 'impacto': 9, 'frecuencia_uso': 9, 'accionabilidad': 9},
# ... más métricas
]
metricas_dashboard = priorizar_metricas(metricas_disponibles, limite=5)
Regla práctica:
- Dashboard ejecutivo: 5-7 métricas principales
- Dashboard operativo: 8-12 métricas
- Dashboard analítico: Hasta 15 métricas con drill-downs
Error 2: Escalas Engañosas
Problema:
- Ejes que no empiezan en cero
- Escalas diferentes en gráficos comparativos
- Distorsión de tendencias
Ejemplo engañoso:
# ❌ MALO: Eje Y no empieza en cero para exagerar diferencias
import plotly.graph_objects as go
fig = go.Figure(go.Bar(
x=['Producto A', 'Producto B'],
y=[98, 100]
))
fig.update_yaxes(range=[95, 101]) # Hace que 2% parezca enorme diferencia
Solución:
# ✓ BUENO: Empieza en cero o indica claramente el rango
fig = go.Figure(go.Bar(
x=['Producto A', 'Producto B'],
y=[98, 100]
))
fig.update_yaxes(range=[0, 110]) # Muestra proporción real
# O si necesitas zoom, indica claramente:
fig.update_layout(
title='Ventas por Producto (Vista ampliada: 95-101)',
annotations=[{
'text': '⚠️ Escala ajustada para detalle',
'showarrow': False,
'xref': 'paper',
'yref': 'paper',
'x': 0,
'y': 1.1
}]
)
Error 3: Uso Inadecuado del Color
Problemas comunes:
- Demasiados colores sin significado
- Colores que no respetan convenciones
- Falta de accesibilidad para daltónicos
Solución - Sistema de Color Semántico:
SISTEMA_COLOR = {
# Estados
'exitoso': '#43A047', # Verde
'advertencia': '#FB8C00', # Naranja
'error': '#E53935', # Rojo
'informacion': '#1E88E5', # Azul
'neutral': '#757575', # Gris
# Categorías (paleta colorblind-friendly)
'categoria_1': '#0173B2',
'categoria_2': '#DE8F05',
'categoria_3': '#029E73',
'categoria_4': '#CC78BC',
'categoria_5': '#CA9161',
# Gradientes
'gradiente_frio': ['#E3F2FD', '#90CAF9', '#42A5F5', '#1E88E5', '#1565C0'],
'gradiente_caliente': ['#FFEBEE', '#EF9A9A', '#EF5350', '#E53935', '#C62828']
}
def aplicar_color_semantico(valor, tipo='estado'):
"""Aplica color basado en reglas de negocio"""
if tipo == 'estado':
if valor >= 90:
return SISTEMA_COLOR['exitoso']
elif valor >= 70:
return SISTEMA_COLOR['informacion']
elif valor >= 50:
return SISTEMA_COLOR['advertencia']
else:
return SISTEMA_COLOR['error']
return SISTEMA_COLOR['neutral']
Prueba de accesibilidad:
def verificar_contraste(color_fondo, color_texto):
"""
Verifica que el contraste cumpla con WCAG AA (mínimo 4.5:1)
"""
# Simplificado - en producción usa librería como colorspacious
from colorspacious import deltaE
contraste = deltaE(color_fondo, color_texto)
cumple_wcag = contraste >= 4.5
return cumple_wcag, contraste
Error 4: Falta de Contexto
Problema:
- Métricas sin referencia
- Sin comparaciones temporales
- Sin metas o benchmarks
Ejemplo sin contexto:
Ventas: $125,430
Con contexto adecuado:
Ventas: $125,430
vs. mes anterior: +15.3% (↑ $16,700)
vs. mismo mes año anterior: +8.2%
Meta mensual: $130,000 (96.5% completado)
Promedio industria: $105,000 (+19.5% vs. industria)
Implementación:
def agregar_contexto(valor_actual, historico, meta=None):
"""Agrega contexto automático a una métrica"""
contexto = {
'valor_actual': valor_actual,
'comparaciones': {}
}
# vs. período anterior
if len(historico) > 0:
valor_anterior = historico[-1]
cambio = ((valor_actual - valor_anterior) / valor_anterior) * 100
contexto['comparaciones']['periodo_anterior'] = {
'valor': valor_anterior,
'cambio_porcentual': cambio,
'cambio_absoluto': valor_actual - valor_anterior
}
# vs. mismo período año anterior
if len(historico) >= 12:
valor_ano_anterior = historico[-12]
cambio_anual = ((valor_actual - valor_ano_anterior) / valor_ano_anterior) * 100
contexto['comparaciones']['año_anterior'] = {
'valor': valor_ano_anterior,
'cambio_porcentual': cambio_anual
}
# vs. meta
if meta:
progreso = (valor_actual / meta) * 100
contexto['meta'] = {
'valor': meta,
'progreso': progreso,
'restante': meta - valor_actual
}
return contexto
Error 5: Métricas No Accionables
Problema:
- Dashboard muestra problemas pero no sugiere acciones
- Falta de drill-down para investigar
- Sin alertas o notificaciones
Solución - Dashboard Accionable:
class DashboardAccionable:
def __init__(self, datos):
self.datos = datos
self.alertas = []
self.recomendaciones = []
def analizar_y_sugerir(self):
"""Genera alertas y recomendaciones basadas en datos"""
# Ejemplo: Detectar tendencias negativas
if self.detectar_tendencia_negativa('conversion_rate'):
self.alertas.append({
'nivel': 'critico',
'metrica': 'Tasa de Conversión',
'problema': 'Bajada sostenida por 3 días consecutivos',
'impacto': 'Pérdida estimada de $15,000',
'acciones_sugeridas': [
'Revisar cambios recientes en el sitio web',
'Verificar problemas técnicos en checkout',
'Analizar feedback de usuarios',
'Comparar con competencia'
]
})
# Ejemplo: Detectar oportunidades
if self.detectar_pico_trafico('social_media'):
self.recomendaciones.append({
'tipo': 'oportunidad',
'metrica': 'Tráfico de Redes Sociales',
'observacion': 'Pico de 300% en tráfico de Instagram',
'acciones_sugeridas': [
'Aumentar presupuesto de ads en Instagram',
'Publicar contenido adicional',
'Preparar ofertas especiales'
]
})
return {
'alertas': self.alertas,
'recomendaciones': self.recomendaciones
}
def detectar_tendencia_negativa(self, metrica, dias=3):
"""Detecta si una métrica ha bajado consistentemente"""
valores = self.datos[metrica][-dias:]
return all(valores[i] > valores[i+1] for i in range(len(valores)-1))
def detectar_pico_trafico(self, fuente, umbral=2.0):
"""Detecta picos anormales en tráfico"""
valor_actual = self.datos[fuente][-1]
promedio = sum(self.datos[fuente][-8:-1]) / 7
return valor_actual > (promedio * umbral)
Implementación Técnica
Stack Tecnológico Común
Frontend (Visualización)
Plotly Dash:
import dash
from dash import dcc, html, Input, Output
import plotly.express as px
app = dash.Dash(__name__)
app.layout = html.Div([
html.H1("Dashboard en Tiempo Real"),
# KPIs principales
html.Div([
html.Div([
html.H3("Ventas Hoy"),
html.H2(id='ventas-hoy'),
html.P(id='comparacion-ayer')
], className='kpi-card'),
html.Div([
html.H3("Conversión"),
html.H2(id='tasa-conversion'),
html.P(id='cambio-conversion')
], className='kpi-card'),
], className='kpi-row'),
# Gráfico principal
dcc.Graph(id='grafico-ventas'),
# Actualización automática cada 30 segundos
dcc.Interval(
id='interval-component',
interval=30*1000, # en milisegundos
n_intervals=0
)
])
@app.callback(
[Output('ventas-hoy', 'children'),
Output('comparacion-ayer', 'children'),
Output('grafico-ventas', 'figure')],
Input('interval-component', 'n_intervals')
)
def actualizar_dashboard(n):
# Obtener datos actualizados
datos = obtener_datos_tiempo_real()
ventas_hoy = f"${datos['ventas_hoy']:,.0f}"
comparacion = f"↑ {datos['cambio_vs_ayer']:.1f}% vs. ayer"
# Crear gráfico
fig = px.line(
datos['historico'],
x='hora',
y='ventas',
title='Ventas por Hora'
)
return ventas_hoy, comparacion, fig
if __name__ == '__main__':
app.run_server(debug=True)
Otras opciones:
- Streamlit: Más simple, ideal para prototipos rápidos
- Tableau: Herramienta empresarial potente
- Power BI: Integración con Microsoft ecosystem
- Grafana: Excelente para métricas de sistemas
Backend (Datos en Tiempo Real)
Conexión a Base de Datos:
import psycopg2
from psycopg2.extras import RealDictCursor
import pandas as pd
class ConectorDatos:
def __init__(self, config):
self.config = config
self.conn = None
def conectar(self):
self.conn = psycopg2.connect(**self.config)
def obtener_kpis_tiempo_real(self):
"""Obtiene KPIs principales con una sola query optimizada"""
query = """
WITH ventas_hoy AS (
SELECT
SUM(monto) as total_ventas,
COUNT(DISTINCT pedido_id) as num_pedidos,
AVG(monto) as ticket_promedio
FROM pedidos
WHERE DATE(fecha) = CURRENT_DATE
),
ventas_ayer AS (
SELECT SUM(monto) as total_ventas_ayer
FROM pedidos
WHERE DATE(fecha) = CURRENT_DATE - INTERVAL '1 day'
)
SELECT
vh.total_ventas,
vh.num_pedidos,
vh.ticket_promedio,
((vh.total_ventas - va.total_ventas_ayer) / va.total_ventas_ayer * 100) as cambio_porcentual
FROM ventas_hoy vh
CROSS JOIN ventas_ayer va;
"""
df = pd.read_sql_query(query, self.conn)
return df.to_dict('records')[0]
def obtener_serie_temporal(self, metrica, intervalo='hour', periodo=24):
"""Obtiene datos para gráficos temporales"""
query = f"""
SELECT
DATE_TRUNC('{intervalo}', fecha) as timestamp,
SUM({metrica}) as valor
FROM pedidos
WHERE fecha >= NOW() - INTERVAL '{periodo} {intervalo}s'
GROUP BY timestamp
ORDER BY timestamp;
"""
return pd.read_sql_query(query, self.conn)
WebSocket para Actualizaciones Push:
import asyncio
import websockets
import json
async def enviar_actualizaciones(websocket, path):
"""Envía actualizaciones en tiempo real a clientes conectados"""
while True:
# Obtener datos actuales
datos = obtener_datos_tiempo_real()
# Enviar a cliente
await websocket.send(json.dumps(datos))
# Esperar antes de siguiente actualización
await asyncio.sleep(5) # cada 5 segundos
# Iniciar servidor WebSocket
start_server = websockets.serve(enviar_actualizaciones, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Optimización de Performance
Caché de Datos:
from functools import lru_cache
import time
class CacheDatos:
def __init__(self, ttl=60):
self.ttl = ttl # Time To Live en segundos
self.cache = {}
def get(self, key):
if key in self.cache:
valor, timestamp = self.cache[key]
if time.time() - timestamp < self.ttl:
return valor
return None
def set(self, key, valor):
self.cache[key] = (valor, time.time())
# Uso
cache = CacheDatos(ttl=30) # Cache de 30 segundos
def obtener_datos_dashboard():
# Intentar obtener de cache
datos = cache.get('kpis_principales')
if datos is None:
# Si no está en cache, consultar base de datos
datos = consultar_base_datos()
cache.set('kpis_principales', datos)
return datos
Agregación Pre-calculada:
# Crear tabla de agregados actualizada cada minuto
CREATE MATERIALIZED VIEW mv_kpis_tiempo_real AS
SELECT
DATE_TRUNC('minute', fecha) as minuto,
SUM(monto) as ventas,
COUNT(*) as num_transacciones,
AVG(monto) as ticket_promedio
FROM pedidos
WHERE fecha >= NOW() - INTERVAL '24 hours'
GROUP BY minuto;
# Refrescar automáticamente
CREATE OR REPLACE FUNCTION refresh_mv_kpis()
RETURNS void AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_kpis_tiempo_real;
END;
$$ LANGUAGE plpgsql;
# Ejecutar cada minuto con cron job o scheduler
Checklist de Dashboard Efectivo
Usa esta checklist antes de lanzar tu dashboard:
Diseño Visual
- Los KPIs más importantes están en la parte superior izquierda
- Se puede entender el estado general en 5 segundos
- No hay más de 7 métricas principales visibles
- Los colores tienen significado consistente
- La tipografía es legible en diferentes dispositivos
- No hay elementos decorativos sin propósito
Contenido
- Cada métrica tiene contexto (comparaciones, metas)
- Las visualizaciones son apropiadas para el tipo de dato
- Los ejes y escalas son correctos y no engañosos
- Hay definiciones claras de cómo se calculan las métricas
- Las unidades están claramente indicadas ($, %, etc.)
Funcionalidad
- Los datos se actualizan automáticamente
- Hay indicadores de última actualización
- Los filtros son intuitivos y útiles
- Existe funcionalidad de drill-down donde es necesario
- El dashboard es responsive (funciona en móvil)
- Los tiempos de carga son aceptables (< 3 segundos)
Accionabilidad
- Hay alertas para situaciones críticas
- Se sugieren acciones basadas en los datos
- Es claro quién debe actuar sobre cada métrica
- Hay enlaces a herramientas relacionadas si es necesario
Técnico
- Los datos son precisos y confiables
- Hay manejo de errores (qué pasa si datos no disponibles)
- El sistema puede manejar la carga esperada
- Hay monitoreo del dashboard mismo
- Los permisos de acceso están correctamente configurados
Próximos Pasos
Para crear dashboards de clase mundial:
Practica con tus propios datos:
- Empieza con un dashboard simple de 3-5 métricas
- Itera basándote en feedback de usuarios
- Incrementa complejidad gradualmente
Estudia dashboards exitosos:
- Google Analytics
- Stripe Dashboard
- Mixpanel
- Datadog
Aprende visualización de datos:
- Lee "The Visual Display of Quantitative Information" de Tufte
- Explora ejemplos en Observable HQ
- Practica con datasets públicos
Domina herramientas:
- Profundiza en Plotly/Dash o Streamlit
- Aprende SQL avanzado para queries eficientes
- Explora herramientas de BI empresariales
Profundiza en temas avanzados:
- Dashboards predictivos con ML
- Visualizaciones interactivas avanzadas
- Optimización de performance a escala
- A/B testing de diseños de dashboard
Siguiente lectura recomendada: "Machine Learning aplicado al Marketing Digital" para aprender cómo incorporar predicciones y recomendaciones inteligentes en tus dashboards.
Un dashboard efectivo es una herramienta viva que evoluciona con las necesidades del negocio. Revísalo y mejóralo continuamente basándote en cómo realmente lo usan tus stakeholders.