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

Conjuntos (Sets) en Python

🧭 Navegación:

Introducción a los Conjuntos

Imagina que en tu almacén tienes estaciones de clasificación especiales que automáticamente eliminan duplicados y organizan los elementos de manera eficiente. Estas estaciones están diseñadas para responder rápidamente a preguntas como: “¿Contiene este elemento?”, “¿Qué elementos tienen en común estas dos colecciones?” o “¿Qué elementos están en una colección pero no en la otra?”.

Los conjuntos (sets) en Python funcionan exactamente así: son colecciones desordenadas de elementos únicos que permiten operaciones matemáticas de conjuntos como uniones, intersecciones y diferencias.

🔍 Perspectiva personal: Cuando necesito eliminar duplicados de una lista o verificar pertenencia de manera eficiente, los conjuntos son mi primera opción. Son increíblemente rápidos para estas operaciones y simplifican enormemente el código.

Creando Conjuntos

Puedes crear un conjunto utilizando llaves {} o la función set():

# Crear un conjunto con llaves
frutas = {"manzana", "naranja", "plátano", "pera", "manzana"}
print(frutas)  # {'naranja', 'plátano', 'manzana', 'pera'}
# Nota: Los duplicados se eliminan automáticamente

# Crear un conjunto a partir de una lista
colores_lista = ["rojo", "azul", "verde", "azul", "amarillo", "rojo"]
colores = set(colores_lista)
print(colores)  # {'verde', 'amarillo', 'rojo', 'azul'}

# Crear un conjunto vacío
# Importante: {} crea un diccionario vacío, no un conjunto
conjunto_vacio = set()
print(type(conjunto_vacio))  # <class 'set'>

diccionario_vacio = {}
print(type(diccionario_vacio))  # <class 'dict'>

🔍 Perspectiva personal: Un error común que cometí al principio fue intentar crear un conjunto vacío con {}, ¡pero eso crea un diccionario vacío! Siempre usa set() para crear un conjunto vacío.

Características de los Conjuntos

Los conjuntos en Python tienen varias características importantes:

  1. Elementos únicos: No permiten duplicados
  2. Desordenados: No mantienen un orden específico
  3. Mutables: Puedes añadir o eliminar elementos
  4. Elementos inmutables: Solo pueden contener elementos inmutables (números, strings, tuplas, pero no listas o diccionarios)
  5. Optimizados para verificar pertenencia: La operación in es muy rápida
# Demostración de características
numeros = {1, 2, 3, 4, 5, 5, 4, 3}
print(numeros)  # {1, 2, 3, 4, 5} - Sin duplicados

# Los conjuntos son desordenados
# El orden de impresión puede variar
print({3, 1, 4, 2, 5})  # Podría ser {1, 2, 3, 4, 5} u otro orden

# Verificación de pertenencia (muy eficiente)
print(3 in numeros)  # True
print(6 in numeros)  # False

# Intentar añadir elementos inmutables
try:
    conjunto_invalido = {1, 2, [3, 4]}  # Esto generará un error
except TypeError as e:
    print(f"Error: {e}")  # Error: unhashable type: 'list'

# Las tuplas son inmutables, por lo que pueden estar en un conjunto
conjunto_valido = {1, 2, (3, 4)}
print(conjunto_valido)  # {1, 2, (3, 4)}

Operaciones con Conjuntos

Añadir y Eliminar Elementos

# Crear un conjunto
inventario = {"laptop", "monitor", "teclado"}
print(f"Inventario inicial: {inventario}")

# Añadir un elemento
inventario.add("mouse")
print(f"Después de añadir: {inventario}")

# Añadir varios elementos
inventario.update(["webcam", "auriculares", "altavoces"])
print(f"Después de update: {inventario}")

# Eliminar un elemento (genera error si no existe)
inventario.remove("webcam")
print(f"Después de remove: {inventario}")

# Eliminar un elemento (no genera error si no existe)
inventario.discard("impresora")  # No existe, pero no genera error
print(f"Después de discard: {inventario}")

# Eliminar y devolver un elemento arbitrario
elemento = inventario.pop()
print(f"Elemento eliminado: {elemento}")
print(f"Después de pop: {inventario}")

