Volver al Módulo 2

COA — Python Práctico

Módulo 3: Colecciones especializadas e iteradores

Duración aproximada

2 horas

Nivel

Intermedio

Requisito recomendado

Módulos 1 y 2 completados

Modalidad

Práctica guiada y proyecto

Tecnologías: Python y biblioteca estándar

Resultado: Un centro de análisis y cola de atención

5 prácticas guiadas3 actividades obligatorias1 proyecto1 evaluación
0%

Bienvenida

Las listas, los diccionarios, los conjuntos y las tuplas resuelven una gran cantidad de problemas. Sin embargo, algunas tareas frecuentes obligan a escribir siempre el mismo código auxiliar:

Pseudocódigo
conteos = {}

for categoria in categorias:
    if categoria not in conteos:
        conteos[categoria] = 0

    conteos[categoria] += 1

Python incluye una colección creada específicamente para contar:

Pseudocódigo
from collections import Counter

conteos = Counter(categorias)

La segunda solución no sustituye tu razonamiento. Primero debes reconocer que el problema es un conteo. Después eliges una estructura cuya operación principal coincide con esa necesidad.

En este módulo trabajarás con tres colecciones especializadas:

Pseudocódigo
Necesito contar o crear un ranking     → Counter
Necesito agrupar sin inicializar       → defaultdict
Necesito una cola eficiente            → deque

También utilizarás herramientas seleccionadas de itertools para encadenar, limitar, combinar y agrupar recorridos sin construir estructuras intermedias innecesarias.

Principio del módulo: una estructura especializada merece utilizarse cuando simplifica una operación central del problema, no solamente porque existe.

Objetivos de aprendizaje

Al completar el módulo serás capaz de:

  • Reconocer cuándo una colección común exige demasiado código auxiliar.
  • Contar elementos y crear rankings mediante Counter.
  • Actualizar, combinar y consultar conteos.
  • Agrupar registros y acumular valores con defaultdict.
  • Comparar defaultdict con dict.get() y setdefault().
  • Administrar colas FIFO mediante deque.
  • Insertar y retirar elementos eficientemente en ambos extremos.
  • Conservar historiales limitados mediante deque(maxlen=...).
  • Encadenar recorridos con itertools.chain().
  • Obtener una parte de un iterable con islice().
  • Crear productos cartesianos, combinaciones y permutaciones cuando el problema lo requiera.
  • Agrupar elementos consecutivos mediante groupby() después de ordenar por la misma clave.
  • Identificar iteradores consumibles y decidir cuándo materializarlos.
  • Construir un sistema de análisis y priorización de solicitudes.

Conocimientos que utilizarás

Necesitas poder:

  • recorrer listas y diccionarios;
  • crear funciones;
  • trabajar con comprensiones;
  • utilizar expresiones generadoras;
  • ordenar mediante sorted(..., key=...);
  • utilizar enumerate(), zip(), any() y all().

Estos conocimientos se aplicarán desde el inicio y no volverán a explicarse desde cero.

Producto que construirás

Desarrollarás un centro local que combine solicitudes pendientes y nuevas, analice su contenido y organice la atención según prioridad:

Pseudocódigo
CENTRO DE SOLICITUDES
============================================================================
Solicitudes recibidas:       14
Categorías líderes:          acceso, contenido
Prioridad más frecuente:     normal
Responsables con más casos:  Ana, Carlos

PRÓXIMAS SOLICITUDES
----------------------------------------------------------------------------
1. SOP-004 | urgente | Error al iniciar sesión
2. SOP-011 | urgente | Cobro duplicado
3. SOP-013 | urgente | Cuenta bloqueada antes de evaluación

HISTORIAL RECIENTE
----------------------------------------------------------------------------
Últimas cinco solicitudes atendidas: ...

El programa deberá preservar el orden de llegada dentro de cada prioridad, conservar un historial limitado, agrupar información y generar parejas de revisión.

Ruta de trabajo y distribución del tiempo

EtapaTiempo aproximado
1. Elegir una colección especializada5 min
2. Conteos y rankings con Counter8 min
3. Agrupaciones con defaultdict8 min
4. Colas e historiales con deque8 min
5. Herramientas prácticas de itertools16 min
6. Prácticas guiadas15 min
7. Actividades obligatorias10 min
8. Proyecto del módulo40 min
9. Evaluación y cierre10 min
Total2 horas

Los recursos complementarios y los retos opcionales pueden ampliar el tiempo de estudio.

Elegir una colección especializada

1 La operación principal orienta la elección

No preguntes primero “¿qué estructura nueva puedo utilizar?”. Pregunta:

  • ¿Qué operación se repite?
  • ¿Cuál es el costo de escribirla manualmente?
  • ¿La estructura especializada comunica mejor la intención?
  • ¿Otra persona reconocerá fácilmente su comportamiento?

Ejemplos:

ProblemaEstructura apropiada
Conservar elementos en ordenlist
Consultar un registro por clavedict
Eliminar duplicadosset
Contar categoríasCounter
Agrupar registros por responsabledefaultdict(list)
Acumular totales por categoríadefaultdict(int)
Añadir y retirar por ambos extremosdeque
Conservar solo los últimos cinco eventosdeque(maxlen=5)

2 Especializada no significa siempre mejor

Para tres elementos que solo se recorren una vez, una lista continúa siendo adecuada. Para un único acceso con valor predeterminado, dict.get() puede ser suficiente.

Evita introducir una estructura que:

  • no simplifica la operación principal;
  • obliga a convertir constantemente a otro tipo;
  • oculta una regla de negocio;
  • resulta más difícil de explicar que la solución común;
  • se utiliza una sola vez sin beneficio apreciable.

3 Importaciones explícitas

Pseudocódigo
from collections import Counter, defaultdict, deque
from itertools import chain, combinations, groupby, islice, product

Importa únicamente lo que utilizas. Evita:

Pseudocódigo
from collections import *

Las importaciones con * dificultan saber de dónde procede cada nombre y pueden producir colisiones.

Conteos y rankings con Counter

1 Contar elementos

Pseudocódigo
from collections import Counter

categorias = [
    "acceso",
    "contenido",
    "acceso",
    "facturación",
    "acceso",
    "contenido",
]

conteo = Counter(categorias)
print(conteo)

Salida aproximada:

Pseudocódigo
Counter({'acceso': 3, 'contenido': 2, 'facturación': 1})

Counter es una subclase de dict. Sus claves son los elementos contados y sus valores son las frecuencias.

Pseudocódigo
print(conteo["acceso"])
print(conteo["desconocida"])

Una clave ausente devuelve 0 en lugar de generar KeyError. Eso es conveniente para conteos, pero no significa que la clave exista:

Pseudocódigo
print("desconocida" in conteo)

2 Normaliza antes de contar

Pseudocódigo
categorias = ["Acceso", "acceso", " ACCESO ", "Contenido"]

conteo = Counter(
    categoria.strip().casefold()
    for categoria in categorias
)

Si cuentas antes de normalizar, valores equivalentes aparecerán como categorías separadas.

3 Obtener los más frecuentes

Pseudocódigo
print(conteo.most_common())
print(conteo.most_common(2))

most_common(2) devuelve las dos parejas con mayor frecuencia:

Pseudocódigo
[('acceso', 3), ('contenido', 1)]

Si existe un empate, no debes inventar que una categoría es “más importante”. Puedes mostrar todas las empatadas:

Pseudocódigo
if conteo:
    frecuencia_maxima = max(conteo.values())
    categorias_lideres = sorted(
        categoria
        for categoria, frecuencia in conteo.items()
        if frecuencia == frecuencia_maxima
    )
else:
    categorias_lideres = []

4 Actualizar conteos

Pseudocódigo
conteo = Counter(["acceso", "contenido"])
conteo.update(["acceso", "facturación", "acceso"])

print(conteo)

update() suma frecuencias. No reemplaza el contador completo.

También acepta un mapeo de incrementos:

Pseudocódigo
conteo.update({"acceso": 2, "contenido": 1})

5 Restar y limpiar conteos

Pseudocódigo
disponibles = Counter({"licencia": 5, "cuenta": 3})
asignadas = Counter({"licencia": 2, "cuenta": 4})

resultado = disponibles - asignadas
print(resultado)

La operación - conserva únicamente conteos positivos. Si necesitas observar ceros o negativos, utiliza subtract() y revisa el resultado:

Pseudocódigo
disponibles.subtract(asignadas)
print(disponibles)

No utilices operaciones aritméticas sin decidir qué significa un conteo negativo en tu dominio.

6 Combinar contadores

Pseudocódigo
turno_manana = Counter({"acceso": 4, "contenido": 2})
turno_tarde = Counter({"acceso": 1, "facturación": 3})

total_dia = turno_manana + turno_tarde
print(total_dia)

Otras operaciones permiten intersección y unión de frecuencias, pero no deben confundirse con sumar. Consulta su significado antes de usarlas.

7 Recuperar elementos

Pseudocódigo
conteo = Counter({"urgente": 2, "normal": 3})
print(list(conteo.elements()))

elements() repite cada elemento según su frecuencia positiva. No representa necesariamente el orden original de llegada; utiliza una cola si ese orden importa.

Agrupaciones con defaultdict

1 El patrón de inicialización repetida

Con un diccionario común:

Pseudocódigo
por_responsable = {}

for solicitud in solicitudes:
    responsable = solicitud["responsable"]

    if responsable not in por_responsable:
        por_responsable[responsable] = []

    por_responsable[responsable].append(solicitud)

Con defaultdict(list):

Pseudocódigo
from collections import defaultdict

por_responsable = defaultdict(list)

for solicitud in solicitudes:
    por_responsable[solicitud["responsable"]].append(solicitud)

list se entrega sin paréntesis porque es la fábrica que defaultdict llamará cuando falte una clave.

2 La fábrica predeterminada

Pseudocódigo
grupos = defaultdict(list)
totales = defaultdict(int)
etiquetas = defaultdict(set)

Comportamiento al acceder a una clave ausente:

FábricaValor creado
list[]
int0
setset()

Ejemplos:

Pseudocódigo
duracion_por_categoria = defaultdict(int)

for solicitud in solicitudes:
    categoria = solicitud["categoria"]
    duracion_por_categoria[categoria] += solicitud["minutos_estimados"]
Pseudocódigo
etiquetas_por_categoria = defaultdict(set)

for solicitud in solicitudes:
    etiquetas_por_categoria[solicitud["categoria"]].update(
        solicitud["etiquetas"]
    )

3 Diferencia frente a dict.get()

get() devuelve un valor, pero no lo inserta:

Pseudocódigo
datos = {}
lista = datos.get("acceso", [])
lista.append("SOP-001")

print(datos)

Salida:

Pseudocódigo
{}

Esto es un error común:

Pseudocódigo
datos.get("acceso", []).append("SOP-001")

La lista temporal se pierde. Para una consulta aislada, get() es apropiado. Para agrupar repetidamente, defaultdict(list) comunica mejor la intención.

4 Diferencia frente a setdefault()

Pseudocódigo
por_categoria = {}

for solicitud in solicitudes:
    categoria = solicitud["categoria"]
    por_categoria.setdefault(categoria, []).append(solicitud)

Esta solución es correcta. defaultdict suele ser más claro cuando toda la estructura comparte el mismo valor predeterminado. setdefault() puede ser conveniente para una operación localizada dentro de un diccionario común.

5 Leer una clave puede modificar el diccionario

Pseudocódigo
grupos = defaultdict(list)
print(grupos["inexistente"])
print(grupos)

El acceso mediante corchetes crea la clave:

Pseudocódigo
defaultdict(<class 'list'>, {'inexistente': []})

Si solo quieres comprobar, utiliza:

Pseudocódigo
if "inexistente" in grupos:
    ...

o:

Pseudocódigo
valor = grupos.get("inexistente")

6 Convertir para presentar o entregar

La representación de defaultdict incluye la fábrica. Si necesitas un diccionario común:

Pseudocódigo
resultado_final = dict(por_responsable)

Las listas y objetos internos continúan siendo los mismos. Esta conversión no crea una copia profunda.

7 Fábricas personalizadas

Una función puede crear estructuras más completas:

Pseudocódigo
def crear_resumen():
    return {
        "cantidad": 0,
        "minutos": 0,
        "identificadores": [],
    }


resumen_por_categoria = defaultdict(crear_resumen)

for solicitud in solicitudes:
    resumen = resumen_por_categoria[solicitud["categoria"]]
    resumen["cantidad"] += 1
    resumen["minutos"] += solicitud["minutos_estimados"]
    resumen["identificadores"].append(solicitud["id"])

Utiliza una fábrica personalizada solo cuando la estructura repetida lo justifique.

Colas e historiales con deque

1 Una lista puede funcionar como cola, pero tiene un costo

Pseudocódigo
cola = ["SOP-001", "SOP-002", "SOP-003"]
primero = cola.pop(0)

Al retirar el primer elemento, los restantes deben desplazarse. Para una cola con muchas operaciones en ambos extremos, utiliza deque:

Pseudocódigo
from collections import deque

cola = deque(["SOP-001", "SOP-002", "SOP-003"])
primero = cola.popleft()

2 Operaciones principales

Pseudocódigo
cola = deque()

cola.append("SOP-001")
cola.append("SOP-002")
cola.appendleft("SOP-URGENTE")

