Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quiz: Estructuras de Datos

🧭 Navegación:

¡Es hora de poner a prueba tus conocimientos sobre las estructuras de datos en Python! Este quiz cubre los conceptos clave de listas, diccionarios, tuplas y conjuntos que hemos explorado en este capítulo.

Instrucciones

  • Lee cada pregunta cuidadosamente
  • Intenta responder antes de ver la solución
  • Las soluciones están ocultas bajo cada pregunta
  • Al final encontrarás una evaluación basada en tus respuestas correctas

¡Buena suerte!


Preguntas Teóricas

1. Analogías del Almacén

¿Qué estructura de datos corresponde a cada analogía del almacén?

a) Estanterías flexibles que pueden reorganizarse
b) Sistema de inventario con etiquetas
c) Paquetes sellados que no pueden modificarse
d) Estaciones de clasificación sin duplicados

Ver respuesta

Respuestas correctas:

  • a) Listas: Estanterías flexibles que pueden reorganizarse
  • b) Diccionarios: Sistema de inventario con etiquetas
  • c) Tuplas: Paquetes sellados que no pueden modificarse
  • d) Conjuntos: Estaciones de clasificación sin duplicados

Estas analogías nos ayudan a entender el propósito y comportamiento de cada estructura de datos:

  • Las listas son flexibles y ordenadas, como estanterías que podemos reorganizar
  • Los diccionarios permiten acceso rápido por clave, como un sistema de inventario
  • Las tuplas son inmutables, como paquetes sellados
  • Los conjuntos eliminan duplicados automáticamente, como estaciones de clasificación

2. Mutabilidad vs Inmutabilidad

¿Cuáles de las siguientes afirmaciones son correctas sobre la mutabilidad de las estructuras de datos?

a) Las listas son mutables y pueden contener cualquier tipo de dato
b) Los diccionarios son inmutables una vez creados
c) Las tuplas son inmutables pero pueden contener objetos mutables
d) Los conjuntos son mutables pero solo pueden contener objetos inmutables

Ver respuesta

Respuestas correctas: a), c) y d)

Explicación:

  • ✅ Las listas son mutables y pueden contener cualquier tipo de dato
  • ❌ Los diccionarios son mutables, no inmutables
  • ✅ Las tuplas son inmutables pero pueden contener objetos mutables (como listas)
  • ✅ Los conjuntos son mutables pero solo pueden contener objetos inmutables (números, strings, tuplas)

Ejemplo con tuplas conteniendo objetos mutables:

# La tupla es inmutable, pero la lista dentro de ella es mutable
t = (1, [2, 3], 4)
t[1].append(5)  # Válido
print(t)  # (1, [2, 3, 5], 4)

# Pero no podemos cambiar la tupla en sí
try:
    t[1] = [6, 7]  # Esto generará un error
except TypeError as e:
    print(f"Error: {e}")

3. Eficiencia de Operaciones

Ordena las siguientes operaciones de más eficiente a menos eficiente:

a) Buscar un elemento en una lista
b) Buscar una clave en un diccionario
c) Verificar si un elemento está en un conjunto
d) Buscar un elemento en una tupla

Ver respuesta

Orden correcto (de más eficiente a menos eficiente):

  1. b) y c) - Buscar en diccionario y conjunto: O(1)
  2. a) y d) - Buscar en lista y tupla: O(n)

Explicación:

  • Los diccionarios y conjuntos usan tablas hash, permitiendo búsquedas en tiempo constante O(1)
  • Las listas y tuplas requieren búsqueda lineal O(n), revisando cada elemento
  • Las tuplas no son más rápidas que las listas para búsquedas, aunque son más eficientes en memoria
# Ejemplo de rendimiento
import time

def medir_tiempo(operacion, n=1000000):
    inicio = time.time()
    operacion()
    return time.time() - inicio

# Preparar estructuras
lista = list(range(n))
conjunto = set(lista)
diccionario = {x: x for x in lista}
elemento = n - 1  # Peor caso

