Visualizando Series Temporales Interactivas con Bokeh en Python: El Clima de tu Ciudad
Este tutorial te guiará paso a paso en la creación de visualizaciones interactivas de series temporales utilizando la biblioteca Bokeh en Python. Aprenderás a cargar datos, preprocesarlos y diseñar gráficos dinámicos para explorar patrones y tendencias en datos climáticos reales.
¡Bienvenido a este tutorial sobre visualización interactiva de series temporales con Bokeh! 📈 En el mundo de la Ciencia de Datos, comprender la evolución de los fenómenos a lo largo del tiempo es crucial. Las series temporales nos permiten observar patrones, tendencias, estacionalidades y anomalías, proporcionando una visión profunda sobre el comportamiento de sistemas complejos.
En esta guía, nos centraremos en los datos climáticos, un ejemplo perfecto de series temporales, para ilustrar cómo Bokeh puede transformar datos estáticos en experiencias interactivas y explorables. Prepárate para darle vida a tus datos con herramientas poderosas y visualizaciones impactantes.
🎯 ¿Por qué Bokeh para Series Temporales?
Bokeh es una biblioteca de visualización interactiva para navegadores web modernos. A diferencia de otras bibliotecas más orientadas a gráficos estáticos, Bokeh se especializa en crear visualizaciones ricas e interactivas que pueden ser incrustadas en dashboards, aplicaciones web o simplemente exploradas en un Jupyter Notebook. Su capacidad para manejar grandes conjuntos de datos y su enfoque en la interactividad la hacen ideal para el análisis exploratorio de series temporales.
🛠️ Herramientas Necesarias
Antes de sumergirnos en el código, asegúrate de tener las siguientes herramientas instaladas en tu entorno Python:
- Python 3.x: La base de nuestro proyecto.
- Jupyter Notebook o JupyterLab: Para ejecutar el código de forma interactiva.
- Pandas: Para la manipulación y preprocesamiento de datos.
- Bokeh: La estrella de nuestro show para la visualización.
Si aún no las tienes, puedes instalarlas fácilmente usando pip:
pip install pandas bokeh jupyterlab
📊 Preparación de los Datos: El Clima de tu Ciudad
Para este tutorial, utilizaremos un conjunto de datos simulado de temperaturas y precipitaciones diarias de una ciudad imaginaria. El objetivo es simular la estructura de datos que encontrarías con datos reales de estaciones meteorológicas.
💾 Carga y Exploración Inicial de Datos
Primero, vamos a crear un DataFrame de Pandas con algunos datos de ejemplo. En un escenario real, cargarías un archivo CSV o de otro formato.
import pandas as pd
import numpy as np
# Generar datos de ejemplo
dates = pd.date_range(start='2022-01-01', end='2023-12-31', freq='D')
temps_mean = 15 + 10 * np.sin(np.linspace(0, 3 * np.pi, len(dates))) + np.random.randn(len(dates)) * 3
temps_min = temps_mean - np.random.rand(len(dates)) * 5 - 2
temps_max = temps_mean + np.random.rand(len(dates)) * 5 + 2
precip = np.random.rand(len(dates)) * 10
precip[precip < 2] = 0 # Muchos días sin lluvia
data = pd.DataFrame({
'date': dates,
'temp_min': temps_min,
'temp_max': temps_max,
'temp_avg': temps_mean,
'precipitation': precip
})
# Asegurar que 'date' es un tipo de dato datetime
data['date'] = pd.to_datetime(data['date'])
print(data.head())
print(data.info())
Observarás la estructura de los datos: una columna de fecha y varias columnas numéricas que representan diferentes métricas climáticas. Es crucial que la columna de fecha sea de tipo datetime para que Bokeh la interprete correctamente.
🧹 Preprocesamiento de Datos
Aunque nuestros datos de ejemplo son bastante limpios, en un escenario real, podrías necesitar:
- Manejar valores faltantes (
NaN). - Convertir unidades (ej. Celsius a Fahrenheit).
- Resamplear los datos (ej. de diario a mensual o anual).
Para este tutorial, nos aseguraremos de que los datos estén listos para Bokeh.
# Establecer la columna 'date' como índice (opcional pero útil para series temporales)
data = data.set_index('date')
# Resampling (ejemplo: promedio mensual de temperatura máxima)
data_monthly = data['temp_max'].resample('M').mean().reset_index()
data_monthly.columns = ['date', 'avg_temp_max_monthly']
print(data_monthly.head())
Este paso de resampling es útil cuando quieres ver tendencias a una escala temporal diferente, como promedios mensuales o anuales.
✨ Creando tu Primera Visualización Interactiva con Bokeh
Ahora que tenemos nuestros datos listos, es hora de usar Bokeh para visualizarlos. Empezaremos con un gráfico de línea simple para la temperatura promedio diaria.
📈 Gráfico de Línea Básico
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, DatetimeTickFormatter
from bokeh.io import output_notebook
# Esto es para mostrar los gráficos directamente en Jupyter Notebook
output_notebook()
# Crear un ColumnDataSource para Bokeh
source = ColumnDataSource(data)
# Crear una nueva figura con un título y ejes adecuados
p = figure(
title="Temperatura Promedio Diaria (2022-2023)",
x_axis_label="Fecha",
y_axis_label="Temperatura (°C)",
x_axis_type="datetime", # Muy importante para series temporales
height=350, width=800,
tools="pan,wheel_zoom,box_zoom,reset,save"
)
# Añadir una línea al gráfico
p.line(x='date', y='temp_avg', source=source, line_width=2, color='blue', legend_label='Temp. Promedio')
# Formatear el eje X para que muestre fechas de manera legible
p.xaxis.formatter = DatetimeTickFormatter(
days=['%Y-%m-%d'],
months=['%Y-%m'],
years=['%Y']
)
# Añadir leyenda interactiva
p.legend.location = "top_left"
p.legend.click_policy="hide"
show(p)
Este código crea un gráfico de línea interactivo. Observa el parámetro x_axis_type="datetime" en figure(), que es crucial para que Bokeh interprete las fechas correctamente. Los tools permiten interactuar con el gráfico (zoom, paneo, etc.).
🎨 Personalizando y Añadiendo Interactividad Avanzada
Vamos a hacer nuestro gráfico más interesante añadiendo rangos de temperatura (mínima y máxima) y tooltips para mostrar información detallada al pasar el ratón.
from bokeh.models import HoverTool
# Crear una nueva figura
p_advanced = figure(
title="Temperaturas Diarias (Mín., Máx., Promedio)",
x_axis_label="Fecha",
y_axis_label="Temperatura (°C)",
x_axis_type="datetime",
height=400, width=900,
tools="pan,wheel_zoom,box_zoom,reset,save"
)
# Añadir líneas para temp_min, temp_max y temp_avg
p_advanced.line(x='date', y='temp_min', source=source, line_width=1, color='lightgreen', legend_label='Temp. Mínima')
p_advanced.line(x='date', y='temp_max', source=source, line_width=1, color='salmon', legend_label='Temp. Máxima')
p_advanced.line(x='date', y='temp_avg', source=source, line_width=2, color='blue', legend_label='Temp. Promedio')
# Añadir un HoverTool para mostrar detalles al pasar el ratón
hover = HoverTool(
tooltips=[
("Fecha", "@date{%Y-%m-%d}"),
("Temp. Mínima", "@temp_min{0.00} °C"),
("Temp. Máxima", "@temp_max{0.00} °C"),
("Temp. Promedio", "@temp_avg{0.00} °C")
],
formatters={'@date': 'datetime'} # Formatear la fecha en el tooltip
)
p_advanced.add_tools(hover)
# Formatear el eje X
p_advanced.xaxis.formatter = DatetimeTickFormatter(
days=['%Y-%m-%d'],
months=['%Y-%m'],
years=['%Y']
)
# Configurar leyenda
p_advanced.legend.location = "top_left"
p_advanced.legend.click_policy="hide"
show(p_advanced)
El HoverTool es una característica poderosa de Bokeh que permite a los usuarios obtener información contextual al interactuar con el gráfico. Hemos definido los tooltips para mostrar la fecha y las diferentes temperaturas con un formato específico.
🌧️ Añadiendo Datos de Precipitación: Ejes Duales
A menudo, querrás visualizar diferentes tipos de datos que tienen escalas muy distintas en el mismo gráfico. Para esto, Bokeh permite usar ejes Y secundarios.
📊 Gráfico con Ejes Y Secundarios
Vamos a añadir la precipitación a nuestro gráfico de temperatura. Dado que las escalas son muy diferentes (temperatura en grados Celsius, precipitación en milímetros), usaremos un eje Y a la derecha para la precipitación.
from bokeh.models import LinearAxis, Range1d
# Crear una figura principal
p_dual_axis = figure(
title="Temperatura y Precipitación Diaria (2022-2023)",
x_axis_label="Fecha",
y_axis_label="Temperatura (°C)",
x_axis_type="datetime",
height=450, width=1000,
tools="pan,wheel_zoom,box_zoom,reset,save"
)
# Configurar el rango del eje Y izquierdo (temperatura)
p_dual_axis.y_range = Range1d(start=data['temp_min'].min() - 5, end=data['temp_max'].max() + 5)
# Añadir la línea de temperatura promedio
p_dual_axis.line(x='date', y='temp_avg', source=source, line_width=2, color='blue', legend_label='Temp. Promedio')
# Crear un segundo eje Y para la precipitación
p_dual_axis.extra_y_ranges = {"precip_range": Range1d(start=0, end=data['precipitation'].max() * 1.2)}
p_dual_axis.add_layout(LinearAxis(y_range_name="precip_range", axis_label="Precipitación (mm)"), 'right')
# Añadir un gráfico de barras para la precipitación en el segundo eje Y
p_dual_axis.vbar(
x='date', top='precipitation', source=source,
width=pd.Timedelta(days=0.9), # Ancho de la barra
color='grey', alpha=0.4, legend_label='Precipitación',
y_range_name="precip_range" # Asignar al eje Y secundario
)
# Añadir HoverTool (adaptado para ambos tipos de datos)
hover_dual = HoverTool(
tooltips=[
("Fecha", "@date{%Y-%m-%d}"),
("Temp. Promedio", "@temp_avg{0.00} °C"),
("Precipitación", "@precipitation{0.00} mm")
],
formatters={'@date': 'datetime'}
)
p_dual_axis.add_tools(hover_dual)
# Formatear el eje X
p_dual_axis.xaxis.formatter = DatetimeTickFormatter(
days=['%Y-%m-%d'],
months=['%Y-%m'],
years=['%Y']
)
# Configurar leyenda
p_dual_axis.legend.location = "top_left"
p_dual_axis.legend.click_policy="hide"
show(p_dual_axis)
Aquí, definimos un extra_y_ranges con un nombre ("precip_range") y luego creamos un LinearAxis que se asocia a ese rango y se añade a la derecha del gráfico. La clave es usar y_range_name="precip_range" al dibujar el glifo de precipitación (vbar).
🗓️ Agregación y Comparación de Periodos
Una técnica común en el análisis de series temporales es agregar datos para ver tendencias a largo plazo o comparar diferentes periodos (ej. año a año, mes a mes).
📈 Gráfico de Temperaturas Mensuales Promedio por Año
Vamos a calcular la temperatura promedio mensual y luego graficar varios años en un solo gráfico para ver la estacionalidad y las diferencias interanuales.
# Calcular la temperatura promedio mensual
data['month'] = data.index.month
data['year'] = data.index.year
monthly_avg_temp = data.groupby(['year', 'month'])['temp_avg'].mean().reset_index()
# Pivotear los datos para tener los años como columnas y los meses como filas
pivoted_data = monthly_avg_temp.pivot(index='month', columns='year', values='temp_avg')
# Crear un ColumnDataSource para los datos pivoteados
source_pivoted = ColumnDataSource(pivoted_data.reset_index())
# Lista de colores para los años
from bokeh.palettes import Category10
colors = Category10[len(pivoted_data.columns) - 1]
# Crear la figura
p_comparison = figure(
title="Temperatura Promedio Mensual por Año",
x_axis_label="Mes",
y_axis_label="Temperatura Promedio (°C)",
height=400, width=700,
x_range=[str(i) for i in range(1, 13)], # Rango de meses como strings
tools="pan,wheel_zoom,box_zoom,reset,save"
)
# Añadir una línea para cada año
for i, year in enumerate(pivoted_data.columns):
if year != 'month': # Evitar la columna 'month' si está ahí
p_comparison.line(
x='month', y=str(year), source=source_pivoted,
line_width=2, color=colors[i], legend_label=str(year)
)
# Configurar el eje X para mostrar nombres de meses (opcional)
month_names = ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic']
p_comparison.xaxis.ticker = list(range(1, 13))
p_comparison.xaxis.major_label_overrides = {i: name for i, name in enumerate(month_names, 1)}
p_comparison.legend.location = "top_left"
p_comparison.legend.click_policy="hide"
show(p_comparison)
En este ejemplo, primero agregamos los datos por mes y año. Luego, usamos pivot para reorganizar el DataFrame, lo que facilita graficar cada año como una línea separada. Esto es excelente para identificar patrones estacionales y comparar el comportamiento de la temperatura entre diferentes años.
¿Por qué el uso de `str(i)` y `str(year)` en el bucle?
Al pivotear el DataFrame, los nombres de las columnas de los años (`2022`, `2023`, etc.) se convierten en enteros. Sin embargo, para usarlos como identificadores de columnas en el `ColumnDataSource` de Bokeh, es más seguro convertirlos a cadenas (`str`) para evitar posibles conflictos o interpretaciones erróneas.🚀 Creando un Dashboard Interactivo Simple
Bokeh no solo permite crear gráficos individuales, sino también combinarlos en diseños complejos y dashboards interactivos. Aquí, mostraremos cómo combinar nuestros gráficos en un diseño simple.
🖼️ Combinando Gráficos con gridplot
Podemos usar gridplot para organizar múltiples gráficos en una cuadrícula. Esto es útil para presentar diferentes aspectos de los datos en un solo vistazo.
from bokeh.layouts import gridplot
# Reutilizamos los gráficos p_advanced y p_dual_axis creados anteriormente.
# Si los ejecutaste, ya están definidos en tu sesión.
# Crear una cuadrícula de gráficos
# Puedes ajustar las dimensiones de cada gráfico si es necesario
dashboard_layout = gridplot([[p_advanced], [p_dual_axis]], width=900, height=450)
# Mostrar el dashboard
show(dashboard_layout)
Este código simple organiza los dos gráficos interactivos que hemos creado en una disposición vertical. Puedes experimentar con diferentes diseños de gridplot (ej. [[p_advanced, p_dual_axis]] para una disposición horizontal, o [[p1, p2], [p3, p4]] para una cuadrícula 2x2).
✅ Conclusión y Próximos Pasos
¡Felicidades! 🎉 Has aprendido los fundamentos para crear visualizaciones interactivas de series temporales con Bokeh en Python. Hemos cubierto desde la preparación de datos hasta la creación de gráficos de línea avanzados con tooltips, el uso de ejes duales y la combinación de gráficos en un dashboard simple.
La visualización de series temporales es una habilidad invaluable en Ciencia de Datos, permitiéndote desentrañar patrones ocultos y comunicar tus hallazgos de manera efectiva. Sigue explorando las capacidades de Bokeh, ¡es una herramienta muy versátil!
🚀 Ideas para Seguir Explorando:
- Añadir Widgets: Incorpora sliders, selectores o botones de Bokeh para permitir a los usuarios filtrar o agregar datos de forma dinámica. Por ejemplo, un slider para seleccionar un rango de fechas específico.
- Conectar Gráficos: Haz que un gráfico filtre o actualice otro gráfico, creando una experiencia de usuario aún más inmersiva.
- Integración con Bokeh Server: Aprende a construir aplicaciones web de datos interactivas con Bokeh Server para compartir tus visualizaciones con otros.
- Más Tipos de Glifos: Experimenta con otros glifos de Bokeh como
area,scatteroquadpara representar diferentes aspectos de tus series temporales. - Datos Reales: Descarga datos climáticos reales (ej. de NOAA, Kaggle o APIs meteorológicas) y aplica las técnicas aprendidas.
# Ejemplo de cómo se vería un slider simple (requiere Bokeh Server para interactividad completa en un entorno web,
# pero puedes definirlo para un Jupyter notebook con `interact` de ipywidgets si se instala ipywidgets)
# from bokeh.models import CustomJS, Slider
# from bokeh.layouts import column
# start_date_slider = Slider(start=data.index.min().timestamp() * 1000,
# end=data.index.max().timestamp() * 1000,
# value=data.index.min().timestamp() * 1000,
# step=pd.Timedelta(days=1).total_seconds() * 1000,
# title="Fecha de Inicio")
# layout = column(start_date_slider, p_advanced)
# show(layout)
Esperamos que este tutorial te haya proporcionado una base sólida para comenzar tu viaje en la visualización interactiva de series temporales con Bokeh. ¡Ahora es tu turno de experimentar y crear tus propias historias con datos! 🌟
Tutoriales relacionados
- Explorando la Biodiversidad con Heatmaps en Python: Mapas de Calor para Datos Biológicosintermediate15 min
- Visualización Interactiva de Datos con Plotly Express en Python: ¡Crea Dashboards Asombrosos!intermediate18 min
- Desentrañando Historias con Gráficos de Red: Un Enfoque Práctico en Pythonintermediate25 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!