{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<p>\n",
    "<font size='5' face='Georgia, Arial'>IIC2233 Apunte Programación Avanzada</font><br>\n",
    "<font size='1'>&copy; 2015 Karim Pichara - Christian Pieringer. Todos los derechos reservados.</font>\n",
    "<br>\n",
    "<font size='1'>Modificado desde 2018-1 al 2025-2 por Equipo Docente IIC2233</font>\n",
    "</p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tabla de contenidos\n",
    "\n",
    "1. [*Sets*](#Sets)\n",
    "    1. [Operaciones sobre *sets*](#Operaciones-sobre-sets)\n",
    "        1. [Obtener cantidad de elementos](#Revisar-la-cantidad-de-elementos)\n",
    "        2. [Agregar elementos](#Add)\n",
    "        3. [Eliminar elementos](#Remove)\n",
    "        5. [Iterar sobre un set](#Iterar-con-for)\n",
    "        6. [Verificar pertenencia](#Verificar-si-un-elemento-pertenece-al-set)\n",
    "        7. [Operaciones de conjuntos](#Unión-de-conjuntos)\n",
    "        7. [Unión-de-conjuntos](#Unión-de-conjuntos)\n",
    "        8. [Intersección de conjuntos](#Intersección-de-conjuntos)\n",
    "        9. [Diferencia de conjuntos](#Diferencia-de-conjuntos)\n",
    "            1. [Diferencia simétrica de conjuntos](#Diferencia-simétrica-de-conjuntos)\n",
    "        10. [Comparar conjuntos](#Comparar-conjuntos)\n",
    "        8. [Ejemplo de eliminación de duplicados](#Ejemplo-de-eliminación-de-duplicados)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## *Sets*\n",
    "\n",
    "Los [*sets*](https://docs.python.org/3/library/stdtypes.html#set) (en español, conjuntos) son contenedores **mutables**, **no *hasheables***, y **no ordenados** que no repiten elementos. Tienen un comportamiento similar a los [conjuntos matemáticos](https://es.wikipedia.org/wiki/Conjunto). Los *sets* pueden contener cualquier objeto *hasheable*, los mismos que pueden ser llave en un diccionario. La razón de esto último es que los *sets* también utilizan *tablas de hash* para almacenar los datos.\n",
    "\n",
    "Los *sets* típicamente se utilizan para eliminar duplicados o para revisar si un elemento se encuentra en esta estructura o no, de forma eficiente. **Revisar si un elemento está o no está** toma tiempo constante; es decir, el tiempo que se demora esta operación no depende de cuán grande es el conjunto.\n",
    "\n",
    "En Python, los *sets* son implementados por la clase `set`. Es posible crear un *set* vacío con `set()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "set()\n"
     ]
    }
   ],
   "source": [
    "conjunto_vacío = set()\n",
    "print(conjunto_vacío)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Es posible crear un conjunto a partir de una lista de elementos. Notemos que el *set* creado **no tendrá elementos repetidos**, y no necesariamente respetará el orden original de los elementos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Mon Laferte', 'Daddy Yankee', 'Dream Theater', 'Olivia Newton-John', 'Sting'}\n"
     ]
    }
   ],
   "source": [
    "lista_artistas = [\"Olivia Newton-John\", \"Daddy Yankee\", \"Sting\", \n",
    "                  \"Dream Theater\", \"Mon Laferte\", \"Sting\"]\n",
    "print(set(lista_artistas))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Es importante notar que el músico Sting —que estaba repetido en la lista— queda una única vez al transformarlo a *set*. Podemos también construir un *set* directamente usando llaves `{`, `}`, donde los elementos están separados por coma."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Olivia Newton-John', 'Mon Laferte', 'Dream Theater', 'Sting', 'Daddy Yankee'}\n"
     ]
    }
   ],
   "source": [
    "conjunto_artistas = {\"Olivia Newton-John\", \"Daddy Yankee\", \"Sting\", \n",
    "                     \"Dream Theater\", \"Mon Laferte\"}\n",
    "print(conjunto_artistas)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La notación con llaves no puede ser utilizada para crear un *set* vacío, ya que esa notación se usa para crear un diccionario."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'dict'>\n"
     ]
    }
   ],
   "source": [
    "intento_de_conjunto_vacío = {}\n",
    "print(type(intento_de_conjunto_vacío))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A diferencia de otros lenguajes como Java o C#, los tipos de los elementos de un *set* pueden ser heterogéneos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{3, 'cuatro', 5, 'seis', 'cero'}\n"
     ]
    }
   ],
   "source": [
    "conjunto_heterogéneo = {\"cero\", 3, \"cero\", 3, \"cuatro\", 5, \"seis\"}\n",
    "print(conjunto_heterogéneo)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Además, en Python se permite la definición por comprensión de *sets*, de forma similar a como es posible con listas y diccionarios. También, esto admite para definiciones más complejas que dependen de otro tipo de estructuras."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Pierre Morel', 'Clint Eastwood', 'Olivier Megaton'}\n"
     ]
    }
   ],
   "source": [
    "from collections import namedtuple\n",
    "\n",
    "\n",
    "Película = namedtuple(\"Pelicula\", [\"título\", \"director\", \"género\"])\n",
    "películas = [\n",
    "    Película(\"Into the Woods\", \"Rob Marshall\", \"Aventura\"),\n",
    "    Película(\"American Sniper\", \"Clint Eastwood\", \"Acción\"),\n",
    "    Película(\"Birdman\", \"Alejandro González Inárritu\", \"Comedia\"),\n",
    "    Película(\"Boyhood\", \"Richard Linklater\", \"Drama\"),\n",
    "    Película(\"Taken\", \"Pierre Morel\", \"Acción\"),\n",
    "    Película(\"Taken 2\", \"Olivier Megaton\", \"Acción\"),\n",
    "    Película(\"Taken 3\", \"Olivier Megaton\", \"Acción\"),\n",
    "    Película(\"The Imitation Game\", \"Morten Tyldum\", \"Biografías\"),\n",
    "    Película(\"Gone Girl\", \"David Fincher\", \"Drama\")\n",
    "]\n",
    "\n",
    "# Set por comprensión\n",
    "directores_acción = {p.director for p in películas if p.género == 'Acción'}\n",
    "# Por cada elemento p en películas,\n",
    "# si el género de p es 'Acción',\n",
    "# entonces el director de p pertenece a directores_acción\n",
    "\n",
    "print(directores_acción)  # Notamos que no hay duplicados, nuevamente"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Operaciones sobre *sets*\n",
    "\n",
    "En Python, los *sets* son capaces de realizar varias operaciones. Algunas son comunes con otros tipos de colecciones, y otras son análogas a las que se hacen con conjuntos matemáticos.\n",
    "\n",
    "Es importante mencionar que los *sets* **no soportan ningún tipo de acceso indexado**, pues no tienen orden. Por ejemplo, el siguiente código arroja un error."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'set' object is not subscriptable",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mTypeError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[7]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m      1\u001b[39m conjunto_artistas = {\u001b[33m\"\u001b[39m\u001b[33mOlivia Newton-John\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mDaddy Yankee\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mSting\u001b[39m\u001b[33m\"\u001b[39m, \n\u001b[32m      2\u001b[39m                      \u001b[33m\"\u001b[39m\u001b[33mDream Theater\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mMon Laferte\u001b[39m\u001b[33m\"\u001b[39m}\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m \u001b[43mconjunto_artistas\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m]\u001b[49m\n",
      "\u001b[31mTypeError\u001b[39m: 'set' object is not subscriptable"
     ]
    }
   ],
   "source": [
    "conjunto_artistas = {\"Olivia Newton-John\", \"Daddy Yankee\", \"Sting\", \n",
    "                     \"Dream Theater\", \"Mon Laferte\"}\n",
    "conjunto_artistas[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahora revisaremos las operaciones más importantes que soportan los *sets*."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Revisar la cantidad de elementos\n",
    "\n",
    "Tal y como con las otras estructuras, esto se hace con la función `len`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n"
     ]
    }
   ],
   "source": [
    "print(len(conjunto_artistas))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### *Add*\n",
    "\n",
    "A un *set* se le pueden añadir elementos con el método `add`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Olivia Newton-John', 'Mon Laferte', 'Dream Theater', 'Taylor Swift', 'Sting', 'Daddy Yankee'}\n"
     ]
    }
   ],
   "source": [
    "conjunto_artistas.add(\"Taylor Swift\")\n",
    "print(conjunto_artistas)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si se intenta agregar un elemento que ya estaba, nada ocurre."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Olivia Newton-John', 'Mon Laferte', 'Dream Theater', 'Taylor Swift', 'Sting', 'Daddy Yankee'}\n"
     ]
    }
   ],
   "source": [
    "conjunto_artistas.add(\"Sting\")\n",
    "print(conjunto_artistas)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### *Remove*\n",
    "\n",
    "Se puede sacar un elemento del *set* con el método `remove`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Olivia Newton-John', 'Mon Laferte', 'Dream Theater', 'Taylor Swift', 'Sting'}\n"
     ]
    }
   ],
   "source": [
    "conjunto_artistas.remove(\"Daddy Yankee\")\n",
    "print(conjunto_artistas)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esta operación resulta en un error si se intenta eliminar algo que no estaba previamente en el *set*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "ename": "KeyError",
     "evalue": "'The Beatles'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mKeyError\u001b[39m                                  Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[12]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mconjunto_artistas\u001b[49m\u001b[43m.\u001b[49m\u001b[43mremove\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mThe Beatles\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[31mKeyError\u001b[39m: 'The Beatles'"
     ]
    }
   ],
   "source": [
    "conjunto_artistas.remove(\"The Beatles\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### *Discard*\n",
    "\n",
    "Es una operación similar a *remove*, pero que no lanza un error en caso de que el elemento no haya estado en el conjunto."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Olivia Newton-John', 'Mon Laferte', 'Taylor Swift', 'Sting'}\n"
     ]
    }
   ],
   "source": [
    "conjunto_artistas.discard(\"Dream Theater\")\n",
    "print(conjunto_artistas)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Olivia Newton-John', 'Mon Laferte', 'Taylor Swift', 'Sting'}\n"
     ]
    }
   ],
   "source": [
    "conjunto_artistas.discard(\"The Beatles\")\n",
    "print(conjunto_artistas)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Iterar con `for`\n",
    "\n",
    "Se puede iterar por los elementos de un conjunto con `for`. Debemos recordar, sin embargo, que el recorrido no se hará en ningún orden en particular, puesto que los *sets* **no** son estructuras ordenadas. Está garantizado que cada elemento será recorrido *exactamente una vez*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Por favor, ¡saluden a Olivia Newton-John!\n",
      "Por favor, ¡saluden a Mon Laferte!\n",
      "Por favor, ¡saluden a Taylor Swift!\n",
      "Por favor, ¡saluden a Sting!\n"
     ]
    }
   ],
   "source": [
    "for artista in conjunto_artistas:\n",
    "    print(f\"Por favor, ¡saluden a {artista}!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Verificar si un elemento pertenece al *set*\n",
    "\n",
    "Podemos verificar si un elemento está en el *set* con la sentencia `in`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n"
     ]
    }
   ],
   "source": [
    "print(\"Natalia Lafourcade\" in conjunto_artistas)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "print(\"Sting\" in conjunto_artistas)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En los *sets*, esta operación es **muy eficiente** y toma un tiempo que *no depende del tamaño del conjunto*.\n",
    "\n",
    "Esto es muy distinto del caso de las listas. Para verificar si un elemento está o no en una lista, internamente se debe recorrer toda la lista hasta encontrarlo, o bien llegar al final para darse cuenta de que no estaba. Esto significa que el *tiempo máximo de búsqueda crece* a medida que el tamaño de la lista aumenta.\n",
    "\n",
    "Para comprobar estas diferencias en tiempo, vamos a crear una lista y un *set*, cada uno con 10.000.000 de elementos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "set  -- La búsqueda de 5000000 demoró... 0.048993 segundos.\n",
      "list -- La búsqueda de 5000000 demoró... 2.651526 segundos.\n",
      "\n",
      "La búsqueda en la lista fue 54.12 veces más lenta que en el set.\n"
     ]
    }
   ],
   "source": [
    "from time import time\n",
    "\n",
    "\n",
    "ELEMENTOS = 10 ** 8\n",
    "ELEMENTO_A_BUSCAR = ELEMENTOS // 20\n",
    "\n",
    "lista_gigante = list(range(ELEMENTOS))\n",
    "set_gigante = set(range(ELEMENTOS))\n",
    "\n",
    "tiempo_inicio = time()\n",
    "ELEMENTO_A_BUSCAR in set_gigante\n",
    "tiempo_fin = time()\n",
    "tiempo_set = tiempo_fin - tiempo_inicio\n",
    "print(f\"set  -- La búsqueda de {ELEMENTO_A_BUSCAR} demoró... {tiempo_set:.6f} segundos.\")\n",
    "\n",
    "tiempo_inicio = time()\n",
    "ELEMENTO_A_BUSCAR in lista_gigante\n",
    "tiempo_fin = time()\n",
    "tiempo_lista = tiempo_fin - tiempo_inicio\n",
    "print(f\"list -- La búsqueda de {ELEMENTO_A_BUSCAR} demoró... {tiempo_lista:.6f} segundos.\")\n",
    "\n",
    "print()\n",
    "print(f\"La búsqueda en la lista fue {tiempo_lista / tiempo_set:.2f} veces más lenta que en el set.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Unión de conjuntos\n",
    "\n",
    "![](https://upload.wikimedia.org/wikipedia/commons/3/32/SetUnion.svg)\n",
    "\n",
    "Sirve para obtener un nuevo conjunto que tenga todos los elementos de los conjuntos que se unen. Esta operación no altera ninguno de los *sets* originales. Se utiliza el operador `|`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0, 1, 2, 3, 4, 5}\n"
     ]
    }
   ],
   "source": [
    "set_a = {0, 1, 2, 3}\n",
    "set_b = {5, 4, 3, 2}\n",
    "set_union = set_a | set_b\n",
    "print(set_union)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "También se puede ocupar el método `union`. Esta operación no altera ninguno de los *sets* originales."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0, 1, 2, 3, 4, 5}\n"
     ]
    }
   ],
   "source": [
    "set_union = set_a.union(set_b)\n",
    "print(set_union)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Intersección de conjuntos\n",
    "\n",
    "![](https://upload.wikimedia.org/wikipedia/commons/c/cb/SetIntersection.svg)\n",
    "\n",
    "Sirve para obtener un nuevo conjunto que tenga los elementos que están en **todos** los conjuntos que se intersectan. Esta operación no altera ninguno de los *sets* originales. Se utiliza el operador `&`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{2, 3}\n"
     ]
    }
   ],
   "source": [
    "set_a = {0, 1, 2, 3}\n",
    "set_b = {4, 3, 2, 5}\n",
    "set_intersection = set_a & set_b\n",
    "print(set_intersection)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "También se puede ocupar el método `intersection`. Esta operación no altera ninguno de los *sets* originales."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{2, 3}\n"
     ]
    }
   ],
   "source": [
    "set_intersection = set_a.intersection(set_b)\n",
    "print(set_intersection)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Diferencia de conjuntos\n",
    "\n",
    "![](https://upload.wikimedia.org/wikipedia/commons/e/ec/SetDifferenceA.svg)\n",
    "\n",
    "Sirve para obtener un nuevo conjunto que tenga los elementos que están en un conjunto, pero que no estén en otro. Esta operación no altera ninguno de los *sets* originales. Se utiliza el operador `-`. Notar que el resultado de esta operación **sí depende** del orden de los factores."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0, 1}\n",
      "{4, 5}\n"
     ]
    }
   ],
   "source": [
    "set_a = {0, 1, 2, 3}\n",
    "set_b = {2, 3, 4, 5}\n",
    "set_difference_a_b = set_a - set_b\n",
    "set_difference_b_a = set_b - set_a\n",
    "print(set_difference_a_b)\n",
    "print(set_difference_b_a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "También se puede ocupar el método `difference`. Esta operación no altera ninguno de los *sets* originales."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0, 1}\n",
      "{4, 5}\n"
     ]
    }
   ],
   "source": [
    "set_difference_a_b = set_a.difference(set_b)\n",
    "set_difference_b_a = set_b.difference(set_a)\n",
    "print(set_difference_a_b)\n",
    "print(set_difference_b_a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Diferencia simétrica de conjuntos\n",
    "\n",
    "![](https://upload.wikimedia.org/wikipedia/commons/f/f2/SetSymmetricDifference.svg)\n",
    "\n",
    "Sirve para obtener un nuevo conjunto de objetos que están en un conjunto o en el otro, pero no en ambos. Esta operación no altera ninguno de los *sets* originales. Se ocupa el operador `^`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0, 1, 4, 5}\n"
     ]
    }
   ],
   "source": [
    "set_a = {0, 1, 2, 3}\n",
    "set_b = {2, 3, 4, 5}\n",
    "set_sym_difference = set_a ^ set_b\n",
    "print(set_sym_difference)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "También se puede ocupar el método `symmetric_difference`. Esta operación no altera ninguno de los *sets* originales."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0, 1, 4, 5}\n"
     ]
    }
   ],
   "source": [
    "set_sym_difference = set_a.symmetric_difference(set_b)\n",
    "print(set_sym_difference)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comparar conjuntos\n",
    "\n",
    "Podemos saber rápidamente si un conjunto es subconjunto, superconjunto, o son iguales a otro. \n",
    "\n",
    "Un conjunto `A` es *subconjunto* de otro `B` si todos los elementos que están en `A` están también en `B`. Esto incluye el caso en que sean iguales. Si no queremos incluir el caso de la igualdad, se habla de *subconjunto propio*.\n",
    "\n",
    "Al revés, un `A` es *superconjunto* de otro `B` si todos los elementos que están en `B` también están en `A`. Esto también incluye el caso en que sean iguales. Si no queremos incluir el caso de la igualdad, se habla de *superconjunto propio*.\n",
    "\n",
    "En el caso de la igualdad de conjuntos, al compararlos con `==`, retorna True si tienen exactamento los mismos elementos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "artistas_lollapalooza vs. artistas_favoritos:\n",
      "- superset: True\n",
      "- subset: False\n",
      "- iguales: False\n",
      "---------------------------------------------\n",
      "artistas_favoritos vs. artistas_lollapalooza:\n",
      "- superset: False\n",
      "- subset: True\n",
      "- iguales: False\n"
     ]
    }
   ],
   "source": [
    "artistas_lollapalooza = {\"Mac Demarco\", \"The Killers\", \"Shakira\", \"Camila Cabello\"}\n",
    "artistas_favoritos = {\"Mac Demarco\", \"Shakira\"}\n",
    "\n",
    "\n",
    "print(\"artistas_lollapalooza vs. artistas_favoritos:\")\n",
    "print(f\"- superset: {artistas_lollapalooza >= artistas_favoritos}\")\n",
    "print(f\"- subset: {artistas_lollapalooza <= artistas_favoritos}\")\n",
    "print(f\"- iguales: {artistas_lollapalooza == artistas_favoritos}\")\n",
    "\n",
    "print(\"-\" * 45)\n",
    "\n",
    "print(\"artistas_favoritos vs. artistas_lollapalooza:\")\n",
    "print(f\"- superset: {artistas_favoritos >= artistas_lollapalooza}\")\n",
    "print(f\"- subset: {artistas_favoritos <= artistas_lollapalooza}\")\n",
    "print(f\"- iguales: {artistas_favoritos == artistas_lollapalooza}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "artistas_lollapalooza es a artistas_favoritos:\n",
      "Superset: True\n",
      "Subset: False\n",
      "---------------------------------------------\n",
      "artistas_favoritos es a artistas_lollapalooza:\n",
      "Superset: False\n",
      "Subset: True\n"
     ]
    }
   ],
   "source": [
    "artistas_lollapalooza = {\"Mac Demarco\", \"The Killers\", \"Shakira\", \"Camila Cabello\"}\n",
    "artistas_favoritos = {\"Mac Demarco\", \"Shakira\"}\n",
    "\n",
    "\n",
    "print(\"artistas_lollapalooza es a artistas_favoritos:\")\n",
    "print(\"Superset: {}\".format(artistas_lollapalooza.issuperset(artistas_favoritos)))\n",
    "print(\"Subset: {}\".format(artistas_lollapalooza.issubset(artistas_favoritos)))\n",
    "\n",
    "print(\"-\" * 45)\n",
    "\n",
    "print(\"artistas_favoritos es a artistas_lollapalooza:\")\n",
    "print(\"Superset: {}\".format(artistas_favoritos.issuperset(artistas_lollapalooza)))\n",
    "print(\"Subset: {}\".format(artistas_favoritos.issubset(artistas_lollapalooza)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ejemplo de eliminación de duplicados\n",
    "\n",
    "Podemos usar *sets* para eliminar duplicados de una lista."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'D', 'Y', 'F', 'X', 'Z', 'B', 'A'}\n"
     ]
    }
   ],
   "source": [
    "lista = ['A', 'B', 'A', 'D', 'F', 'X', 'X', 'X', 'Z', 'Z', 'Y']\n",
    "lista_set = set(lista)\n",
    "print(lista_set)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y también es posible crear una lista a partir de un *set*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['D', 'Y', 'F', 'X', 'Z', 'B', 'A']\n"
     ]
    }
   ],
   "source": [
    "lista = list(lista_set)\n",
    "print(lista)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Podemos ordenar la lista si queremos convencernos que no hay repetidos:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['A', 'B', 'D', 'F', 'X', 'Y', 'Z']\n"
     ]
    }
   ],
   "source": [
    "lista.sort()\n",
    "print(lista)"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