# Medir tiempos
tiempo_lista = medir_tiempo(lambda: elemento in lista)
tiempo_conjunto = medir_tiempo(lambda: elemento in conjunto)
tiempo_dict = medir_tiempo(lambda: elemento in diccionario)

print(f"Tiempo lista: {tiempo_lista:.6f}s")
print(f"Tiempo conjunto: {tiempo_conjunto:.6f}s")
print(f"Tiempo diccionario: {tiempo_dict:.6f}s")

Preguntas Prácticas

4. Manipulación de Listas

¿Qué imprimirá el siguiente código?

lista = [1, 2, 3, 4, 5]
lista[1:4] = [10]
print(lista)
Ver respuesta

Respuesta: [1, 10, 5]

Explicación:

  1. La lista original es [1, 2, 3, 4, 5]
  2. El slice [1:4] selecciona los elementos en los índices 1, 2 y 3
  3. Estos tres elementos son reemplazados por un solo elemento [10]
  4. El resultado es [1, 10, 5]

Este ejemplo demuestra que:

  • Los slices pueden ser reemplazados por un número diferente de elementos
  • La lista se ajusta automáticamente al nuevo tamaño
  • Los elementos fuera del slice permanecen sin cambios

5. Diccionarios Anidados

Dado el siguiente diccionario:

almacen = {
    'electrónicos': {
        'computadoras': {'stock': 10, 'precio': 1200},
        'tablets': {'stock': 15, 'precio': 300}
    },
    'muebles': {
        'sillas': {'stock': 20, 'precio': 50},
        'mesas': {'stock': 8, 'precio': 150}
    }
}

Escribe una función que calcule el valor total del inventario.

Ver respuesta

Solución:

def valor_total_inventario(inventario):
    total = 0
    for categoria in inventario.values():
        for producto in categoria.values():
            total += producto['stock'] * producto['precio']
    return total

# Calcular el valor total
total = valor_total_inventario(almacen)
print(f"Valor total del inventario: ${total:,}")  # $19,200

Explicación:

  1. La función recorre cada categoría del almacén
  2. Para cada categoría, recorre cada producto
  3. Multiplica el stock por el precio de cada producto
  4. Suma todos los valores para obtener el total

Alternativa usando comprensión de diccionarios:

def valor_total_inventario_comprension(inventario):
    return sum(
        producto['stock'] * producto['precio']
        for categoria in inventario.values()
        for producto in categoria.values()
    )

6. Tuplas y Desempaquetado

¿Qué valores tendrán las variables después de ejecutar este código?

datos = [(1, 'a'), (2, 'b'), (3, 'c')]
numeros, letras = zip(*datos)
print(numeros)
print(letras)
Ver respuesta

Respuesta:

numeros = (1, 2, 3)
letras = ('a', 'b', 'c')

Explicación:

  1. zip(*datos) desempaqueta la lista de tuplas
  2. El operador * desempaqueta datos en argumentos individuales
  3. zip() crea un nuevo iterador que agrupa los elementos por posición
  4. El resultado se asigna a numeros y letras mediante desempaquetado

Este patrón es útil para:

  • Transponer matrices
  • Separar datos en columnas
  • Procesar datos estructurados

Ejemplo adicional:

# Crear una lista de tuplas desde columnas separadas
nums = [1, 2, 3]
lets = ['a', 'b', 'c']
combinados = list(zip(nums, lets))
print(combinados)  # [(1, 'a'), (2, 'b'), (3, 'c')]

# Volver a separar
n, l = zip(*combinados)
print(n)  # (1, 2, 3)
print(l)  # ('a', 'b', 'c')

7. Operaciones con Conjuntos

Dado el siguiente código:

empleados_python = {'Ana', 'Carlos', 'Diana', 'Eduardo'}
empleados_java = {'Bruno', 'Carlos', 'Diana', 'Fernando'}
empleados_javascript = {'Carlos', 'Gabriel', 'Diana', 'Helena'}

Escribe expresiones para encontrar: a) Empleados que conocen los tres lenguajes
b) Empleados que solo conocen Python
c) Empleados que conocen al menos dos lenguajes

Ver respuesta

Solución:

# a) Empleados que conocen los tres lenguajes
todos_lenguajes = empleados_python & empleados_java & empleados_javascript
print(f"Conocen los tres lenguajes: {todos_lenguajes}")  # {'Carlos', 'Diana'}

# b) Empleados que solo conocen Python
solo_python = empleados_python - (empleados_java | empleados_javascript)
print(f"Solo conocen Python: {solo_python}")  # {'Ana'}

# c) Empleados que conocen al menos dos lenguajes
python_java = empleados_python & empleados_java
python_js = empleados_python & empleados_javascript
java_js = empleados_java & empleados_javascript
al_menos_dos = python_java | python_js | java_js
print(f"Conocen al menos dos lenguajes: {al_menos_dos}")  # {'Carlos', 'Diana'}

Explicación de operadores:

  • &: intersección (elementos comunes)
  • |: unión (todos los elementos)
  • -: diferencia (elementos en el primer conjunto pero no en el segundo)

Alternativa usando métodos:

# Los mismos resultados usando métodos
todos_lenguajes = empleados_python.intersection(empleados_java, empleados_javascript)
solo_python = empleados_python.difference(empleados_java.union(empleados_javascript))
al_menos_dos = (empleados_python.intersection(empleados_java)
                .union(empleados_python.intersection(empleados_javascript))
                .union(empleados_java.intersection(empleados_javascript)))

8. Proyecto Práctico: Sistema de Inventario

Implementa un sistema simple de inventario que permita:

  • Añadir productos con cantidad y precio
  • Actualizar el stock
  • Calcular el valor total del inventario
  • Listar productos con bajo stock (menos de 5 unidades)
Ver respuesta

Solución:

class Inventario:
    def __init__(self):
        self.productos = {}
    
    def agregar_producto(self, codigo, nombre, cantidad, precio):
        """Añade un nuevo producto o actualiza uno existente."""
        self.productos[codigo] = {
            'nombre': nombre,
            'cantidad': cantidad,
            'precio': precio
        }
    
    def actualizar_stock(self, codigo, cantidad):
        """Actualiza la cantidad de un producto."""
        if codigo in self.productos:
            self.productos[codigo]['cantidad'] += cantidad
            return True
        return False
    
    def valor_total(self):
        """Calcula el valor total del inventario."""
        return sum(
            prod['cantidad'] * prod['precio']
            for prod in self.productos.values()
        )
    
    def productos_bajo_stock(self, limite=5):
        """Lista productos con stock menor al límite."""
        return {
            codigo: datos
            for codigo, datos in self.productos.items()
            if datos['cantidad'] < limite
        }
    
    def mostrar_inventario(self):
        """Muestra el inventario completo."""
        print("\nInventario Actual:")
        print("-" * 50)
        print(f"{'Código':<10} {'Nombre':<20} {'Cantidad':<10} {'Precio':>8}")
        print("-" * 50)
        for codigo, datos in self.productos.items():
            print(f"{codigo:<10} {datos['nombre']:<20} {datos['cantidad']:<10} ${datos['precio']:>7.2f}")
        print("-" * 50)
        print(f"Valor total del inventario: ${self.valor_total():,.2f}")

# Ejemplo de uso
inventario = Inventario()

# Agregar productos
inventario.agregar_producto('LAP001', 'Laptop Dell', 10, 1200)
inventario.agregar_producto('MON001', 'Monitor 24"', 15, 200)
inventario.agregar_producto('TEC001', 'Teclado Mecánico', 3, 80)
inventario.agregar_producto('MOU001', 'Mouse Inalámbrico', 20, 30)

# Mostrar inventario inicial
inventario.mostrar_inventario()

# Actualizar stock
inventario.actualizar_stock('LAP001', -2)  # Venta de 2 laptops
inventario.actualizar_stock('MON001', 5)   # Recepción de 5 monitores

# Mostrar productos con bajo stock
print("\nProductos con bajo stock:")
for codigo, datos in inventario.productos_bajo_stock().items():
    print(f"{datos['nombre']}: {datos['cantidad']} unidades")

# Mostrar inventario actualizado
inventario.mostrar_inventario()

Este ejemplo demuestra:

  1. Uso de diccionarios anidados para almacenar datos
  2. Métodos para manipular el inventario
  3. Comprensiones de diccionarios para filtrar datos
  4. Formateo de strings para presentación

Características adicionales que podrías implementar:

  • Validación de datos (cantidades no negativas, precios válidos)
  • Historial de transacciones usando tuplas
  • Categorización de productos usando conjuntos
  • Búsqueda de productos por nombre o código

9. Desafío: Análisis de Datos

Dado un conjunto de datos de ventas, escribe funciones para:

  • Encontrar los productos más vendidos
  • Calcular las ventas por día
  • Identificar patrones de compra (productos que se compran juntos)
ventas = [
    ('2023-07-01', ['laptop', 'mouse', 'teclado']),
    ('2023-07-01', ['monitor', 'teclado']),
    ('2023-07-02', ['laptop', 'monitor']),
    ('2023-07-02', ['mouse', 'teclado', 'auriculares']),
    ('2023-07-03', ['laptop', 'mouse'])
]
Ver respuesta

Solución:

from collections import Counter
from itertools import combinations

def productos_mas_vendidos(ventas):
    """Encuentra los productos más vendidos y su cantidad."""
    contador = Counter()
    for _, productos in ventas:
        contador.update(productos)
    return contador.most_common()

def ventas_por_dia(ventas):
    """Calcula el número de ventas por día."""
    ventas_dia = {}
    for fecha, productos in ventas:
        ventas_dia[fecha] = ventas_dia.get(fecha, 0) + 1
    return dict(sorted(ventas_dia.items()))

def productos_relacionados(ventas, min_frecuencia=2):
    """Identifica productos que se compran juntos frecuentemente."""
    pares = Counter()
    for _, productos in ventas:
        # Generar todos los pares posibles de productos
        for par in combinations(sorted(productos), 2):
            pares[par] += 1
    
    # Filtrar pares que aparecen al menos min_frecuencia veces
    return {par: freq for par, freq in pares.items() 
            if freq >= min_frecuencia}

# Análisis de ventas
print("Productos más vendidos:")
for producto, cantidad in productos_mas_vendidos(ventas):
    print(f"{producto}: {cantidad} ventas")

print("\nVentas por día:")
for fecha, cantidad in ventas_por_dia(ventas).items():
    print(f"{fecha}: {cantidad} ventas")

print("\nProductos frecuentemente comprados juntos:")
for (prod1, prod2), freq in productos_relacionados(ventas).items():
    print(f"{prod1} + {prod2}: {freq} veces")

Este ejemplo demuestra:

  1. Uso de Counter para contar ocurrencias
  2. Diccionarios para agrupar datos por fecha
  3. Combinaciones para analizar patrones
  4. Comprensiones de diccionarios para filtrar resultados

Mejoras posibles:

  • Añadir análisis de tendencias temporales
  • Calcular correlaciones entre productos
  • Visualizar datos con gráficos
  • Generar recomendaciones basadas en patrones

10. Desafío Final: Optimización de Código

Optimiza el siguiente código que procesa datos de ventas:

def procesar_ventas(ventas):
    productos_vendidos = []
    ventas_por_producto = {}
    clientes_por_producto = {}
    
    for venta in ventas:
        producto = venta['producto']
        cantidad = venta['cantidad']
        cliente = venta['cliente']
        
        productos_vendidos.append(producto)
        
        if producto in ventas_por_producto:
            ventas_por_producto[producto] += cantidad
        else:
            ventas_por_producto[producto] = cantidad
            
        if producto not in clientes_por_producto:
            clientes_por_producto[producto] = []
        if cliente not in clientes_por_producto[producto]:
            clientes_por_producto[producto].append(cliente)
    
    return (list(set(productos_vendidos)), 
            ventas_por_producto,
            clientes_por_producto)
Ver respuesta

Solución optimizada:

from collections import defaultdict

def procesar_ventas_optimizado(ventas):
    """
    Procesa datos de ventas de manera eficiente.
    
    Args:
        ventas: Lista de diccionarios con datos de ventas
        
    Returns:
        Tupla con (productos únicos, ventas por producto, clientes por producto)
    """
    productos_vendidos = set()  # Usar set desde el inicio
    ventas_por_producto = defaultdict(int)  # Valor default 0
    clientes_por_producto = defaultdict(set)  # Valor default set()
    
    for venta in ventas:
        producto = venta['producto']
        productos_vendidos.add(producto)
        ventas_por_producto[producto] += venta['cantidad']
        clientes_por_producto[producto].add(venta['cliente'])
    
    return (list(productos_vendidos),
            dict(ventas_por_producto),
            {k: list(v) for k, v in clientes_por_producto.items()})

# Comparación de rendimiento
import time

# Datos de prueba
datos_prueba = [
    {'producto': 'laptop', 'cantidad': 1, 'cliente': 'Ana'},
    {'producto': 'monitor', 'cantidad': 2, 'cliente': 'Bruno'},
    {'producto': 'laptop', 'cantidad': 1, 'cliente': 'Carlos'},
    {'producto': 'teclado', 'cantidad': 3, 'cliente': 'Ana'},
    {'producto': 'monitor', 'cantidad': 1, 'cliente': 'Diana'}
]

# Medir tiempo de la versión original
inicio = time.time()
resultado_original = procesar_ventas(datos_prueba)
tiempo_original = time.time() - inicio

# Medir tiempo de la versión optimizada
inicio = time.time()
resultado_optimizado = procesar_ventas_optimizado(datos_prueba)
tiempo_optimizado = time.time() - inicio

print(f"Tiempo versión original: {tiempo_original:.6f}s")
print(f"Tiempo versión optimizada: {tiempo_optimizado:.6f}s")
print(f"Mejora: {(tiempo_original/tiempo_optimizado):.2f}x más rápido")

Mejoras realizadas:

  1. Uso de set() desde el inicio para productos únicos
  2. Uso de defaultdict para evitar verificaciones de existencia
  3. Uso de set() para clientes por producto, eliminando duplicados automáticamente
  4. Conversión final a los tipos de datos requeridos

Ventajas:

  • Código más conciso y legible
  • Mejor rendimiento
  • Menos propenso a errores
  • Más eficiente en memoria

La versión optimizada es especialmente más eficiente con conjuntos de datos grandes debido a:

  • Menos operaciones de verificación
  • Uso de estructuras de datos optimizadas
  • Mejor manejo de memoria

Evaluación

Cuenta tus respuestas correctas:

  • 9-10 correctas: ¡Excelente! Dominas las estructuras de datos en Python.
  • 7-8 correctas: ¡Muy bien! Tienes una comprensión sólida.
  • 5-6 correctas: Bien. Entiendes los conceptos básicos pero necesitas más práctica.
  • 3-4 correctas: Regular. Repasa los conceptos clave.
  • 0-2 correctas: Necesitas revisar el capítulo nuevamente.

Próximos Pasos

  1. Revisa los conceptos en los que tuviste dificultades
  2. Practica con los ejercicios adicionales de cada sección
  3. Intenta resolver problemas reales usando estas estructuras
  4. Avanza al siguiente capítulo: Funciones y Módulos

🧭 Navegación: