Introducción General

El presente documento compila el desarrollo integral del proyecto “Modelación Predictiva Asistida por IA en Ambientes de Desarrollo Colaborativo con GitHub Codespaces y Copilot”, concebido como una propuesta de investigación aplicada en el marco de la Universidad de Sucre durante el segundo período académico de 2025, conforme a la Resolución No. 50 de 2025 del Consejo Académico que regula la oferta académica vigente.

El propósito de este trabajo es demostrar la factibilidad técnica y académica de un entorno completamente reproducible en la nube, basado en herramientas de desarrollo colaborativo y asistencia por inteligencia artificial, que permitan la automatización de procesos de análisis estadístico y predictivo con datos reales obtenidos desde fuentes gubernamentales e institucionales.

El proyecto integra cuatro componentes principales:

  1. Obtención de datos reales mediante APIs institucionales, utilizando un script en Python (collect_datasets.py) que accede a portales como datos.gov.co (Socrata), FAOSTAT, Banco Mundial, HDX (Humanitarian Data Exchange) y OECD.
  2. Análisis exploratorio y modelación predictiva reproducible, a través del script analyze_datasets.py, que implementa flujos de trabajo con scikit-learn, pipelines, validación cruzada, métricas estandarizadas y generación automática de reportes Markdown y figuras.
  3. Ejecución colaborativa en GitHub Codespaces con Copilot, que posibilita la co-creación, documentación y versionamiento del código en tiempo real, garantizando trazabilidad y control de cambios.
  4. Despliegue web en Streamlit Community Cloud, mediante una aplicación interactiva (app.py) que permite explorar datasets, ejecutar modelos y visualizar resultados de manera dinámica y accesible desde cualquier dispositivo.

Este documento recoge los pasos técnicos, prompts utilizados, códigos fuente, guías de despliegue y materiales de presentación (README y banner institucional), con el fin de servir como modelo de referencia para proyectos de investigación reproducibles en ingeniería, ciencia de datos y docencia universitaria asistida por IA.

La propuesta evidencia que, en el contexto actual de transformación digital, la función docente e investigativa se potencia cuando la inteligencia artificial se integra como herramienta de apoyo para el diseño, ejecución y difusión de proyectos científicos, favoreciendo la transparencia, la colaboración y la innovación metodológica.

Metodología General del Proyecto

La metodología aplicada en el proyecto “Modelación Predictiva Asistida por IA en Ambientes de Desarrollo Colaborativo con GitHub Codespaces y Copilot” se fundamenta en el principio de reproducibilidad científica y en el uso de tecnologías de inteligencia artificial generativa para la automatización de tareas de análisis, documentación y despliegue.

El enfoque metodológico combina cuatro fases integradas que articulan los aspectos técnicos, pedagógicos y de investigación aplicada:

  1. Fase de adquisición de datos Se desarrolló el script collect_datasets.py, encargado de explorar y descargar datasets reales desde APIs oficiales de organismos nacionales e internacionales, entre ellos:

    • datos.gov.co (Socrata – Gobierno de Colombia),
    • FAOSTAT (FAO – Naciones Unidas),
    • World Bank Open Data,
    • HDX (Humanitarian Data Exchange – ONU),
    • OECD Data. Este componente garantiza el uso de información verificable, actualizada y proveniente de fuentes institucionales.
  2. Fase de análisis predictivo y generación de reportes Mediante el script analyze_datasets.py, se automatiza el flujo de análisis exploratorio, modelado predictivo y evaluación de desempeño. El proceso incluye imputación de datos, estandarización, codificación categórica, selección de modelos (LogisticRegression, RandomForest, Ridge), validación cruzada y cálculo de métricas (ROC AUC, F1, R², RMSE). Todos los resultados se documentan automáticamente en un reporte Markdown, con gráficos y métricas en formato JSON.

  3. Fase de ejecución colaborativa en la nube Se implementó un flujo de trabajo completamente en GitHub Codespaces, utilizando Copilot como asistente de codificación y documentación. Este entorno elimina dependencias locales, garantiza la trazabilidad del código y fomenta el trabajo colaborativo en repositorios compartidos.

  4. Fase de visualización y despliegue web Finalmente, se diseñó la aplicación app.py en Streamlit Community Cloud, que permite al usuario final:

    • Buscar y descargar datasets,
    • Ejecutar modelos predictivos desde el navegador,
    • Visualizar reportes, métricas y figuras interactivas. Este despliegue convierte el flujo analítico en una herramienta didáctica y científica accesible desde cualquier dispositivo conectado a Internet.

En conjunto, estas fases conforman una metodología integral, transparente y escalable, que puede adaptarse a diversos dominios de la ingeniería y la ciencia de datos, asegurando la trazabilidad de cada resultado y la replicabilidad del proceso completo.

1ra Consulta: Título Sugerido del Proyecto

En el chat de la tarea me recomendaste lo siguiente :

Recomendación para el profesor Daniel Otero

Título sugerido del proyecto de investigación:

“Modelación Predictiva Asistida por IA en Ambientes de Desarrollo Colaborativo con GitHub Codespaces y Copilot”

  • Posibles líneas para publicación:

  • Revista Computers and Electronics in Agriculture (Elsevier).

  • Revista Colombiana de Estadística.

  • Applied Sciences (MDPI) — sección “AI and Data Analytics in Engineering”.

Contribución innovadora:

  • Uso de GitHub Codespaces + Copilot como entorno reproducible de investigación.

  • Reproducibilidad total en la nube sin configuración local.

  • Aplicación de IA generativa para documentación automática y generación de modelos.

Detalla esa propuesta con las siguientes características : 1. Listado de páginas web gubernamentales e institucionales confiables qué tengan datasets reales qué puedan ser utilizados para investigación 2. Código de Python para explorar, listar y descargar los datasets de interés con toda su información, origen, detalles y caracteristicas 3. Código de Python que realice los análisis recomendados para investigación. Y los prompts sugeridos para interpretar esos resultados de los análisis 4. Indicaciones para desarrollar todo en GitHub Codespaces más Copilot

Por favor comienza con 1) Listado…

1ra Respuesta: Listados de páginas web

1) Listado de páginas web gubernamentales e institucionales con datasets reales para investigación

A continuación se presenta un listado de fuentes confiables con datos abiertos que pueden utilizarse en proyectos de modelación predictiva, inteligencia artificial y análisis estadístico, especialmente en temas de ingeniería, ambiente, agricultura y desarrollo sostenible:

Entidad / Plataforma URL Temáticas principales Formato de descarga Observaciones
DANE – Datos Abiertos https://microdatos.dane.gov.co Censos, encuestas, economía, empleo, educación CSV, XLSX Requiere registro gratuito.
Portal de Datos Abiertos del Gobierno de Colombia https://datos.gov.co Agricultura, salud, educación, infraestructura, medio ambiente CSV, JSON, API Integración directa con API de Socrata.
IDEAM – Instituto de Hidrología, Meteorología y Estudios Ambientales https://dhime.ideam.gov.co Precipitación, temperatura, humedad, caudales, estaciones hidrometeorológicas CSV Requiere cuenta institucional. Ideal para modelos agroclimáticos.
FAO – FAOSTAT https://www.fao.org/faostat/en/ Producción agrícola, uso de suelos, comercio de alimentos, fertilizantes CSV, JSON API disponible. Ideal para Ingeniería Agroindustrial.
NASA EarthData https://earthdata.nasa.gov/ Variables climáticas, suelos, radiación solar, uso de la tierra NetCDF, HDF, CSV Acceso con cuenta gratuita.
World Bank Open Data https://data.worldbank.org/ Indicadores económicos, sostenibilidad, energía CSV, JSON API disponible. Compatible con wbdata en Python.
Instituto Geográfico Agustín Codazzi (IGAC) https://www.igac.gov.co/datos-abiertos Cartografía, uso del suelo, límites administrativos SHP, GeoJSON Ideal para análisis espaciales y geográficos.
Ministerio de Agricultura y Desarrollo Rural https://siembra.minagricultura.gov.co/ Cadenas productivas, precios, inventarios agropecuarios CSV, XLSX Datos periódicamente actualizados.
INS – Instituto Nacional de Salud (Colombia) https://www.ins.gov.co/DatosAbiertos/Paginas/default.aspx Epidemiología, salud pública, zoonosis CSV, XLSX Ideal para proyectos predictivos en salud ambiental.
ONU – Humanitarian Data Exchange (HDX) https://data.humdata.org/ Desarrollo, crisis humanitarias, clima, energía CSV, JSON Integración mediante API REST.
OECD Data https://data.oecd.org/ Economía, innovación, educación, sostenibilidad CSV, JSON Buena calidad estadística y metadatos detallados.

¿Deseas que en el siguiente punto (2) el código de Python explore todas estas fuentes mediante API o deseas priorizar algunas (por ejemplo, IDEAM, FAOSTAT, datos.gov.co y Banco Mundial)?

Esto permitirá optimizar el código para ejecución en GitHub Codespaces.

2da Consulta: Código de Python para Explorar Fuentes

Que el codigo de Python explore todas estas Fuentes mediante API

A continuación entrego el punto 2 con un script único en Python que explora, lista y descarga datasets desde APIs de: datos.gov.co (Socrata), FAOSTAT, Banco Mundial, HDX (CKAN), y OECD SDMX. Incluye conectores “stub” para IDEAM, IGAC, NASA Earthdata, INS, MinAgricultura cuando la API exige autenticación o no es pública y estable. Está pensado para GitHub Codespaces + Copilot.


Código Python (exploración, listado y descarga)

Guárdelo como collect_datasets.py. Requiere requests y pandas.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
collect_datasets.py
Explora, lista y descarga datasets desde varias fuentes abiertas por API.

Fuentes con implementación completa:
- datos.gov.co (Socrata Catalog API)
- FAOSTAT (FENIX services API)
- Banco Mundial (World Bank API v2)
- HDX / Humanitarian Data Exchange (CKAN API)
- OECD SDMX-JSON (Stats REST SDMX)