# Eliminar todos los elementos
inventario.clear()
print(f"Después de clear: {inventario}")  # set()

Operaciones Matemáticas de Conjuntos

Los conjuntos en Python soportan todas las operaciones matemáticas estándar de teoría de conjuntos:

# Definir dos conjuntos
a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}

# Unión (elementos en A o B)
union = a | b  # También: a.union(b)
print(f"Unión: {union}")  # {1, 2, 3, 4, 5, 6, 7, 8}

# Intersección (elementos en A y B)
interseccion = a & b  # También: a.intersection(b)
print(f"Intersección: {interseccion}")  # {4, 5}

# Diferencia (elementos en A pero no en B)
diferencia = a - b  # También: a.difference(b)
print(f"Diferencia (A-B): {diferencia}")  # {1, 2, 3}

# Diferencia simétrica (elementos en A o B pero no en ambos)
dif_simetrica = a ^ b  # También: a.symmetric_difference(b)
print(f"Diferencia simétrica: {dif_simetrica}")  # {1, 2, 3, 6, 7, 8}

🔍 Perspectiva personal: Visualizo estas operaciones como si estuviera moviendo productos entre diferentes estaciones de clasificación en el almacén. La unión combina todos los productos, la intersección muestra solo los productos comunes, y la diferencia muestra los productos exclusivos de una estación.

Verificación de Subconjuntos y Superconjuntos

# Definir conjuntos para verificación
frutas = {"manzana", "naranja", "plátano", "pera", "uva"}
citricos = {"naranja", "limón", "lima"}
frutas_tropicales = {"plátano", "piña", "mango"}
solo_naranjas = {"naranja"}

# Verificar si un conjunto es subconjunto de otro
print(solo_naranjas.issubset(frutas))  # True
print(solo_naranjas <= frutas)  # True (operador de subconjunto)

# Verificar si un conjunto es superconjunto de otro
print(frutas.issuperset(solo_naranjas))  # True
print(frutas >= solo_naranjas)  # True (operador de superconjunto)

# Verificar si dos conjuntos son disjuntos (no tienen elementos en común)
print(citricos.isdisjoint(frutas_tropicales))  # True
print(citricos.isdisjoint(frutas))  # False (tienen "naranja" en común)

Comprensiones de Conjuntos

Al igual que las listas y los diccionarios, los conjuntos también admiten comprensiones:

# Crear un conjunto de cuadrados de números del 1 al 10
cuadrados = {x**2 for x in range(1, 11)}
print(cuadrados)  # {1, 4, 9, 16, 25, 36, 49, 64, 81, 100}

# Crear un conjunto de vocales en una cadena
texto = "Python es un lenguaje de programación versátil y poderoso"
vocales = {letra.lower() for letra in texto if letra.lower() in "aeiou"}
print(vocales)  # {'e', 'a', 'i', 'o', 'u'}

# Crear un conjunto de números pares del 1 al 20
pares = {x for x in range(1, 21) if x % 2 == 0}
print(pares)  # {2, 4, 6, 8, 10, 12, 14, 16, 18, 20}

Conjuntos Inmutables (frozenset)

Python también ofrece una versión inmutable de los conjuntos llamada frozenset. Una vez creado, un frozenset no puede ser modificado:

# Crear un frozenset
colores_inmutables = frozenset(["rojo", "verde", "azul"])
print(colores_inmutables)  # frozenset({'verde', 'azul', 'rojo'})

# Intentar modificar un frozenset generará un error
try:
    colores_inmutables.add("amarillo")
except AttributeError as e:
    print(f"Error: {e}")  # Error: 'frozenset' object has no attribute 'add'

# Los frozensets pueden ser usados como claves en diccionarios
paletas = {
    frozenset(["rojo", "verde", "azul"]): "RGB",
    frozenset(["cian", "magenta", "amarillo", "negro"]): "CMYK"
}
print(paletas[frozenset(["rojo", "verde", "azul"])])  # "RGB"

🔍 Perspectiva personal: Los frozenset son útiles cuando necesitas la eficiencia de los conjuntos para verificar pertenencia, pero también necesitas inmutabilidad, como al usar conjuntos como claves en un diccionario.

