Visualización de datos de un dataset real
Introducción
La visualización de datos es una de las habilidades más valiosas para cualquier programador, especialmente cuando trabajamos con información del mundo real. Transformar números y estadísticas en gráficos comprensibles nos permite identificar patrones, tendencias y anomalías que de otra forma pasarían desapercibidos. En este artículo, aplicaremos las herramientas de visualización de Python para analizar un conjunto de datos real, combinando los conocimientos de bibliotecas como Pandas y Matplotlib para extraer conclusiones significativas.
Preparación del entorno
Antes de comenzar, necesitamos instalar las bibliotecas necesarias:
# Instalación de las bibliotecas (ejecutar solo si no están instaladas)
# pip install pandas matplotlib seaborn
# Importamos las bibliotecas
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
Carga de un dataset real
Empezaremos utilizando un dataset público sobre calidad del aire en España:
# Descargamos el dataset directamente de una URL pública
url = "https://raw.githubusercontent.com/datos-abiertos/calidad-aire-spain/main/datos_simplificados.csv"
# Intentamos cargar el dataset
try:
# Si estás trabajando sin conexión, puedes comentar el código anterior y usar un archivo local
datos = pd.read_csv(url)
# Mostramos las primeras filas para entender su estructura
print("Primeras 5 filas del dataset:")
print(datos.head())
# Información básica del dataset
print("\nInformación del dataset:")
print(datos.info())
# Estadísticas descriptivas
print("\nEstadísticas descriptivas:")
print(datos.describe())
except Exception as e:
print(f"Error al cargar el dataset: {e}")
# En caso de error, creamos un dataset de ejemplo
import numpy as np
# Creamos fechas para un año
fechas = pd.date_range(start='2023-01-01', end='2023-12-31', freq='D')
# Simulamos datos de contaminantes para tres ciudades
np.random.seed(42) # Para reproducibilidad
ciudades = ['Madrid', 'Barcelona', 'Valencia']
datos = pd.DataFrame()
for ciudad in ciudades:
# Simulamos valores de NO2 con variación estacional
no2_base = 20 + 15 * np.sin(np.linspace(0, 2*np.pi, len(fechas)))
no2 = no2_base + np.random.normal(0, 5, len(fechas))
# Simulamos valores de PM10 con tendencia ascendente
pm10_base = np.linspace(15, 25, len(fechas))
pm10 = pm10_base + np.random.normal(0, 3, len(fechas))
# Simulamos valores de O3 con pico en verano
o3_base = 40 + 30 * np.sin(np.linspace(-np.pi/2, 3*np.pi/2, len(fechas)))
o3 = o3_base + np.random.normal(0, 8, len(fechas))
# Creamos un dataframe para esta ciudad
df_ciudad = pd.DataFrame({
'fecha': fechas,
'ciudad': ciudad,
'no2': np.round(no2, 1),
'pm10': np.round(pm10, 1),
'o3': np.round(o3, 1)
})
# Concatenamos con el dataframe principal
datos = pd.concat([datos, df_ciudad])
# Reseteamos el índice
datos.reset_index(drop=True, inplace=True)
print("Se ha creado un dataset de ejemplo sobre calidad del aire en tres ciudades españolas.")
print(datos.head())
Explorando el dataset visualmente
Una vez cargados los datos, podemos comenzar a explorarlos visualmente:
1. Visualización de la distribución de contaminantes
# Configuramos el estilo de los gráficos
sns.set(style="whitegrid")
plt.figure(figsize=(14, 8))
# Creamos subplots para cada contaminante
plt.subplot(1, 3, 1)
sns.histplot(datos['no2'].dropna(), kde=True, color='red')
plt.title('Distribución de NO2')
plt.xlabel('Concentración (µg/m³)')
plt.subplot(1, 3, 2)
sns.histplot(datos['pm10'].dropna(), kde=True, color='brown')
plt.title('Distribución de PM10')
plt.xlabel('Concentración (µg/m³)')
plt.subplot(1, 3, 3)
sns.histplot(datos['o3'].dropna(), kde=True, color='blue')
plt.title('Distribución de O3')
plt.xlabel('Concentración (µg/m³)')
plt.tight_layout()
plt.savefig('distribucion_contaminantes.png') # Guardamos el gráfico
plt.show()
2. Comparación entre ciudades
# Preparamos los datos para visualización por ciudad
plt.figure(figsize=(16, 6))
# Gráfico de barras comparando medias por ciudad
ax = sns.barplot(x='ciudad', y='no2', data=datos, palette='YlOrRd')
plt.title('Niveles medios de NO2 por ciudad')
plt.ylabel('Concentración media (µg/m³)')
plt.xlabel('')
# Añadimos los valores sobre las barras
for p in ax.patches:
ax.annotate(f'{p.get_height():.1f}',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha = 'center', va = 'bottom',
xytext = (0, 5), textcoords = 'offset points')
plt.tight_layout()
plt.savefig('comparacion_ciudades.png')
plt.show()
3. Evolución temporal
# Aseguramos que la fecha está en formato datetime
datos['fecha'] = pd.to_datetime(datos['fecha'])
# Agrupamos por fecha y calculamos la media diaria de todos los contaminantes
datos_diarios = datos.groupby(['fecha', 'ciudad']).mean().reset_index()
# Creamos un gráfico de líneas para cada ciudad mostrando la evolución del NO2
plt.figure(figsize=(15, 7))
for ciudad in datos['ciudad'].unique():
datos_ciudad = datos_diarios[datos_diarios['ciudad'] == ciudad]
plt.plot(datos_ciudad['fecha'], datos_ciudad['no2'], label=ciudad)
plt.title('Evolución de NO2 a lo largo del tiempo')
plt.xlabel('Fecha')
plt.ylabel('Concentración (µg/m³)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('evolucion_temporal.png')
plt.show()
4. Relación entre contaminantes
# Analizamos la correlación entre contaminantes
plt.figure(figsize=(10, 8))
sns.heatmap(datos[['no2', 'pm10', 'o3']].corr(), annot=True, cmap='coolwarm', vmin=-1, vmax=1)
plt.title('Correlación entre contaminantes')
plt.tight_layout()
plt.savefig('correlacion_contaminantes.png')
plt.show()
# Gráfico de dispersión con regresión
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
sns.scatterplot(x='no2', y='pm10', data=datos, hue='ciudad', alpha=0.6)
plt.title('Relación entre NO2 y PM10')
plt.subplot(1, 2, 2)
sns.scatterplot(x='no2', y='o3', data=datos, hue='ciudad', alpha=0.6)
plt.title('Relación entre NO2 y O3')
plt.tight_layout()
plt.savefig('relacion_contaminantes.png')
plt.show()
5. Visualización avanzada: mapas de calor temporales
# Convertimos los datos a un formato adecuado para un mapa de calor
# Seleccionamos una ciudad para este ejemplo
ciudad_ejemplo = datos['ciudad'].unique()[0]
datos_ciudad = datos[datos['ciudad'] == ciudad_ejemplo].copy()
# Creamos una columna para el mes y otra para el día
datos_ciudad['mes'] = datos_ciudad['fecha'].dt.month
datos_ciudad['dia'] = datos_ciudad['fecha'].dt.day
# Pivotamos los datos para crear una matriz de calor
pivot_no2 = datos_ciudad.pivot_table(index='dia', columns='mes', values='no2', aggfunc='mean')
# Creamos el mapa de calor
plt.figure(figsize=(12, 8))
sns.heatmap(pivot_no2, cmap='YlOrRd', annot=False)
plt.title(f'Mapa de calor de NO2 en {ciudad_ejemplo} por día y mes')
plt.xlabel('Mes')
plt.ylabel('Día')
plt.tight_layout()
plt.savefig('mapa_calor_temporal.png')
plt.show()
Creación de un dashboard simple
Finalmente, podemos combinar varios gráficos en un dashboard básico:
# Creamos un dashboard combinando varios gráficos
plt.figure(figsize=(18, 12))
# 1. Gráfico de líneas de evolución temporal (arriba)
plt.subplot(2, 2, 1)
for ciudad in datos['ciudad'].unique():
datos_ciudad = datos_diarios[datos_diarios['ciudad'] == ciudad]
# Agrupamos por mes para mayor claridad
datos_mensuales = datos_ciudad.groupby(datos_ciudad['fecha'].dt.month)['no2'].mean()
plt.plot(datos_mensuales.index, datos_mensuales.values, marker='o', label=ciudad)
plt.title('Evolución mensual de NO2')
plt.xlabel('Mes')
plt.ylabel('Concentración media (µg/m³)')
plt.legend()
plt.grid(True, alpha=0.3)
# 2. Gráfico de barras de comparación entre ciudades (arriba derecha)
plt.subplot(2, 2, 2)
comparacion = datos.groupby('ciudad')[['no2', 'pm10', 'o3']].mean()
comparacion.plot(kind='bar', ax=plt.gca())
plt.title('Comparación de contaminantes por ciudad')
plt.ylabel('Concentración media (µg/m³)')
plt.grid(True, alpha=0.3)
# 3. Mapa de calor de correlaciones (abajo izquierda)
plt.subplot(2, 2, 3)
sns.heatmap(datos[['no2', 'pm10', 'o3']].corr(), annot=True, cmap='coolwarm', vmin=-1, vmax=1)
plt.title('Correlación entre contaminantes')
# 4. Boxplot de distribución por ciudad (abajo derecha)
plt.subplot(2, 2, 4)
sns.boxplot(x='ciudad', y='no2', data=datos)
plt.title('Distribución de NO2 por ciudad')
plt.ylabel('Concentración (µg/m³)')
plt.tight_layout()
plt.savefig('dashboard_calidad_aire.png')
plt.show()
Interpretación de los resultados
Al analizar las visualizaciones generadas, podemos extraer varias conclusiones:
-
Patrones de distribución: Los histogramas nos muestran si la concentración de contaminantes sigue una distribución normal o presenta asimetrías.
-
Comparación geográfica: Las gráficas de barras revelan diferencias entre ciudades, pudiendo identificar cuáles tienen mayores problemas de contaminación.
-
Patrones temporales: Los gráficos de línea y los mapas de calor muestran cómo varían los contaminantes a lo largo del tiempo, revelando patrones estacionales.
-
Correlaciones: El mapa de calor de correlaciones indica qué contaminantes tienden a aumentar juntos, lo que podría sugerir fuentes comunes.
-
Outliers y eventos extremos: Los diagramas de caja (boxplots) ayudan a identificar valores atípicos que podrían corresponder a episodios de contaminación elevada.
Resumen
La visualización de datos aplicada a datasets reales nos permite transformar números en conocimiento accionable. A través de diferentes técnicas gráficas, hemos podido analizar la calidad del aire desde múltiples perspectivas, identificando patrones, comparando zonas geográficas y detectando tendencias temporales. Estas habilidades son fundamentales no solo para el análisis de datos ambientales, sino para cualquier campo donde se trabaje con información cuantitativa. En el siguiente tema, exploraremos cómo integrar el control de versiones con Git en nuestros proyectos de Python, lo que nos permitirá mantener un registro organizado de los cambios en nuestro código y colaborar eficientemente con otros desarrolladores.