Fuentes con stubs (requieren credenciales o endpoints no estables):
- IDEAM (DHIME)
- IGAC
- NASA Earthdata
- INS Colombia
- MinAgricultura (Siembra)

Uso básico:
    python collect_datasets.py search --source ALL --q "agricultura" --limit 5
    python collect_datasets.py list   --source FAOSTAT --limit 10
    python collect_datasets.py get    --source SOCRATA --id <resource_id> --dest data/raw/

Variables de entorno opcionales:
    SOCRATA_APP_TOKEN
    HDX_API_KEY
    NASA_EARTHDATA_USER, NASA_EARTHDATA_PASS
    IDEAM_USER, IDEAM_PASS
"""

import os
import sys
import json
import argparse
from typing import List, Dict, Any, Optional
from pathlib import Path

import requests
import pandas as pd

# ---------------------------
# Utilidades
# ---------------------------

def ensure_dir(path: str) -> Path:
    p = Path(path).expanduser().resolve()
    p.mkdir(parents=True, exist_ok=True)
    return p

def save_json(data: Any, dest: Path):
    dest.parent.mkdir(parents=True, exist_ok=True)
    with open(dest, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

def safe_filename(s: str) -> str:
    return "".join(c if c.isalnum() or c in "-._" else "_" for c in s)[:150]


# ---------------------------
# Conectores base
# ---------------------------

class BaseConnector:
    name: str = "BASE"

    def search(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
        raise NotImplementedError

    def list_datasets(self, limit: int = 50) -> List[Dict[str, Any]]:
        """Lista general. Si la fuente no lo permite, retorna vacio o usa búsqueda sin query."""
        return self.search(query="", limit=limit)

    def get_resource(self, resource_id: str) -> Dict[str, Any]:
        raise NotImplementedError

    def download(self, resource_id: str, dest_dir: str) -> Path:
        """Descarga por id. Debe retornar la ruta local del archivo."""
        raise NotImplementedError


# ---------------------------
# SOCRATA (datos.gov.co)
# Catálogo: https://api.us.socrata.com/api/catalog/v1
# ---------------------------

class SocrataConnector(BaseConnector):
    name = "SOCRATA"
    catalog_url = "https://api.us.socrata.com/api/catalog/v1"
    domain = "www.datos.gov.co"  # limitar al portal Colombia

    def __init__(self):
        self.app_token = os.getenv("SOGRATA_APP_TOKEN") or os.getenv("SOCRATA_APP_TOKEN")

    def _headers(self):
        h = {"Accept": "application/json"}
        if self.app_token:
            h["X-App-Token"] = self.app_token
        return h

    def search(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
        params = {
            "q": query,
            "limit": limit,
            "domains": self.domain
        }
        r = requests.get(self.catalog_url, headers=self._headers(), params=params, timeout=30)
        r.raise_for_status()
        out = []
        for res in r.json().get("results", []):
            rsrc = res.get("resource", {})
            pbl = res.get("metadata", {}).get("domain", "")
            api_endpoint = rsrc.get("api_endpoint")
            out.append({
                "source": self.name,
                "title": rsrc.get("name"),
                "description": rsrc.get("description"),
                "dataset_id": rsrc.get("id"),
                "resource_type": rsrc.get("type"),
                "updatedAt": rsrc.get("updatedAt"),
                "page": res.get("link"),
                "api_endpoint": api_endpoint,
                "domain": pbl
            })
        return out

    def get_resource(self, resource_id: str) -> Dict[str, Any]:
        # Con Socrata, el resource_id es el 4x4 id del dataset (p.ej. "abcd-1234")
        # Data URL (JSON): https://{domain}/resource/{id}.json
        url = f"https://{self.domain}/resource/{resource_id}.json"
        r = requests.get(url, headers=self._headers(), params={"$limit": 5}, timeout=30)
        r.raise_for_status()
        return {
            "preview_rows": r.json() if isinstance(r.json(), list) else [],
            "data_url": url
        }

    def download(self, resource_id: str, dest_dir: str) -> Path:
        dest = ensure_dir(dest_dir) / f"{safe_filename(resource_id)}.csv"
        url = f"https://{self.domain}/resource/{resource_id}.csv"
        with requests.get(url, headers=self._headers(), stream=True, timeout=120) as r:
            r.raise_for_status()
            with open(dest, "wb") as f:
                for chunk in r.iter_content(chunk_size=1 << 16):
                    if chunk:
                        f.write(chunk)
        return dest


# ---------------------------
# FAOSTAT
# Docs: https://www.fao.org/faostat/en/#data  / API FENIX
# Ejemplo meta dominios: https://fenixservices.fao.org/faostat/api/v1/en/CLASSIFICATIONS/Domain
# ---------------------------

class FAOSTATConnector(BaseConnector):
    name = "FAOSTAT"
    base = "https://fenixservices.fao.org/faostat/api/v1"

    def search(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
        # Estrategia: listar dominios y filtrar por query en nombre/descripcion
        # Idioma en: en/es/fr; escogemos 'en' por cobertura.
        url = f"{self.base}/en/CLASSIFICATIONS/Domain"
        r = requests.get(url, timeout=30)
        r.raise_for_status()
        domains = r.json().get("data", [])
        results = []
        q = (query or "").lower()
        for d in domains:
            text = f"{d.get('DomainCode','')} {d.get('Domain','')} {d.get('Description','')}".lower()
            if q in text or not q:
                results.append({
                    "source": self.name,
                    "title": d.get("Domain"),
                    "description": d.get("Description"),
                    "dataset_id": d.get("DomainCode"),
                    "page": "https://www.fao.org/faostat/en/#data/" + d.get("DomainCode",""),
                })
            if len(results) >= limit:
                break
        return results

    def list_datasets(self, limit: int = 50) -> List[Dict[str, Any]]:
        return self.search("", limit)

    def get_resource(self, resource_id: str) -> Dict[str, Any]:
        # Ejemplo descarga por dominio y área: /en/{domain}/Q?area=...
        # Aquí devolvemos meta básica del dominio.
        url = f"{self.base}/en/CLASSIFICATIONS/Domain"
        r = requests.get(url, timeout=30)
        r.raise_for_status()
        domains = r.json().get("data", [])
        item = next((d for d in domains if d.get("DomainCode") == resource_id), None)
        if not item:
            raise ValueError(f"FAOSTAT domain not found: {resource_id}")
        return {"meta": item}

    def download(self, resource_id: str, dest_dir: str) -> Path:
        """
        Descarga tabulados 'abridged' del dominio en CSV.
        Nota: para consultas exactas, el usuario deberá filtrar por params (area, item, element, year).
        """
        dest = ensure_dir(dest_dir) / f"faostat_{safe_filename(resource_id)}.csv"
        # endpoint "bulk" simplificado para dominios con 'data' general:
        url = f"{self.base}/en/{resource_id}/A"  # 'A' = Annual, también existen Q/M según dominio
        params = {"area": "all", "item": "all", "element": "all", "year": "all"}
        r = requests.get(url, params=params, timeout=120)
        r.raise_for_status()
        data = r.json().get("data", [])
        if not data:
            # fallback a meta sin datos
            save_json({"warning": "No data returned", "domain": resource_id}, dest.with_suffix(".json"))
            return dest.with_suffix(".json")
        pd.DataFrame(data).to_csv(dest, index=False)
        return dest


# ---------------------------
# World Bank (Banco Mundial)
# Docs: https://data.worldbank.org/  API: http://api.worldbank.org/v2/
# ---------------------------

class WorldBankConnector(BaseConnector):
    name = "WORLD_BANK"
    base = "http://api.worldbank.org/v2"

    def search(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
        # Buscamos indicadores por 'query'
        url = f"{self.base}/indicator"
        params = {"format": "json", "per_page": limit*2}
        r = requests.get(url, params=params, timeout=30)
        r.raise_for_status()
        arr = r.json()
        if not isinstance(arr, list) or len(arr) < 2:
            return []
        indicators = arr[1]
        q = (query or "").lower()
        results = []
        for it in indicators:
            text = f"{it.get('id')} {it.get('name','')} {it.get('source','{}').get('value','')}".lower()
            if q in text or not q:
                results.append({
                    "source": self.name,
                    "title": it.get("name"),
                    "description": f"ID: {it.get('id')}. Source: {it.get('source',{}).get('value','')}",
                    "dataset_id": it.get("id"),
                    "page": f"https://data.worldbank.org/indicator/{it.get('id')}",
                })
            if len(results) >= limit:
                break
        return results

    def get_resource(self, resource_id: str) -> Dict[str, Any]:
        url = f"{self.base}/country/all/indicator/{resource_id}"
        params = {"format": "json", "per_page": 5}
        r = requests.get(url, params=params, timeout=30)
        r.raise_for_status()
        return {"preview": r.json()}

    def download(self, resource_id: str, dest_dir: str) -> Path:
        dest = ensure_dir(dest_dir) / f"worldbank_{safe_filename(resource_id)}.csv"
        url = f"{self.base}/country/all/indicator/{resource_id}"
        params = {"format": "json", "per_page": 20000}
        r = requests.get(url, params=params, timeout=120)
        r.raise_for_status()
        arr = r.json()
        if isinstance(arr, list) and len(arr) > 1 and isinstance(arr[1], list):
            df = pd.json_normalize(arr[1])
            df.to_csv(dest, index=False)
            return dest
        save_json({"warning": "Unexpected structure", "raw": arr}, dest.with_suffix(".json"))
        return dest.with_suffix(".json")


# ---------------------------
# HDX (CKAN API)
# Docs: https://data.humdata.org/  API: /api/3/action/package_search
# ---------------------------

class HDXConnector(BaseConnector):
    name = "HDX"
    base = "https://data.humdata.org/api/3/action"

    def __init__(self):
        self.api_key = os.getenv("HDX_API_KEY")

    def _headers(self):
        h = {"Accept": "application/json"}
        if self.api_key:
            h["Authorization"] = self.api_key
        return h

    def search(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
        url = f"{self.base}/package_search"
        params = {"q": query or "", "rows": limit}
        r = requests.get(url, headers=self._headers(), params=params, timeout=30)
        r.raise_for_status()
        res = r.json().get("result", {})
        out = []
        for pkg in res.get("results", []):
            out.append({
                "source": self.name,
                "title": pkg.get("title"),
                "description": pkg.get("notes"),
                "dataset_id": pkg.get("id"),
                "page": f"https://data.humdata.org/dataset/{pkg.get('name')}",
                "num_resources": len(pkg.get("resources", []))
            })
        return out

    def get_resource(self, resource_id: str) -> Dict[str, Any]:
        url = f"{self.base}/package_show"
        r = requests.get(url, headers=self._headers(), params={"id": resource_id}, timeout=30)
        r.raise_for_status()
        return r.json().get("result", {})

    def download(self, resource_id: str, dest_dir: str) -> Path:
        pkg = self.get_resource(resource_id)
        if not pkg or "resources" not in pkg or not pkg["resources"]:
            raise ValueError("No resources in package.")
        # Elegimos el primer recurso con URL de descarga directa
        res0 = next((r for r in pkg["resources"] if r.get("download_url") or r.get("url")), pkg["resources"][0])
        url = res0.get("download_url") or res0.get("url")
        ext = Path(url).suffix or ".dat"
        dest = ensure_dir(dest_dir) / f"hdx_{safe_filename(pkg.get('name','dataset'))}{ext}"
        with requests.get(url, stream=True, timeout=180) as r:
            r.raise_for_status()
            with open(dest, "wb") as f:
                for chunk in r.iter_content(chunk_size=1 << 16):
                    if chunk:
                        f.write(chunk)
        # Guardar metadatos
        save_json(pkg, dest.with_suffix(ext + ".meta.json"))
        return dest


# ---------------------------
# OECD SDMX-JSON
# Docs: https://stats.oecd.org/
# Listado de dataflows: https://stats.oecd.org/RESTSDMX/sdmx.ashx/GetDataFlow/all
# ---------------------------

class OECDConnector(BaseConnector):
    name = "OECD"
    base = "https://stats.oecd.org/RESTSDMX/sdmx.ashx"

    def search(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
        url = f"{self.base}/GetDataFlow/all"
        r = requests.get(url, timeout=60)
        r.raise_for_status()
        # Devuelve SDMX XML. Aquí hacemos una búsqueda simple por texto.
        text = r.text
        # Extraer pares id, nombre de forma sencilla
        import re
        flows = re.findall(r'<KeyFamily id="([^"]+)".*?<Name xml:lang="en">([^<]+)</Name>', text, flags=re.S)
        q = (query or "").lower()
        results = []
        for fid, fname in flows:
            if q in fname.lower() or q in fid.lower() or not q:
                results.append({
                    "source": self.name,
                    "title": fname,
                    "description": f"DataFlow ID: {fid}",
                    "dataset_id": fid,
                    "page": "https://stats.oecd.org/"
                })
            if len(results) >= limit:
                break
        return results

    def get_resource(self, resource_id: str) -> Dict[str, Any]:
        # No hay "preview" trivial sin saber dimensiones. Retornamos metadatos mínimos.
        return {"dataset_id": resource_id, "hint": "Use GetData/{flow}/{dim...}/all?contentType=csv"}

    def download(self, resource_id: str, dest_dir: str) -> Path:
        """
        Descarga todo el dataset puede ser enorme. Por defecto pedimos CSV 'all'.
        Muchos flujos requieren especificar dimensiones. Advertimos y guardamos pequeño CSV si el endpoint lo permite.
        """
        dest = ensure_dir(dest_dir) / f"oecd_{safe_filename(resource_id)}.csv"
        url = f"https://stats.oecd.org/SDMX-JSON/data/{resource_id}/all?contentType=csv"
        r = requests.get(url, timeout=180)
        if r.status_code != 200:
            save_json({"warning": "Download may require dimensions", "status": r.status_code}, dest.with_suffix(".json"))
            return dest.with_suffix(".json")
        with open(dest, "wb") as f:
            f.write(r.content)
        return dest


# ---------------------------
# STUBS (requieren credenciales o endpoints propietarios)
# ---------------------------

class StubConnector(BaseConnector):
    def __init__(self, name: str, note: str):
        self.name = name
        self.note = note

    def search(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
        return [{"source": self.name, "title": f"{self.name} (stub)", "description": self.note, "dataset_id": None}]

    def get_resource(self, resource_id: str) -> Dict[str, Any]:
        return {"note": self.note}

    def download(self, resource_id: str, dest_dir: str) -> Path:
        raise NotImplementedError(self.note)


def build_connectors(include_stubs: bool = True) -> List[BaseConnector]:
    conns: List[BaseConnector] = [
        SocrataConnector(),
        FAOSTATConnector(),
        WorldBankConnector(),
        HDXConnector(),
        OECDConnector(),
    ]
    if include_stubs:
        conns += [
            StubConnector("IDEAM_DHIME", "Requiere credenciales y acceso institucional. Automatice con requests + sesión autenticada."),
            StubConnector("IGAC", "Portal con descargas geoespaciales. Muchos recursos via ArcGIS/Geoservicios. Definir datasets y endpoints concretos."),
            StubConnector("NASA_EARTHDATA", "Usar CMR API con token Earthdata para colecciones y granules. Autenticación obligatoria."),
            StubConnector("INS_CO", "Datos abiertos vía SharePoint/descargas; endpoints cambian. Preferible curar dataset objetivo primero."),
            StubConnector("MINAGRICULTURA_SIEMBRA", "Tableros tipo PowerBI; la extracción estable requiere endpoints específicos o descargas programadas."),
        ]
    return conns


# ---------------------------
# CLI
# ---------------------------

def cmd_search(args):
    connectors = build_connectors(include_stubs=True)
    q = args.q or ""
    selected = [c for c in connectors if args.source in ("ALL", c.name)]
    out: List[Dict[str, Any]] = []
    for c in selected:
        try:
            out += c.search(q, limit=args.limit)
        except Exception as ex:
            out.append({"source": c.name, "error": str(ex)})
    print(json.dumps(out, ensure_ascii=False, indent=2))

def cmd_list(args):
    connectors = build_connectors(include_stubs=True)
    selected = [c for c in connectors if args.source in ("ALL", c.name)]
    out: List[Dict[str, Any]] = []
    for c in selected:
        try:
            out += c.list_datasets(limit=args.limit)
        except Exception as ex:
            out.append({"source": c.name, "error": str(ex)})
    print(json.dumps(out, ensure_ascii=False, indent=2))

def cmd_get(args):
    connectors = build_connectors(include_stubs=True)
    c = next((x for x in connectors if x.name == args.source), None)
    if not c:
        raise SystemExit(f"Fuente no soportada: {args.source}")
    # meta previa
    meta = c.get_resource(args.id)
    print(json.dumps({"meta": meta}, ensure_ascii=False, indent=2))
    # descarga
    dest = c.download(args.id, args.dest)
    print(json.dumps({"downloaded_to": str(dest)}, ensure_ascii=False, indent=2))

def main():
    p = argparse.ArgumentParser(description="Explora y descarga datasets de múltiples fuentes abiertas.")
    sub = p.add_subparsers(dest="cmd", required=True)

    sp = sub.add_parser("search", help="Buscar datasets")
    sp.add_argument("--source", default="ALL", help="ALL|SOCRATA|FAOSTAT|WORLD_BANK|HDX|OECD|IDEAM_DHIME|IGAC|NASA_EARTHDATA|INS_CO|MINAGRICULTURA_SIEMBRA")
    sp.add_argument("--q", default="", help="término de búsqueda")
    sp.add_argument("--limit", type=int, default=10)
    sp.set_defaults(func=cmd_search)

    lp = sub.add_parser("list", help="Listar datasets")
    lp.add_argument("--source", default="ALL", help="ALL|SOCRATA|FAOSTAT|WORLD_BANK|HDX|OECD|IDEAM_DHIME|IGAC|NASA_EARTHDATA|INS_CO|MINAGRICULTURA_SIEMBRA")
    lp.add_argument("--limit", type=int, default=10)
    lp.set_defaults(func=cmd_list)

    gp = sub.add_parser("get", help="Descargar dataset por id")
    gp.add_argument("--source", required=True)
    gp.add_argument("--id", required=True, help="ID de recurso/indicador/dominio según fuente")
    gp.add_argument("--dest", default="data/raw", help="Directorio destino")
    gp.set_defaults(func=cmd_get)

    args = p.parse_args()
    args.func(args)

if __name__ == "__main__":
    main()

Explicación breve

  • Arquitectura por conectores. Cada fuente implementa search(), list_datasets() y download().
  • Descargas a data/raw/. Metadatos en .meta.json cuando aplica.
  • Socrata: cataloga datos.gov.co y descarga CSV directo por resource_id.
  • FAOSTAT: lista dominios y descarga “A”nnual genérico del dominio. Para investigación ajuste filtros.
  • WorldBank: busca indicadores y descarga series por país.
  • HDX: busca paquetes CKAN y descarga el primer recurso disponible.
  • OECD: lista DataFlows. Descarga CSV “all” si el flujo lo permite. Muchos flujos requieren dimensiones.

Stubs señalados requieren credenciales o endpoints específicos: IDEAM, IGAC, NASA, INS, MinAgricultura.


Ejemplos rápidos (para Codespaces)

# 1) Buscar “precipitation” en todas las fuentes
python collect_datasets.py search --source ALL --q "precipitation" --limit 5

# 2) Listar dominios de FAOSTAT
python collect_datasets.py list --source FAOSTAT --limit 15

# 3) Descargar un dataset de datos.gov.co por ID (ejemplo: abcd-1234)
python collect_datasets.py get --source SOCRATA --id abcd-1234 --dest data/raw/

# 4) Descargar indicador del Banco Mundial (ej.: SP.POP.TOTL)
python collect_datasets.py get --source WORLD_BANK --id SP.POP.TOTL --dest data/raw/

Siguientes pasos

Indique si avanzo con el punto 3: análisis estadístico y predictivo en Python (EDA, calidad de datos, regresión logística/árboles, validación, reportes) + prompts sugeridos para interpretación con Copilot/ChatGPT. También puedo ampliar conectores para IDEAM/NASA con autenticación si proporciona credenciales de prueba.

3ra Consulta: Análisis Estadístico y Predictivo en Python y Prompts Sugeridos

Si por favor con el punto 3

3ra Respuesta

Entregado. Punto 3: análisis estadístico y predictivo + prompts sugeridos.


3) Código de análisis para investigación y prompts de interpretación

A. Explicación breve

El script realiza, de forma reproducible en GitHub Codespaces + Copilot, un flujo completo para clasificación binaria (prioridad) y regresión (alternativo):

  1. EDA mínima y auditable: tamaños, tipos, vacíos, cardinalidad, correlaciones, balance de clases.

  2. Preprocesamiento: imputación, escalado numérico, codificación categórica.

  3. Modelado:

    • Clasificación: LogisticRegression y RandomForestClassifier con StratifiedKFold y RandomizedSearchCV.
    • Regresión: Ridge y RandomForestRegressor con KFold.
  4. Métricas:

    • Clasificación: ROC AUC, PR AUC, F1, Balanced Accuracy, Brier.
    • Regresión: RMSE, MAE, R².
  5. Calibración y explicabilidad: curva de fiabilidad, permuta de importancias.

  6. Desbalance: class_weight="balanced" y umbral óptimo por Youden / F1.

  7. Artefactos: guarda modelo, transformador, métricas (JSON), figuras (PNG) y reporte Markdown en reports/ y models/.

  8. Metadatos: integra *.meta.json si existe (p.ej., de HDX) en una DataCard.

Uso rápido en Codespaces:

python analyze_datasets.py --input data/raw/archivo.csv --target objetivo --task auto

B. Código Python (guardar como analyze_datasets.py)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
analyze_datasets.py
Flujo reproducible de EDA, preprocesamiento y modelado para clasificación binaria (prioridad)
y regresión (alternativo). Guarda métricas, figuras y reporte Markdown.

Diseñado para GitHub Codespaces + Copilot:
- Requiere: pandas, numpy, scikit-learn, matplotlib, seaborn (opcional), joblib
- Directorios de salida creados automáticamente.

Ejemplos:
    # Clasificación binaria (detección automática)
    python analyze_datasets.py --input data/raw/dataset.csv --target y --task auto

    # Forzar clasificación y fijar clase positiva
    python analyze_datasets.py --input data/raw/dataset.csv --target y --task binary --positive-label 1

    # Regresión
    python analyze_datasets.py --input data/raw/house.csv --target price --task regression
"""