primero = cola.popleft()
ultimo = cola.pop()
OperaciónEfecto
append(x)Añade a la derecha.
appendleft(x)Añade a la izquierda.
pop()Retira de la derecha.
popleft()Retira de la izquierda.

3 FIFO y LIFO

Cola FIFO: el primero en entrar es el primero en salir.

Pseudocódigo
cola.append(solicitud)
solicitud_actual = cola.popleft()

Pila LIFO: el último en entrar es el primero en salir.

Pseudocódigo
pila.append(accion)
ultima_accion = pila.pop()

La estructura no decide la política. La combinación de operaciones define el comportamiento.

4 Prioridad sin invertir el orden de llegada

Insertar cada urgente con appendleft() puede invertir el orden entre urgentes:

Pseudocódigo
cola.appendleft("URGENTE-1")
cola.appendleft("URGENTE-2")

El segundo quedará antes que el primero. Para preservar FIFO dentro de cada prioridad, utiliza colas separadas:

Pseudocódigo
colas = {
    "urgente": deque(),
    "alta": deque(),
    "normal": deque(),
}

for solicitud in solicitudes:
    colas[solicitud["prioridad"]].append(solicitud)

Para atender:

Pseudocódigo
if colas["urgente"]:
    siguiente = colas["urgente"].popleft()
elif colas["alta"]:
    siguiente = colas["alta"].popleft()
elif colas["normal"]:
    siguiente = colas["normal"].popleft()
else:
    siguiente = None

Una política que siempre atiende urgentes puede postergar indefinidamente solicitudes normales si continúan llegando urgentes. En un sistema real debes analizar reglas contra la inanición. El proyecto trabajará con un conjunto finito para concentrarse en las estructuras.

5 Historial limitado con maxlen

Pseudocódigo
historial = deque(maxlen=3)

historial.append("SOP-001")
historial.append("SOP-002")
historial.append("SOP-003")
historial.append("SOP-004")

print(historial)

Salida:

Pseudocódigo
deque(['SOP-002', 'SOP-003', 'SOP-004'], maxlen=3)

Al alcanzar el límite, añadir un elemento descarta automáticamente el más antiguo. Utilízalo solo cuando esa pérdida sea parte explícita del diseño.

6 Vista previa sin retirar

Puedes consultar el primer elemento:

Pseudocódigo
if cola:
    siguiente = cola[0]

Para obtener varios elementos sin retirar, islice() resulta útil:

Pseudocódigo
from itertools import islice

proximas_tres = list(islice(cola, 3))

Recorrer una deque mediante islice() no elimina elementos de la cola.

7 Rotación

Pseudocódigo
turnos = deque(["Ana", "Luis", "María"])
turnos.rotate(-1)
print(turnos)

Salida:

Pseudocódigo
deque(['Luis', 'María', 'Ana'])

rotate() puede apoyar una asignación circular, pero no reemplaza reglas más complejas de carga, disponibilidad o especialidad.

Herramientas prácticas de itertools

itertools reúne funciones que construyen iteradores eficientes. No necesitas memorizar todo el módulo. Aprende a reconocer los problemas que resuelven las herramientas más útiles.

1 chain() para recorrer fuentes consecutivas

Pseudocódigo
from itertools import chain

pendientes = ["SOP-001", "SOP-002"]
nuevas = ["SOP-003", "SOP-004"]

combinadas = chain(pendientes, nuevas)

for solicitud in combinadas:
    print(solicitud)

chain() recorre una fuente y después la siguiente sin crear primero una lista combinada.

Si necesitas procesar los datos varias veces, puedes materializarlos una vez:

Pseudocódigo
todas = list(chain(pendientes, nuevas))

No confundas esto con ordenar o mezclar. chain() conserva el orden de cada fuente y las coloca una después de otra.

chain.from_iterable()

Cuando ya tienes una colección de colecciones:

Pseudocódigo
grupos = [
    ["SOP-001", "SOP-002"],
    ["SOP-003"],
    ["SOP-004", "SOP-005"],
]

todas = chain.from_iterable(grupos)

Esto evita tener que conocer cuántos grupos existen para pasarlos como argumentos separados.

2 islice() para limitar un iterable

Pseudocódigo
from itertools import islice

primeras_tres = list(islice(solicitudes, 3))

También acepta inicio, fin y paso de forma parecida a un segmento:

Pseudocódigo
seleccion = list(islice(solicitudes, 2, 8, 2))

La diferencia es que funciona con iteradores que no permiten índices.

islice() consume el iterador que recibe. Si se utiliza sobre una lista o una deque, la colección permanece; si se utiliza sobre un generador guardado, ese generador avanzará.

3 product() para combinaciones cartesianas

Pseudocódigo
from itertools import product

equipos = ["Soporte", "Contenido"]
turnos = ["mañana", "tarde"]

asignaciones = list(product(equipos, turnos))
print(asignaciones)

Salida:

Pseudocódigo
[
    ('Soporte', 'mañana'),
    ('Soporte', 'tarde'),
    ('Contenido', 'mañana'),
    ('Contenido', 'tarde'),
]

product() produce todas las parejas posibles tomando un elemento de cada colección. La cantidad de resultados crece multiplicando los tamaños. Antes de materializar, estima:

Pseudocódigo
cantidad = len(equipos) * len(turnos)

4 combinations() para grupos sin importar el orden

Pseudocódigo
from itertools import combinations

responsables = ["Ana", "Luis", "María", "Carlos"]
parejas = list(combinations(responsables, 2))

Cada pareja aparece una sola vez:

Pseudocódigo
('Ana', 'Luis')
('Ana', 'María')
('Ana', 'Carlos')
('Luis', 'María')
('Luis', 'Carlos')
('María', 'Carlos')

('Ana', 'Luis') y ('Luis', 'Ana') representan el mismo grupo y no se repiten.

5 permutations() cuando el orden sí importa

Pseudocódigo
from itertools import permutations

pasos = ["validar", "aprobar", "publicar"]
ordenes = list(permutations(pasos, 3))

Las permutaciones crecen muy rápido. Tres elementos producen seis órdenes; diez elementos producen millones. No materialices resultados sin estimar su cantidad.

En procesos reales, la mayoría de los órdenes posibles pueden ser inválidos por dependencias. No utilices permutations() si las reglas ya determinan un orden.

6 Diferencia entre producto, combinaciones y permutaciones

NecesidadHerramienta
Elegir un elemento de cada colecciónproduct()
Formar grupos donde el orden no importacombinations()
Generar órdenes donde la posición importapermutations()

7 groupby() agrupa elementos consecutivos

Pseudocódigo
from itertools import groupby

solicitudes_ordenadas = sorted(
    solicitudes,
    key=lambda solicitud: solicitud["categoria"],
)

for categoria, grupo in groupby(
    solicitudes_ordenadas,
    key=lambda solicitud: solicitud["categoria"],
):
    elementos = list(grupo)
    print(categoria, len(elementos))

groupby() no reúne automáticamente todas las apariciones separadas. Agrupa elementos consecutivos con la misma clave.

Ejemplo incorrecto:

Pseudocódigo
categorias = ["acceso", "contenido", "acceso"]

Sin ordenar, groupby() produce tres grupos, no dos.

La regla segura es:

Ordena por la misma clave que utilizarás para agrupar.

8 El grupo también es un iterador

Pseudocódigo
for categoria, grupo in groupby(datos, key=clave):
    print(list(grupo))
    print(list(grupo))

La segunda lista queda vacía porque el grupo ya fue consumido. Además, debes procesar el grupo dentro de la iteración principal. Si necesitas conservarlo, materialízalo una vez.

9 Herramientas que no necesitas todavía

itertools incluye cycle(), repeat(), count(), tee(), takewhile(), dropwhile() y otras funciones. Son útiles en contextos concretos, pero añadirlas sin práctica suficiente convertiría el módulo en un catálogo. Consulta la documentación cuando un problema real sugiera su necesidad.

10 Decide cuándo materializar

Materializa con list() cuando:

  • necesitas recorrer varias veces;
  • necesitas conocer todos los valores;
  • necesitas ordenar;
  • necesitas acceder por índice;
  • necesitas entregar una estructura concreta.

Mantén el iterador cuando:

  • lo consumirás una sola vez;
  • una función puede consumirlo directamente;
  • la cantidad de resultados puede ser grande;
  • producir cada valor bajo demanda es suficiente.

Prácticas guiadas

🧭 Práctica guiada

Práctica guiada 1 — Ranking de categorías

Pseudocódigo
categorias = [
    "Acceso", "contenido", " ACCESO ", "facturación",
    "Contenido", "acceso", "cuenta", "CONTENIDO",
]

Debes:

  1. Normalizar antes de contar.
  2. Crear un Counter.
  3. Mostrar todas las frecuencias ordenadas.
  4. Mostrar las dos categorías más frecuentes.
  5. Detectar todos los líderes si existe empate.
🧭 Práctica guiada

Práctica guiada 2 — Agrupar por responsable

Pseudocódigo
solicitudes = [
    {"id": "SOP-001", "responsable": "Ana", "minutos": 20},
    {"id": "SOP-002", "responsable": "Luis", "minutos": 35},
    {"id": "SOP-003", "responsable": "Ana", "minutos": 15},
    {"id": "SOP-004", "responsable": "María", "minutos": 40},
]

Utiliza defaultdict para crear:

  • solicitudes por responsable;
  • minutos totales por responsable;
  • identificadores únicos por responsable.

Convierte los resultados a diccionarios comunes antes de mostrarlos.

🧭 Práctica guiada

Práctica guiada 3 — Colas por prioridad

Pseudocódigo
entradas = [
    ("SOP-001", "normal"),
    ("SOP-002", "urgente"),
    ("SOP-003", "alta"),
    ("SOP-004", "urgente"),
    ("SOP-005", "normal"),
]

Crea tres deque, atiende primero urgentes, después altas y finalmente normales. Comprueba que SOP-002 se atienda antes que SOP-004.

Conserva un historial con maxlen=3 y muestra su contenido al terminar.

🧭 Práctica guiada

Práctica guiada 4 — Vista previa y fuentes combinadas

Pseudocódigo
pendientes = ["SOP-001", "SOP-002", "SOP-003"]
nuevas = ["SOP-004", "SOP-005"]

Utiliza chain() para recorrer ambas fuentes y islice() para obtener una vista previa de las primeras cuatro. Explica por qué debes materializar una vez si utilizarás la colección combinada para más de un análisis.

🧭 Práctica guiada

Práctica guiada 5 — Parejas y agrupaciones

Con responsables Ana, Luis, María y Carlos:

  1. Genera todas las parejas de revisión sin repetir el orden.
  2. Combina cada responsable con los turnos mañana y tarde.
  3. Agrupa una lista de solicitudes por categoría utilizando groupby().
  4. Demuestra qué ocurre si omites el ordenamiento previo.

Actividades obligatorias

📝 Actividad obligatoria

Actividad 1 — Refactorizar un conteo y una agrupación

Recibe una colección de registros con categoria y responsable. Escribe primero una solución con diccionarios comunes y luego refactoriza:

  • el conteo con Counter;
  • la agrupación con defaultdict(list).

Compara ambas versiones y explica qué código auxiliar desaparece. No afirmes que la versión especializada es mejor solo por ser más corta.

📝 Actividad obligatoria

Actividad 2 — Corregir una cola que invierte urgentes

Analiza:

Pseudocódigo
cola = deque()

for solicitud in solicitudes:
    if solicitud["prioridad"] == "urgente":
        cola.appendleft(solicitud)
    else:
        cola.append(solicitud)

Debes:

  1. Demostrar con dos urgentes que el orden se invierte.
  2. Implementar colas separadas por prioridad.
  3. Preservar FIFO dentro de cada prioridad.
  4. Crear una vista previa de tres elementos sin retirarlos.
  5. Explicar una limitación de priorizar siempre lo urgente.
📝 Actividad obligatoria

Actividad 3 — Diagnosticar groupby()

Pseudocódigo
categorias = ["acceso", "contenido", "acceso", "facturación", "contenido"]

Agrupa directamente y registra el resultado. Después ordena por la misma clave, vuelve a agrupar y explica por qué cambió la cantidad de grupos. Incluye una comprobación que materialice cada grupo exactamente una vez.

Proyecto del módulo — Centro de análisis y cola de atención

Desafío

COA necesita analizar solicitudes de soporte que proceden de dos fuentes: casos pendientes y solicitudes nuevas. El sistema debe producir estadísticas, organizar los casos por responsable, crear colas por prioridad y simular la atención sin perder el orden de llegada dentro de cada nivel.

El programa también deberá conservar únicamente las últimas cinco solicitudes atendidas, crear parejas de revisión y generar un resumen agrupado por categoría.

No utilizarás archivos, bases de datos ni librerías externas. Los datos estarán en un módulo de Python.

Datos de trabajo

Pseudocódigo
SOLICITUDES_PENDIENTES = [
    {
        "id": "SOP-001",
        "titulo": "Video de una lección no carga",
        "categoria": "contenido",
        "prioridad": "normal",
        "responsable": "Ana",
        "etiquetas": ["curso", "video"],
        "minutos_estimados": 25,
    },
    {
        "id": "SOP-002",
        "titulo": "Factura con datos incorrectos",
        "categoria": "facturación",
        "prioridad": "alta",
        "responsable": "Luis",
        "etiquetas": ["pago", "factura"],
        "minutos_estimados": 20,
    },
    {
        "id": "SOP-003",
        "titulo": "No puedo cambiar mi nombre",
        "categoria": "cuenta",
        "prioridad": "normal",
        "responsable": "María",
        "etiquetas": ["perfil", "datos"],
        "minutos_estimados": 15,
    },
    {
        "id": "SOP-004",
        "titulo": "Error al iniciar sesión",
        "categoria": "acceso",
        "prioridad": "urgente",
        "responsable": "Carlos",
        "etiquetas": ["inicio de sesión", "bloqueo"],
        "minutos_estimados": 10,
    },
    {
        "id": "SOP-005",
        "titulo": "Ejercicio marcado como incompleto",
        "categoria": "contenido",
        "prioridad": "alta",
        "responsable": "Ana",
        "etiquetas": ["curso", "ejercicio"],
        "minutos_estimados": 30,
    },
    {
        "id": "SOP-006",
        "titulo": "No aparece el certificado",
        "categoria": "certificado",
        "prioridad": "normal",
        "responsable": "Luis",
        "etiquetas": ["certificado", "finalización"],
        "minutos_estimados": 20,
    },
]

SOLICITUDES_NUEVAS = [
    {
        "id": "SOP-007",
        "titulo": "Correo de recuperación no llega",
        "categoria": "acceso",
        "prioridad": "normal",
        "responsable": "María",
        "etiquetas": ["correo", "recuperación"],
        "minutos_estimados": 15,
    },
    {
        "id": "SOP-008",
        "titulo": "La página se cierra inesperadamente",
        "categoria": "técnico",
        "prioridad": "alta",
        "responsable": "Carlos",
        "etiquetas": ["navegador", "error"],
        "minutos_estimados": 35,
    },
    {
        "id": "SOP-009",
        "titulo": "Certificado muestra un curso incompleto",
        "categoria": "certificado",
        "prioridad": "alta",
        "responsable": "Ana",
        "etiquetas": ["certificado", "curso"],
        "minutos_estimados": 25,
    },
    {
        "id": "SOP-010",
        "titulo": "Texto de una lección está cortado",
        "categoria": "contenido",
        "prioridad": "normal",
        "responsable": "Luis",
        "etiquetas": ["curso", "texto"],
        "minutos_estimados": 15,
    },
    {
        "id": "SOP-011",
        "titulo": "Cobro duplicado",
        "categoria": "facturación",
        "prioridad": "urgente",
        "responsable": "María",
        "etiquetas": ["pago", "duplicado"],
        "minutos_estimados": 20,
    },
    {
        "id": "SOP-012",
        "titulo": "El editor no guarda el progreso",
        "categoria": "técnico",
        "prioridad": "normal",
        "responsable": "Carlos",
        "etiquetas": ["editor", "progreso"],
        "minutos_estimados": 30,
    },
    {
        "id": "SOP-013",
        "titulo": "Cuenta bloqueada antes de evaluación",
        "categoria": "acceso",
        "prioridad": "urgente",
        "responsable": "Ana",
        "etiquetas": ["bloqueo", "evaluación"],
        "minutos_estimados": 10,
    },
    {
        "id": "SOP-014",
        "titulo": "Preferencias de cuenta no se actualizan",
        "categoria": "cuenta",
        "prioridad": "normal",
        "responsable": "Carlos",
        "etiquetas": ["perfil", "preferencias"],
        "minutos_estimados": 20,
    },
]

RESPONSABLES = ["Ana", "Luis", "María", "Carlos"]
TURNOS = ["mañana", "tarde"]

Reglas del proyecto

  1. Combina las dos fuentes conservando primero los pendientes y después los nuevos.
  2. Materializa la combinación una sola vez porque será utilizada en varios análisis.
  3. Normaliza categorías, prioridades y etiquetas antes de contarlas.
  4. Las prioridades admitidas son urgente, alta y normal.
  5. La atención debe respetar ese orden de prioridad.
  6. Dentro de cada prioridad se conserva FIFO.
  7. El historial conserva únicamente las últimas cinco solicitudes atendidas.
  8. Los datos originales no deben modificarse.
  9. Los grupos creados con groupby() deben ordenarse previamente por la misma clave.

Requisitos funcionales

El programa deberá:

  1. Combinar las fuentes mediante chain().
  2. Calcular solicitudes por categoría, prioridad y responsable con Counter.
  3. Crear un ranking de las tres categorías más frecuentes.
  4. Detectar todos los líderes si existe empate en el primer lugar.
  5. Contar etiquetas a partir de todas las listas internas.
  6. Agrupar solicitudes por responsable mediante defaultdict(list).
  7. Agrupar solicitudes por categoría.
  8. Acumular minutos estimados por responsable con defaultdict(int).
  9. Construir tres colas deque, una por prioridad.
  10. Crear una vista previa global de las próximas tres solicitudes sin retirarlas.
  11. Simular la atención de todas las solicitudes.
  12. Preservar FIFO dentro de cada prioridad.
  13. Mantener un historial deque(maxlen=5).
  14. Mostrar el orden completo de atención.
  15. Crear todas las parejas de responsables mediante combinations().
  16. Crear la matriz responsable-turno mediante product().
  17. Agrupar solicitudes por categoría mediante groupby().
  18. Generar un reporte con estadísticas, grupos, vista previa, orden de atención e historial.

Herramientas obligatorias

  • Counter y most_common();
  • Counter.update() en un conteo útil;
  • defaultdict(list);
  • defaultdict(int);
  • deque;
  • deque(maxlen=5);
  • chain() o chain.from_iterable();
  • islice();
  • combinations();
  • product();
  • groupby() después de sorted();
  • enumerate() para numerar el reporte;
  • al menos una comprensión o expresión generadora del Módulo 2.

Estructura de funciones recomendada

Pseudocódigo
def combinar_solicitudes(pendientes, nuevas):
    ...


def crear_conteos(solicitudes):
    ...


def contar_etiquetas(solicitudes):
    ...


def crear_agrupaciones(solicitudes):
    ...


def crear_colas(solicitudes):
    ...


def obtener_vista_previa(colas, cantidad=3):
    ...


def extraer_siguiente(colas):
    ...


def simular_atencion(colas, limite_historial=5):
    ...


def agrupar_con_groupby(solicitudes):
    ...


def crear_parejas_revision(responsables):
    ...


def crear_matriz_turnos(responsables, turnos):
    ...


def mostrar_reporte(resultado):
    ...


def main():
    ...


if __name__ == "__main__":
    main()

Flujo recomendado

Fase 1. Combina y conserva

Pseudocódigo
solicitudes = list(chain(SOLICITUDES_PENDIENTES, SOLICITUDES_NUEVAS))

Comprueba que la lista contiene 14 solicitudes y que las fuentes originales no cambiaron.

Fase 2. Analiza frecuencias

Crea contadores separados para categorías, prioridades y responsables. Para etiquetas, puedes actualizar un contador mientras recorres las solicitudes o encadenar las listas internas.

Fase 3. Agrupa

Construye agrupaciones y acumulaciones mediante defaultdict. Convierte a dict únicamente al preparar la salida si lo necesitas.

Fase 4. Prepara las colas

Crea una deque por prioridad y añade cada solicitud con append(). No utilices appendleft() para urgentes.

Fase 5. Obtén una vista previa

La vista previa global debe observar primero urgentes, luego altas y normales:

Pseudocódigo
orden_global = chain(
    colas["urgente"],
    colas["alta"],
    colas["normal"],
)
vista_previa = list(islice(orden_global, cantidad))

Este recorrido no retira elementos de las colas.

Fase 6. Simula la atención

Retira mediante popleft() de la primera cola no vacía según el orden de prioridad. Añade cada solicitud al historial limitado y a una lista completa de atención.

Fase 7. Crea combinaciones

Genera parejas de revisión sin invertir el orden y todas las asignaciones responsable-turno.

Fase 8. Agrupa con groupby()

Ordena por categoría y materializa cada grupo una sola vez dentro del ciclo.

Fase 9. Presenta y prueba

Muestra todos los resultados sin recalcular o consumir iteradores ya agotados.

Formato mínimo del reporte

Pseudocódigo
CENTRO DE SOLICITUDES
================================================================================
Solicitudes recibidas:        14
Urgentes:                     ...
Altas:                        ...
Normales:                     ...
Categorías líderes:           ...
Responsable con más casos:    ...
Minutos estimados totales:    ...

RANKING DE CATEGORÍAS
--------------------------------------------------------------------------------
1. acceso          ...
2. contenido       ...
3. ...             ...

PRÓXIMAS SOLICITUDES
--------------------------------------------------------------------------------
1. SOP-004 | urgente | Error al iniciar sesión
2. SOP-011 | urgente | Cobro duplicado
3. SOP-013 | urgente | Cuenta bloqueada antes de evaluación

ORDEN DE ATENCIÓN
--------------------------------------------------------------------------------
1. SOP-004 | urgente | Carlos
2. SOP-011 | urgente | María
3. SOP-013 | urgente | Ana
...

HISTORIAL RECIENTE
--------------------------------------------------------------------------------
Últimas cinco solicitudes atendidas: ...

PAREJAS DE REVISIÓN
--------------------------------------------------------------------------------
Ana + Luis
Ana + María
...

El programa debe calcular todos los valores. La vista previa anterior permite comprobar que el orden entre urgentes respeta su llegada a las fuentes combinadas.

Plan de pruebas obligatorio

CasoPreparaciónResultado esperadoResultado obtenido
Fuentes originalesDatos proporcionados14 solicitudes combinadas
FrecuenciaContar categoríasRanking correcto
FIFO urgenteSOP-004, SOP-011, SOP-013Se conserva ese orden
Vista previaObtener tres próximasLas colas no cambian
Historial limitadoAtender las 14Solo quedan las últimas 5
Parejas4 responsables6 parejas únicas
Matriz4 responsables y 2 turnos8 asignaciones
groupby()Ordenar por categoríaUn grupo por categoría
Colección vacíaSin solicitudesReporte vacío sin error

Añade tres casos propios: empate en la categoría más frecuente, una sola prioridad con datos y un historial con menos de cinco atenciones.

Buenas prácticas obligatorias

  • Normaliza antes de contar o agrupar.
  • No accedas a una clave ausente de defaultdict solo para comprobarla.
  • No uses appendleft() para cada urgente si necesitas preservar FIFO.
  • No dependas de Counter.elements() para reconstruir el orden original.
  • No consumas dos veces un iterador o grupo.
  • Ordena por la misma clave antes de groupby().
  • Estima la cantidad de resultados antes de materializar productos o permutaciones.
  • No utilices list.pop(0) como operación principal de una cola.
  • Conserva las fuentes originales.

Entregables

Pseudocódigo
python-practico_modulo-03_nombre-apellido/
├── centro_solicitudes.py
├── datos_solicitudes.py
├── README.md
└── evidencia/
    ├── reporte_terminal.txt o captura_reporte.png
    └── plan_pruebas.md

El README.md debe explicar:

  • versión de Python y comando de ejecución;
  • por qué Counter es apropiado para frecuencias;
  • por qué se eligió defaultdict para agrupaciones;
  • cómo se preserva FIFO dentro de cada prioridad;
  • por qué se materializó el resultado de chain();
  • diferencia entre combinations() y product();
  • requisito de ordenamiento de groupby();
  • una situación donde una lista o un diccionario común sería suficiente.

Preguntas de reflexión

  1. ¿Qué código auxiliar eliminó Counter?
  2. ¿Qué efecto secundario puede producir leer una clave ausente de defaultdict con corchetes?
  3. ¿Por qué no se añaden urgentes siempre con appendleft()?
  4. ¿Qué información descarta un historial con maxlen=5?
  5. ¿Qué ocurriría si se utiliza groupby() sin ordenar?

Punto de entrega obligatorio

Realiza una sola entrega para todo el módulo.

Pseudocódigo
Curso: Python Práctico
Módulo: 3 — Colecciones especializadas e iteradores
Estudiante: nombre completo
Versión: primera entrega o corrección número X
Archivo principal: centro_solicitudes.py
Versión de Python: número utilizado

Entrega del Módulo 3

Último paso del módulo

Cuando hayas completado las actividades, el proyecto, las pruebas y la reflexión, reúne todo en un único archivo comprimido.

La entrega única debe incluir:

  • Las tres actividades obligatorias
  • centro_solicitudes.py y archivos de datos
  • README.md con instrucciones y respuestas de reflexión
  • Evidencia del reporte generado
  • Plan de pruebas completado
  • Versión de Python utilizada

Ejemplo de nombre: python-practico_modulo-03_juan-perez.zip

Antes de enviar, verifica que el archivo tenga el nombre solicitado.

Entregar Módulo 3

Condición de avance

Para continuar al Módulo 4 necesitas:

  • obtener al menos 70% en la evaluación;
  • completar las actividades obligatorias;
  • obtener Aprobado en el proyecto;
  • corregir cualquier requisito crítico señalado.

Rúbrica de evaluación del proyecto

CriterioPonderaciónDesempeño esperado
Funcionalidad35%Combina, analiza, agrupa, prioriza, atiende y presenta correctamente.
Herramientas del módulo25%Utiliza collections e itertools de acuerdo con el problema.
Claridad y organización20%Mantiene funciones enfocadas, políticas explícitas e iteradores controlados.
Robustez10%Maneja colecciones vacías, empates, historiales cortos y orden de prioridad.
Documentación y evidencia10%Incluye README, pruebas, resultados y reflexión.
Total100%

Escala de interpretación

ResultadoCondición
90–100Dominio sobresaliente
80–89Dominio sólido
70–79Dominio suficiente
Menos de 70Pendiente de corrección

Requisitos críticos

El proyecto no puede aprobarse si:

  • el programa no se ejecuta;
  • altera las fuentes originales;
  • invierte el orden de solicitudes dentro de una prioridad;
  • mezcla prioridades sin aplicar la política definida;
  • groupby() produce grupos incorrectos por no ordenar;
  • consume un iterador antes de generar un resultado requerido;
  • no conserva únicamente las últimas cinco atenciones;
  • omite herramientas obligatorias;
  • faltan archivos de entrega;
  • el estudiante no puede explicar el código.

Evaluación del módulo

Responde primero sin ejecutar el código. Después comprueba tus respuestas.

Pregunta 1

¿Qué devuelve un Counter cuando se consulta una clave ausente con corchetes?

A. None B. 0 C. KeyError D. Una lista vacía

Pregunta 2

¿Qué hace most_common(2)?

A. Elimina los dos valores menos frecuentes. B. Devuelve los dos elementos con mayor frecuencia y sus conteos. C. Ordena alfabéticamente todas las claves. D. Devuelve únicamente dos números.

Pregunta 3

¿Qué crea defaultdict(list) cuando se accede con corchetes a una clave ausente?

A. None B. 0 C. Una lista vacía insertada en el diccionario. D. Un conjunto vacío sin insertarlo.

Pregunta 4

¿Por qué datos.get("clave", []).append(valor) suele ser incorrecto para agrupar?

A. get() no acepta listas. B. Si la clave falta, se modifica una lista temporal que no se guarda. C. append() elimina el diccionario. D. Solo funciona con números.

Pregunta 5

¿Qué combinación implementa una cola FIFO con deque?

A. append() y popleft() B. append() y pop() C. appendleft() y popleft() D. rotate() y pop()

Pregunta 6

¿Qué ocurre cuando una deque(maxlen=3) llena recibe un cuarto elemento con append()?

A. Genera un error. B. Aumenta automáticamente su límite. C. Descarta el elemento más antiguo del extremo izquierdo. D. Descarta el nuevo elemento.

Pregunta 7

¿Qué herramienta forma todas las parejas únicas donde el orden no importa?

A. product() B. permutations() C. combinations() D. chain()

Pregunta 8

¿Qué hace chain(fuente_a, fuente_b)?

A. Ordena ambas fuentes. B. Recorre primero una fuente y después la otra. C. Elimina duplicados. D. Agrupa por una clave.

Pregunta 9

¿Cuál es el requisito esencial antes de utilizar groupby() para reunir todas las categorías iguales?

A. Convertir los datos en conjunto. B. Invertir la colección. C. Ordenar por la misma clave utilizada para agrupar. D. Utilizar Counter primero.

Pregunta 10

¿Qué afirmación sobre un grupo devuelto por groupby() es correcta?

A. Es un iterador y puede consumirse. B. Siempre es una lista. C. Puede recorrerse infinitas veces. D. Modifica automáticamente la colección original.

Criterio de aprobación

Necesitas al menos 7 respuestas correctas. Si no alcanzas el resultado, vuelve a ejecutar las prácticas de agrupación, colas e iteradores antes de intentarlo nuevamente.

Soluciones de las prácticas guiadas — consultar después de intentarlas

Soluciones de las prácticas guiadas

Consulta estas soluciones únicamente después de completar tu intento.

Solución 1

Pseudocódigo
from collections import Counter

categorias = [
    "Acceso", "contenido", " ACCESO ", "facturación",
    "Contenido", "acceso", "cuenta", "CONTENIDO",
]

conteo = Counter(
    categoria.strip().casefold()
    for categoria in categorias
)
ranking = conteo.most_common()
dos_primeras = conteo.most_common(2)

if conteo:
    frecuencia_maxima = max(conteo.values())
    lideres = sorted(
        categoria
        for categoria, frecuencia in conteo.items()
        if frecuencia == frecuencia_maxima
    )
else:
    lideres = []

Solución 2

Pseudocódigo
from collections import defaultdict

por_responsable = defaultdict(list)
minutos_por_responsable = defaultdict(int)
ids_por_responsable = defaultdict(set)

for solicitud in solicitudes:
    responsable = solicitud["responsable"]
    por_responsable[responsable].append(solicitud)
    minutos_por_responsable[responsable] += solicitud["minutos"]
    ids_por_responsable[responsable].add(solicitud["id"])

por_responsable = dict(por_responsable)
minutos_por_responsable = dict(minutos_por_responsable)
ids_por_responsable = dict(ids_por_responsable)

Solución 3

Pseudocódigo
from collections import deque

colas = {
    "urgente": deque(),
    "alta": deque(),
    "normal": deque(),
}
historial = deque(maxlen=3)
orden = []

for identificador, prioridad in entradas:
    colas[prioridad].append(identificador)

for prioridad in ("urgente", "alta", "normal"):
    while colas[prioridad]:
        actual = colas[prioridad].popleft()
        orden.append(actual)
        historial.append(actual)

La solución conserva FIFO dentro de cada deque.

Solución 4

Pseudocódigo
from itertools import chain, islice

combinadas = list(chain(pendientes, nuevas))
vista_previa = list(islice(combinadas, 4))

Se materializa una vez porque la colección combinada será recorrida para la vista previa y otros análisis.

Solución 5

Pseudocódigo
from itertools import combinations, groupby, product

responsables = ["Ana", "Luis", "María", "Carlos"]
turnos = ["mañana", "tarde"]

parejas = list(combinations(responsables, 2))
asignaciones = list(product(responsables, turnos))

solicitudes_ordenadas = sorted(
    solicitudes,
    key=lambda solicitud: solicitud["categoria"],
)

grupos = {}

for categoria, grupo in groupby(
    solicitudes_ordenadas,
    key=lambda solicitud: solicitud["categoria"],
):
    grupos[categoria] = list(grupo)

Sin el ordenamiento, categorías iguales separadas producen grupos diferentes.

Retos adicionales

🚀 Reto

Reto 1 — Asignación circular

Utiliza una deque de responsables y rotate(-1) para asignar nuevas solicitudes por turnos. Explica por qué esta regla no considera especialidad ni carga real.

🚀 Reto

Reto 2 — Ventana de actividad

Conserva los últimos cinco tiempos de atención en una deque(maxlen=5) y calcula su promedio después de cada caso.

🚀 Reto

Reto 3 — Subconjuntos de revisión

Genera grupos de tres responsables con combinations(). Calcula primero cuántos grupos se producirán y evita materializar si la lista de responsables crece demasiado.

🚀 Reto

Reto 4 — Comparar agrupaciones

Resuelve el resumen por categoría con defaultdict y con groupby(). Explica cuál elegirías si los datos ya están ordenados y cuál si llegan desordenados continuamente.

🚀 Reto

Reto 5 — Política contra inanición

Diseña una regla donde, después de atender tres urgentes consecutivas, se atienda una solicitud alta o normal si existe. Conserva FIFO dentro de cada cola.

Videos recomendados

Ejecuta los ejemplos y adapta al menos uno al proyecto.

Reproductores de video

🎥 Video recomendado

Counter del módulo collections — The Dojo MX

🎥 Video recomendado

defaultdict del módulo collections — The Dojo MX

🎥 Video recomendado

Procesar recorridos con itertools — Juan Bosh García

Para deque, utiliza la documentación oficial y reproduce las operaciones con una cola pequeña antes de integrarlas.

Documentación y recursos de lectura

Nivel esencial

Iteradores

Lectura gratuita

Práctica de documentación

Busca deque y responde:

  1. ¿Qué complejidad aproximada tienen append() y popleft()?
  2. ¿Qué ocurre al añadir a una deque llena con maxlen?
  3. ¿Qué diferencia existe entre extend() y extendleft()?
  4. ¿Qué signo de rotate() mueve elementos hacia la izquierda?
  5. ¿Qué operaciones de lista no son la fortaleza principal de deque?

Material descargable

El módulo incluye:

  • datos_solicitudes.py: fuentes, responsables y turnos.
  • plantilla_proyecto.py: estructura funcional sin solución.
  • GUIA_COLECCIONES_ITERTOOLS.md: mapa de decisión y advertencias.
  • CHECKLIST_PROYECTO.md: comprobación previa a la entrega.

raise NotImplementedError funciona únicamente como marcador en la plantilla. Sustituye cada marcador antes de ejecutar el proyecto completo.

Descargas

Descargar todos los materiales del Módulo 3

Descargar datos_solicitudes.py

Descargar plantilla_proyecto.py

Descargar GUIA_COLECCIONES_ITERTOOLS.md

Descargar CHECKLIST_PROYECTO.md

Errores comunes

Contar antes de normalizar

"Acceso", "acceso" y " ACCESO " se convierten en claves diferentes si no se normalizan.

Suponer que most_common(1) resuelve empates

Devuelve un elemento, aunque varias claves compartan la frecuencia máxima. Detecta líderes si el empate importa.

Confundir Counter.update() con reemplazo

update() suma frecuencias a las existentes.

Utilizar Counter.elements() como historial

Reconstruye repeticiones positivas, no el orden de llegada original.

Pasar list() en lugar de list a defaultdict

Pseudocódigo
defaultdict(list)    # correcto
defaultdict(list())  # incorrecto

La fábrica debe ser invocable.

Crear claves al consultar

grupos["ausente"] crea una clave en defaultdict. Utiliza in o get() si solo necesitas comprobar.

Perder una lista temporal con get()

datos.get(clave, []).append(valor) no guarda la lista cuando la clave falta.

Utilizar pop(0) repetidamente

Para una cola con muchas retiradas al inicio, utiliza deque.popleft().

Invertir urgentes con appendleft()

Cada nueva inserción queda antes de las urgentes anteriores. Utiliza colas separadas si necesitas FIFO por prioridad.

Olvidar la pérdida automática de maxlen

El elemento más antiguo se descarta sin confirmación cuando la deque está llena.

Consumir un iterador dos veces

chain(), islice() y los grupos de groupby() producen iteradores consumibles.

Utilizar groupby() sin ordenar

Solo agrupa coincidencias consecutivas. Ordena por la misma clave.

Confundir combinaciones con permutaciones

En combinaciones el orden no crea un grupo nuevo; en permutaciones sí.

Materializar un producto enorme

Estima el número de resultados antes de llamar a list().

Glosario

TérminoSignificado
Colección especializadaEstructura diseñada para operaciones frecuentes concretas.
CounterDiccionario especializado en frecuencias.
FrecuenciaCantidad de apariciones de un elemento.
defaultdictDiccionario que crea valores mediante una fábrica para claves ausentes.
FábricaFunción o tipo invocable que produce el valor predeterminado.
dequeCola de doble extremo con operaciones eficientes en ambos lados.
FIFOPrimero en entrar, primero en salir.
LIFOÚltimo en entrar, primero en salir.
maxlenLongitud máxima de una deque.
InaniciónEspera indefinida de elementos de baja prioridad.
IteradorObjeto que produce valores secuencialmente y puede agotarse.
chain()Iterador que recorre varias fuentes consecutivamente.
islice()Herramienta que selecciona una parte de un iterable.
Producto cartesianoConjunto de combinaciones tomando un elemento de cada colección.
CombinaciónGrupo donde el orden no crea un resultado distinto.
PermutaciónOrden posible donde la posición sí importa.
groupby()Herramienta que agrupa elementos consecutivos con la misma clave.
MaterializarConvertir un recorrido en una colección almacenada.

Resumen del módulo

Aprendiste que una colección especializada resulta útil cuando su operación central coincide con el problema:

  • Counter elimina código auxiliar de conteo y facilita rankings;
  • defaultdict simplifica agrupaciones y acumulaciones;
  • deque permite colas, pilas e historiales limitados;
  • itertools permite encadenar, limitar, combinar y agrupar recorridos.

También identificaste los riesgos: contar datos sin normalizar, crear claves accidentalmente, invertir una prioridad, perder historial por maxlen, consumir iteradores y utilizar groupby() sin ordenar.

Habilidades obtenidas

  • Conteo y ranking de datos.
  • Agrupación y acumulación por claves.
  • Diseño de colas FIFO con prioridades.
  • Conservación de ventanas e historiales limitados.
  • Combinación eficiente de fuentes.
  • Formación de parejas y matrices de posibilidades.
  • Agrupación correcta de datos ordenados.
  • Selección razonada de estructuras especializadas.

Antes de continuar

  • Puedo explicar cuándo Counter aporta valor.
  • Sé agrupar con defaultdict(list).
  • Comprendo cuándo una lectura crea una clave.
  • Puedo implementar FIFO con deque.
  • Sé preservar el orden dentro de prioridades.
  • Comprendo la pérdida automática de maxlen.
  • Puedo utilizar chain() e islice() sin consumir resultados necesarios.
  • Distingo producto, combinación y permutación.
  • Sé ordenar antes de groupby().
  • Completé las actividades obligatorias.
  • Obtuve al menos 70% en la evaluación.
  • El proyecto fue aprobado.

Cuando cumplas estas condiciones, estarás preparado para el Módulo 4: Archivos y rutas con la biblioteca estándar.

¡Has completado el Módulo 3 de Python Práctico!

Revisa tus actividades, confirma la entrega única y consulta nuevamente los recursos antes de avanzar.

Ir al Módulo 4
Escríbenos por WhatsApp