Mostrando entradas con la etiqueta Diccionario. Mostrar todas las entradas
Mostrando entradas con la etiqueta Diccionario. Mostrar todas las entradas

martes, 15 de septiembre de 2026

LENGUAJES. Python

Diccionarios complejos (III)

Colecciones de datos

En entradas anteriores analizamos cómo estructurar los datos básicos de un expediente educativo en formato JSON (nombre, curso, nee) y cómo gestionar colecciones sencillas como la lista de medidas_apoyo.

Sin embargo también necesitamos almacenar información más compleja de tipo evolutivo, como la que supone el campo intervenciones, un elemento complejo formado por una lista de diccionarios. En esta entrada nos centraremos en cómo trabajar con este tipo de datos mediante Python.


import json
import os
from collections import Counter

# 1. Definimos la ruta del archivo JSON (puedes ajustarla según tu estructura de carpetas)
RUTA_ARCHIVO = "D:/Tu_Ubicacion/archivo_JSON/exped_orienta.JSON"

def cargar_datos_expediente(ruta):
    """Carga y devuelve el contenido completo del archivo JSON."""
    if not os.path.exists(ruta):
        print(f" Error: El archivo no existe en la ruta: '{ruta}'")
        return None

    try:
        with open(ruta, "r", encoding="utf-8") as f:
            return json.load(f)
    except json.JSONDecodeError:
        print(f" Error: El archivo '{ruta}' no tiene un formato JSON válido.")
        return None
    except Exception as e:
        print(f" Error inesperado al leer el archivo: {e}")
        return None


def mostrar_datos (datos_totales):
    """
    Itera sobre todos los registros, muestra las intervenciones,
    el total de actuaciones por alumno y un resumen estadístico global.
    """
    if not datos_totales:
        print("No hay datos para mostrar.")
        return

    total_registros = len(datos_totales)
    sumatorio_actuaciones = 0
    conteo_actuaciones_por_alumno = []

    print("=" * 65)
    print(f" REPORTES DE INTERVENCIONES - TOTAL REGISTROS: {total_registros}")
    print("=" * 65)

    # Iteramos sobre cada alumno en el diccionario principal
    for id_alumno, expediente in datos_totales.items():
        intervenciones = expediente.get("intervenciones", [])
        num_actuaciones = len(intervenciones)

        # Actualizamos métricas globales
        sumatorio_actuaciones += num_actuaciones
        conteo_actuaciones_por_alumno.append(num_actuaciones)

        print(f"\n>>> EXPEDIENTE: {id_alumno}")
        print("-" * 45)

        if not intervenciones:
            print("  (Sin intervenciones registradas)")
        else:
            for idx, sesion in enumerate(intervenciones, 1):
                fecha = sesion.get("fecha", "Sin fecha")
                motivo = sesion.get("motivo", "Sin especificar")
                acuerdos = sesion.get("acuerdos", "Sin acuerdos registrados")

                print(f"  [{idx}] Fecha:   {fecha}")
                print(f"      Motivo:  {motivo}")
                print(f"      Acuerdos: {acuerdos}\n")

        # Muestra del total al final de cada expediente
        print(f"  --> Total de actuaciones en {id_alumno}: {num_actuaciones}")

    # ---------------------------------------------------------
    # RESUMEN ESTADÍSTICO FINAL
    # ---------------------------------------------------------
    print("\n" + "=" * 65)
    print(" RESUMEN ESTADÍSTICO DE ACTUACIONES")
    print("=" * 65)
    print(f" • Total de alumnos/registros analizados: {total_registros}")
    print(f" • Sumatorio total de actuaciones:        {sumatorio_actuaciones}")
    print("\n Distribución de registros según número de actuaciones:")

    # Contamos la frecuencia de cada cantidad de actuaciones
    distribucion = Counter(conteo_actuaciones_por_alumno)

    # Ordenamos por número de actuaciones de menor a mayor
    for num_act, cant_alumnos in sorted(distribucion.items()):
        porcentaje = (cant_alumnos / total_registros) * 100
        print(
            f"   - Registros con {num_act} actuaciones: "
            f"{cant_alumnos} alumno(s) ({porcentaje:.1f}%)"
        )

    print("=" * 65)


# --- EJECUCIÓN DEL SCRIPT ---
if __name__ == "__main__":
    datos_json = cargar_datos_expediente(RUTA_ARCHIVO)
    mostrar_datos (datos_json)


Con este primer script cargamos un archivo JSON que contiene los datos desde una ubicación que será distintan en cada caso y que en este script de identifica como RUTA_ARCHIVO = "D:/Tu_Ubicacion/archivo_JSON/exped_orienta.JSON" siendo exped_orienta.JSON el nombre del archivo (datos inventados) que puedes descargar desde este enlace. Así sólo tienes que ajustar la ruta en el script.

La primera función del script def cargar_datos_expediente(ruta): se encarga de cargar el archivo, controlando los posibles errores que se pudieran cometer, por lo que se puede simplificar una vez que, como programadores de nuestro script y usuarios, ya tengamos asegurada la carga del documento JSON. En ese caso la función podría quedar simplificada como sigue:



def cargar_datos_expediente(ruta):
    """Carga y devuelve el contenido completo del archivo JSON."""
    with open(ruta, "r", encoding="utf-8") as f:
        return json.load(f)


El siguiente paso consiste en acceder a los datos y mostrarlos por pantalla. Para ello implementamos en el script la segunda función def mostrar_datos (datos_totales):, que además de mostrar el listado de registros con las actuaciones de cada uno de ellos, también realiza un recuento de cuantas actuaciones tiene cada registro y finaliza con una pequeña estadística que se muestra al final del listado. Por este motivo esta función se vuelve especialmente extensa y compleja, requiendo una explicación de cada una de sus partes.

Además de controlar la existencia de datos en el archivo JSON, en el inicio de la función se establecen unas variables que nos servirán para realizar los recuentos solicitados...



if not datos_totales:
        print("No hay datos para mostrar.")
        return

    total_registros = len(datos_totales)
    sumatorio_actuaciones = 0
    conteo_actuaciones_por_alumno = []


... y después se define el formato de la cabecera del informe que se imprimirá en su momento en el CMD



print("=" * 65)
    print(f" REPORTES DE INTERVENCIONES - TOTAL REGISTROS: {total_registros}")
    print("=" * 65)


Pero es a partir de este punto que empezamos a construir la estructura del contenido de ese informe, recorriendo el contenido del archivo...



# Iteramos sobre cada alumno en el diccionario principal
    for id_alumno, expediente in datos_totales.items():
        intervenciones = expediente.get("intervenciones", [])
        num_actuaciones = len(intervenciones)

        # Actualizamos métricas globales
        sumatorio_actuaciones += num_actuaciones
        conteo_actuaciones_por_alumno.append(num_actuaciones)


... y accediendo a los datos del diccionario anidado en el campo...



print(f"\n>>> EXPEDIENTE: {id_alumno}")
        print("-" * 45)

        if not intervenciones:
            print("  (Sin intervenciones registradas)")
        else:
            for idx, sesion in enumerate(intervenciones, 1):
                fecha = sesion.get("fecha", "Sin fecha")
                motivo = sesion.get("motivo", "Sin especificar")
                acuerdos = sesion.get("acuerdos", "Sin acuerdos registrados")

                print(f"  [{idx}] Fecha:   {fecha}")
                print(f"      Motivo:  {motivo}")
                print(f"      Acuerdos: {acuerdos}\n")

        # Muestra del total al final de cada expediente
        print(f"  --> Total de actuaciones en {id_alumno}: {num_actuaciones}")


El cuarto bloque de esta función está dedicada a la composición del conteo de datos con el que finaliza el informe que se muestra por pantalla, por lo que se puede omitir si no se desea esta información.



  # ---------------------------------------------------------
    # RESUMEN ESTADÍSTICO FINAL
    # ---------------------------------------------------------
    print("\n" + "=" * 65)
    print(" RESUMEN ESTADÍSTICO DE ACTUACIONES")
    print("=" * 65)
    print(f" • Total de alumnos/registros analizados: {total_registros}")
    print(f" • Sumatorio total de actuaciones:        {sumatorio_actuaciones}")
    print("\n Distribución de registros según número de actuaciones:")

    # Contamos la frecuencia de cada cantidad de actuaciones
    distribucion = Counter(conteo_actuaciones_por_alumno)

    # Ordenamos por número de actuaciones de menor a mayor
    for num_act, cant_alumnos in sorted(distribucion.items()):
        porcentaje = (cant_alumnos / total_registros) * 100
        print(
            f"   - Registros con {num_act} actuaciones: "
            f"{cant_alumnos} alumno(s) ({porcentaje:.1f}%)"
        )

    print("=" * 65)


Además de mostrar los registros (las actuaciones) por pantalla, también podemos realizar modificaciones sobre esos contenidos. Una de ellas (posiblemente la de mayor interés en la práctica) es la de añadir una nueva actuación dentro del registro de un determinado alumno. Para ello añadiremos este script al anterior.



# =============================================================================
# FASE 2: EDICIÓN E INCREMENTO DE INTERVENCIONES
# =============================================================================

def seleccionar_alumno(datos_totales):
    """
    Presenta los alumnos con un índice numérico para seleccionar uno.
    """
    print("\n" + "=" * 60)
    print(" SELECCIÓN DE ALUMNO / REGISTRO PARA AÑADIR INTERVENCIÓN")
    print("=" * 60)

    lista_ids = list(datos_totales.keys())

    for idx, id_alumno in enumerate(lista_ids, 1):
        nombre = datos_totales[id_alumno].get("nombre", "Sin nombre")
        num_intervenciones = len(datos_totales[id_alumno].get("intervenciones", []))
        print(f"  [{idx}] ID: {id_alumno} | Nombre: {nombre} ({num_intervenciones} intervenciones)")

    print("-" * 60)

    while True:
        opcion = input(f"Seleccione un alumno (1-{len(lista_ids)}): ").strip()
        if opcion.isdigit() and 1 <= int(opcion) <= len(lista_ids):
            return lista_ids[int(opcion) - 1]
        print(" Opción no válida. Introduzca un número de la lista.")


def capturar_nueva_intervencion():
    """
    Captura por consola los campos del nuevo diccionario anidado.
    """
    print("\n--- NUEVA ACTUACIÓN ---")
    fecha = input("• Fecha (AAAA-MM-DD): ").strip()
    motivo = input("• Motivo de la intervención: ").strip()
    acuerdos = input("• Acuerdos alcanzados: ").strip()

    return {
        "fecha": fecha if fecha else "Sin fecha",
        "motivo": motivo if motivo else "Sin especificar",
        "acuerdos": acuerdos if acuerdos else "Sin acuerdos registrados"
    }

def agregar_intervencion_interactiva(datos_totales):
    """
    Permite agregar de forma iterativa intervenciones en el mismo registro
    o cambiar a otros expedientes.
    """
    while True:
        id_alumno = seleccionar_alumno(datos_totales)
        expediente = datos_totales[id_alumno]

        if "intervenciones" not in expediente:
            expediente["intervenciones"] = []

        # Bucle para añadir varias intervenciones al mismo alumno
        while True:
            print(f"\nAñadiendo actuación para: {id_alumno} ({expediente.get('nombre', '')})")
            
            nueva_actuacion = capturar_nueva_intervencion()
            expediente["intervenciones"].append(nueva_actuacion)
            
            print(f"\n [OK] Actuación añadida. Total actual en {id_alumno}: {len(expediente['intervenciones'])}")

            mas_mismo = input("\n¿Desea añadir OTRA intervención a ESTE MISMO alumno? (s/n): ").strip().lower()
            if mas_mismo != "s":
                break

        mas_otros = input("\n¿Desea seleccionar OTRO alumno para añadir intervenciones? (s/n): ").strip().lower()
        if mas_otros != "s":
            print("\n Finalizada la sesión de altas.")
            break


Con él creamos un listado de alumnos para identificar aquel en el que queremos añadir una nueva actuación def seleccionar_alumno(datos_totales): y después añadimos los datos de esa actuación def agregar_intervencion_interactiva(datos_totales):, previa identificación de sus campos def capturar_nueva_intervencion():

Además podemos añadir más de una actuación respecto al mismo alumno, o añadir actuaciones realizadas con otro alumno def agregar_intervencion_interactiva(datos_totales):

Hasta el momento sólo hemos consultado los registros de actuaciones, identificado a un alumno en concreto e incrementado las actuaciones que se han realizado respecto a ese alumno, pero si dejamos así el script perderemos los cambios introducidos. Para que esto no suceda deberemos implementar otro segmento de código a nuestro script que se encargue de guardar en la dirección que corresponda un nuevo archivo JSON.



# =============================================================================
# FASE 3: PERSISTENCIA Y ACTUALIZACIÓN EN DISCO
# =============================================================================

def guardar_datos_actualizados(datos_totales, ruta=RUTA_ARCHIVO):
    """
    Sobrescribe el archivo JSON original con la estructura de datos
    actualizada en memoria, aplicando formato indentado.
    """
    print("\n" + "=" * 60)
    print(" GUARDANDO CAMBIOS EN EL ARCHIVO JSON")
    print("=" * 60)

    # Sobrescribimos el archivo con la información acumulada en memoria
    with open(ruta, "w", encoding="utf-8") as f:
        json.dump(datos_totales, f, ensure_ascii=False, indent=4)

    print(f" [OK] El archivo '{ruta}' se ha actualizado correctamente.")
    

En este segmento sólo se identifica una función def guardar_datos_actualizados(datos_totales, ruta=RUTA_ARCHIVO): cuyo nombre es suficientemente expresivo y que no requiere más explicación.

Lo que sí es necesario (ahora) es modificar el segmento inicial de la función if __name__ == "__main__": para incluir las nuevas funcinalidades. El resultado es el que sigue. Corre de tu cuenta actualizarla en el script final que construyas con los segmentos que te he ido proporcionando.



# =============================================================================
# BLOQUE PRINCIPAL DE EJECUCIÓN
# =============================================================================

if __name__ == "__main__":

    # 1. Cargar archivo JSON
    datos_json = cargar_datos_expediente(RUTA_ARCHIVO)

    # 2. Mostrar datos iniciales y resumen estadístico
    mostrar_datos(datos_json)

    # 3. Iniciar proceso interactivo de alta de intervenciones
    agregar_intervencion_interactiva(datos_json)

    # 4. Guardar los cambios acumulados en el archivo JSON
    guardar_datos_actualizados(datos_json, RUTA_ARCHIVO)


Podríamos implementar más utilidades dentro de este script, pero para el objetivo de la entrada del blog creo que tenemos más que suficiente. Puede que lo qua haga falta sea crear un procedimiento de trabajo que incluya la creación, actualización, manejo y archivo de un documentoJSON a modo de base de datos. En ese caso habría que identificar el objetivo y la estructura de contenidos. Un sistema de seguimiento de actuaciones como el que hemos simulado podría ser un buen tema de trabajo.

viernes, 7 de agosto de 2026

LENGUAJES. Python

Diccionarios complejos (II)

Colecciones de datos

En un diccionario complejo el value (par clave:valor) de un posible "campo" puede estar formado por una lista de datos ([]), de modo que el tratamiento de esos datos haga encesario implementar estrategias específicas. Sin pretender ser exhaustivos, ese tratamiento incluye operaciones como conteo, visualización y consulta (de toda la lista o de un elemento), añadido, supresión o modificación... En esta entrada estudiaremos cómo realizar estas operaciones.

Ya que por algún punto hay que empezar, nos situaremos en el siguiente contexto: hemos definido la estructura de diccionario...



 "ALU-cod-n": {
        "nombre": "al_nombre",
        "curso": "al_curso",
        "nee": true,
        "medidas_apoyo": [
            "mad_1"
        ],
        "intervenciones": [
            {
                "fecha": "int_aa_mm_dd",
                "motivo": "int_mot",
                "acuerdos": "int_acuerdos"
            }
        ]
    },


... ya sabemos cómo añadir elementos a los "campos" simples nombre - curso - nee...



import json
import os

def solicitar_booleano(mensaje):
    """
    Solicita una respuesta booleana al usuario garantizando que la entrada sea s/n.
    """
    while True:
        respuesta = input(f"{mensaje} (s/n): ").strip().lower()
        if respuesta in ['s', 'si', 'sí']:
            return True
        elif respuesta in ['n', 'no']:
            return False
        print(" [!] Entrada no válida. Por favor, introduzca 's' para Sí o 'n' para No.")

def capturar_ficha(codigo_num):
    cod_alumno = f"ALU-cod-{codigo_num}"
    print(f"\n==========================================")
    print(f"  NUEVA FICHA DE EXPEDIENTE: {cod_alumno}")
    print(f"==========================================")
    
    print("\n--- AVISO DE FORMATO DE ENTRADA ---")
    print("  * Nombre del alumno: Apellidos, Nombre (ejemplo: García López, María)")
    print("  * Curso: Etapa y grupo (ejemplo: 2º ESO B  o  1º Bachillerato A)")
    print("-------------------------------------\n")
    
    nombre = input("Nombre completo del alumno (ej: García López, María): ").strip()
    curso = input("Curso y grupo (ej: 2º ESO B): ").strip()
    nee = solicitar_booleano("¿Presenta Necesidades Educativas Especiales (NEE)?")
    

... pero todavía no sabemos cómo introducir datos en el "campo" "medidas_apoyo": [] cuyo value es una lista, como podemos identificar por el uso corchetes [] como delimitadores. El script que sigue cubre esta necesidad, aunque tiene una funcionalidad limitada.



import json
import os


def solicitar_booleano(mensaje):
    """Solicita una respuesta booleana al usuario garantizando que la entrada sea s/n."""
    while True:
        respuesta = input(f"{mensaje} (s/n): ").strip().lower()
        if respuesta in ["s", "si", "sí"]:
            return True
        elif respuesta in ["n", "no"]:
            return False
        print(
            " [!] Entrada no válida. Por favor, introduzca 's' para Sí o 'n' para No."
        )


# --- FUNCIÓN Cumplimentar datos de medidas_apoyo ---------------------------------------

def capturar_medidas_apoyo():
    """Captura de forma interactiva la lista de medidas de apoyo."""
    medidas = []
    quiere_anadir = solicitar_booleano(
        "¿Desea registrar alguna medida de apoyo?"
    )

    contador = 1
    while quiere_anadir:
        medida = input(f" -> Ingrese la medida de apoyo #{contador}: ").strip()
        if medida:
            medidas.append(medida)
            contador += 1
        quiere_anadir = solicitar_booleano("¿Desea añadir otra medida de apoyo?")

    return medidas


# --- FUNCIÓN PRINCIPAL de ejecución. -----------------------------------------------

def capturar_ficha(codigo_num):
    cod_alumno = f"ALU-cod-{codigo_num}"
    print(f"\n==========================================")
    print(f"  NUEVA FICHA DE EXPEDIENTE: {cod_alumno}")
    print(f"==========================================")

    print("\n--- AVISO DE FORMATO DE ENTRADA ---")
    print(
        "  * Nombre del alumno: Apellidos, Nombre (ejemplo: García López, María)"
    )
    print(
        "  * Curso: Etapa y grupo (ejemplo: 2º ESO B  o  1º Bachillerato A)"
    )
    print("-------------------------------------\n")

    nombre = input("Nombre completo del alumno (ej: García López, María): ").strip()
    curso = input("Curso y grupo (ej: 2º ESO B): ").strip()
    nee = solicitar_booleano(
        "¿Presenta Necesidades Educativas Especiales (NEE)?"
    )

    # 1. Llamamos a la nueva utilidad para generar la lista
    medidas_apoyo = capturar_medidas_apoyo()

    # 2. Retornamos el diccionario completo con el nuevo campo
    registro = {
        "codigo_alumno": cod_alumno,
        "nombre": nombre,
        "curso": curso,
        "nee": nee,
        "medidas_apoyo": medidas_apoyo,
    }

    return registro

# ==============================================================================
# BLOQUE DE EJECUCIÓN PRINCIPAL
# ==============================================================================
if __name__ == "__main__":
    # Solicitamos o asignamos el número identificador para el código de alumno
    num_expediente = 1

    # Llamada a la función principal para capturar los datos interactivos
    expediente_alumno = capturar_ficha(num_expediente)

    # Mostramos el resultado por pantalla con formato JSON legible
    print("\n==========================================")
    print("      REGISTRO GENERADO CON ÉXITO")
    print("==========================================")
    print(json.dumps(expediente_alumno, indent=4, ensure_ascii=False))


Este script complementa el de creación de registro añadiendo, a partir de def capturar_medidas_apoyo(): le procedimiento para obtener los primeros posibles datos de medidas_apoyo. Esta función es llamada desde la función principal def capturar_ficha(codigo_num): y se basa, en lo fundamental, en el bucle while quiere_anadir: que permite al usuario introducir cuantas medidas de apoyo desee.

Resuelto el procedimiento de creación de datos, nos situamos ahora en el segundo contexto: ya disponemos de un archivo JSON que contiene datos suficientes como para que sea pertinente realizar diferentes operaciones con la información de medidas_apoyo de los distintos registros-diccionarios.

En este contexto empezamos por acceder al archivo JSON para trabajar con él desde el script (en memoria) como diccionario complejo. Este procedimiento ya nos es conocido.



import json

# Ruta absoluta al archivo JSON
ruta_archivo = r"C:\MisDatosPropios\exped_orienta.JSON"

# 1. Cargar el contenido del archivo JSON en memoria
with open(ruta_archivo, "r", encoding="utf-8") as archivo:
    expedientes = json.load(archivo)
    

De las entradas anteriores sabemos crear el listado las claves principales, acceder al contenido de un archivo concreto o a todos ellos. También sabemos manipular el contenido de deteminados campos. Ahora nos detendremos en el acceso y las posibles modificaciones del contenido del campo cuyo value es una lista. Empezaremos por obtener el listado completo de registros mostrando cuántas y cuales son las medidas educativas en cada uno de ellos.



import json

# Ruta absoluta al archivo JSON
ruta_archivo = r"C:\MisDatosPropios\exped_orienta.JSON"

try:
    # 1. Cargar el contenido del archivo JSON en memoria
    with open(ruta_archivo, "r", encoding="utf-8") as archivo:
        expedientes = json.load(archivo)

    print("\n==================================================")
    print("    LISTADO COMPLETO DE EXPEDIENTES Y MEDIDAS     ")
    print("==================================================\n")

    # Adaptación dinámica según si el JSON es una lista [...] o un diccionario {...}
    registros = (
        expedientes.items()
        if isinstance(expedientes, dict)
        else enumerate(expedientes, 1)
    )

    for id_registro, datos in registros:
        # Obtenemos la clave principal (clave del dict o campo 'codigo_alumno')
        if isinstance(datos, dict):
            clave_principal = datos.get("codigo_alumno", f"Registro #{id_registro}")
            medidas = datos.get("medidas_apoyo", [])
        else:
            clave_principal = str(id_registro)
            medidas = datos if isinstance(datos, list) else []

        # Contamos la cantidad total de elementos en la lista
        total_medidas = len(medidas)

        # Imprimimos la salida formateada en CMD
        print(f"📌 Clave Principal: {clave_principal}")
        print(f"   Total de medidas educativas aplicadas: {total_medidas}")

        if total_medidas > 0:
            print("   Listado de medidas:")
            for i, medida in enumerate(medidas, 1):
                print(f"      {i}. {medida}")
        else:
            print("   Listado de medidas: (Sin medidas registradas)")

        print("-" * 50)

except FileNotFoundError:
    print(f" [!] Error: No se encontró el archivo en la ruta '{ruta_archivo}'.")
except json.JSONDecodeError:
    print(
        " [!] Error: El archivo JSON no tiene un formato válido o está corrupto."
    )


Podríamos dirigir nuestra atención al análisis del contenido de este "campo", pero dejaremos estos procedimientos para una tercera fase del estudio de los diccionarios y nos atendremos a explicar los procedimientos de manejo y modificación del value lista, si bien no es muy diferente del del que ya estudiamos en el tratamiento de estas colecciones de datos.

Al tratarse de un diccionario ya desarrollado en cuanto a su contenido, lo primero que deberemos hacer es identificar y acceder al registro de nuestro interés. Para ello debemos usar la clave principal /(vg. "ALU-2024-01") y, a partir de ahí a la clave "medidas_apoyo".

Disponemos de dos procedimientos para acceder a los datos de la lista. Bien directamente identificando al alumno por su clave:



import json

# Ruta absoluta al archivo JSON
ruta_archivo = r"C:\MisDatosPropios\exped_orienta.JSON"

# 1. Cargar el contenido del archivo JSON en memoria
with open(ruta_archivo, "r", encoding="utf-8") as archivo:
    expedientes = json.load(archivo)

# 2. Identificamos directamente al alumno por su clave

id_alumno = "ALU-2024-01"

# 3. Verificar existencia y mostrar las medidas elemento por elemento
if id_alumno in expedientes:
    print(f"Medidas de apoyo para {id_alumno}:")
    for medida in expedientes[id_alumno]["medidas_apoyo"]:
        print(f"- {medida}")



... o bien hacerlo de forma interactiva introducciendo por input() el identificador de su clave, suponiendo que tenemos acceso a un listado de claves y nombres que nos facilitan la identificación del sujeto deseado.



import json

# Ruta absoluta al archivo JSON
ruta_archivo = r"C:\MisDatosPropios\exped_orienta.JSON"

# 1. Cargar el contenido del archivo JSON en memoria
with open(ruta_archivo, "r", encoding="utf-8") as archivo:
    expedientes = json.load(archivo)
    
# 2. Listado de claves-sujetos

listado = [f"{clave} - {datos['nombre']}" for clave, datos in expedientes.items()]
for al in listado:
    print(al)

# 3. Solicitar la clave directamente al usuario
clave = input("Introduce la clave del expediente: ")

# 4. Acceder al valor del campo 'medidas_apoyo' del registro indicado
medidas = expedientes[clave]["medidas_apoyo"]

# 5. Mostrar el contenido actual
print("Medidas de apoyo actuales:", medidas)


Una vez que accedemos al listado de medidas, podemos limitarnos a mostrarlo, pero también podemos actuar sobre él. Veamos algunas acciones posibles. Se escriben a continuación del script anterior).



print("\n--- ACCIONES SOBRE LAS MEDIDAS ---")

# (1) Conocer el número de medidas
print(f"\n4. Número total de medidas registradas: {len(medidas)}")

# (2) Añadir una nueva medida
nueva = input("1. Introduce la nueva medida a añadir: ").strip()
medidas.append(nueva)
print("   Lista actualizada:", medidas)

# (3) Borrar una medida
a_borrar = input("\n2. Introduce la medida a eliminar: ").strip()
medidas.remove(a_borrar)
print("   Lista tras borrar:", medidas)

# (4) Ordenar alfabéticamente las medidas
medidas.sort()
print("\n3. Medidas ordenadas alfabéticamente:", medidas)

# (5) Saber si existe una determinada medida
buscar = input("\n5. Consulta si existe una medida concreta: ").strip()
print(f"   -> ¿Existe '{buscar}' en la lista?: {buscar in medidas}")


Lo suyo sería actualizar el archivo JSON, pero por ahora no es necesario ya que sólo estamos aprendiendo a trabajar con los componentes del diccionario.

martes, 4 de agosto de 2026

LENGUAJES. Python

Diccionarios complejos (I)

Colecciones de datos

En las dos entradas previas en las que hemos trabajado con diccionarios hemos empleado de forma intencional formulaciones simples de esta colección de datos. Se trataba de comprender en qué consistía y cómo se podía trabajar con ella, así que la simplicidad se imponía como necesidad. Pero la realidad es que un diccionario "normal" no suele ser tan sencillo ni su virtud o interés requiere de que los sea.

En esta entrada idearemos una estructura compleja (y más realista, incluso) de un diccionario y trabajaremos con ella. Se trata de comprobar la realidad y la utilidad real de un diccionario. Para ello vamos a plantearlo como colección de datos potencialmente aplicable a un proyecto real de trabajo (como SEO) aunque ajustado a las necesidades didácticas del momento.

Imaginemos que sobre una estructura de diccionario queremos construir un recurso de seguimiento del alumnado. Se trata de una forma de expediente, muy parecido a lo que se podría dar soporte con una base de datos, pero también registrar y mantener como un documento JSON. Algo como esto:



 "ALU-cod-n": {
        "nombre": "al_nombre",
        "curso": "al_curso",
        "nee": true,
        "medidas_apoyo": [
            "mad_1"
        ],
        "intervenciones": [
            {
                "fecha": "int_aa_mm_dd",
                "motivo": "int_mot",
                "acuerdos": "int_acuerdos"
            }
        ]
    },


Muestro aquí la estructura de un elemento del documento CSV sin contenidos ni reales ni supuestos (eso viene después) para que podemos observar en qué consiste la complejidad de la que estamos hablando cuando lo trasladamos a diccionario (ya sabemos que estructura y contenido se mantienen): ahora cada elemento del diccionario - aquí "ALU-cod-n": - es la clave de un value que es a su vez un diccionario, de ahí que el conjunto se pueda considerar como un diccionario de diccionarios.

Ese value además de ser un diccionario (de ahí que todo él se encuentre delimitado por {}, es un diccionario complejo ya que sus diferentes elementos-campos lo son de diferente tipología:

  • Tenemos contenidos-campos clave:valor simples (y de distinto tipo de datos), como "nombre": "al_nombre"
  • Pero también un contenido-campo cuyo value es una lista "medidas_apoyo": ["mad_1"],
  • Y más aun, un contenido-campo cuyo value es una lista de diccionarios (de la que ahora sólo muestro su primer componente clave:valor por simplificar: "intervenciones": [{"fecha": "int_aa_mm_dd",}]

Sobra decir que debermos conocer cómo tratar a cada subestructura o tipo de componente, tanto como con el conjunto. Empezaremos con ello a continuación.



import json
import os

def solicitar_booleano(mensaje):
    """
    Solicita una respuesta booleana al usuario garantizando que la entrada sea s/n.
    """
    while True:
        respuesta = input(f"{mensaje} (s/n): ").strip().lower()
        if respuesta in ['s', 'si', 'sí']:
            return True
        elif respuesta in ['n', 'no']:
            return False
        print(" [!] Entrada no válida. Por favor, introduzca 's' para Sí o 'n' para No.")

def capturar_ficha(codigo_num):
    cod_alumno = f"ALU-cod-{codigo_num}"
    print(f"\n==========================================")
    print(f"  NUEVA FICHA DE EXPEDIENTE: {cod_alumno}")
    print(f"==========================================")
    
    print("\n--- AVISO DE FORMATO DE ENTRADA ---")
    print("  * Nombre del alumno: Apellidos, Nombre (ejemplo: García López, María)")
    print("  * Curso: Etapa y grupo (ejemplo: 2º ESO B  o  1º Bachillerato A)")
    print("-------------------------------------\n")
    
    nombre = input("Nombre completo del alumno (ej: García López, María): ").strip()
    curso = input("Curso y grupo (ej: 2º ESO B): ").strip()
    nee = solicitar_booleano("¿Presenta Necesidades Educativas Especiales (NEE)?")
    
    # Se mantienen las estructuras de listas para su posterior implementación
    return cod_alumno, {
        "nombre": nombre,
        "curso": curso,
        "nee": nee,
        "medidas_apoyo": [],
        "intervenciones": []
    }

def guardar_en_json(nuevos_expedientes, ruta_salida):
    # Limpieza de comillas iniciales o finales introducidas por el usuario
    ruta_salida = ruta_salida.strip('\"\'')
    ruta_salida = os.path.normpath(ruta_salida)
    
    # 1. Comprobar si el directorio existe y crearlo si no
    directorio = os.path.dirname(ruta_salida)
    if directorio and not os.path.exists(directorio):
        os.makedirs(directorio, exist_ok=True)
        print(f" [+] Directorio creado: {os.path.abspath(directorio)}")
        
    datos_acumulados = {}
    
    # 2. Comprobar si el archivo JSON existe para cargar su contenido previo
    if os.path.exists(ruta_salida):
        try:
            with open(ruta_salida, mode="r", encoding="utf-8") as archivo:
                contenido = archivo.read().strip()
                if contenido:
                    datos_acumulados = json.loads(contenido)
            print(f" [+] Archivo existente detectado. Se añadirán los nuevos registros a los {len(datos_acumulados)} existentes.")
        except Exception as e:
            print(f" [!] Advertencia: No se pudo leer el archivo existente ({e}). Se iniciará un JSON nuevo.")
            datos_acumulados = {}
    else:
        print(" [+] El archivo no existe. Se creará un nuevo archivo JSON.")

    # 3. Anexar/actualizar los registros nuevos sobre el objeto cargado
    datos_acumulados.update(nuevos_expedientes)
    
    # 4. Sobrescribir el archivo con el diccionario actualizado en formato JSON legible
    with open(ruta_salida, mode="w", encoding="utf-8") as archivo:
        json.dump(datos_acumulados, archivo, ensure_ascii=False, indent=4)
        
    return os.path.abspath(ruta_salida)

def main():
    expedientes_sesion = {}
    contador = 1
    
    print("----------------------------------------------------------------")
    print(" SISTEMA DE REGISTRO DE EXPEDIENTES ACADÉMICOS")
    print("----------------------------------------------------------------")
    
    # Bucle de captura continua de fichas
    while True:
        codigo, ficha = capturar_ficha(contador)
        expedientes_sesion[codigo] = ficha
        contador += 1
        
        if not solicitar_booleano("\n¿Desea incorporar la ficha de otro alumno?"):
            break
            
    # Guardado de datos en JSON
    if expedientes_sesion and solicitar_booleano("\n¿Desea guardar los expedientes recopilados en un archivo JSON local?"):
        print("\n=========================================================================")
        print(" INSTRUCCIONES PARA LA RUTA DEL ARCHIVO JSON:")
        print(" 1. NO use comillas al principio ni al final (ejemplo: NO use \"C:\\datos\\alumnos.json\")")
        print(" 2. Puede usar barras inclinadas normales ( / ) o barras invertidas ( \\ ).")
        print(" 3. Asegúrese de incluir la extensión .json al final del nombre.")
        print(" 4. Ejemplos de rutas válidas:")
        print("    - Windows (ruta absoluta): C:/MisDatos/expedientes.json  ó  C:\\MisDatos\\expedientes.json")
        print("    - Carpeta actual (ruta relativa): ./expedientes.json  ó  expedientes.json")
        print("=========================================================================\n")
        
        ruta_archivo = input("Introduzca la ruta y nombre del archivo JSON: ").strip()
        try:
            ruta_final = guardar_en_json(expedientes_sesion, ruta_archivo)
            print(f"\n[OK] Registros guardados con éxito en:\n     {ruta_final}")
        except Exception as e:
            print(f"\n[ERROR] No se pudo guardar el archivo JSON: {e}")
            
    print("\nCierre de sesión del sistema.")

if __name__ == "__main__":
    main()



Aunque el script resultante sea mejorable, ya resulta suficientemente complejo (además de funcional) como para que nos hagamos una idea de qué estamos planteando cuando implementamos un diccionario de este nivel de complejidad. Lo que hemos pretendido es ejemplificar lo que sería la primera y básica implementación de una utilidad de este tipo: obtener del usuario los datos básicos (y simples, se podría añadir) que componen la estructura del diccionario y archivar esa información partiendo desde cero y en formato JSON. Cualquier proyecto de este tipo debe contener estas funcionalidades y ajustarse a la estructura del JSON deseado.

Pero si el contexto primero es correcto (y realista), el que planteo a continuación también lo es. Dando por supuesto el exito del anterior, creamos ahora una utilidad de acceso a los datos. Partimos de que ya tenemos algunos creados y deseamos acceder a ellos, entendiendo que ya hemos recopilado cierto volumen de información, disponible como archivo JSON. Evidentemente los datos que incluímos son inventados (sintéticos, que se dice) y no nos vamos a detener en valorar su ajuste a la realidad ni su interés pedagógico real. Para que lo que aquí te presento te sea de ayuda, proporcionaré acceso al documento "inventado" al final de esta entrada.

Dicho lo anterior, lo que ahora necesitamos es un script que nos permita acceder al documento JSON para trabajar con él en memoria como diccionario.



import json

# Ruta absoluta al archivo JSON
ruta_archivo = r"C:\MisDatosPropios\exped_orienta.JSON"

# 1. Cargar el contenido del archivo JSON en memoria
with open(ruta_archivo, "r", encoding="utf-8") as archivo:
    expedientes = json.load(archivo)

# 2. Obtener la clave y los datos del primer registro
primer_id = list(expedientes.keys())[0]
primer_registro = expedientes[primer_id]

# 3. Mostrar el resultado por pantalla (CMD)
print(f"--- PRIMER REGISTRO: {primer_id} ---")
print(json.dumps(primer_registro, ensure_ascii=False, indent=4))


Dado que estamos trabajando con archivos JSON, lo primero que haremos será importar la biblioteca correspondiente import json para cargar en memoria el archivo expedientes = json.load(archivo) desde su ubicación local ruta_archivo y accedemos a él with open() as archivo. Después obtenemos las claves de todos los archivos en formato lista list(expedientes.keys()) y el contenido del primer registro primer_registro = expedientes[primer_id] y lo mostramos por CMD print(json.dumps(primer_registro, ensure_ascii=False, indent=4)).

Mediante este par de script abordamos las dos maniobras básicas que deberemos realizar con diccionarios, simples o complejos. Pero como hay tarea para rato, parece conveniente finalizar ahora esta entrada.

Archivo de datos (formato JSON). Acceso para descarga desde este enlace. Deberás ajustar la dirección ruta_archivo a la que tú elijas como directorio del archivo que has descargado.

jueves, 30 de julio de 2026

LENGUAJES. Python

Diccionarios (II)

Colecciones de datos

Damos continuidad en esta entrada a la temática iniciada en la anterior sobre los diccionarios Python y sus usos. Ahora trataremos cuestiones básicas de manejo de los diccionarios y su contenido.



#A. Procedimientos directos de creación de diccionarios simples ========================

# 1. Creación estándar con llaves

usuario_ana = {
    "nombre": "Ana",
    "edad": 28,
    "lenguajes": ["Python", "JavaScript"],
    "activo": True
}

# 2. Creación con la función dict()

usuario_carlos= dict(nombre="Carlos", edad=35,lenguajes=["OOo Basic","Python"],activo=True)

#B. Acceso a datos =================================================

# 1. Acceso directo con llaves

print(usuario_ana["nombre"])  # Salida: Ana
print(usuario_carlos["edad"])  #Salida: 35


# 2. Acceso seguro con .get() (evita que el programa falle si la clave no existe)

print(usuario_ana.get("edad"))  # Salida: True
print(usuario_carlos.get("salario", "No especificado"))  # Salida: No especificada

#3. Acceso cuando el valor es una lista 

# Cuando el valor es una lista, sólo con la clave obtenemos como valor la propia lista

print(usuario_ana.get("lenguajes"))  # Salida: ["Python", "JavaScript"]

# 3.1. Usando corchetes. Obtener el primer elemento de la lista (índice 0)

primer_lenguaje = usuario_ana["lenguajes"][0]
print(primer_lenguaje)  # Salida: Python

# 3.2. Usando get(). Obtener el primer elemento con .get()

primer_lenguaje = usuario_carlos.get("lenguajes")[0]
print(primer_lenguaje)  # Salida: OOo Basic

# C. Modificar datos: cambiar, borrar y añadir datos ===============================

# 1. Modificar un valor existente
usuario_ana["edad"] = 29

# 2. Agregar una nueva pareja clave-valor
usuario_ana["ciudad"] = "Madrid"

# 3. Agregar elemento a un valor-lista (ej. lenguajes:[lista_lenguajes]

# 3.1. Agregar un nuevo elemento a la lista 'lenguajes' con append()
usuario_ana["lenguajes"].append("C++")

# (3.1.b) Si no estás seguro de que exista la clave 'lenguajes' usa setdefault() para crearlo y luego añade el elemento con append()
usuario_ana.setdefault("lenguajes", []).append("LUA")

# 3.2. Agregar un nuevo elemento a la lista 'lenguajes' usando .extend() (varios elementos)
usuario_carlos["lenguajes"].extend(["JavaScript", "C++", "PHP"])

# (3.2.b) ...o usando +=
usuario_ana["lenguajes"] += ["OOo Basic"]

print(usuario_carlos["lenguajes"])
print(usuario_ana["lenguajes"])

# 4. Borrar

# 4.1 Eliminar una clave específica con 'del'
del usuario_ana["activo"] #Elimina el par activo:True

# Extraer y eliminar un valor con .pop()
edad = usuario_ana.pop("edad") # Elimina el valor (29) del par "edad":29

# Limpiar todo el diccionario
usuario_carlos.clear() # Elimina todo el diccionario (usuario_carlos)

print(usuario_ana.get("activo", "No especificado")) #con get() evitamos mensaje de error. Devuelve mensaje ("No especificado")
print(edad) #Devuelve el valor del par 'edad':29
print(usuario_ana.get('edad')) #Devuelve None por defecto (ya que no se espifica mensaje)
print (usuario_carlos) # Devuelve un diccionario vacío ({})


Reproduzco en este script la creación de dos diccionarios simples para desarrollar la lógica de posibles manipulaciones sobre un diccionario y sus contenidos. La división del script en cuatro partes y la explicitación como comentarios de cada una de las posibles acciones facilita la comprensión del contenido y agiliza esta explicación complementaria.

Podemos diferenciar tres tipos de actuaciones sobre un diccionario:

  • Los procedimientos de acceso a los datos (sección B)
    • mediante el uso de llaves print(usuario_ana["nombre"]) - Salida: Ana
    • mediante la función get() para controlar el posible error de acceso
    • y el acceso en el caso específico de valores-lista, bien para visualizar la lista completa de valores print(usuario_ana.get("lenguajes")), bien para acceder a uno de ellos en concreto, por ejemplo, print(primer_lenguaje = usuario_ana["lenguajes"][0]), mediante el uso de corchetes
  • La modificación de datos (cambiar o añadir) (sección C del script)
    • mediante procedimientos simples usuario_ana["edad"] = 29 de cambio
    • o usuario_ana["ciudad"] = "Madrid" para añadir clave:valor, ambos basados en el uso de corchetes
    • o más complejos como es el caso de la modificación de valores-listas, que requieren el uso de funciones como append(), (sóla en combinación con setdefault()) para añadir un elemento-valor o la función extend() para añadir varios.
  • Finalmente, en la sección D tratamos el borrado de datos: clave: valor del usuario_ana["activo"], sólo valor edad = usuario_ana.pop("edad") y del diccionario completo usuario_carlos.clear()

Finalizo aquí esta segunda entrada sobre los diccionarios, pero aun hay materia para más...

miércoles, 29 de julio de 2026

LENGUAJES. Python

Diccionarios (I)

Colecciones de datos

Aunque ya sabemos algo sobre diccionarios en Python, lo cierto es que en esta subsección de LENGUAJES hemos hablado mucho de listas (y de tuplas) y muy poco de diccionarios, al menos mucho menos de lo que se merece.

Cuando uno comienza a programar en OOo Basic deberá trabajar con listas o arrays. Si después programa en Python lo "lógico" es que traslade su interés al uso las listas. Además las listas en Python son sencillas e intuitivas de usar y muy útiles.

Pero Python es mucho más rico y complejo que OOo Basic también es esto de las colecciones de datos y los diccionarios son también expresión de esa superioridad: un diccionario mejora la organización de los datos y también la velocidad y precisión del acceso, que está en función del significado y no de la posición como en las listas.

Un diccionario es una colección de elementos organizados mediante pares clave → valor (key:value) en la que cada dato-valor (que sí se puede modificar y duplicar) se almacena asociado a una clave única (por lo que es inmutable y única).



persona = {
    "nombre": "Ana",
    "edad": 34,
    "profesion": "Ingeniera"
}


En este caso el diccionario persona consta de tres claves y cada clave contiene un dato o valor. v.g. la clave "nombre" contiene el valor "Ana"

Si estuviéramos trabajando con listas persona = ["Ana", 34, "Ingeniera"] (1), para obtener la profesión deberíamos expresarlo como persona[2], mientras que como diccionario diremos persona["profesion"]. En el caso de las listas, cualquier modificación del contenido implica un cambio en el procedimiento de acceso que deberemos conocer de antemano; en el caso de los diccionarios es suficiente con conocer el nombre de la clave.

Si lo anterior no fuera suficiente motivo para entender las ventajas de los diccionarios sobre las listas, piensa en la cantidad de "objetos" del mundo real que se pueden expresar como diccionarios:

  • ejemplo: alumno - identificador - nombre - curso - etapa - centro - resultados académicos - MAD - ...
  • Pero también archivo - ruta - tamaño - fecha...

Estos "objetos" y otros muchos se pueden expresar en estructuras clave - valor. Además, Python implementa internamente los diccionarios mediante tablas hash (2), lo que hace que la búsqueda de un elemento por su clave sea, en condiciones normales, extremadamente rápida. En la práctica, acceder a un valor suele realizarse en tiempo constante, independientemente del tamaño del diccionario.

El método más habitual para crear un diccionario consiste en utilizar llaves, como en provincias = {}, instrucción con la que creamos un diccionario vacío que después podemos dotar de contenido, aunque también es posible (y muy frecuente) crear directamente el diccionario definiendo su estructura y contenido, como en este caso...



provincias = {
    "Asturias": "Oviedo",
    "León": "Leon",
    "Cantabria": "Santander"
}


... aunque también se puede crear un diccionario mediante la función dict() - provincias=dict().



# Bien...

provincias = dict(
    Asturias="Oviedo",
    Leon="León",
    Cantabria="Santander"
)

# O también...

provincias = dict([
    ("Asturias", "Oviedo"),
    ("León", "León"),
    ("Cantabria", "Santander")
])


Aunque esta es la forma simple de expresar un diccionario, lo normal es que su contenido sea más complejo. Pensemos, por ejemplo, que creamos el diccionario provincias pero queremos que contenga una colección de datos (capital - población_2025 - peso_sector_primario , peso_sector_secundario, peso_sector_terciario. Ahora ya no hay un ínico dato asociado a la clave, por lo que debemos expresar esta complejidad de otro modo. Y caben dos posibilidades:

  • Como lista provincia:[capital - población_2025 - peso_sector_primario , peso_sector_secundario]
  • o Como diccionarios anidados

Veamos el desarrollo de ambas posibilidades en un script.



# Valor como lista de valores

provincias = {
    "Asturias": ["Oviedo", 1004326, 4.1, 22.7, 73.2],
    "León": ["León", 442000, 8.5, 24.3, 67.2],
    "Cantabria": ["Santander", 593000, 3.8, 20.6, 75.6]
}


# Valor como diccionario anidado lista:valor

provincias = {
    "Asturias": {
        "capital": "Oviedo",
        "poblacion_2025": 1004326,
        "sector_primario": 4.1,
        "sector_secundario": 22.7,
        "sector_terciario": 73.2
    },

    "León": {
        "capital": "León",
        "poblacion_2025": 442000,
        "sector_primario": 8.5,
        "sector_secundario": 24.3,
        "sector_terciario": 67.2
    },

    "Cantabria": {
        "capital": "Santander",
        "poblacion_2025": 593000,
        "sector_primario": 3.8,
        "sector_secundario": 20.6,
        "sector_terciario": 75.6
    }
}


Aunque la primera formulación parece más sencilla de crear, en realidad su manejo resulta más complicado, por lo que es preferible recurrir a los diccionarios anidados por varias razones que no vamos a analizar aquí. Además, los diccionarios anidados constituyen la representación natural en Python de la estructura de un documento JSON. Esta correspondencia casi directa explica que los diccionarios anidados sean la estructura de datos preferida para trabajar con información procedente de diferentes fuentes, espcialmente con las que se expresan como archivos JSON.

Entre otras ventajas, esto hace posible que...



import json

with open("provincias.json", "w", encoding="utf-8") as archivo:
    json.dump(provincias, archivo, ensure_ascii=False, indent=4)


... nos permita almacenar los datos en un documento en formato JSON, el cual podremos visualizar desde bloc de notas.

{ "Asturias": { "capital": "Oviedo", "poblacion_2025": 1004326, "sector_primario": 4.1, "sector_secundario": 22.7, "sector_terciario": 73.2 }, "León": { "capital": "León", "poblacion_2025": 442000, "sector_primario": 8.5, "sector_secundario": 24.3, "sector_terciario": 67.2 }, "Cantabria": { "capital": "Santander", "poblacion_2025": 593000, "sector_primario": 3.8, "sector_secundario": 20.6, "sector_terciario": 75.6 } }

Como puedes comprobar, el archivo JSON reproduce prácticamente la misma estructura jerárquica que nuestro diccionario. Esta correspondencia es precisamente una de las razones por las que los diccionarios tienen un papel tan importante en Python cuando se trabaja con datos estructurados.

Para realizar el proceso inverso (cargar en Python un archivo JSON) deberemos ejecutar este script:



import json

with open("provincias.json", "r", encoding="utf-8") as archivo:
    provincias = json.load(archivo)


Llegados a este punto parece evidente que los diccionarios dan para algo más que una única entrada. Este es un buen momento para reconocerlo y dar por finalizada la actual. Continuará...

NOTAS
1 Esta sería una de las formas en que se podría expresar lo mismo como lista. Pero no es la única. Personalmente, para casos como ese, yo previero usar un procedimiento multilista (una por "campo") y trabajar después con procedimientos de acceso basados precisamente en las posiciones de los elementos de cada una de ellas.
2 Sin entrar en explicaciones que poco nos aportan aquí, una tabla hash es una estructura de datos que utiliza una función hash para transformar una clave arbitraria en un índice numérico que determina dónde se almacenará o buscará el dato asociado dentro de una tabla. Esto hace que se puedan localizar los datos igual de rápido con independencia del tamaño del diccionario (tiempo constante o O(1)), ya que evita la búsqueda secuencial.

jueves, 5 de marzo de 2026

DATOS. Acceso a datos

CSV. Datos estructurados (III)

Acceso y descarga de csv

Vamos a centrarnos en este script en el trabajo con un archivo .csv, pensando no en el acceso (tema ya tratado en esta entrada), sino en tener disponible su contendio desde el script. Eso sí, seguiremos usando el módulo propio CSV, si bien ahora en la opción de diccionario.

Para que se entienda mejor el proceso a seguir, empezaremos por el acceso al archivo .csv. La diferencia principal respecto al script parejo de la entrada anterior es que sustituímos lector = csv.reader(archivo, delimiter=',') por lector_dict = csv.DictReader(archivo). El resto derivan de ésta.



import csv

archivo_nombre = 'datos/datos.csv'										# 1. Definimos la ruta del archivo

try:
    with open(archivo_nombre, mode='r', encoding='utf-8') as archivo:  # 2. Abrimos el archivo en modo lectura ('r')
        lector_dict = csv.DictReader(archivo)                          # 3. Creamos el lector de diccionarios
        print(f"Leyendo datos de: {archivo_nombre}\n" + "-"*30)
        for fila in lector_dict:                                       # 4. Recorremos cada fila (cada fila es un diccionario)
            nombre = fila['Nombre']                                                         
            edad = fila['Edad']        
            print(f"Usuario: {nombre} | Edad: {edad}")                          

except FileNotFoundError:                                              #Control de errores
    print(f"Error: El archivo '{archivo_nombre}' no existe.")
except KeyError as e:
    print(f"Error: No se encontró la columna {e} en el archivo.")


A pesar de las diferencias, ambos archivo comparten la misma limitación: la base de datos sólo está disponible dentro del ámbito de with open(), pero fuera no podemos acceder a los datos, así que si nos interesa deberemos trasladarlos a una colección de datos del propio script, concretamente a una lista de diccionarios.



import csv

archivo_nombre = 'datos/datos.csv'                                                  # 1. Definimos la ruta del archivo

datos_internos = []                                                                 # Lista para almacenar la base de datos en memoria


try:
    with open(archivo_nombre, mode='r', encoding='utf-8') as archivo:  				# 2. Abrimos el archivo en modo lectura ('r')
        lector_dict = csv.DictReader(archivo)                                       # 3. Creamos el lector de diccionarios
        print(f"Leyendo datos de: {archivo_nombre}\n" + "-"*30)
        for fila in lector_dict:                                                    # 4. Recorremos cada fila (que es un diccionario)
            nombre = fila['Nombre']                                                         
            edad = int(fila['Edad'])
            datos_internos.append(fila)        
            print(f"Usuario: {nombre} | Edad: {edad}")                          
        print(f"--- Carga finalizada ---")
        print(f"Se han almacenado {len(datos_internos)} registros en el diccionario interno.")

    if datos_internos:																# Ejemplo: Acceder al primer registro almacenado
        primero = datos_internos[0]
        print(f"Primer registro en memoria: {primero['Nombre']} tiene {primero['Edad']} años.")
        
except FileNotFoundError:                                                           #Control de errores
    print(f"Error: El archivo '{archivo_nombre}' no existe.")
except KeyError as e:
    print(f"Error: No se encontró la columna {e} en el archivo.")

#Ejemplo de acceso a la estructura del diccionario ---------------------------------------------------------------------------------------

registro_ejemplo = datos_internos[0]                   # Tomamos el primer diccionario de nuestra lista para el ejemplo
print(f"Claves: {list(registro_ejemplo.keys())}")      # A. Identificar solo las CLAVES
print(f"Valores: {list(registro_ejemplo.values())}")   # B. Identificar solo los VALORES


Lo que nos permite importar al script el contenido del archivo .csv empieza aquí (datos_internos = []) y se concreta en estas dos líneas:
  • for fila in lector_dict: ----> Recorre el archivo .csv
  • ----datos_internos.append(fila) ----> Añade contenido a la lista (de diccionarios) datos_internos

Ellas hacen posible el correcto acceso a los datos mediante instrucciones como print(f"Claves: {list(registro_ejemplo.keys())}"), que muestra las claves del diccionario.

Cierto que la consecuencia es un incremento de la carga de memoria, pero la ventaja en rapidez de funcionamiento compensa cuando necesitamos realizar diferentes acciones sobre los datos desde un mismo script. Tal es el caso del que sigue y con el que finalizamos esta entrada: un script mediante el que accedemos a una base de datos, la cargamos en memoria y realizamso sobre ella un conjunto de acciones que aquí se concretan en seleccionar y filtrar registros.



import csv

# --- FASE 1: CARGA DE DATOS  ---------------------------------------------------

ruta_archivo = 'datos/datos.csv'
datos_internos = []

try:
    with open(ruta_archivo, mode='r', encoding='utf-8') as archivo:
        lector = csv.DictReader(archivo)
        for fila in lector:
            fila['Edad'] = int(fila['Edad']) # Convertimos el valor de [Edad] a entero para poder filtrar
            datos_internos.append(fila)
    print(f"Sistema listo. {len(datos_internos)} registros cargados.\n")

except FileNotFoundError:
    print("Error: No se encontró el archivo en D:/. Por favor, créalo primero.")
    exit() # Finaliza el script si no hay datos

# --- FASE 2: OPCIONES ------------------------------------------------------------

while True:
    print("\n===============================")
    print("   GESTOR DE CONSULTAS")
    print("===============================")
    print("1. Buscar por NOMBRE (Coincidencia parcial)")
    print("2. Filtrar por EDAD (Exacta o Rango)")
    print("3. Salir")
    
    opcion = input("\nSeleccione una opción: ").strip()

    if opcion == '3':
        print("Saliendo del sistema...")
        break

# --- OPCIÓN 1: BÚSQUEDA POR NOMBRE ---
    if opcion == '1':
        termino = input("Escriba el nombre o parte de él: ").strip().lower()
        encontrados = [r for r in datos_internos if termino in r['Nombre'].lower()]
        
        print(f"\nResultados para '{termino}':")
        if encontrados:
            for e in encontrados:
                print(f"ID: {e['Nombre']} | Edad: {e['Edad']}")
        else:
            print("No se encontraron coincidencias.")

# --- OPCIÓN 2: FILTRO POR EDAD ---
    elif opcion == '2':
        print("\n--- Filtro de Edad ---")
        print("A. Edad exacta")
        print("B. Rango (Mínimo y Máximo)")
        sub = input("Elija modalidad (A/B): ").strip().upper()

        resultados = []
        try:
            if sub == 'A':
                objetivo = int(input("Edad a buscar: "))
                resultados = [r for r in datos_internos if r['Edad'] == objetivo]  # Identificamos si el valor de la clave 'Edad' coincide
            
            elif sub == 'B':
                v_min = int(input("Edad mínima: "))
                v_max = int(input("Edad máxima: "))
                resultados = [r for r in datos_internos if v_min <= r['Edad'] <= v_max]  # Filtro por intervalo de valores          
         
            if resultados:
                print(f"\nSe hallaron {len(resultados)} personas:")
                for r in resultados:
                    print(f"• {r['Nombre']} - {r['Edad']} años")
            else:
                print("No hay registros en ese rango.")
                
        except ValueError:
            print("Error: Por favor, ingrese solo números enteros para la edad.")

    else:
        print("Opción no válida. Por favor, marque 1, 2 o 3.")

print("\nPrograma finalizado correctamente.")


NOTA: El archivo .csv de prueba es necesario para el correcto funcionamiento de estos tres script, aunque siempre los puedes adaptar para que funcionen con un .csv diferente. No obstante, si prefieren no hacerlo, aquí lo puedes descargar.