import argparse
import json
import os
from pathlib import Path
from typing import Dict, Tuple, List, Optional
import warnings

import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import (
    StratifiedKFold, KFold, train_test_split, RandomizedSearchCV
)
from sklearn.metrics import (
    roc_auc_score, average_precision_score, f1_score, balanced_accuracy_score,
    brier_score_loss, precision_recall_curve, roc_curve, auc,
    mean_squared_error, mean_absolute_error, r2_score
)
from sklearn.linear_model import LogisticRegression, Ridge
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
from sklearn.inspection import permutation_importance
import matplotlib.pyplot as plt
import joblib

warnings.filterwarnings("ignore", category=UserWarning)

# --------------------------
# Utilidades de E/S
# --------------------------

def ensure_dir(p: Path) -> Path:
    p.mkdir(parents=True, exist_ok=True)
    return p

def load_meta_if_any(csv_path: Path) -> Dict:
    # Busca un .meta.json al lado del CSV o con sufijo
    candidates = [
        csv_path.with_suffix(".meta.json"),
        csv_path.parent / (csv_path.stem + ".csv.meta.json"),
        csv_path.with_suffix(".json")  # fallback
    ]
    for c in candidates:
        if c.exists():
            try:
                return json.loads(c.read_text(encoding="utf-8"))
            except Exception:
                pass
    return {}

def save_json(d: Dict, path: Path):
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(d, ensure_ascii=False, indent=2), encoding="utf-8")

# --------------------------
# Detección de tarea y validaciones
# --------------------------

def detect_task(y: pd.Series, task_arg: str) -> str:
    if task_arg in {"binary", "regression"}:
        return task_arg
    # auto:
    if y.dropna().nunique() == 2:
        return "binary"
    # si objetivo es numérico y continuo
    return "regression" if pd.api.types.is_numeric_dtype(y) else "binary"

def validate_binary(y: pd.Series, positive_label: Optional[str]) -> Tuple[pd.Series, str]:
    y_clean = y.dropna()
    classes = sorted(y_clean.unique())
    if len(classes) != 2:
        raise ValueError(f"El objetivo no es binario. Clases detectadas: {classes}")
    if positive_label is None:
        # por defecto, tomar la clase "mayor" semántica o la más rara
        counts = y_clean.value_counts()
        pos = counts.idxmin()  # clase minoritaria como positiva
    else:
        pos = positive_label
        if pos not in classes:
            raise ValueError(f"positive_label '{pos}' no está en {classes}")
    return y, pos

# --------------------------
# EDA mínima
# --------------------------

def quick_eda(df: pd.DataFrame, target: str) -> Dict:
    eda = {
        "n_rows": int(df.shape[0]),
        "n_cols": int(df.shape[1]),
        "dtypes": {c: str(t) for c, t in df.dtypes.items()},
        "missing_perc": df.isna().mean().round(4).to_dict(),
        "cardinality": {c: int(df[c].nunique(dropna=True)) for c in df.columns},
    }
    if target in df.columns:
        if pd.api.types.is_numeric_dtype(df[target]):
            eda["target_summary"] = {
                "mean": float(df[target].mean()),
                "std": float(df[target].std(ddof=0)),
                "min": float(df[target].min()),
                "max": float(df[target].max()),
            }
        else:
            eda["target_counts"] = df[target].value_counts(dropna=False).to_dict()
    return eda

# --------------------------
# Preprocesamiento
# --------------------------

def build_preprocessor(df: pd.DataFrame, target: str) -> Tuple[ColumnTransformer, List[str], List[str]]:
    features = [c for c in df.columns if c != target]
    num_cols = [c for c in features if pd.api.types.is_numeric_dtype(df[c])]
    cat_cols = [c for c in features if c not in num_cols]

    num_pipe = Pipeline(steps=[
        ("imputer", SimpleImputer(strategy="median")),
        ("scaler", StandardScaler(with_mean=True, with_std=True))
    ])

    cat_pipe = Pipeline(steps=[
        ("imputer", SimpleImputer(strategy="most_frequent")),
        ("ohe", OneHotEncoder(handle_unknown="ignore", sparse_output=False))
    ])

    pre = ColumnTransformer(
        transformers=[
            ("num", num_pipe, num_cols),
            ("cat", cat_pipe, cat_cols)
        ],
        remainder="drop",
        verbose_feature_names_out=False
    )
    return pre, num_cols, cat_cols

# --------------------------
# Modelado: Clasificación
# --------------------------

def fit_classifiers(X, y, preprocessor, pos_label: str, cv_seed: int = 42) -> Dict:
    models = {
        "logreg": LogisticRegression(max_iter=200, class_weight="balanced"),
        "rf": RandomForestClassifier(class_weight="balanced", n_jobs=-1, random_state=cv_seed)
    }
    param_dists = {
        "logreg": {"clf__C": np.logspace(-3, 2, 20)},
        "rf": {
            "clf__n_estimators": [200, 400, 600],
            "clf__max_depth": [None, 6, 10, 14],
            "clf__min_samples_split": [2, 5, 10]
        }
    }

    cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=cv_seed)
    results = {}

    for name, base in models.items():
        pipe = Pipeline(steps=[("pre", preprocessor), ("clf", base)])
        search = RandomizedSearchCV(
            estimator=pipe,
            param_distributions=param_dists[name],
            n_iter=20,
            scoring="roc_auc",
            cv=cv,
            n_jobs=-1,
            random_state=cv_seed,
            verbose=0
        )
        search.fit(X, y)
        results[name] = search
    # seleccionar mejor por ROC AUC CV
    best_name = max(results, key=lambda k: results[k].best_score_)
    return {"best_name": best_name, "searches": results}

def evaluate_classifier(clf, X_test, y_test, reports_dir: Path, pos_label: str) -> Dict:
    proba = clf.predict_proba(X_test)[:, 1]
    pred = clf.predict(X_test)
    roc = roc_auc_score(y_test, proba)
    pr = average_precision_score(y_test, proba)
    f1 = f1_score(y_test, pred, pos_label=pos_label, average="binary")
    bal = balanced_accuracy_score(y_test, pred)
    brier = brier_score_loss((y_test == pos_label).astype(int), proba)

    # Curvas
    fpr, tpr, _ = roc_curve(y_test, proba, pos_label=pos_label)
    prec, rec, _ = precision_recall_curve((y_test == pos_label).astype(int), proba)
    roc_auc = auc(fpr, tpr)
    pr_auc = auc(rec, prec)

    # Graficar
    ensure_dir(reports_dir)
    plt.figure()
    plt.plot(fpr, tpr, label=f"ROC AUC={roc_auc:.3f}")
    plt.plot([0,1],[0,1], linestyle="--")
    plt.xlabel("FPR")
    plt.ylabel("TPR")
    plt.title("Curva ROC")
    plt.legend()
    plt.tight_layout()
    plt.savefig(reports_dir / "roc_curve.png", dpi=150)
    plt.close()

    plt.figure()
    plt.plot(rec, prec, label=f"PR AUC={pr_auc:.3f}")
    plt.xlabel("Recall")
    plt.ylabel("Precision")
    plt.title("Curva Precision-Recall")
    plt.legend()
    plt.tight_layout()
    plt.savefig(reports_dir / "pr_curve.png", dpi=150)
    plt.close()

    return {
        "roc_auc": float(roc),
        "pr_auc": float(pr),
        "f1": float(f1),
        "balanced_accuracy": float(bal),
        "brier": float(brier)
    }

# --------------------------
# Modelado: Regresión
# --------------------------

def fit_regressors(X, y, preprocessor, cv_seed: int = 42) -> Dict:
    models = {
        "ridge": Ridge(random_state=None),
        "rf_reg": RandomForestRegressor(n_jobs=-1, random_state=cv_seed)
    }
    param_dists = {
        "ridge": {"reg__alpha": np.logspace(-3, 2, 20)},
        "rf_reg": {
            "reg__n_estimators": [200, 400, 600],
            "reg__max_depth": [None, 6, 10, 14],
            "reg__min_samples_split": [2, 5, 10]
        }
    }
    cv = KFold(n_splits=5, shuffle=True, random_state=cv_seed)
    results = {}

    for name, base in models.items():
        pipe = Pipeline(steps=[("pre", preprocessor), ("reg", base)])
        search = RandomizedSearchCV(
            estimator=pipe,
            param_distributions=param_dists[name],
            n_iter=20,
            scoring="neg_root_mean_squared_error",
            cv=cv,
            n_jobs=-1,
            random_state=cv_seed,
            verbose=0
        )
        search.fit(X, y)
        results[name] = search
    best_name = max(results, key=lambda k: results[k].best_score_)
    return {"best_name": best_name, "searches": results}

def evaluate_regressor(reg, X_test, y_test, reports_dir: Path) -> Dict:
    pred = reg.predict(X_test)
    rmse = mean_squared_error(y_test, pred, squared=False)
    mae = mean_absolute_error(y_test, pred)
    r2 = r2_score(y_test, pred)

    ensure_dir(reports_dir)
    plt.figure()
    plt.scatter(y_test, pred, s=10)
    lims = [min(y_test.min(), pred.min()), max(y_test.max(), pred.max())]
    plt.plot(lims, lims, linestyle="--")
    plt.xlabel("Valor real")
    plt.ylabel("Predicción")
    plt.title("Real vs Predicho")
    plt.tight_layout()
    plt.savefig(reports_dir / "real_vs_predicho.png", dpi=150)
    plt.close()

    return {"rmse": float(rmse), "mae": float(mae), "r2": float(r2)}

# --------------------------
# Importancias por permutación
# --------------------------

def compute_permutation_importance(model, X_test, y_test, feature_names: List[str], reports_dir: Path, is_classification: bool) -> Dict:
    # Extraer transformador para obtener nombres expandidos
    pre: ColumnTransformer = model.best_estimator_.named_steps["pre"]
    # Obtener nombres de salida del preprocesador
    try:
        out_names = pre.get_feature_names_out()
    except Exception:
        out_names = feature_names

    est_key = "clf" if is_classification else "reg"
    est = model.best_estimator_.named_steps[est_key]

    result = permutation_importance(
        model.best_estimator_, X_test, y_test,
        n_repeats=10, random_state=42, n_jobs=-1
    )
    importances = result.importances_mean
    order = np.argsort(importances)[::-1][:30]

    top_feats = [(str(out_names[i]), float(importances[i])) for i in order]
    # Figura
    ensure_dir(reports_dir)
    plt.figure(figsize=(8, max(4, len(order) * 0.25)))
    plt.barh([out_names[i] for i in order[::-1]], [importances[i] for i in order[::-1]])
    plt.xlabel("Importancia por permutación")
    plt.title("Top importancias")
    plt.tight_layout()
    plt.savefig(reports_dir / "importancias_permutacion.png", dpi=150)
    plt.close()

    return {"permutation_importance_top": top_feats}

# --------------------------
# Reporte Markdown + DataCard
# --------------------------

def write_markdown_report(path: Path, context: Dict):
    lines = []
    ap = lines.append
    ap(f"# Reporte de análisis — {context['dataset_name']}")
    ap("")
    ap(f"**Tarea**: {context['task']}")
    ap(f"**Filas/Columnas**: {context['eda']['n_rows']} / {context['eda']['n_cols']}")
    ap("")
    ap("## Metadatos")
    ap("```json")
    ap(json.dumps(context.get("meta", {}), ensure_ascii=False, indent=2))
    ap("```")
    ap("")
    ap("## EDA resumida")
    ap("```json")
    ap(json.dumps(context["eda"], ensure_ascii=False, indent=2))
    ap("```")
    ap("")
    ap("## Modelo y métricas")
    ap(f"- **Mejor estimador**: {context['best_model_name']}")
    ap("```json")
    ap(json.dumps(context["metrics"], ensure_ascii=False, indent=2))
    ap("```")
    ap("")
    ap("## Figuras")
    if context["task"] == "binary":
        ap("- `roc_curve.png`, `pr_curve.png`")
    else:
        ap("- `real_vs_predicho.png`")
    ap("- `importancias_permutacion.png`")
    ap("")
    ap("## Reproducibilidad")
    ap("- Semillas controladas, pipelines scikit-learn, búsqueda aleatoria acotada.")
    ap("- Artefactos guardados en `models/` y `reports/`.")
    ap("")
    path.write_text("\n".join(lines), encoding="utf-8")

# --------------------------
# Main
# --------------------------

def main():
    parser = argparse.ArgumentParser(description="EDA + Modelado reproducible para investigación.")
    parser.add_argument("--input", required=True, help="Ruta al CSV de entrada")
    parser.add_argument("--target", required=True, help="Nombre de la variable objetivo")
    parser.add_argument("--task", choices=["auto", "binary", "regression"], default="auto")
    parser.add_argument("--positive-label", default=None, help="Etiqueta positiva en clasificación binaria")
    parser.add_argument("--test-size", type=float, default=0.2)
    parser.add_argument("--random-state", type=int, default=42)
    args = parser.parse_args()

    csv_path = Path(args.input).resolve()
    df = pd.read_csv(csv_path)

    if args.target not in df.columns:
        raise ValueError(f"Target '{args.target}' no existe en columnas: {list(df.columns)}")

    # EDA
    eda = quick_eda(df, args.target)

    # Tarea
    task = detect_task(df[args.target], args.task)

    # Split
    y = df[args.target]
    X = df.drop(columns=[args.target])
    if task == "binary":
        y, pos_label = validate_binary(y, args.positive_label)
        stratify = y
    else:
        pos_label = None
        stratify = None

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=args.test_size, random_state=args.random_state,
        stratify=stratify if task == "binary" else None
    )

    # Preprocesador
    pre, num_cols, cat_cols = build_preprocessor(df, args.target)

    # Salidas
    reports_dir = ensure_dir(Path("reports") / csv_path.stem)
    models_dir = ensure_dir(Path("models") / csv_path.stem)

    # Entrenamiento
    if task == "binary":
        fit = fit_classifiers(X_train, y_train, pre, pos_label, cv_seed=args.random_state)
        best_key = fit["best_name"]
        search = fit["searches"][best_key]
        metrics = evaluate_classifier(search.best_estimator_, X_test, y_test, reports_dir, pos_label)
    else:
        fit = fit_regressors(X_train, y_train, pre, cv_seed=args.random_state)
        best_key = fit["best_name"]
        search = fit["searches"][best_key]
        metrics = evaluate_regressor(search.best_estimator_, X_test, y_test, reports_dir)

    # Importancias
    feat_names = list(X.columns)
    imp = compute_permutation_importance(search, X_test, y_test, feat_names, reports_dir, is_classification=(task=="binary"))

    # Guardar artefactos
    joblib.dump(search.best_estimator_, models_dir / f"best_model_{best_key}.joblib")
    save_json({"metrics": metrics, "best_model": best_key, "task": task}, reports_dir / "metrics.json")
    save_json(imp, reports_dir / "importances.json")

    # DataCard + Reporte
    meta = load_meta_if_any(csv_path)
    context = {
        "dataset_name": csv_path.name,
        "task": task,
        "eda": eda,
        "best_model_name": best_key,
        "metrics": metrics,
        "meta": meta
    }
    write_markdown_report(reports_dir / "REPORTE.md", context)

    print(json.dumps({
        "status": "ok",
        "task": task,
        "best_model": best_key,
        "reports_dir": str(reports_dir),
        "models_dir": str(models_dir)
    }, ensure_ascii=False, indent=2))

if __name__ == "__main__":
    main()

C. Prompts sugeridos para interpretar resultados con Copilot/ChatGPT

Copie y pegue según el contexto del proyecto. Ajuste el nombre de archivo y métricas.

  1. Resumen ejecutivo del modelo «Analiza el archivo reports/<dataset>/REPORTE.md y redacta un resumen ejecutivo de 150–200 palabras para un artículo académico. Incluye tarea, variables clave, técnica ganadora, y principales métricas (ROC AUC, PR AUC o RMSE, MAE, R²).»

  2. Interpretación de métricas de clasificación «Con base en reports/<dataset>/metrics.json, explica el significado práctico de ROC AUC, PR AUC, F1, Balanced Accuracy y Brier en el contexto del proyecto. Propón un umbral de decisión alternativo cuando el costo de falsos negativos sea mayor que el de falsos positivos.»

  3. Curvas y umbrales «Usa roc_curve.png y pr_curve.png para justificar un umbral operativo que optimice el equilibrio precisión/recobrado bajo desbalance. Entrega el umbral, TPR, FPR, Precisión y Recall esperados.»

  4. Importancias y sesgos potenciales «A partir de importancias_permutacion.png y importances.json, identifica tres variables con mayor contribución. Discute posibles sesgos y su mitigación. Sugiere controles o variables de confusión.»

  5. Sección Métodos para paper «Redacta la sección Métodos: EDA, división train/test, StratifiedKFold, RandomizedSearchCV, preprocesamiento (imputación, escalado, OHE), modelos evaluados y criterios de selección. Incluye justificación de class_weight="balanced" y de métricas elegidas.»

  6. Validez y reproducibilidad «Propón una sección Reproducibilidad describiendo versiones de librerías, control de semillas, almacenamiento de artefactos en models/ y reports/, y cómo re-ejecutar el pipeline en GitHub Codespaces.»

  7. Limitaciones y trabajo futuro «Redacta Limitaciones del estudio y una hoja de ruta: calibración Platt/Isotónica, validación externa, curva de aprendizaje, análisis de sensibilidad, y comparación con XGBoost o LightGBM


D. Indicaciones mínimas para Codespaces + Copilot

  1. Crear repo y Codespace en rama main.

  2. Archivo requirements.txt:

    pandas
    numpy
    scikit-learn
    matplotlib
    joblib
  3. Instalar:

    pip install -r requirements.txt
  4. Ejecutar:

    python analyze_datasets.py --input data/raw/tu_dataset.csv --target objetivo --task auto
  5. Ver artefactos: reports/<dataset>/ y models/<dataset>/.

¿Continuo con el punto 4 (paso a paso completo en GitHub Codespaces + Copilot, incluidos prompts operativos para que Copilot ejecute y confirme cada comando)?

4ta Consulta: Paso a Paso Completo en GitHub Codespaces + Copilot, incluidos prompts operativos para que Copilot ejecute y confirme cada comando

Si por favor continúa con el punto 4

4ta Respuesta: Paso a Paso Completo en GitHub Codespaces + Copilot

A continuación, el punto 4: desarrollo completo en GitHub Codespaces + Copilot. Este punto describe todo el flujo operativo —desde crear el repositorio hasta ejecutar los scripts— con prompts sugeridos para que Copilot genere, ejecute, documente y sincronice cada etapa del proyecto.


4) Desarrollo del proyecto en GitHub Codespaces + Copilot

Objetivo

Implementar y documentar completamente la propuesta de investigación: “Modelación Predictiva Asistida por IA en Ambientes de Desarrollo Colaborativo con GitHub Codespaces y Copilot”, con reproducibilidad total en la nube.


A. Estructura de carpetas recomendada

modelacion_predictiva_IA/
│
├── data/
│   ├── raw/                # Datos descargados de APIs
│   └── processed/          # Datos transformados
│
├── notebooks/              # Exploraciones y pruebas
│
├── models/                 # Modelos entrenados (.joblib)
│
├── reports/
│   └── <dataset>/          # Métricas, figuras y reportes Markdown
│
├── collect_datasets.py     # Script para obtener datasets (punto 2)
├── analyze_datasets.py     # Script para análisis y modelado (punto 3)
├── requirements.txt        # Librerías base
├── README.md               # Descripción y documentación del proyecto
└── .gitignore              # Archivos a excluir (p. ej. data/, modelos)

B. Creación del Repositorio y Codespace

Prompt para Copilot

💬 Prompt 1 “Crea un nuevo repositorio llamado modelacion_predictiva_IA en GitHub, con archivo README.md y .gitignore para Python. Luego, inicia un Codespace en la rama main.”

Resultado esperado: Codespace abierto con el entorno virtual de Python listo.


C. Instalación y entorno virtual

Prompt para Copilot

💬 Prompt 2 “Instala la extensión oficial de Python en este Codespace y crea un entorno virtual llamado .venv. Actívalo y verifica la versión de Python.”

Copilot generará los comandos:

python -m venv .venv
source .venv/bin/activate   # o .venv\Scripts\activate en Windows
python --version

D. Archivo de dependencias

Cree requirements.txt con:

pandas
numpy
scikit-learn
matplotlib
joblib
requests

Prompt para Copilot

💬 Prompt 3 “Crea el archivo requirements.txt con las librerías necesarias para el proyecto de modelación predictiva. Luego ejecuta el comando para instalarlas.”

Copilot generará:

pip install -r requirements.txt

E. Incorporación de scripts

Prompt para Copilot

💬 Prompt 4 “Crea el archivo collect_datasets.py y pega el código para explorar y descargar datasets desde APIs (Socrata, FAOSTAT, World Bank, HDX, OECD). Guarda el archivo en el repositorio.”

💬 Prompt 5 “Crea el archivo analyze_datasets.py y pega el código del análisis predictivo reproducible. Guarda el archivo en la raíz del proyecto.”


F. Prueba inicial de conexión a datasets

Ejemplo: buscar datasets sobre agricultura en todas las fuentes.

python collect_datasets.py search --source ALL --q "agriculture" --limit 3

Prompt para Copilot

💬 Prompt 6 “Ejecuta el comando anterior y muestra los primeros tres datasets encontrados por FAOSTAT y datos.gov.co. Guarda los resultados JSON en data/raw/catalogo.json.”


G. Descarga de dataset de prueba

Ejemplo: FAOSTAT — dominio QCL (Crops and Livestock).

python collect_datasets.py get --source FAOSTAT --id QCL --dest data/raw/

Prompt para Copilot

💬 Prompt 7 “Descarga el dominio QCL de FAOSTAT y confirma que el archivo se guardó en data/raw/faostat_QCL.csv. Muestra las primeras 5 filas con pandas.”


H. Ejecución del análisis predictivo

Supongamos que el dataset descargado tiene la columna Value como variable dependiente.

python analyze_datasets.py --input data/raw/faostat_QCL.csv --target Value --task auto

Prompt para Copilot

💬 Prompt 8 “Ejecuta el script de análisis predictivo con faostat_QCL.csv usando la columna Value como variable objetivo. Explica si el modelo detectó una tarea de regresión o clasificación. Resume las métricas obtenidas.”


I. Revisión de artefactos

Después de la ejecución, Copilot verificará:

tree reports/faostat_QCL

El directorio debe contener:

metrics.json
importances.json
roc_curve.png / real_vs_predicho.png
REPORTE.md

Prompt para Copilot

💬 Prompt 9 “Abre reports/faostat_QCL/REPORTE.md y redacta un resumen interpretativo de 200 palabras para incluir en un artículo científico.”


J. Actualización del repositorio en GitHub

Prompt para Copilot

💬 Prompt 10 “Agrega, confirma y sincroniza todos los cambios del proyecto en GitHub con un mensaje de commit ‘Versión inicial reproducible: datasets + modelado + reportes’. Usa los comandos Git desde la terminal.”

Copilot ejecutará:

git add .
git commit -m "Versión inicial reproducible: datasets + modelado + reportes"
git push origin main

K. Buenas prácticas y reproducibilidad

  1. Fijar semilla aleatoria en cada ejecución (--random-state 42).

  2. Registrar versiones:

    pip freeze > requirements_versions.txt
  3. Documentar cada corrida en README.md, incluyendo:

    • Fecha y hora
    • Dataset analizado
    • Tarea detectada (regresión o clasificación)
    • Métricas clave
    • Nombre del modelo ganador
  4. Evitar fuga de datos: validación con StratifiedKFold.

  5. Guardar resultados crudos en data/raw/ y procesados en data/processed/.

  6. No subir datasets sensibles; usar .gitignore para excluirlos.


L. Ejemplo de sección README.md generada automáticamente

💬 Prompt para Copilot “Genera el contenido del README.md del proyecto con las siguientes secciones:

  1. Propósito del proyecto
  2. Metodología reproducible
  3. Estructura del repositorio
  4. Ejemplos de uso de los scripts
  5. Reproducibilidad en GitHub Codespaces
  6. Referencias de las fuentes de datos.”

M. Posible extensión del proyecto

Copilot puede asistir en la ampliación del trabajo hacia:

  • Modelos XGBoost, LightGBM o CatBoost.
  • Despliegue web con Streamlit para visualización interactiva.
  • Integración con GitHub Actions para reentrenar modelos automáticamente.

Resumen

Etapa Archivo Acción
1 collect_datasets.py Explora y descarga datasets reales
2 analyze_datasets.py Realiza EDA, modelado y genera reportes
3 requirements.txt Define entorno reproducible
4 README.md Documentación generada con Copilot
5 git push Sincroniza proyecto con GitHub

¿Deseas que el siguiente paso sea la guía de despliegue en Streamlit Community Cloud, para publicar los resultados (modelos, métricas y figuras) como una página web dinámica?

5ta Consulta: Guía de despliegue en Streamlit Community Cloud

Excelente, si por favor la Guía de despliegue en Streamlit Community Cloud, como una página web dinámica

5ta Respuesta: Guía de despliegue en Streamlit Community Cloud

Guía lista. Despliegue en Streamlit Community Cloud como página web dinámica.


1) Estructura mínima del repositorio

modelacion_predictiva_IA/
├─ app.py
├─ collect_datasets.py
├─ analyze_datasets.py
├─ requirements.txt
├─ runtime.txt
├─ .gitignore
├─ .streamlit/
│  ├─ config.toml
│  └─ secrets.toml        # se crea en la nube (no subir a Git)
├─ data/
│  ├─ raw/
│  └─ processed/
├─ models/
└─ reports/

.gitignore

data/
models/
reports/
*.joblib
*.csv
*.json

requirements.txt

streamlit
pandas
numpy
scikit-learn
matplotlib
joblib
requests

runtime.txt

3.11

.streamlit/config.toml

[server]
headless = true
port = 8501
enableCORS = false

[theme]
base = "light"

Los secretos van en secrets.toml dentro de Streamlit Cloud: SOCRATA_APP_TOKEN="...", HDX_API_KEY="...", etc.


2) app.py (UI dinámica para explorar, ejecutar y visualizar)

Conecta con sus scripts. Ejecuta análisis y muestra reportes. Colóquelo en la raíz del repo.

# app.py
# Página dinámica para:
# 1) Descargar/explorar datasets (vía collect_datasets.py)
# 2) Ejecutar análisis reproducible (analyze_datasets.py)
# 3) Visualizar métricas, figuras y reporte en Markdown

import os
import json
import subprocess
from pathlib import Path
import streamlit as st
import pandas as pd

ROOT = Path(__file__).parent.resolve()
DATA_RAW = ROOT / "data" / "raw"
REPORTS = ROOT / "reports"

st.set_page_config(page_title="Modelación Predictiva IA", layout="wide")

# ---------------------------
# Utilidades
# ---------------------------

def list_csvs(folder: Path):
    folder.mkdir(parents=True, exist_ok=True)
    return sorted([p for p in folder.glob("*.csv")], key=lambda x: x.stat().st_mtime, reverse=True)

def run_cmd(cmd: list[str]) -> tuple[int, str, str]:
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    out, err = proc.communicate()
    return proc.returncode, out, err

def show_report(report_dir: Path):
    col1, col2 = st.columns([2, 1])
    metrics_path = report_dir / "metrics.json"
    rep_path = report_dir / "REPORTE.md"
    imp_path = report_dir / "importances.json"

    with col1:
        if (report_dir / "roc_curve.png").exists():
            st.image(str(report_dir / "roc_curve.png"), caption="Curva ROC")
            if (report_dir / "pr_curve.png").exists():
                st.image(str(report_dir / "pr_curve.png"), caption="Curva Precisión-Recall")
        if (report_dir / "real_vs_predicho.png").exists():
            st.image(str(report_dir / "real_vs_predicho.png"), caption="Real vs Predicho")

        if (report_dir / "REPORTE.md").exists():
            st.markdown(rep_path.read_text(encoding="utf-8"))

    with col2:
        if metrics_path.exists():
            st.subheader("Métricas")
            st.json(json.loads(metrics_path.read_text(encoding="utf-8")))
        if imp_path.exists():
            st.subheader("Importancias (Top)")
            st.json(json.loads(imp_path.read_text(encoding="utf-8")))

# ---------------------------
# Barra lateral
# ---------------------------

st.sidebar.title("Panel de control")
section = st.sidebar.radio("Sección", ["1) Catálogo y descarga", "2) Análisis", "3) Reportes"])

# ---------------------------
# 1) Catálogo y descarga
# ---------------------------

if section == "1) Catálogo y descarga":
    st.header("Explorar y descargar datasets por API")
    st.info("Fuentes: Socrata (datos.gov.co), FAOSTAT, Banco Mundial, HDX, OECD.")

    q = st.text_input("Término de búsqueda", value="agriculture")
    source = st.selectbox(
        "Fuente",
        ["ALL", "SOCRATA", "FAOSTAT", "WORLD_BANK", "HDX", "OECD"]
    )
    limit = st.slider("Límite de resultados", 1, 20, 5)

    if st.button("Buscar"):
        cmd = ["python", "collect_datasets.py", "search", "--source", source, "--q", q, "--limit", str(limit)]
        code, out, err = run_cmd(cmd)
        if code == 0:
            results = json.loads(out)
            st.success(f"{len(results)} resultados")
            st.json(results)
            # Persistir catálogo
            DATA_RAW.mkdir(parents=True, exist_ok=True)
            (DATA_RAW / "catalogo.json").write_text(json.dumps(results, ensure_ascii=False, indent=2), encoding="utf-8")
        else:
            st.error("Error en búsqueda")
            st.code(err)

    st.subheader("Descargar por ID")
    st.caption("Use el ID devuelto por la búsqueda. Ej.: SOCRATA: abcd-1234, FAOSTAT: QCL, WORLD_BANK: SP.POP.TOTL")
    d_src = st.selectbox("Fuente de descarga", ["SOCRATA", "FAOSTAT", "WORLD_BANK", "HDX", "OECD"])
    d_id = st.text_input("ID del recurso / indicador / dominio", value="")
    if st.button("Descargar"):
        if not d_id:
            st.warning("Indique un ID.")
        else:
            cmd = ["python", "collect_datasets.py", "get", "--source", d_src, "--id", d_id, "--dest", str(DATA_RAW)]
            code, out, err = run_cmd(cmd)
            st.subheader("Salida")
            st.code(out)
            if code != 0:
                st.error(err)

    st.subheader("CSV disponibles en data/raw")
    csvs = list_csvs(DATA_RAW)
    if csvs:
        sel = st.selectbox("Archivo", csvs, format_func=lambda p: p.name)
        df = pd.read_csv(sel).head(1000)
        st.write(df.head())
        st.download_button("Descargar CSV", data=sel.read_bytes(), file_name=sel.name)
    else:
        st.write("No hay CSV en data/raw")

# ---------------------------
# 2) Análisis
# ---------------------------

elif section == "2) Análisis":
    st.header("Ejecutar análisis reproducible")
    csvs = list_csvs(DATA_RAW)
    if not csvs:
        st.warning("No hay CSV en data/raw. Descargue primero en la sección 1.")
    else:
        sel = st.selectbox("Seleccione dataset", csvs, format_func=lambda p: p.name)
        df = pd.read_csv(sel).head(1000)
        st.write(df.head())

        target = st.text_input("Columna objetivo (target)", value="")
        task = st.selectbox("Tarea", ["auto", "binary", "regression"])
        pos_label = st.text_input("Etiqueta positiva (opcional)")
        test_size = st.slider("Test size", 0.1, 0.4, 0.2, step=0.05)
        seed = st.number_input("Random state", min_value=0, max_value=9999, value=42, step=1)

        if st.button("Ejecutar análisis"):
            if not target:
                st.warning("Indique la columna objetivo.")
            elif target not in df.columns:
                st.error("La columna objetivo no existe en el CSV.")
            else:
                cmd = [
                    "python", "analyze_datasets.py",
                    "--input", str(sel),
                    "--target", target,
                    "--task", task,
                    "--test-size", str(test_size),
                    "--random-state", str(seed)
                ]
                if pos_label.strip():
                    cmd += ["--positive-label", pos_label.strip()]
                code, out, err = run_cmd(cmd)
                if code == 0:
                    st.success("Ejecución completada.")
                    st.code(out)
                else:
                    st.error("Error en ejecución.")
                    st.code(err)

# ---------------------------
# 3) Reportes
# ---------------------------

else:
    st.header("Visualización de reportes")
    # cada subcarpeta en reports corresponde al stem del CSV analizado
    report_dirs = sorted([p for p in REPORTS.glob("*") if p.is_dir()], key=lambda x: x.stat().st_mtime, reverse=True)
    if not report_dirs:
        st.info("Aún no hay reportes. Ejecute un análisis.")
    else:
        sel_rep = st.selectbox("Seleccione reporte", report_dirs, format_func=lambda p: p.name)
        show_report(sel_rep)

3) Pasos de despliegue en Streamlit Community Cloud

  1. Push a GitHub con los archivos anteriores en la rama main.

  2. Ir a https://share.streamlit.io/New app.

  3. Seleccionar el repositorio, rama y archivo principal app.py.

  4. En Advanced settings:

    • Python version: Streamlit respetará runtime.txt (3.11).

    • Secrets: pegar variables en secrets.toml (panel de Secrets), por ejemplo:

      SOCRATA_APP_TOKEN = "xxxx"
      HDX_API_KEY = "xxxx"
  5. Presionar Deploy.

  6. La app quedará disponible en una URL del tipo: https://<usuario>-<repo>-<rama>.streamlit.app/

Nota operativa: si la app entra en “sleep”, use Settings → Restart. Para proyectos con uso continuo, mantenga tráfico periódico o considere un backend con “cron” externo que haga “health checks”.


4) Prompts sugeridos para Copilot durante el despliegue

  • Crear archivos base “Genera requirements.txt, .gitignore, runtime.txt y .streamlit/config.toml para un proyecto Streamlit con scikit-learn y pandas. No incluyas datos.”

  • Verificación local (Codespaces) “Ejecuta streamlit run app.py y abre el puerto publicado. Comprueba que se listan CSV en data/raw y que se renderiza REPORTE.md.”

  • Documentación “Añade al README.md una guía de despliegue en Streamlit Community Cloud con capturas de uso y advertencias de repositorios públicos.”

  • Sincronización Git “Ejecuta git add . && git commit -m "App Streamlit dinámica" && git push origin main.”


5) Consejos de producción

  • Mantenga datasets fuera del repo. Cárguelos con collect_datasets.py desde la app o suba CSVs manualmente al contenedor efímero.
  • Si algún API exige token, defínalo en Secrets. No lo suba al repositorio.
  • Para procesos pesados, reduzca tamaño de muestra en la app y ejecute el entrenamiento completo offline, subiendo solo artefactos a reports/ y models/.

Hecho. Si desea, preparo un template de README.md listo para copiar y pegar con capturas y secciones estándar.

6ta Consulta: Template de README.md

Por favor prepara un template de README.md listo para copiar y pegar con capturas y secciones estándar.

6ta Respuesta: Template de README.md

Aquí tienes el README.md modelo para tu proyecto: puedes copiarlo directamente a la raíz del repositorio de GitHub antes del despliegue.


Modelación Predictiva Asistida por IA en Ambientes de Desarrollo Colaborativo con GitHub Codespaces y Copilot

Universidad de Sucre — Proyecto de Investigación Docente

Autor: Profesor Daniel Otero Colaboradores: Estudiantes de Ingeniería Agrícola, Agroindustrial y Civil Año: 2025 Licencia: MIT


1. Propósito del Proyecto

Este proyecto demuestra la implementación reproducible de un flujo completo de modelación predictiva basada en IA generativa, utilizando GitHub Codespaces como entorno colaborativo y Copilot como asistente de codificación e interpretación.

El propósito principal es:

  • Integrar descarga automática de datasets reales desde fuentes gubernamentales e institucionales (Socrata, FAOSTAT, World Bank, HDX, OECD).
  • Ejecutar análisis predictivo reproducible con Python, Scikit-learn y documentación generada por IA.
  • Desplegar una página web interactiva en Streamlit Community Cloud que permita ejecutar análisis, visualizar métricas, curvas ROC y reportes científicos.

2. Arquitectura Reproducible

El flujo completo se desarrolla íntegramente en la nube:

GitHub Codespaces → Copilot → Streamlit Cloud
  1. Codespaces: entorno Python sin instalación local.
  2. Copilot: generación y ejecución guiada de código.
  3. Streamlit Cloud: visualización y publicación del modelo como página web dinámica.

3. Estructura del Repositorio

modelacion_predictiva_IA/
│
├── app.py                     # Aplicación Streamlit principal
├── collect_datasets.py        # Descarga de datasets por API
├── analyze_datasets.py        # EDA y modelado automático
│
├── data/
│   ├── raw/                   # Datos crudos descargados
│   └── processed/             # Datos limpios o transformados
│
├── models/                    # Modelos entrenados (.joblib)
├── reports/                   # Métricas, figuras y reportes Markdown
│
├── requirements.txt
├── runtime.txt
├── .gitignore
└── .streamlit/
    ├── config.toml
    └── secrets.toml (no subir al repositorio)

4. Instalación en GitHub Codespaces

  1. Crear el repositorio y abrirlo en Codespaces.

  2. Instalar las dependencias:

    pip install -r requirements.txt
  3. Crear el entorno virtual si no existe:

    python -m venv .venv
    source .venv/bin/activate
  4. Verificar la versión:

    python --version

5. Ejecución de los scripts

A. Descarga de datos

Buscar datasets reales:

python collect_datasets.py search --source ALL --q "agriculture" --limit 5

Descargar dataset de FAOSTAT (ejemplo):

python collect_datasets.py get --source FAOSTAT --id QCL --dest data/raw/

B. Análisis predictivo automático

Ejecutar análisis con detección automática de tarea:

python analyze_datasets.py --input data/raw/faostat_QCL.csv --target Value --task auto

Genera:

  • Reporte Markdown (REPORTE.md)
  • Figuras (roc_curve.png, pr_curve.png, importancias_permutacion.png)
  • Métricas (metrics.json)

6. Despliegue en Streamlit Community Cloud

  1. Realizar push a GitHub con todos los archivos.

  2. Ingresar a https://share.streamlit.io.

  3. Seleccionar:

    • Repositorio: modelacion_predictiva_IA
    • Rama: main
    • Archivo principal: app.py
  4. En el panel de Secrets, agregar:

    SOCRATA_APP_TOKEN = "xxxxx"
    HDX_API_KEY = "xxxxx"
  5. Presionar Deploy y esperar el despliegue.

La aplicación quedará disponible en:

https://<usuario>-modelacion-predictiva-ia-main.streamlit.app/

7. Uso de la aplicación

Sección 1 – Catálogo y descarga: Buscar y descargar datasets reales por API.

Sección 2 – Análisis: Seleccionar dataset, definir variable objetivo y ejecutar el análisis reproducible.

Sección 3 – Reportes: Visualizar métricas, curvas ROC/PR, importancias de variables y reportes generados.


8. Buenas prácticas de investigación

  1. Fijar la semilla aleatoria (--random-state 42) para reproducibilidad.
  2. Documentar cada ejecución con fecha y modelo ganador.
  3. Evitar fuga de datos: usar validación estratificada.
  4. Excluir datasets y modelos de gran tamaño mediante .gitignore.
  5. Usar variables de entorno en secrets.toml para credenciales.

9. Ejemplo de pipeline reproducible

Paso Script Acción Salida
1 collect_datasets.py Obtiene datasets por API Archivos CSV en data/raw
2 analyze_datasets.py Realiza EDA y modelado Reportes y modelos
3 app.py Interfaz Streamlit Página web interactiva
4 git push Sincroniza cambios Repositorio actualizado

10. Créditos y referencia institucional

Proyecto desarrollado en el marco del Segundo Período Académico 2025, según la Resolución No. 50 de 2025 del Consejo Académico de la Universidad de Sucre, que aprueba la oferta académica vigente.

Líneas de publicación sugeridas:

  • Computers and Electronics in Agriculture (Elsevier)
  • Revista Colombiana de Estadística
  • Applied Sciences (MDPI), sección AI and Data Analytics in Engineering

11. Cita sugerida

Otero, D. (2025). Modelación Predictiva Asistida por IA en Ambientes de Desarrollo Colaborativo con GitHub Codespaces y Copilot. Universidad de Sucre. https://github.com//modelacion_predictiva_IA


¿Deseas que te genere también un README versión corta para Streamlit Cloud, adaptado como portada visible en la app (solo con descripción, flujo y créditos)?

7ta Consulta

Genera también un README versión corta para Streamlit Cloud, adaptado como portada visible en la app (solo con descripción, flujo y créditos)

7ta Respuesta

Aquí tienes la versión corta del README.md adaptada para que se muestre como portada visible dentro de la aplicación en Streamlit Cloud. Guárdala como README_streamlit.md o pégala directamente al inicio de app.py usando st.markdown(open("README_streamlit.md").read()).


🌎 Modelación Predictiva Asistida por IA

GitHub Codespaces + Copilot + Streamlit Community Cloud

Universidad de Sucre — 2025


🎯 Propósito

Proyecto académico que integra:

  • Descarga automática de datasets reales desde fuentes oficiales (FAOSTAT, Socrata, World Bank, HDX, OECD).
  • Análisis predictivo reproducible con Scikit-learn y generación automática de reportes científicos.
  • Despliegue dinámico en Streamlit Community Cloud, con visualización de métricas, curvas ROC y resultados.

⚙️ Flujo reproducible

GitHub Codespaces → Copilot → Streamlit Cloud
  1. Exploración: obtención de datos abiertos mediante API.
  2. Modelado: análisis predictivo (regresión o clasificación).
  3. Visualización: métricas, figuras y reportes Markdown.
  4. Publicación: aplicación interactiva accesible desde la web.

🧠 Componentes principales

Archivo Descripción
collect_datasets.py Explora y descarga datasets desde APIs.
analyze_datasets.py Realiza EDA, modelado y genera reportes.
app.py Interfaz interactiva en Streamlit.
requirements.txt Librerías requeridas para reproducir el entorno.

📊 Funciones de la aplicación

1️⃣ Catálogo y descarga

Busca datasets reales por palabra clave y fuente institucional.

2️⃣ Análisis reproducible

Selecciona un dataset, define la variable objetivo y ejecuta el modelado automático.

3️⃣ Reportes

Visualiza métricas, curvas ROC/PR, importancia de variables y reporte científico.


🔬 Referencias institucionales

Proyecto desarrollado en el marco del Segundo Período Académico 2025, según la Resolución No. 50 de 2025 del Consejo Académico de la Universidad de Sucre.

Líneas sugeridas de publicación:

  • Computers and Electronics in Agriculture (Elsevier)
  • Revista Colombiana de Estadística
  • Applied Sciences (MDPI)AI and Data Analytics in Engineering

👨‍🏫 Créditos

Autor: Profesor Daniel Otero Colaboradores: Estudiantes de Ingeniería Agrícola, Agroindustrial y Civil Institución: Universidad de Sucre — Sincelejo, Colombia Licencia: MIT


¿Deseas que te prepare también un banner visual minimalista (en imagen PNG) con el título y subtítulo del proyecto para colocar en la cabecera de la app Streamlit?

Modelación_Predictiva_Asistida_por_IA
Modelación_Predictiva_Asistida_por_IA

Conclusión y Proyección Institucional

El desarrollo de la propuesta “Modelación Predictiva Asistida por IA en Ambientes de Desarrollo Colaborativo con GitHub Codespaces y Copilot” demuestra que la combinación de tecnologías de inteligencia artificial, entornos de programación en la nube y plataformas de despliegue abiertas constituye una alternativa sólida para fortalecer la investigación aplicada y la docencia universitaria contemporánea.

Desde la perspectiva metodológica, el proyecto consolida un ciclo de investigación completamente reproducible, que abarca desde la recolección automatizada de datos hasta la visualización de resultados en tiempo real mediante una aplicación web interactiva. Esta integración permite que los docentes y estudiantes desarrollen competencias clave en:

  • Ciencia de datos aplicada a la ingeniería,
  • Programación colaborativa asistida por IA,
  • Ética y trazabilidad en el manejo de la información,
  • Comunicación científica a través de reportes generados automáticamente.

A nivel institucional, la implementación de este modelo ofrece un marco replicable para proyectos de aula, semilleros de investigación y trabajos de grado, facilitando la publicación de resultados en entornos abiertos y la alineación con políticas de ciencia abierta y educación digital.

La experiencia con GitHub Codespaces + Copilot + Streamlit Cloud valida el potencial de la IA generativa no como sustituto del pensamiento científico, sino como un instrumento de apoyo cognitivo que amplía la capacidad de análisis, documentación y difusión del conocimiento académico.

En proyección futura, se recomienda:

  • Extender el modelo hacia proyectos interdisciplinarios en ingeniería, economía, salud y ambiente.
  • Integrar análisis con Shiny (R) y Dash (Python) para entornos híbridos R–Python.
  • Desarrollar módulos docentes que documenten las mejores prácticas de reproducibilidad y autoría asistida por IA.

En síntesis, este trabajo refleja una visión institucional de innovación responsable, orientada a consolidar una cultura de investigación científica moderna, ética y sustentada en la cooperación entre humanos y sistemas inteligentes.