Conjuntos en Python

Cuando estaba preparando mi charla introductoria a Python, le pasé mis slides a un amigo para que me diga su opinión y una de las cosas que me dijo fue

Nunca senti que set sea algo nativo de python, le daria mas importancia a los diccionarios, aunque tal vez tu publico este mas interesado en sets, no lo se.

Me sorprendió el comentario. Para mi, set es un tipo de dato muy útil y poderoso. En este post voy a intentar hacer una apología de set, el tipo de dato que incorpora Python para representar la noción matemática de conjunto.

Presentación

Un objeto set es una colección sin orden de objetos hasheables. Puede contener objetos de todos los tipos inmutables de Python, pero no los contenedores mutables como listas. También puede contener objetos de clases definidas por el usuario (Los objetos instancias de clases definidas por el usuario son por defecto hasheables.).
Los conjuntos se pueden crear, por ejemplo, a partir de una lista. Podemos quitar elementos (al azar o uno en concreto) o agregarlos:

>>> heladera = ['huevo', 'huevo', 'queso', 'leche', 'pera', 'pera', 'pera']
>>> alimentos = set(heladera)
>>> alimentos
set(['queso', 'leche', 'huevo', 'pera'])
>>> alimentos.pop()
'queso'
>>> alimentos.remove('leche')
>>> alimentos
set(['huevo', 'pera'])
>>> alimentos.add('empanada')
>>> alimentos
set(['empanada', 'huevo', 'pera'])

Prueba de pertenencia

Otra función muy común y útil es probar la pertenencia de objetos al conjunto:

>>> 'empanada' in alimentos
True
>>> 'leche' in alimentos
False
>>> 'leche' not in alimentos
True

Iterar sobre conjuntos

Podemos interar sobre conjuntos de la misma forma que lo hacemos sobre listas:

>>> for a in alimentos:
...     "Debo comer " + a
...
'Debo comer empanada'
'Debo comer huevo'
'Debo comer pera'

Operaciones sobre conjuntos

Los conjuntos en Python soportan las operaciones típicas de conjuntos: restas, intersección, unión y diferencia simétrica (los elementos que están en uno de los conjuntos, pero no en ambos). Repasar operaciones con conjuntos.

>>> frutas = set(['banana', 'naranja', 'pera'])
>>> frutas - alimentos
set(['banana', 'naranja'])
>>> alimentos - frutas
set(['huevo', 'empanada'])
>>> frutas & alimentos
set(['pera'])
>>> frutas | alimentos
set(['huevo', 'empanada', 'pera', 'banana', 'naranja'])
>>> frutas ^ alimentos
set(['huevo', 'empanada', 'banana', 'naranja'])

También podemos preguntar sin un conjunto es subconjunto de otro. En los ejemplos se utiliza set(), el conjunto vacío:

>>> alimentos < set()
False
>>> set() < alimentos
True
>>> set() > alimentos
False
>>> alimentos <= alimentos
True

El problema de las dos comisiones

Este problema está basado en un caso real y lo escuché en la charla Escribí menos código, pensá como un (buen) matemático de Gustavo Carmona (FCEYN – UBA) bio y Matías A Graña (FCEyN – UBA) bio.

Se tienen dos archivos de textos con una lista de e-mails en cada uno. Cada archivo tiene los mails de los funcionarios de una comisión; los archivos no están bien depurados, por lo que pueden contener direcciones repetidas; hay funcionarios trabajando en las dos comisiones. Luego de una reunión en la que trabajaron ambas comisiones, se generó un material que se necesita enviar a todos los participantes. ¿Cómo obtener la lista de destinatarios?

archivo1
dir1@mail.com
dir2@mail.com
dir3@mail.com
dir1@mail.com
archivo2
dir21@mail.com
dir23@mail.com
dir3@mail.com
dir1@mail.com

Queremos una tercera lista que tenga la primera más la segunda, pero que no estén repetidos.

El enfoque tradicional que utilizaría un programador para resolver este problema mediante bucles es:

def unionlarga(l1, l2):
    l3 = []
    for x in l1:
        if not x in l3:
            l3.append(x)
    for x in l2:
        if not x in l3:
            l3.append(x)
    return l3

Supongamos que ya tenemos los archivos leídos y almacenamos el contenidos en listas:

>>> unionlarga(archivo1, archivo2)
['dir1@mail.com', 'dir2@mail.com', 'dir3@mail.com',
'dir21@mail.com', 'dir23@mail.com']

La solución es genérica para cualquier lenguaje. Sin embargo, puede lograr una mejor solución utilizando sets en Python:

>>> list(set(archivo1) | set(archivo2))
['dir21@mail.com', 'dir3@mail.com', 'dir2@mail.com',
'dir1@mail.com', 'dir23@mail.com']

Convertimos ambas listas a conjuntos (con lo que se eliminan los repetidos dentro de las listas), realizamos la unión de ambos conjuntos (con lo que se eliminan los repetidos entre listas y finalmente se convierte el resultado en una nueva lista.

Más sobre conjuntos en la referencia del lenguaje.

Acerca de Juanjo

Mi nombre es Juanjo Conti, vivo en Santa Fe y soy Ingeniero en Sistemas de Información. Mi lenguaje de programación de cabecera es Python; lo uso para trabajar, estudiar y jugar. Como hobby escribí algunos libros.
Esta entrada fue publicada en Aprendiendo Python y etiquetada , . Guarda el enlace permanente.
  • gustavo c

    Está buenísimo este tutorial. Buen trabajo. Se entiende mejor de esta manera, el tema de la utilidad de conjuntos(set).
    La pobre explicación de la documentacion oficial, genera que muchos pasen por alto este tema.

    +10 por este post.

  • http://pupeno.com J. Pablo Fernández

    Buen post Juanjo! Algo que le falto, supongo, es como se evalua la igualdad de los objetos en el set, sobre todo cuando son clases creadas por el usuario.

  • http://lalisantafe.wordpress.com Lali

    Muchas gracias Juanjo… Muy buena tu explicación, conocía los cojuntos pero no lo comprendia del todo. Los ejemplo que diste son muy claros…
    Lali

  • Juanjo

    Juan Pablo,

    La igualdad se evalúa comparando los resultados del método __hash__ sobre cada objeto. En los objetos instancias de clases definidas por el usuario, __hash__ devuelve __id__.

  • http://elotaku.blogspot.com Alquimista

    no sabia que esto se podia hacer en python, esto lo utilizaba mucho en probabilidad.

  • ive

    hola como podria convertir un conjunto en una lista, sin tener que iterar por el conjunto???

  • jjconti

    Es muy fácil, solo tenés que instanciar una lista usando tu conjunto como argumento:

    list(tuConjunto)

  • ive

    hola como podria convertir un conjunto en una lista, sin tener que iterar por el conjunto???

  • jjconti

    Es muy fácil, solo tenés que instanciar una lista usando tu conjunto como argumento:

    list(tuConjunto)

  • Jrodriguezr

    list(set(archivo1) | set(archivo2)) seria equivalente a lista1[0:0]=lista2 eso insertaría el contenido de una lista dentro de otra uniendo los conjuntos no?

  • jjconti

    No es lo mismo, por que con set eliminás los repetidos, mientras que susando solamente listas se quedan ahí.

  • jesus

    Buena explicación, juanjo. Sin embargo, tengo una duda: en python, una lista no puede convertirse a conjunto porque tira un error. ¿O estoy equivocado?

  • jjconti

    Sí se puede!

    >>> lista = [1,2,2,3,4,4,5]
    >>> conjunto = set(lista)
    >>> conjunto
    set([1, 2, 3, 4, 5])