Conjuntos (Sets) en Python
🧭 Navegación:
- Anterior: Tuplas
- Siguiente: Quiz: Estructuras de Datos
- Volver al Índice de Estructuras de Datos
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:
- Elementos únicos: No permiten duplicados
- Desordenados: No mantienen un orden específico
- Mutables: Puedes añadir o eliminar elementos
- Elementos inmutables: Solo pueden contener elementos inmutables (números, strings, tuplas, pero no listas o diccionarios)
- 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?
Estructura | Características | Casos 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:
- Necesites elementos únicos: Eliminar duplicados automáticamente
- La verificación de pertenencia sea crítica: Operaciones
in
muy rápidas - Necesites operaciones matemáticas de conjuntos: Uniones, intersecciones, diferencias
- 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 ena
o enb
a & b
es la intersección: elementos comunes aa
yb
a - b
es la diferencia: elementos ena
pero no enb
b - a
es la diferencia: elementos enb
pero no ena
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:
- Anterior: Tuplas
- Siguiente: Quiz: Estructuras de Datos
- Volver al Índice de Estructuras de Datos