Aplicaciones Prácticas de los Conjuntos

1. Eliminar duplicados de una lista

# Lista con elementos duplicados
numeros_con_duplicados = [1, 2, 3, 2, 4, 5, 3, 6, 1, 7]

# Eliminar duplicados manteniendo el orden
numeros_sin_duplicados = list(dict.fromkeys(numeros_con_duplicados))
print(f"Sin duplicados (manteniendo orden): {numeros_sin_duplicados}")

# Eliminar duplicados (sin mantener el orden)
numeros_sin_duplicados_set = list(set(numeros_con_duplicados))
print(f"Sin duplicados (sin mantener orden): {numeros_sin_duplicados_set}")

2. Encontrar elementos únicos y comunes entre listas

# Listas de productos en diferentes almacenes
almacen_a = ["laptop", "monitor", "teclado", "mouse", "impresora", "scanner"]
almacen_b = ["laptop", "tablet", "smartphone", "monitor", "cables", "accesorios"]

# Convertir a conjuntos
set_a = set(almacen_a)
set_b = set(almacen_b)

# Productos en ambos almacenes
productos_comunes = set_a & set_b
print(f"Productos en ambos almacenes: {productos_comunes}")

# Productos únicos del almacén A
productos_unicos_a = set_a - set_b
print(f"Productos únicos del almacén A: {productos_unicos_a}")

# Productos únicos del almacén B
productos_unicos_b = set_b - set_a
print(f"Productos únicos del almacén B: {productos_unicos_b}")

# Todos los productos disponibles
todos_productos = set_a | set_b
print(f"Todos los productos disponibles: {todos_productos}")

3. Verificar si una lista contiene todos los elementos requeridos

# Ingredientes requeridos para una receta
ingredientes_requeridos = {"harina", "huevos", "azúcar", "leche", "mantequilla"}

# Ingredientes disponibles en la cocina
ingredientes_disponibles = {"harina", "huevos", "azúcar", "sal", "levadura", "mantequilla"}

# Verificar si tenemos todos los ingredientes requeridos
if ingredientes_requeridos.issubset(ingredientes_disponibles):
    print("¡Tenemos todos los ingredientes para la receta!")
else:
    # Encontrar qué ingredientes faltan
    faltantes = ingredientes_requeridos - ingredientes_disponibles
    print(f"Faltan los siguientes ingredientes: {faltantes}")

4. Análisis de texto

def analizar_texto(texto):
    """Analiza un texto y devuelve estadísticas sobre las palabras."""
    # Convertir a minúsculas y dividir en palabras
    palabras = texto.lower().split()
    
    # Eliminar signos de puntuación
    palabras = [palabra.strip(".,;:!?()[]{}\"'") for palabra in palabras]
    
    # Contar palabras únicas
    palabras_unicas = set(palabras)
    
    return {
        "total_palabras": len(palabras),
        "palabras_unicas": len(palabras_unicas),
        "palabras_comunes": sorted(palabras_unicas)[:5] if len(palabras_unicas) >= 5 else sorted(palabras_unicas)
    }

# Ejemplo de uso
texto_ejemplo = """
Python es un lenguaje de programación versátil y poderoso. 
Python es fácil de aprender y tiene una sintaxis clara.
Los programadores disfrutan usando Python para diversos proyectos.
"""

resultado = analizar_texto(texto_ejemplo)
print(f"Total de palabras: {resultado['total_palabras']}")
print(f"Palabras únicas: {resultado['palabras_unicas']}")
print(f"Algunas palabras comunes: {resultado['palabras_comunes']}")

Rendimiento de los Conjuntos

Los conjuntos en Python están implementados como tablas hash, lo que los hace extremadamente eficientes para operaciones como verificar pertenencia, añadir elementos y eliminar elementos. Estas operaciones tienen una complejidad de tiempo promedio de O(1), lo que significa que son muy rápidas incluso con conjuntos grandes.

import time

# Comparar rendimiento entre lista y conjunto para verificar pertenencia
def comparar_rendimiento(n):
    # Crear una lista y un conjunto con n elementos
    lista = list(range(n))
    conjunto = set(range(n))
    
    # Elemento a buscar (peor caso)
    elemento = n - 1
    
    # Medir tiempo para lista
    inicio = time.time()
    elemento in lista
    tiempo_lista = time.time() - inicio
    
    # Medir tiempo para conjunto
    inicio = time.time()
    elemento in conjunto
    tiempo_conjunto = time.time() - inicio
    
    return tiempo_lista, tiempo_conjunto

# Probar con diferentes tamaños
tamaños = [1000, 10000, 100000, 1000000]

print("Comparación de rendimiento (verificación de pertenencia):")
print("Tamaño\t\tTiempo Lista\tTiempo Conjunto\tVeces más rápido")
print("-" * 70)

for n in tamaños:
    tiempo_lista, tiempo_conjunto = comparar_rendimiento(n)
    veces_mas_rapido = tiempo_lista / tiempo_conjunto if tiempo_conjunto > 0 else "∞"
    
    print(f"{n:,}\t\t{tiempo_lista:.6f}s\t{tiempo_conjunto:.6f}s\t{veces_mas_rapido:.1f}x")

🔍 Perspectiva personal: La primera vez que vi la diferencia de rendimiento entre buscar en una lista vs. buscar en un conjunto con miles de elementos, quedé impresionado. Los conjuntos pueden ser cientos o miles de veces más rápidos para estas operaciones.

Conjuntos vs Listas vs Diccionarios: ¿Cuándo usar cada uno?

EstructuraCaracterísticasCasos de uso
Conjuntos- Elementos únicos
- Desordenados
- Operaciones matemáticas de conjuntos
- Verificación de pertenencia O(1)
- Eliminar duplicados
- Verificar pertenencia eficientemente
- Operaciones matemáticas entre colecciones
Listas- Permite duplicados
- Ordenados por índice
- Acceso por posición
- Verificación de pertenencia O(n)
- Cuando el orden importa
- Cuando necesitas duplicados
- Cuando necesitas acceder por índice
Diccionarios- Pares clave-valor
- Claves únicas
- Acceso por clave O(1)
- Mapeo de valores
- Búsqueda rápida por clave
- Almacenar propiedades de objetos

Usa conjuntos cuando:

  1. Necesites elementos únicos: Eliminar duplicados automáticamente
  2. La verificación de pertenencia sea crítica: Operaciones in muy rápidas
  3. Necesites operaciones matemáticas de conjuntos: Uniones, intersecciones, diferencias
  4. El orden no sea importante: Los conjuntos no mantienen un orden específico

Comprueba tu comprensión

Vamos a poner a prueba tu comprensión de los conjuntos con algunos ejercicios prácticos:

Ejercicio 1: Operaciones básicas con conjuntos

¿Qué imprimirá el siguiente código?

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b)
print(a & b)
print(a - b)
print(b - a)
Ver solución
# Código:
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

# Resultados:
print(a | b)  # {1, 2, 3, 4, 5, 6}
print(a & b)  # {3, 4}
print(a - b)  # {1, 2}
print(b - a)  # {5, 6}
  • a | b es la unión: todos los elementos que están en a o en b
  • a & b es la intersección: elementos comunes a a y b
  • a - b es la diferencia: elementos en a pero no en b
  • b - a es la diferencia: elementos en b pero no en a

Ejercicio 2: Eliminar duplicados manteniendo el orden

Escribe una función que elimine los duplicados de una lista manteniendo el orden original de los elementos.

Ver solución
def eliminar_duplicados_ordenados(lista):
    """
    Elimina duplicados de una lista manteniendo el orden original.
    
    Args:
        lista: Lista con posibles duplicados
        
    Returns:
        Lista sin duplicados, manteniendo el orden original
    """
    return list(dict.fromkeys(lista))

# Ejemplo de uso
numeros = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
print(eliminar_duplicados_ordenados(numeros))  # [3, 1, 4, 5, 9, 2, 6]

Esta solución utiliza un diccionario para mantener el orden (en Python 3.7+, los diccionarios mantienen el orden de inserción). Al convertir la lista a un diccionario con las claves como elementos de la lista, automáticamente se eliminan los duplicados mientras se preserva el orden.

Ejercicio 3: Encontrar elementos únicos en múltiples listas

Escribe una función que tome varias listas y devuelva los elementos que aparecen en una y solo una de las listas.

Ver solución
def elementos_unicos(*listas):
    """
    Encuentra elementos que aparecen en exactamente una de las listas.
    
    Args:
        *listas: Múltiples listas
        
    Returns:
        Conjunto con elementos que aparecen en exactamente una lista
    """
    if not listas:
        return set()
    
    # Convertir todas las listas a conjuntos
    conjuntos = [set(lista) for lista in listas]
    
    # Unión de todos los conjuntos
    todos = set().union(*conjuntos)
    
    # Elementos que aparecen en más de una lista
    comunes = set()
    for i, conjunto_a in enumerate(conjuntos):
        for j, conjunto_b in enumerate(conjuntos):
            if i < j:  # Evitar comparar un conjunto consigo mismo o repetir comparaciones
                comunes.update(conjunto_a & conjunto_b)
    
    # Elementos únicos = todos - comunes
    return todos - comunes

# Ejemplo de uso
lista1 = [1, 2, 3, 4, 5]
lista2 = [4, 5, 6, 7, 8]
lista3 = [7, 8, 9, 10]

unicos = elementos_unicos(lista1, lista2, lista3)
print(unicos)  # {1, 2, 3, 6, 9, 10}

Esta función convierte cada lista a un conjunto, encuentra la unión de todos los conjuntos, y luego resta los elementos que aparecen en más de una lista.

Ejercicio 4: Verificar anagramas

Escribe una función que determine si dos palabras son anagramas (contienen exactamente las mismas letras).

Ver solución
def son_anagramas(palabra1, palabra2):
    """
    Verifica si dos palabras son anagramas.
    
    Args:
        palabra1: Primera palabra
        palabra2: Segunda palabra
        
    Returns:
        True si son anagramas, False en caso contrario
    """
    # Eliminar espacios y convertir a minúsculas
    palabra1 = palabra1.lower().replace(" ", "")
    palabra2 = palabra2.lower().replace(" ", "")
    
    # Verificar si tienen las mismas letras
    return set(palabra1) == set(palabra2) and len(palabra1) == len(palabra2)

# Ejemplos de uso
print(son_anagramas("amor", "roma"))  # True
print(son_anagramas("listen", "silent"))  # True
print(son_anagramas("hello", "world"))  # False
print(son_anagramas("astronomer", "moon starer"))  # True

Esta función convierte ambas palabras a conjuntos y verifica si contienen las mismas letras. También verifica que tengan la misma longitud, ya que dos palabras con diferentes cantidades de las mismas letras no son anagramas (por ejemplo, “aab” y “aba” tienen las mismas letras pero no son anagramas).

Resumen

Los conjuntos son estructuras de datos poderosas y eficientes que ofrecen varias ventajas:

  • Elementos únicos: Eliminan automáticamente los duplicados
  • Operaciones matemáticas: Soportan uniones, intersecciones y diferencias
  • Eficiencia: Verificación de pertenencia extremadamente rápida (O(1))
  • Flexibilidad: Pueden ser mutables (set) o inmutables (frozenset)

Recuerda nuestra analogía del almacén: los conjuntos son como estaciones de clasificación que automáticamente eliminan duplicados y permiten operaciones eficientes entre diferentes grupos de elementos. Son ideales para eliminar duplicados, verificar pertenencia eficientemente y realizar operaciones matemáticas entre colecciones.

Con esto, hemos completado nuestro recorrido por las principales estructuras de datos en Python:

  • Listas: Estanterías flexibles para secuencias ordenadas
  • Diccionarios: Sistema de inventario con etiquetas para acceso rápido
  • Tuplas: Paquetes sellados que no pueden modificarse
  • Conjuntos: Estaciones de clasificación que eliminan duplicados

Cada una tiene sus propias fortalezas y casos de uso, y dominarlas te permitirá elegir la herramienta adecuada para cada tarea en tus proyectos de Python.


🧭 Navegación: