Módulo de Laboratorio

Python II

Estructuras de datos, ficheros y errores

Profesor
Sandro Hurtado Requena
Hoja de ruta de hoy

De los datos sueltos a un análisis

01

Colecciones

  • Listas y tuplas
  • Diccionarios
  • Conjuntos
02

Comprensiones

  • Listas en una línea
  • Ordenar con key
03

Ficheros

  • pathlib
  • CSV y JSON
04

Errores

  • try / except
  • raise
05

Biblioteca estándar

  • collections, datetime
  • Práctica: bicicletas
Objetivo

Al salir de clase…

  • Eliges la estructura adecuada para cada tipo de dato
  • Lees y escribes ficheros CSV y JSON
  • Tu código no se rompe con datos "sucios"
  • Analizas un conjunto de datos real sin librerías externas
salida/resumen.json
{
  "viajes_validos": 1928,
  "duracion_media": 16.84,
  "top3_origen": [
    "Alameda Principal",
    "Estación María Zambrano",
    "Plaza de la Merced"
  ],
  "hora_punta": 18,
  "dia_mas_viajes": "viernes"
}
01

Colecciones de datos

Colecciones

¿Qué estructura uso?

EstructuraEjemploCaracterísticasÚsala para
list[24.1, 26.3, 27.8]Ordenada · modificable · admite repetidosSecuencias de elementos
tuple(36.71, -4.42)Ordenada · inmutableRegistros fijos, coordenadas
dict{"id": 1, "nombre": "Soho"}Pares clave → valor · claves únicasDatos con nombre, JSON, buscar por clave
set{"Centro", "Este"}Sin orden · sin repetidosQuitar duplicados, comprobar pertenencia
Colecciones

Listas

  • Índices y slicing, igual que en las cadenas
  • A diferencia de las cadenas, se pueden modificar
  • l.sort() cambia la lista. sorted(l) devuelve una nueva
listas
ciudades = ["Málaga", "Sevilla"]
ciudades.append("Cádiz")      # al final
ciudades.insert(0, "Jaén")    # en una posición
ciudades.remove("Sevilla")   # por valor
ultima = ciudades.pop()      # quita el último

numeros = [5, 2, 9]
sorted(numeros)              # [2, 5, 9], nueva
numeros.sort(reverse=True)    # la modifica
Cuidado

Asignar no copia

a b c [1, 2, 3, 4] [1, 2, 3]
referencias
a = [1, 2, 3]
b = a           # otro nombre, MISMA lista
c = a.copy()    # lista nueva

b.append(4)
print(a)        # [1, 2, 3, 4]  ¡a también!
print(c)        # [1, 2, 3]

Pasa con todo lo modificable: listas, diccionarios y conjuntos.

Colecciones

Tuplas y desempaquetado

  • Como una lista, pero inmutable
  • Agrupan datos que van juntos: coordenadas, registros
  • Desempaquetar: cada elemento a una variable en una línea
  • Una función que devuelve varios valores devuelve una tupla
tuplas
punto = (36.7178, -4.4254)
lat, lon = punto

a, b = b, a                  # intercambio
primero, *resto = [10, 20, 30]

for nombre, capacidad in estaciones:
    print(nombre, capacidad)
Colecciones

Diccionarios

  • Pares clave → valor: accedes por nombre, no por posición
  • d["clave"] da KeyError si no existe. d.get("clave") devuelve None
  • items() recorre claves y valores a la vez
  • Es la forma natural de representar un JSON
diccionarios
estacion = {"nombre": "Muelle Uno", "capacidad": 20}

estacion["nombre"]                # 'Muelle Uno'
estacion.get("telefono", "-")    # '-'
estacion["barrio"] = "Centro"     # añadir

for clave, valor in estacion.items():
    print(clave, valor)

conteo = {}
for letra in "abracadabra":
    conteo[letra] = conteo.get(letra, 0) + 1
Colecciones

Conjuntos

python java AnaMartaPablo LuisLucía JorgeSara
conjuntos
set(["Centro", "Este", "Centro"])  # sin repetidos

python & java     # en las dos: Luis, Lucía
python - java     # solo en python
python | java     # en alguna

"Ana" in python  # casi instantáneo

Buscar en un conjunto es inmediato aunque tenga millones de elementos. En una lista, Python la recorre entera.

Práctica

Notebook · apartados 1 a 4

estructuras_ficheros.ipynb

  1. Copia la carpeta notebooks completa a tu carpeta de trabajo
  2. Ábrela en VS Code y elige el kernel
  3. Listas, tuplas, diccionarios y conjuntos
  4. Cada ejercicio, con su comprobación Correcto
  • Ejercicios: temperaturas, estaciones, inventario, contar palabras y optativas
  • No te saltes la celda de la trampa de copiar listas
02

Comprensiones y ordenación

Comprensiones

Lo mismo, en una línea

con un bucle
pares = []
for x in numeros:
    if x % 2 == 0:
        pares.append(x ** 2)
con una comprensión
pares = [x ** 2 for x in numeros if x % 2 == 0]
también diccionarios y conjuntos
{c: len(c) for c in ciudades}
{c[0] for c in ciudades}

Se lee casi como una frase: "x al cuadrado para cada x en números, si x es par".

Ordenación

Ordenar con criterio

  • sorted, min y max aceptan key: una función que dice por qué comparar
  • Una lambda es una función pequeña y sin nombre, en una línea
  • lambda e: e["capacidad"] equivale a una función que devuelve la capacidad de e
key y lambda
estaciones = [
    {"nombre": "Muelle Uno", "capacidad": 20},
    {"nombre": "Alameda", "capacidad": 30},
]

sorted(estaciones,
       key=lambda e: e["capacidad"],
       reverse=True)

max(estaciones, key=lambda e: e["capacidad"])
sorted(palabras, key=str.lower)
03

Ficheros

Ficheros

Rutas y open

  • pathlib.Path construye rutas que funcionan en Windows, macOS y Linux
  • Abre siempre con with: el fichero se cierra solo, aunque haya un error
  • Indica siempre encoding="utf-8"
  • Modos: "r" leer · "w" escribir (borra lo anterior) · "a" añadir
ficheros
from pathlib import Path

ruta = Path("datos") / "viajes.csv"
ruta.exists()
Path("salida").mkdir(exist_ok=True)

with open("salida/notas.txt", "w", encoding="utf-8") as f:
    f.write("Primera línea\n")

with open("salida/notas.txt", encoding="utf-8") as f:
    for linea in f:
        print(linea.rstrip())
Ficheros

CSV

  • Una tabla en texto plano: una fila por línea, columnas separadas por comas
  • csv.DictReader: cada fila es un diccionario con los nombres de las columnas
  • Todo llega como texto: convierte con int() y float()
  • Abre con newline="" para evitar líneas en blanco de más en Windows
datos/viajes.csv
id_viaje,fecha,hora_inicio,estacion_origen,...
1,2026-09-04,12:26,12,6,33.5,ocasional,21.8
2,2026-09-26,19:18,9,4,23.6,ocasional,26.1
leer
import csv

with open("datos/viajes.csv", encoding="utf-8", newline="") as f:
    viajes = list(csv.DictReader(f))

viajes[0]["duracion_min"]   # '33.5' (str)
Ficheros

JSON

PythonJSON
dictobjeto {...}
listarray [...]
str · int · floatcadena · número
True · Nonetrue · null

El formato estándar para intercambiar datos entre programas y en la web.

json
import json

with open("datos/estaciones.json", encoding="utf-8") as f:
    datos = json.load(f)          # JSON → Python

with open("salida/resumen.json", "w",
          encoding="utf-8") as f:
    json.dump(resumen, f,             # Python → JSON
              ensure_ascii=False, indent=2)
04

Errores y excepciones

Errores

Datos sucios, código robusto

ExcepciónEjemplo
ValueErrorfloat("N/D")
TypeError"3" + 4
KeyErrorClave que no existe
FileNotFoundErrorArchivo que no existe
try / except
for texto in ["12.5", "N/D", ""]:
    try:
        valor = float(texto)
    except ValueError:
        print(f"{texto!r} no es un número")
    else:
        print("leído:", valor)   # si no hubo error
    finally:
        pass                    # siempre
Errores

Lanzar tus propios errores

  • raise lanza una excepción cuando un dato no cumple las reglas
  • Puedes crear tipos nuevos heredando de Exception
  • Captura solo lo que esperas: un except: sin tipo esconde también tus propios fallos
raise
class DatoInvalido(Exception):
    pass


def validar_duracion(minutos):
    if minutos <= 0:
        raise DatoInvalido(f"Duración no válida: {minutos}")
    return minutos


try:
    validar_duracion(-4)
except DatoInvalido as error:
    print("Descartado:", error)
Práctica

Notebook · apartados 5 a 7

Comprensiones, ficheros y errores

  1. Comprensiones y max con key
  2. Lee los datos de la práctica: CSV y JSON
  3. Construye un diccionario {id: nombre}
  4. Escribe a_float() con try/except
  • Abre datos/viajes.csv y busca las filas raras: N/D, vacías, negativas
  • Las encontrarás en la práctica final
05

Biblioteca estándar

Pilas incluidas

Módulos que ya tienes

collections
from collections import Counter, defaultdict

Counter(tipos).most_common(3)

grupos = defaultdict(list)
grupos[barrio].append(estacion)
datetime
from datetime import datetime, timedelta

f = datetime.strptime("2026-09-18", "%Y-%m-%d")
f.weekday()            # 0 = lunes
f + timedelta(days=7)
f.strftime("%d/%m/%Y")

Y muchos más: math, random, statistics, os, sys, itertools… sin instalar nada.

Módulos

Tus propios módulos

  • Un módulo es un archivo .py
  • Sus funciones se importan desde otros programas con import
  • if __name__ == "__main__": se ejecuta solo al lanzar el archivo directamente, no al importarlo
utilidades.py
def minutos_a_texto(minutos):
    horas, resto = divmod(round(minutos), 60)
    return f"{horas} h {resto} min"


if __name__ == "__main__":
    print(minutos_a_texto(90))
otro programa
from utilidades import minutos_a_texto
Práctica final

Servicio de bicicletas de Málaga

Notebook · apartado 9

  1. Carga viajes.csv y estaciones.json
  2. Descarta los viajes con errores
  3. Duración media y estaciones más usadas
  4. Tipos de usuario, hora punta, barrios y día de la semana
  5. Exporta todo a salida/resumen.json
  • 2.000 viajes de un mes. Datos simulados
  • Sin librerías externas: solo lo que has visto hoy
  • Cada paso usa el anterior: hazlos en orden
Hasta la próxima

¿Preguntas?

Guía, chuleta y notebook en el repositorio del curso

Prueba la práctica con un CSV de datosabiertos.malaga.eu