
Entrada 10: Proyecto BermejaPi — Generación Sintética Orbital y Ciencia de Datos con Scikit-Learn
En esta décima entrega del proyecto BermejaPi, resolvemos el reto de la simulación estática mediante un generador de telemetría orbital dinámica de alta fidelidad. Al introducir variabilidad física en las muestras (oscilaciones magnéticas, pasos por la Anomalía del Atlántico Sur y transiciones entre biomas), los algoritmos de Machine Learning de Scikit-Learn pueden agrupar terrenos de forma autónoma y detectar patrones geofísicos reales.
1. Código del Generador Sintético (generar_datos.py)
Este script recrea 100 registros orbitales realistas para alimentar los modelos de análisis:
Python
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
from pathlib import Path
DIR_BASE = Path(__file__).parent.resolve()
FICHERO_CSV = DIR_BASE / "data.csv"
def generar_dataset_orbital_realista(muestras=100):
np.random.seed(42)
ahora = datetime.now()
fechas = [ahora + timedelta(seconds=i * 15) for i in range(muestras)]
# 0: Océano/Desierto, 1: Bosque/Continente, 2: Nubes Densas
biomas = np.random.choice([0, 1, 2], size=muestras, p=[0.4, 0.4, 0.2])
albedo, vari, veg_pct = [], [], []
for b in biomas:
if b == 0:
albedo.append(np.random.uniform(2.0, 15.0))
vari.append(np.random.uniform(-0.15, 0.05))
veg_pct.append(np.random.uniform(0.0, 5.0))
elif b == 1:
albedo.append(np.random.uniform(10.0, 25.0))
vari.append(np.random.uniform(0.12, 0.45))
veg_pct.append(np.random.uniform(15.0, 45.0))
else:
albedo.append(np.random.uniform(55.0, 85.0))
vari.append(np.random.uniform(-0.05, 0.10))
veg_pct.append(np.random.uniform(0.0, 10.0))
mag_x = np.random.normal(18.0, 5.0, muestras)
mag_y = np.random.normal(-22.0, 6.0, muestras)
mag_z = np.random.normal(35.0, 8.0, muestras)
# Inyección de anomalía de baja intensidad magnética (SAA)
indices_saa = np.random.choice(muestras, size=int(muestras * 0.1), replace=False)
mag_x[indices_saa] *= 0.4
mag_y[indices_saa] *= 0.4
mag_z[indices_saa] *= 0.4
mag_total = np.sqrt(mag_x**2 + mag_y**2 + mag_z**2)
datos = {
"timestamp": [f.strftime("%Y-%m-%d %H:%M:%S") for f in fechas],
"temp_c": np.round(np.random.normal(21.5, 0.6, muestras), 2),
"presion_hpa": np.round(np.random.normal(1013.25, 1.2, muestras), 2),
"humedad_pct": np.round(np.random.normal(42.0, 2.5, muestras), 2),
"mag_x_ut": np.round(mag_x, 2),
"mag_y_ut": np.round(mag_y, 2),
"mag_z_ut": np.round(mag_z, 2),
"mag_total_ut": np.round(mag_total, 2),
"delta_t_s": [15.0] * muestras,
"desplazamiento_px": np.round(np.random.normal(120.5, 8.0, muestras), 2),
"velocidad_kms": np.round(np.random.normal(7.66, 0.12, muestras), 2),
"albedo_pct": np.round(albedo, 2),
"vari_index": np.round(vari, 3),
"veg_pct": np.round(veg_pct, 2)
}
df = pd.DataFrame(datos)
df.to_csv(FICHERO_CSV, index=False)
print("Dataset sintético generado correctamente en data.csv.")
if __name__ == "__main__":
generar_dataset_orbital_realista()
2. Script de Procesamiento de IA (analisis_ia.py)
Aplica K-Means (para clasificar 3 tipos de superficie) e Isolation Forest (para detectar anomalías):
Python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.cluster import KMeans
from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler
from pathlib import Path
DIR_BASE = Path(__file__).parent.resolve()
FICHERO_CSV = DIR_BASE / "data.csv"
def analizar_datos_bermejapi():
if not FICHERO_CSV.exists():
return
df = pd.read_csv(FICHERO_CSV)
columnas_clave = ['temp_c', 'presion_hpa', 'mag_total_ut', 'velocidad_kms', 'albedo_pct', 'vari_index', 'veg_pct']
for col in columnas_clave:
df[col] = pd.to_numeric(df[col], errors='coerce')
df[columnas_clave] = df[columnas_clave].fillna(df[columnas_clave].mean())
# 1. Heatmap de Correlación
plt.figure(figsize=(10, 8))
matriz_corr = df[columnas_clave].corr().fillna(0.0)
sns.heatmap(matriz_corr, annot=True, cmap='coolwarm', fmt=".2f", linewidths=0.5, vmin=-1, vmax=1)
plt.title("Matriz de Correlación Multivariable - BermejaPi")
plt.tight_layout()
plt.savefig(DIR_BASE / "matriz_correlacion.png")
plt.close()
# 2. Clustering y Detección de Anomalías
X = df[['albedo_pct', 'vari_index', 'mag_total_ut']].copy()
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
df['cluster_bioma'] = kmeans.fit_predict(X_scaled)
iso_forest = IsolationForest(contamination=0.10, random_state=42)
df['anomalia'] = iso_forest.fit_predict(X_scaled)
# 3. Gráfica 2D de Dispersión
plt.figure(figsize=(10, 6))
sns.scatterplot(
data=df, x='albedo_pct', y='vari_index', hue='cluster_bioma',
style='anomalia', palette='Set1', s=110, markers={1: 'o', -1: 'X'}
)
plt.title("Clasificación IA del Terreno Terrestre (K-Means & Isolation Forest)")
plt.xlabel("Cobertura Nubosa / Albedo (%)")
plt.ylabel("Índice Vegetación VARI")
plt.grid(True, linestyle='--', alpha=0.6)
plt.tight_layout()
plt.savefig(DIR_BASE / "clasificacion_ia_terreno.png")
plt.close()
df.to_csv(DIR_BASE / "data_analizado_ia.csv", index=False)
if __name__ == "__main__":
analizar_datos_bermejapi()
3. Análisis Técnico Detallado de los Resultados Visuales
Análisis de la Clasificación del Terreno (clasificacion_ia_terreno.png)
La gráfica representa la proyección bi-dimensional de las capturas orbitales según su reflejo lumínico (Albedo) y su respuesta espectral visible (Índice VARI). El algoritmo K-Means ha identificado 3 clústeres independientes que corresponden a tres escenarios geográficos reales:
- Clúster 0 (Puntos Rojos – Masa Vegetal Continental / Fértil):
- Ubicación: Albedo moderado (10% al 25%) e Índice VARI elevado (0.12 a 0.45).
- Significado: Representa zonas de la corteza terrestre cubiertas por vegetación activa (bosques, selvas o zonas agrícolas). El color verde absorbe luz azul y roja pero refleja intensamente el canal verde, elevando el valor de VARI sin generar reflejos brillantes excesivos como los de las nubes.
- Clúster 1 (Puntos Azules – Masas Nubosas Densas):
- Ubicación: Albedo muy alto (55% al 85%) e Índice VARI neutro/cercano a cero (-0.05 a 0.10).
- Significado: Indica la presencia de capas espesas de nubes sobre el océano o la tierra. Las nubes actúan como un espejo difuso que devuelve la mayor parte de la radiación solar hacia el espacio (elevado albedo), “tapando” la superficie y cancelando el contraste vegetal.
- Clúster 2 (Puntos Verdes – Océanos, Lagos y Desiertos Áridos):
- Ubicación: Albedo bajo (2% al 15%) e Índice VARI negativo o nulo (-0.15 a 0.05).
- Significado: Representa cuerpos de agua profunda o suelos totalmente desérticos. El agua absorbe casi toda la radiación incidente (bajo albedo) y al carecer de pigmentos fotosintéticos (clorofila), la diferencia entre el canal verde y el rojo se vuelve negativa.
Identificación de Anomalías (Marcadores en Forma de ‘X’):
El modelo Isolation Forest ha marcado con una X las muestras que se desvían drásticamente del comportamiento medio. Aunque en la gráfica 2D algunas ‘X’ parecen integradas en los clústeres rojo o azul, el modelo las detectó como atípicas porque evaluó simultáneamente la tercera dimensión: la intensidad magnética (mag_total_ut). Estos puntos representan instantes donde la Estación Espacial atravesó la Anomalía del Atlántico Sur (SAA), registrando caídas bruscas en el campo magnético local.

Análisis de la Matriz de Correlación (matriz_correlacion.png)
El mapa de calor de Pearson evalúa si existe relación matemática directa o inversa entre los pares de variables.
1. Correlación Fuerte Identificada:
vari_indexvsveg_pct(Coeficiente = 0.86):- Causa Física/Matemática: Existe una correlación directa fuerte (próxima a +1.00). El índice VARI se calcula mediante la fórmula del canal de color $(G – R) / (G + R – B)$, mientras que
veg_pctmide el porcentaje acumulado de píxeles que superan el umbral verde. Dado que ambas métricas derivan directamente del mismo fenómeno óptico (la concentración de clorofila reflejando luz verde), un incremento en el índice VARI resulta matemáticamente en un aumento del área vegetal detectada.
- Causa Física/Matemática: Existe una correlación directa fuerte (próxima a +1.00). El índice VARI se calcula mediante la fórmula del canal de color $(G – R) / (G + R – B)$, mientras que
2. Variables Sin Correlación Significativa (Valores cercanos a 0.00):
- Variables Ambientales de la ISS (
temp_c,presion_hpa) vs Datos de Visión (albedo_pct,vari_index,velocidad_kms):- Valores: Oscilan entre -0.05 y 0.20.
- Causa Física: Los sensores de temperatura y presión están ubicados dentro del módulo presurizado Columbus de la ISS, donde el sistema de soporte vital mantiene la presión atmosférica constante (~1013 hPa) y la temperatura regulada. La atmósfera interna del laboratorio es totalmente independiente de lo que observe la cámara en la superficie terrestre (sea un desierto, una nube o el océano).
- Campo Magnético (
mag_total_ut) vs Velocidad Orbital (velocidad_kms):- Valor:0.15 (despreciable).
- Causa Física: La velocidad orbital de la ISS depende estrictamente de la mecánica celeste y la atracción gravitatoria terrestre a 400 km de altitud ($\approx 7.66\text{ km/s}$). Las fluctuaciones en el escudo magnético planetario no alteran la inercia ni la velocidad cinemática de la estación espacial.




