Open Data en educación no consiste solo en usar datos públicos dentro del aula. Bien aplicado, permite que el alumnado investigue su entorno, formule preguntas reales y construya evidencias con mapas, visualizaciones y herramientas digitales accesibles.
La educación con sentido geográfico parte de una idea sencilla: el territorio también enseña. Las escuelas, los centros de formación y las universidades pueden convertir barrios, municipios, rutas, servicios públicos y desigualdades locales en escenarios de aprendizaje significativo.
En este artículo se propone una guía práctica para trabajar Open Data en educación con enfoque EdTech. El objetivo es mostrar cómo pasar de los datos abiertos a un proyecto de aula con mapas interactivos, Python, Streamlit y una lectura crítica del contexto.

TL;DR
El Open Data en educación permite trabajar con datos reales en lugar de ejemplos artificiales.
La combinación de datos abiertos, mapas y tecnología educativa ayuda a desarrollar pensamiento crítico, competencia digital, análisis territorial y aprendizaje basado en proyectos.
El ejemplo práctico del artículo utiliza centros educativos de la Comunitat Valenciana para crear un mapa interactivo con filtros y estadísticas en Streamlit.

Qué es Open Data en educación y por qué importa
El Open Data en educación es el uso pedagógico de datos abiertos, públicos y reutilizables para diseñar experiencias de aprendizaje conectadas con la realidad. Puede aplicarse en primaria, secundaria, formación profesional, universidad o formación corporativa, siempre que exista una pregunta relevante y una mediación didáctica adecuada.
No se trata de descargar un CSV y proyectarlo en clase. La clave está en convertir los datos en preguntas: dónde están los centros públicos, qué barrios tienen menos recursos, cómo se distribuye la conectividad, qué zonas tienen más distancia a servicios educativos o qué diferencias territoriales aparecen al visualizar la información.
Cuando el alumnado trabaja con datos reales, deja de limitarse a resolver ejercicios cerrados. Empieza a observar patrones, detectar ausencias, comparar contextos y argumentar con evidencias. Por tanto, el dato deja de ser un número aislado y se convierte en una herramienta para comprender el mundo.
Idea clave
La tecnología no sustituye la mirada pedagógica. Un mapa, una gráfica o una app en Streamlit solo tienen valor educativo cuando ayudan a formular mejores preguntas y a comprender mejor el contexto.

Educación con sentido geográfico: aprender desde el territorio
La educación con sentido geográfico propone que el aprendizaje no ocurra de espaldas al lugar donde viven los estudiantes. El territorio se convierte en un recurso didáctico: calles, barrios, equipamientos, rutas, servicios públicos, centros educativos, bibliotecas, zonas verdes o indicadores socioeconómicos.
Este enfoque conecta especialmente bien con el aprendizaje situado. Una pregunta sobre una plaza, una ruta escolar o una zona sin servicios puede activar contenidos de geografía, matemáticas, lengua, tecnología, ciudadanía digital y educación en valores.
Además, el Open Data en educación permite trabajar con información pública verificable. Esto ayuda a desarrollar una cultura de transparencia, interpretación crítica de fuentes y participación ciudadana basada en evidencias.
- Geografía: lectura del territorio, mapas, escalas, distancia y distribución espacial.
- Matemáticas: estadística, porcentajes, visualización, comparación y análisis de tendencias.
- Lengua: argumentación, informes, exposiciones orales y comunicación de resultados.
- Tecnología: tratamiento de datos, dashboards, mapas interactivos y prototipado de aplicaciones.
- Valores cívicos: equidad, acceso a servicios, transparencia y toma de decisiones informada.
Fuentes fiables para trabajar datos abiertos en el aula
Para que un proyecto de Open Data en educación funcione, conviene empezar por fuentes oficiales, estables y bien documentadas. No hace falta trabajar con grandes volúmenes de información. A menudo, un dataset pequeño, bien contextualizado y con coordenadas geográficas puede generar una actividad potente.
En España, el portal datos.gob.es es un buen punto de partida para localizar datasets públicos. En el contexto valenciano, el portal Dades Obertes GVA ofrece recursos útiles para trabajar educación, territorio, equipamientos, movilidad o servicios públicos.
También pueden consultarse fuentes estadísticas como INEbase, así como indicadores internacionales de UNESCO Institute for Statistics, OECD Education GPS o World Bank Open Data.
En mi blog también puedes encontrar ejemplos complementarios en la categoría Open Data, recursos técnicos en Leaflet y experiencias relacionadas con campus virtuales en Moodle.
Criterio antes de elegir un dataset
Antes de llevar un conjunto de datos al aula, revisa cuatro aspectos: fuente, licencia, fecha de actualización y campos disponibles. Si el dataset tiene coordenadas, categorías claras y una documentación mínima, será mucho más fácil transformarlo en una actividad educativa.
Ventajas pedagógicas del Open Data en educación
La primera ventaja es la autenticidad. El alumnado trabaja con datos reales, no con ejemplos fabricados. Esto cambia el tipo de conversación en el aula: ya no se pregunta únicamente cuál es el resultado correcto, sino qué muestra el dato, qué oculta, cómo se interpreta y qué decisiones podrían tomarse a partir de él.
La segunda ventaja es la transversalidad. Un proyecto basado en datos abiertos puede integrar competencias digitales, análisis matemático, comunicación escrita, pensamiento crítico y comprensión social. Por eso encaja muy bien con metodologías como el aprendizaje basado en proyectos, el aprendizaje-servicio o los laboratorios de innovación educativa.
La tercera ventaja es la motivación. Cuando los estudiantes analizan su barrio, su municipio o su entorno cercano, el contenido deja de parecer abstracto. La pregunta se vuelve concreta: qué recursos tenemos cerca, qué zonas están peor conectadas, qué diferencias hay entre municipios o cómo podríamos mejorar una situación local.
- Más contexto: el alumnado entiende para qué sirven los datos.
- Más pensamiento crítico: aprende a interpretar, no solo a calcular.
- Más competencia digital: busca, limpia, filtra, visualiza y comunica información.
- Más conexión social: relaciona el currículo con problemas reales.
- Más cultura democrática: comprende el valor de la transparencia y la evidencia.
De los datos a la acción: herramientas EdTech útiles
La EdTech aporta la capa práctica para transformar datos en experiencias de aprendizaje. Un dataset puede convertirse en un mapa, una gráfica, una historia interactiva, una app sencilla o un informe visual. Lo importante no es la herramienta elegida, sino la pregunta que guía el proyecto.
Para comenzar, un docente puede utilizar herramientas sin código como Google My Maps, Datawrapper, Flourish o uMap. Si se quiere avanzar un poco más, Python, Folium, GeoPandas y Streamlit permiten crear prototipos más flexibles y reutilizables.
El ejemplo práctico de este artículo utiliza Python y Streamlit porque permite construir una aplicación web sencilla, con filtros, mapa, estadísticas y descarga de CSV. Además, es un buen punto intermedio entre visualización, programación básica y análisis de datos.
No necesitas empezar programando
Una buena secuencia puede comenzar con una hoja de cálculo, seguir con un mapa simple y terminar con una reflexión escrita. El código puede aparecer después, cuando el alumnado ya entiende la pregunta y necesita automatizar o visualizar mejor la información.
Ideas de proyectos escolares con Open Data en educación
El Open Data en educación se entiende mejor cuando se convierte en proyectos concretos. A continuación tienes varias líneas de trabajo que pueden adaptarse a diferentes edades, asignaturas y niveles de complejidad técnica.
1. Mapeo de recursos educativos y culturales
El alumnado puede analizar la distribución de bibliotecas, centros educativos, espacios juveniles, centros culturales o instalaciones deportivas. La pregunta no es solo cuántos recursos existen, sino dónde están y quién tiene más o menos acceso a ellos.
2. Rutas escolares seguras y sostenibles
Este proyecto permite estudiar movilidad, seguridad vial, accesibilidad, contaminación o tiempo de desplazamiento. Puede combinar trabajo de campo, encuestas, mapas y propuestas de mejora para el centro o el municipio.
3. Brecha digital y conectividad
Los estudiantes pueden investigar diferencias de acceso a internet, disponibilidad de dispositivos, cobertura o servicios digitales. Es una vía clara para trabajar ciudadanía digital, desigualdad y toma de decisiones basada en datos.
4. Comparativa de inversión y equipamientos
A partir de presupuestos públicos, datos municipales o indicadores estadísticos, el alumnado puede comparar inversión, dotaciones y resultados. Este tipo de actividad desarrolla argumentación y lectura crítica de datos institucionales.
5. Narrativas digitales sobre el territorio
Los datos también pueden contar historias. Un mapa enriquecido con testimonios, fotografías, textos breves y estadísticas permite construir relatos sobre memoria local, desigualdad, patrimonio, movilidad o acceso a servicios.

Tutorial práctico: mapa educativo con Python, Streamlit y datos abiertos


Dataset utilizado: centros docentes de la Comunitat Valenciana
Para el ejemplo práctico trabajaremos con el conjunto Centros docentes de la Comunitat Valenciana, publicado en el portal de Datos Abiertos de la Generalitat Valenciana. Es un dataset especialmente útil porque incluye nombre del centro, tipo, régimen, provincia, comarca, localidad y coordenadas geográficas.
La ficha oficial del recurso está disponible en Dades Obertes GVA. Para una actividad de aula, esta fuente permite trabajar localización, distribución territorial, acceso a servicios educativos y comparación por municipios o provincias.
El código usa columnas como dlibre para el nombre del centro, dgenerica_cas para el tipo de estudios, regimen para la titularidad, noms_mun para el municipio y latitud / longitud para representar cada punto en el mapa.
Qué vas a construir
Una aplicación en Streamlit con un mapa interactivo de centros educativos, filtros por provincia, comarca, municipio, régimen y tipo de estudios, estadísticas dinámicas y descarga en CSV de los centros visibles en el mapa.
Requisitos de instalación
Para ejecutar el ejemplo necesitas Python 3.9 o superior y las librerías principales del proyecto: Streamlit, Pandas, GeoPandas, Folium, Streamlit-Folium y Plotly.
- Python: entorno base para ejecutar la aplicación.
- Pandas y GeoPandas: carga, filtrado y tratamiento geográfico de datos.
- Folium: creación del mapa interactivo.
- Streamlit: interfaz web sencilla para filtros, pestañas y métricas.
- Plotly: generación de gráficos y estadísticas visuales.
Instalación recomendada:
pip install streamlit pandas geopandas folium streamlit-folium plotly
Código completo de la aplicación
El siguiente bloque contiene el script completo. En una sesión formativa, puede trabajarse por partes: primero la carga de datos, después los filtros, luego el mapa y finalmente las estadísticas.
# -*- coding: utf-8 -*-
# Mapa educativo con Streamlit (docentes)
# Asume que SIEMPRE hay columnas 'latitud' y 'longitud' válidas.
import pandas as pd
import geopandas as gpd
import streamlit as st
from streamlit_folium import st_folium
import folium
from folium.plugins import MarkerCluster, Fullscreen
import plotly.express as px
# -----------------------
# Ajustes de la página
# -----------------------
st.set_page_config(page_title="Mapa Educativo GVA", layout="wide", page_icon="🌍")
st.title("🌍 Mapa Interactivo de Centros Educativos | Comunitat Valenciana")
st.caption("Fuente: Dades Obertes GVA. Uso educativo.")
# -----------------------
# Colores consistentes
# -----------------------
REGIMEN_COLOR_MAP = {
"PÚBLICO": "blue",
"PUBLICO": "blue", # por si llega sin tilde
"CONCERTADO": "green",
"PRIVADO": "red",
}
PAL_MUNI = px.colors.qualitative.Set3
PAL_DGEN = px.colors.qualitative.Set2
PAL_PROV = px.colors.qualitative.Set1
def color_regimen(reg: str) -> str:
r = (reg or "").upper()
if "PÚBLIC" in r or "PUBLIC" in r: return REGIMEN_COLOR_MAP["PÚBLICO"]
if "CONCERT" in r: return REGIMEN_COLOR_MAP["CONCERTADO"]
if "PRIVAD" in r: return REGIMEN_COLOR_MAP["PRIVADO"]
return "purple"
# ---- helper para tarjetas de métrica con barra ----
def metric_card(label: str, value: int, color: str, icon: str, total: int | None = None):
"""Tarjeta de métrica con icono y (opcional) barra de progreso porcentual."""
val_txt = f"{int(value):,}".replace(",", ".")
pct = (value / total) if (total and total > 0) else None
bar_html = ""
if pct is not None:
bar_html = f"""
<div style="height:6px; background:#f1f3f5; border-radius:999px; margin-top:10px;">
<div style="height:6px; width:{pct*100:.0f}%; background:{color};
border-radius:999px;"></div>
</div>
<div style="font-size:12px; color:#667085; margin-top:6px;">{pct*100:.0f}%</div>
"""
st.markdown(
f"""
<div style="
border-radius:14px; padding:14px 16px; background:#fff;
border:1px solid #eaeaea; box-shadow:0 2px 10px rgba(0,0,0,.04);
">
<div style="font-size:13px; color:#667085; display:flex; gap:.5rem; align-items:center;">
<span style="font-size:16px;">{icon}</span>
<span>{label}</span>
</div>
<div style="font-size:28px; font-weight:700; color:#101828; line-height:1; margin-top:6px;">
{val_txt}
</div>
{bar_html}
</div>
""",
unsafe_allow_html=True,
)
# -----------------------
# URL oficial (WFS -> CSV)
# -----------------------
URL_GVA = (
"https://terramapas.icv.gva.es/12_Centros_wfs"
"?request=GetFeature&service=WFS&version=2.0.0"
"&typename=CentrosDocentesRegimen&outputformat=csv"
)
# -----------------------
# Carga de datos (caché)
# -----------------------
@st.cache_data(show_spinner="Cargando centros…", ttl=3600 * 60)
def cargar_datos(url: str) -> gpd.GeoDataFrame:
usar = [
"id", "codcen", "dlibre", "dgenerica_cas", "regimen", "titular", "direccion",
"noms_mun", "comarca", "provincia", "latitud", "longitud", "mail", "web"
]
df = pd.read_csv(url, dtype=str)
gdf = gpd.GeoDataFrame(
df[usar].copy(),
geometry=gpd.points_from_xy(df["longitud"].astype(float), df["latitud"].astype(float)),
crs="EPSG:4326"
)
return gdf
gdf = cargar_datos(URL_GVA)
st.success(f"✅ Centros cargados: {len(gdf):,}".replace(",", "."))
# -----------------------
# Filtros (barra lateral)
# -----------------------
st.sidebar.header("🔎 Filtros")
q = st.sidebar.text_input("Buscar por nombre del centro")
sel_provs = st.sidebar.multiselect("Provincia", sorted(gdf["provincia"].dropna().unique()))
sel_comarcs = st.sidebar.multiselect("Comarca", sorted(gdf["comarca"].dropna().unique()))
sel_munis = st.sidebar.multiselect("Municipio", sorted(gdf["noms_mun"].dropna().unique()))
sel_tipos = st.sidebar.multiselect("Tipo de centro (régimen)", sorted(gdf["regimen"].dropna().unique()))
sel_dgen = st.sidebar.multiselect("Tipo de estudios (dgenerica_cas)", sorted(gdf["dgenerica_cas"].dropna().unique()))
ajustar_al_cambiar = st.sidebar.checkbox(
"🔄 Ajustar mapa al cambiar filtros", value=True,
help="Si está activo, al tocar filtros el mapa se reencuadra a los datos filtrados."
)
# Firma de filtros para detectar cambios
filtro_sig = (
q,
tuple(sel_provs),
tuple(sel_comarcs),
tuple(sel_munis),
tuple(sel_tipos),
tuple(sel_dgen),
)
# -----------------------
# Estado (bounds y subset)
# -----------------------
if "last_bounds" not in st.session_state:
st.session_state["last_bounds"] = None
if "last_filter_sig" not in st.session_state:
st.session_state["last_filter_sig"] = None
if "subset_vista" not in st.session_state:
st.session_state["subset_vista"] = gdf.copy()
if "need_fit" not in st.session_state:
st.session_state["need_fit"] = True # primera carga: sí ajustamos
# Si cambian filtros y está activado "ajustar", reseteamos bounds y marcamos que hay que ajustar una vez
if filtro_sig != st.session_state["last_filter_sig"]:
st.session_state["last_filter_sig"] = filtro_sig
if ajustar_al_cambiar:
st.session_state["last_bounds"] = None
st.session_state["need_fit"] = True
# -----------------------
# Aplicamos filtros base (laterales)
# -----------------------
gdf_f = gdf.copy()
if q:
gdf_f = gdf_f[gdf_f["dlibre"].astype(str).str.contains(q, case=False, na=False)]
if sel_provs:
gdf_f = gdf_f[gdf_f["provincia"].isin(sel_provs)]
if sel_munis:
gdf_f = gdf_f[gdf_f["noms_mun"].isin(sel_munis)]
if sel_comarcs:
gdf_f = gdf_f[gdf_f["comarca"].isin(sel_comarcs)]
if sel_tipos:
gdf_f = gdf_f[gdf_f["regimen"].isin(sel_tipos)]
if sel_dgen:
gdf_f = gdf_f[gdf_f["dgenerica_cas"].isin(sel_dgen)]
st.sidebar.write(f"📊 Filtrado lateral: **{len(gdf_f)}** centros")
# -----------------------
# Bounds helpers (antibucle)
# -----------------------
def norm_bounds(b: dict | None):
"""Devuelve una tupla normalizada (redondeada) para comparar bounds de forma estable."""
if not b:
return None
try:
sw, ne = b["_southWest"], b["_northEast"]
return (
round(float(sw["lat"]), 6),
round(float(sw["lng"]), 6),
round(float(ne["lat"]), 6),
round(float(ne["lng"]), 6),
)
except Exception:
return None
def bounds_changed(old_b, new_b) -> bool:
"""True si los bounds han cambiado (con tolerancia)."""
return norm_bounds(old_b) != norm_bounds(new_b)
def filtrar_por_bounds(df_in: pd.DataFrame, bounds: dict | None) -> pd.DataFrame:
"""Filtra puntos dentro de los límites visibles del mapa (bounds de st_folium)."""
if not bounds:
return df_in
sw = bounds.get("_southWest", {})
ne = bounds.get("_northEast", {})
if not sw or not ne:
return df_in
lat_min, lat_max = float(sw["lat"]), float(ne["lat"])
lon_min, lon_max = float(sw["lng"]), float(ne["lng"])
return df_in[
(df_in["latitud"].astype(float).between(lat_min, lat_max)) &
(df_in["longitud"].astype(float).between(lon_min, lon_max))
].copy()
# Mantén el subset con los últimos bounds (si existen)
st.session_state["subset_vista"] = filtrar_por_bounds(gdf_f, st.session_state.get("last_bounds"))
# -----------------------
# Pestañas
# -----------------------
tab_mapa, tab_stats, tab_aula = st.tabs(["🗺️ Mapa", "📊 Estadísticas", "🎓 Para el Aula"])
# -------- Mapa --------
with tab_mapa:
if gdf_f.empty:
st.info("No hay centros con esos filtros. Prueba a ampliar la búsqueda.")
else:
# Centro "neutro" por si no ajustamos
lat0 = gdf_f["latitud"].astype(float).mean()
lon0 = gdf_f["longitud"].astype(float).mean()
m = folium.Map(location=[lat0, lon0], zoom_start=8,
tiles="CartoDB Positron", control_scale=True)
Fullscreen().add_to(m)
cluster = MarkerCluster(name="Centros").add_to(m)
for _, row in gdf_f.iterrows():
lat = float(row["latitud"]); lon = float(row["longitud"])
popup = f"""
<div style='font-family:system-ui; font-size:13px; max-width:300px;'>
<strong>{row.get('dlibre','')}</strong><br/>
<b>Tipo:</b> {row.get('regimen','')}<br/>
<b>Estudios:</b> {row.get('dgenerica_cas','')}<br/>
<b>Municipio:</b> {row.get('noms_mun','')}<br/>
<b>Comarca:</b> {row.get('comarca','')}<br/>
<b>Provincia:</b> {row.get('provincia','')}<br/>
<b>Dirección:</b> {row.get('direccion','')}<br/>
<b>Web:</b> {row.get('web','') or '—'}<br/>
<b>Email:</b> {row.get('mail','') or '—'}
</div>
"""
folium.CircleMarker(
location=[lat, lon],
radius=6,
color=color_regimen(row.get("regimen")),
fill=True, fill_opacity=0.85,
popup=folium.Popup(popup, max_width=320),
tooltip=f"{row.get('dlibre','')} — {row.get('noms_mun','')}",
).add_to(cluster)
# 🔑 Antibucle: sólo ajusta (fit_bounds) cuando "need_fit" sea True
if st.session_state.get("need_fit", False):
m.fit_bounds([
[gdf_f["latitud"].astype(float).min(), gdf_f["longitud"].astype(float).min()],
[gdf_f["latitud"].astype(float).max(), gdf_f["longitud"].astype(float).max()],
])
st.session_state["need_fit"] = False # ya hemos ajustado esta vez
# Render del mapa y captura de bounds
map_state = st_folium(m, height=620, use_container_width=True, key="mapa_gva")
# Guarda bounds SÓLO si han cambiado realmente (evita reruns innecesarios)
new_bounds = map_state.get("bounds") if map_state else None
if new_bounds and bounds_changed(st.session_state.get("last_bounds"), new_bounds):
st.session_state["last_bounds"] = new_bounds
# Recalcula subset visible con los bounds actuales
st.session_state["subset_vista"] = filtrar_por_bounds(gdf_f, st.session_state.get("last_bounds"))
gdf_vista = st.session_state["subset_vista"]
st.caption(f"🧭 En vista: **{len(gdf_vista)}** centros (total filtrado: {len(gdf_f)})")
# Métricas rápidas (con tarjetas e indicadores)
col_a, col_b, col_c, col_d = st.columns(4)
total_visible = len(gdf_vista) # lo que ves ahora en el mapa
total_dataset = len(gdf) # TODO el dataset (sin filtros)
# (Opcional) útil si quieres mostrarlo en algún sitio:
total_filtrado = len(gdf_f) # tras filtros laterales, antes de bounds
n_pub = (gdf_vista["regimen"].str.upper().str.contains("PÚBLIC|PUBLIC", na=False)).sum()
n_con = (gdf_vista["regimen"].str.upper().str.contains("CONCERT", na=False)).sum()
n_pri = (gdf_vista["regimen"].str.upper().str.contains("PRIVAD", na=False)).sum()
with col_a:
# 👀 Progreso sobre el total del dataset completo
metric_card("Centros visibles", total_visible, "#34495e", "👀", total=total_dataset)
# (Opcional) pequeña nota bajo la tarjeta
st.caption(f"de {total_dataset:,}".replace(",", "."))
with col_b:
# Composición dentro de lo visible
metric_card("Públicos", int(n_pub), "blue", "🔵", total=total_visible)
with col_c:
metric_card("Concertados", int(n_con), "green", "🟢", total=total_visible)
with col_d:
metric_card("Privados", int(n_pri), "red", "🔴", total=total_visible)
# CSV descarga (según la vista actual del mapa)
cols_export = ["dlibre","regimen","dgenerica_cas","noms_mun","comarca","provincia",
"direccion","mail","web","latitud","longitud"]
st.download_button(
"💾 Descargar (CSV) — centros visibles en el mapa",
data=gdf_vista[cols_export].to_csv(index=False).encode("utf-8"),
file_name="centros_visibles.csv",
mime="text/csv"
)
# -------- Estadísticas --------
with tab_stats:
st.subheader("📊 Un vistazo rápido a los datos")
gdf_stats = st.session_state.get("subset_vista", gdf_f)
if gdf_stats.empty:
st.info("No hay datos con la vista actual del mapa.")
else:
top_muni = gdf_stats["noms_mun"].value_counts().reset_index()
top_muni.columns = ["Municipio", "Centros"]
st.write("**Municipios (vista actual)**")
fig_muni = px.bar(
top_muni, x="Municipio", y="Centros",
color="Municipio", color_discrete_sequence=PAL_MUNI,
title="Municipios (colores por municipio)"
)
st.plotly_chart(fig_muni, use_container_width=True)
reparto = gdf_stats["regimen"].value_counts().reset_index()
reparto.columns = ["Tipo de centro", "Centros"]
regimen_unique = reparto["Tipo de centro"].astype(str).str.upper().tolist()
regimen_color_map_dyn = {r: color_regimen(r) for r in regimen_unique}
st.write("**Reparto por tipo (régimen) — vista actual**")
fig_reg = px.pie(
reparto,
names="Tipo de centro",
values="Centros",
color=reparto["Tipo de centro"].astype(str).str.upper(),
color_discrete_map=regimen_color_map_dyn,
title="Reparto por régimen (colores coherentes con el mapa)"
)
fig_reg.update_traces(textinfo="label+percent", pull=0.03)
st.plotly_chart(fig_reg, use_container_width=True)
reparto_dgen = gdf_stats["dgenerica_cas"].value_counts().reset_index()
reparto_dgen.columns = ["Estudios (dgenerica_cas)", "Centros"]
st.write("**Reparto por estudios (dgenerica_cas) — vista actual**")
fig_dgen = px.bar(
reparto_dgen, x="Estudios (dgenerica_cas)", y="Centros",
color="Estudios (dgenerica_cas)", color_discrete_sequence=PAL_DGEN,
title="Reparto por estudios"
)
fig_dgen.update_layout(showlegend=False)
st.plotly_chart(fig_dgen, use_container_width=True)
por_prov = gdf_stats["provincia"].value_counts().reset_index()
por_prov.columns = ["Provincia", "Centros"]
st.write("**Centros por provincia (vista actual)**")
fig_prov = px.bar(
por_prov, x="Provincia", y="Centros",
color="Provincia", color_discrete_sequence=PAL_PROV,
title="Comparativa por provincias"
)
st.plotly_chart(fig_prov, use_container_width=True)
st.caption(f"📊 Estadísticas calculadas sobre **{len(gdf_stats)}** centros (según filtros y vista del mapa).")
# -------- Para el Aula --------
with tab_aula:
st.subheader("🎓 Ideas fáciles para llevar a clase")
st.markdown("""
- **Filtra por provincia** y comparad la oferta entre Alicante, Castellón y València.
- **Filtra por estudios** (dgenerica_cas) y observad la distribución territorial.
- **Usa el mapa**: al mover/zoom, las estadísticas y la descarga se ajustan a lo visible.
- **Mini informe**: exportad el CSV visible y cread gráficas en clase.
""")
Cómo ejecutar la aplicación
Guarda el fichero como mapa_centros.py y ejecútalo desde la terminal con el siguiente comando:
streamlit run mapa_centros.pyStreamlit abrirá una aplicación web local desde la que podrás filtrar centros, mover el mapa, analizar estadísticas y descargar el CSV de los datos visibles.
Cómo usarlo en el aula
Una forma sencilla de llevarlo a clase es empezar por una pregunta concreta: qué tipos de centros hay en una comarca, cómo se distribuyen los centros públicos y privados, qué municipios concentran más oferta educativa o qué zonas quedan más alejadas de determinados servicios.
- Actividad 1: filtrar por provincia y comparar la distribución de centros.
- Actividad 2: analizar la presencia de centros públicos, concertados y privados.
- Actividad 3: mover el mapa y observar cómo cambian las estadísticas visibles.
- Actividad 4: descargar el CSV visible y crear un breve informe de conclusiones.
- Actividad 5: formular una propuesta de mejora basada en la evidencia obtenida.
Pregunta para el aula
Después de explorar el mapa, pide al alumnado que responda con evidencias: ¿qué patrón territorial hemos encontrado?, ¿qué dato lo demuestra?, ¿qué explicación podría tener?, ¿qué información adicional necesitaríamos para confirmarlo?
Errores comunes al trabajar Open Data en educación
El primer error es empezar por la herramienta en lugar de empezar por la pregunta. Si el objetivo pedagógico no está claro, el proyecto puede convertirse en una demostración tecnológica sin impacto real.
El segundo error es utilizar datasets demasiado grandes o demasiado complejos en las primeras experiencias. Para comenzar, es mejor elegir pocos campos, categorías comprensibles y una visualización clara.
El tercer error es olvidar la lectura crítica de los datos. Todo dataset tiene límites: fechas de actualización, campos incompletos, sesgos de recogida, problemas de calidad o ausencia de contexto. Precisamente ahí aparece una oportunidad educativa muy valiosa.
- No empieces por la app: empieza por la pregunta.
- No uses demasiadas variables al principio.
- No presentes los datos como verdades absolutas.
- No conviertas la visualización en el producto final: úsala como punto de partida para debatir.
- No olvides documentar fuente, fecha, licencia y limitaciones.
Checklist para diseñar una actividad con datos abiertos
Checklist operativo
1. Define una pregunta conectada con el currículo y el contexto local.
2. Selecciona una fuente oficial y revisa licencia, fecha y campos.
3. Limpia o simplifica el dataset antes de llevarlo al aula.
4. Elige una visualización adecuada: tabla, gráfico, mapa o dashboard.
5. Diseña una tarea de interpretación, no solo de observación.
6. Pide una conclusión argumentada con evidencias.
7. Cierra con una propuesta de mejora, debate o producto final.
Preguntas frecuentes sobre Open Data en educación
¿Hace falta saber programar para empezar?
No. Puedes empezar con hojas de cálculo, mapas sencillos o herramientas sin código. Python y Streamlit son útiles cuando quieres automatizar, filtrar o reutilizar la experiencia, pero no son el punto de partida obligatorio.
¿Qué edad debe tener el alumnado?
Depende del enfoque. En etapas iniciales se puede trabajar lectura de mapas y observación del entorno. En secundaria, FP o universidad se puede avanzar hacia análisis estadístico, programación, visualización y argumentación con evidencias.
¿Qué tipo de datos conviene elegir?
Los más útiles para empezar suelen ser datasets con localización, categorías claras y pocas columnas: centros educativos, bibliotecas, zonas verdes, equipamientos, transporte, población o indicadores municipales.
¿Cómo se evalúa una actividad de este tipo?
Puede evaluarse mediante una rúbrica que contemple calidad de la pregunta, uso correcto de fuentes, interpretación de datos, claridad visual, argumentación y propuesta final.

Conclusión: datos abiertos para aprender con sentido
El Open Data en educación abre una oportunidad muy concreta: enseñar a mirar el mundo con datos, contexto y criterio. No se trata de convertir a todos los estudiantes en analistas, sino de ayudarles a comprender que la información pública puede servir para hacer mejores preguntas y tomar mejores decisiones.
Cuando el aula se conecta con el territorio, el aprendizaje gana profundidad. Un mapa deja de ser una imagen decorativa y se convierte en una herramienta para investigar. Una tabla deja de ser un conjunto de números y se convierte en una evidencia. Una app sencilla deja de ser una curiosidad técnica y se convierte en un producto educativo con propósito.
Por eso, trabajar con datos abiertos, mapas y EdTech no es una moda. Es una forma práctica de desarrollar pensamiento crítico, competencia digital, ciudadanía activa y aprendizaje situado.
Cierre
La mejor tecnología educativa no es la que impresiona más, sino la que ayuda a comprender mejor. Y los datos abiertos, cuando se trabajan con intención pedagógica, pueden convertir el entorno cercano en el mejor laboratorio de aprendizaje.


Deja una respuesta