{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Variables, números y texto\n",
    "\n",
    "Los cuatro tipos que vas a usar siempre, el error de sumar texto con número, y por qué 0,1 más 0,2 no da 0,3.\n",
    "\n",
    "Cuaderno de práctica del capítulo 3 de **Python desde cero**, de Miss Yera.\n",
    "\n",
    "Corre de arriba abajo. Si lo abres en Google Colab no necesitas instalar nada.\n",
    "\n",
    "Capítulo completo: https://missyera.com/guias/python-desde-cero/variables-y-tipos/\n",
    "\n",
    "Los ejercicios están al final y traen una celda vacía debajo de cada uno. Las\n",
    "respuestas viven en el cuaderno de soluciones, y merece la pena pelearse un\n",
    "rato antes de abrirlo 💛"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Antes de empezar\n",
    "\n",
    "Esta celda baja el ayudante que corrige tus ejercicios. Después, en cada\n",
    "ejercicio que se pueda corregir solo, vas a ver `%%revisa` arriba de la celda:\n",
    "escribe tu respuesta debajo, ejecuta, y te digo si te salió 💛"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import urllib.request\n",
    "\n",
    "# El ayudante de los cuadernos. Trae la corrección de los ejercicios y, en los\n",
    "# capítulos de consola, la celda mágica que ejecuta los comandos. Se baja en\n",
    "# vez de venir pegado aquí para que siempre sea el último.\n",
    "urllib.request.urlretrieve(\n",
    "    \"https://missyera.com/static/cuadernos/revisa.py\", \"revisa.py\")\n",
    "import revisa\n",
    "revisa.carga({\n",
    "    1: \"PGNsYXNzICdpbnQnPiA8Y2xhc3MgJ2Zsb2F0Jz4gPGNsYXNzICdzdHInPiA8Y2xhc3MgJ2Jvb2wnPg==\",\n",
    "    2: \"VG90YWw6IFMvMzI5LjQ2\",\n",
    "    3: \"Mzc1LjA=\",\n",
    "    4: \"J0JvZGVnYSBEb8OxYSBSb3NhJw==\",\n",
    "    5: \"Q3VzY28KMTgyLjcw\",\n",
    "    6: \"My4zMDAwMDAwMDAwMDAwMDAzCkZhbHNlClRydWU=\",\n",
    "    7: \"TGltYSAgICAgICAgIFMvICAgMTIsNTAwLjUwCkFyZXF1aXBhICAgICBTLyAgICAgIDk4MC4yNQpDdXNjbyAgICAgICAgUy8gIDE0NSw2MDAuMDA=\",\n",
    "    8: \"Mw==\",\n",
    "    9: \"MTUwCjEwMDUw\",\n",
    "    10: \"MiBkZSAzIHRpZW5lbiBmZWNoYQ==\",\n",
    "    11: \"Y2l1ZGFkICAgICAgICAnTGltYScgICB2YWNpbyBwYXJhIFB5dGhvbjogRmFsc2UgIGZhbHRhIGVsIGRhdG86IEZhbHNlCmRlc2N1ZW50byAgICAgMC4wICAgICAgdmFjaW8gcGFyYSBQeXRob246IFRydWUgICBmYWx0YSBlbCBkYXRvOiBGYWxzZQpub3RhICAgICAgICAgICcnICAgICAgIHZhY2lvIHBhcmEgUHl0aG9uOiBUcnVlICAgZmFsdGEgZWwgZGF0bzogRmFsc2UKdGVsZWZvbm8gICAgICAnICAgJyAgICB2YWNpbyBwYXJhIFB5dGhvbjogRmFsc2UgIGZhbHRhIGVsIGRhdG86IEZhbHNlCnNhdGlzZmFjY2lvbiAgTm9uZSAgICAgdmFjaW8gcGFyYSBQeXRob246IFRydWUgICBmYWx0YSBlbCBkYXRvOiBUcnVlCmNvZGlnbyAgICAgICAgJzAnICAgICAgdmFjaW8gcGFyYSBQeXRob246IEZhbHNlICBmYWx0YSBlbCBkYXRvOiBGYWxzZQ==\",\n",
    "    12: \"cmVzcHVlc3RhcyBjb250YWRhcywgbWFsIDogNQpyZXNwdWVzdGFzIGNvbnRhZGFzLCBiaWVuOiA3CnByb21lZGlvIG1hbCA6IDMuNgpwcm9tZWRpbyBiaWVuOiAyLjU3CmluZmxhZG8gZW4gICA6IDQwLjAgJQ==\",\n",
    "    13: \"ICAgMjUwMCAgPT0gIFRydWUgICBpcyAgRmFsc2UKICAgIDI1MCAgPT0gIFRydWUgICBpcyAgVHJ1ZQogICAgICA3ICA9PSAgVHJ1ZSAgIGlzICBUcnVlCiAxMDAwMDAgID09ICBUcnVlICAgaXMgIEZhbHNl\",\n",
    "    14: \"ZXhhY3RvICAgICA6IFMvIDQyNDIwMS45MgpyZWRvbmRlYW5kbzogUy8gNDI0MjAxLjkxICBzZSBkZXN2aWEgLTAuMDEKdHJ1bmNhbmRvICA6IFMvIDQyNDE5Ni45OSAgc2UgZGVzdmlhIC00LjkzCmVsIHRydW5jYWRvIHNlIGNvbWUsIHBvciBmYWN0dXJhOiBTLyAwLjAwNDk=\",\n",
    "}, lenguaje=\"python\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Una variable es una etiqueta, no una caja"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hola! Vamos con lo que hay dentro de una variable, que es la base de todo lo\n",
    "demás 🌸\n",
    "\n",
    "Casi siempre te van a explicar que una variable es una caja donde guardas\n",
    "algo. Yo prefiero otra imagen, porque te va a ahorrar un dolor de cabeza más\n",
    "adelante: **una variable es una etiqueta que le pegas a un valor**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ciudad = 'Arequipa'\n",
    "ventas = 3\n",
    "promedio = 1250.75\n",
    "\n",
    "print(ciudad, ventas, promedio)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El signo igual no significa \"es igual a\" como en matemáticas. Significa\n",
    "\"desde ahora, este nombre apunta a este valor\". Por eso esto que parecería una\n",
    "locura funciona sin problema:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "contador = 10\n",
    "contador = contador + 5\n",
    "\n",
    "print(contador)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Se lee de derecha a izquierda: agarra lo que hay en `contador`,\n",
    "súmale 5, y pega la etiqueta `contador` al resultado.\n",
    "\n",
    "### Cómo se nombran las variables\n",
    "\n",
    "Hay reglas que Python obliga y convenciones que obliga la comunidad. Las dos\n",
    "importan.\n",
    "\n",
    "Lo que Python no te deja: empezar con un número, usar espacios, usar guiones,\n",
    "o usar una palabra reservada como `class` o `for`.\n",
    "\n",
    "Lo que se acostumbra, y viene escrito en un documento que se llama PEP 8 (la\n",
    "guía de estilo oficial de Python):\n",
    "\n",
    "- 🐍 Todo en minúsculas y las palabras separadas con guion bajo:\n",
    "`total_ventas`, `fecha_de_corte`. Eso se llama snake_case.\n",
    "\n",
    "- 📝 Nombres que digan qué hay dentro. `df2` no dice nada;\n",
    "`ventas_limpias` sí.\n",
    "\n",
    "- 🚫 Nada de tildes ni ñ, aunque Python las acepte. Te van a dar problemas al\n",
    "compartir código.\n",
    "\n",
    "Te lo digo por experiencia: el código que escribes hoy lo lee otra persona\n",
    "dentro de tres meses, y muchas veces esa otra persona eres tú 😅"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Los cuatro tipos que vas a usar siempre"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python tiene muchos tipos, pero para datos son cuatro los que aparecen todo\n",
    "el tiempo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "unidades = 12                 # int:   numero entero\n",
    "monto = 1250.75               # float: numero con decimales\n",
    "cliente = 'Minimarket El Sol' # str:   texto\n",
    "pago = True                   # bool:  verdadero o falso\n",
    "\n",
    "print(type(unidades))\n",
    "print(type(monto))\n",
    "print(type(cliente))\n",
    "print(type(pago))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`type()` es la pregunta \"qué eres tú\" y la vas a usar más de lo que\n",
    "crees. Cuando algo no funcione y no entiendas por qué, imprime el tipo antes de\n",
    "seguir buscando 🔍\n",
    "\n",
    "Un detalle sobre `bool`: por dentro es un número. Eso parece un\n",
    "dato curioso y en realidad es un truco que vas a usar seguido."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(True + True)\n",
    "print(sum([True, False, True, True]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sumar una lista de verdaderos y falsos te da cuántos verdaderos hay. Cuando\n",
    "lleguemos a pandas, así vas a contar cuántas filas cumplen una condición."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error de la primera semana"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Este te va a pasar. A todo el mundo le pasa, así que mejor que te pase aquí\n",
    "conmigo y no un viernes a las siete."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "unidades = '12'      # ojo: vino como texto, con comillas\n",
    "precio = 40\n",
    "\n",
    "print(unidades * precio)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ajá 😅\n",
    "\n",
    "Multiplicar un texto por un número no es un error para Python: es repetir el\n",
    "texto esa cantidad de veces. Así que te repitió \"12\" cuarenta veces y no te\n",
    "avisó de nada.\n",
    "\n",
    "**Ese es el peor tipo de error: el que no falla.** Un error que\n",
    "revienta lo ves; este te deja seguir trabajando con un resultado que no\n",
    "significa nada.\n",
    "\n",
    "Con la suma sí revienta, y menos mal:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Esto revienta a propósito.** Se ejecuta dentro de un `try` para que puedas seguir con \"ejecutar todo\" y aun así ver la queja."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(unidades + precio)\n",
    "except Exception as e:\n",
    "    print(f'{type(e).__name__}: {e}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "TypeError: can only concatenate str (not \"int\") to str\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Léelo con calma que el mensaje es bueno: \"solo puedo concatenar texto con\n",
    "texto, y me diste un int\". O sea, Python vio un texto a la izquierda y asumió\n",
    "que querías pegar palabras.\n",
    "\n",
    "La solución es convertir:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "unidades = int('12')\n",
    "\n",
    "print(unidades + precio)\n",
    "print(type(unidades))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las tres conversiones que vas a usar son `int()`,\n",
    "`float()` y `str()`. Y una advertencia que te ahorra rato:\n",
    "`int()` con un texto que tenga decimales no funciona."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Esto revienta a propósito.** Se ejecuta dentro de un `try` para que puedas seguir con \"ejecutar todo\" y aun así ver la queja."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    int('40.5')\n",
    "except Exception as e:\n",
    "    print(f'{type(e).__name__}: {e}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "ValueError: invalid literal for int() with base 10: '40.5'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí hay que pasar por `float()` primero, o directamente usar\n",
    "`float()` si no necesitas un entero."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Por qué 0,1 más 0,2 no da 0,3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Este es de los que más desconciertan, y no es un bug de Python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(0.1 + 0.2)\n",
    "print(0.1 + 0.2 == 0.3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "No te asustes. Pasa en todos los lenguajes, y en Excel también (solo que\n",
    "Excel te lo esconde al mostrarte el número).\n",
    "\n",
    "La razón es que la computadora guarda los decimales en binario, y hay\n",
    "decimales que en binario no tienen representación exacta, igual que un tercio no\n",
    "la tiene en decimal: 0,3333... nunca termina.\n",
    "\n",
    "Lo que importa son las dos consecuencias prácticas:\n",
    "\n",
    "- ⚠️ **Nunca compares dos decimales con `==`.** Usa\n",
    "`round()` o compara la diferencia.\n",
    "\n",
    "- 💰 **Para dinero, redondea al final** y no en cada paso. Si el\n",
    "proyecto es contable de verdad, existe un tipo llamado `Decimal` que\n",
    "guarda los números exactos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(round(0.1 + 0.2, 2) == 0.3)\n",
    "\n",
    "from decimal import Decimal\n",
    "print(Decimal('0.1') + Decimal('0.2'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fíjate en que `Decimal` recibe el número **entre\n",
    "comillas**. Si le pasas `Decimal(0.1)` le estás dando el float\n",
    "que ya venía roto, y no arregla nada."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Texto, que es la mitad de tu trabajo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En datos reales, el 90% de lo sucio es texto: nombres con espacios de más,\n",
    "ciudades escritas de cuatro formas, números que vinieron como cadena. Así que\n",
    "estos métodos los vas a usar muchísimo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cliente = '  minimarket EL SOL  '\n",
    "\n",
    "print(cliente.strip())            # quita espacios de los extremos\n",
    "print(cliente.strip().title())    # Primera Letra De Cada Palabra\n",
    "print(cliente.strip().lower())    # todo minusculas\n",
    "print(cliente.replace('EL', 'el').strip())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un detalle importante: **estos métodos no modifican la variable, te\n",
    "devuelven una copia**. Si quieres conservar el cambio hay que\n",
    "reasignarlo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cliente.strip()\n",
    "print(repr(cliente))              # sigue con sus espacios\n",
    "\n",
    "cliente = cliente.strip()\n",
    "print(repr(cliente))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Usé `repr()` para que se vean las comillas y los espacios. En la\n",
    "primera línea siguen ahí, aunque hayamos llamado a `.strip()` justo\n",
    "antes: el método devolvió una copia limpia y nadie la guardó.\n",
    "\n",
    "Ese `repr()` va a ser tu mejor amigo depurando texto, porque un\n",
    "espacio de más es invisible con `print` normal 🔍\n",
    "\n",
    "Y el que más rinde de todos, `.split()`, que parte un texto en una\n",
    "lista:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fila = 'C0045;Arequipa;Minimarket;480.37'\n",
    "partes = fila.split(';')\n",
    "\n",
    "print(partes)\n",
    "print(partes[1])\n",
    "print(float(partes[3]) * 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eso de arriba es, en miniatura, lo que hace pandas cuando lee un CSV 📊\n",
    "\n",
    "### f-strings: la forma de armar texto\n",
    "\n",
    "Antes había tres maneras de meter una variable dentro de un texto y todas\n",
    "eran incómodas. Desde Python 3.6 hay una sola que vale la pena, y se llama\n",
    "f-string: una `f` antes de la comilla y las variables entre llaves."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cliente = 'Minimarket El Sol'\n",
    "monto = 1250.756\n",
    "\n",
    "print(f'{cliente} compró S/{monto}')\n",
    "print(f'{cliente} compró S/{monto:.2f}')\n",
    "print(f'{cliente:>25} | S/{monto:>10.2f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esos dos puntos dentro de las llaves son el formato, y valen oro:\n",
    "\n",
    "- 💵 `:.2f` deja dos decimales. Para plata, siempre.\n",
    "\n",
    "- 📐 `:>10` alinea a la derecha en diez espacios, que es lo que\n",
    "hace que una tabla impresa se vea derecha.\n",
    "\n",
    "- 🔢 `:,` pone el separador de miles: `f'{1250000:,}'`\n",
    "sale `1,250,000`.\n",
    "\n",
    "El error que da la primera vez es olvidar la `f`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('{cliente} compró algo')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "No revienta, simplemente te imprime las llaves tal cual. Si ves llaves en tu\n",
    "salida, te faltó la `f` 🙃"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## None, que no es cero ni vacío"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`None` es el valor que dice \"aquí no hay nada\". No es el 0, no es\n",
    "el texto vacío, es la ausencia."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fecha_ultima_compra = None\n",
    "\n",
    "print(fecha_ultima_compra is None)\n",
    "print(fecha_ultima_compra == 0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Se compara con `is None` y no con `== None`. Es la\n",
    "convención de Python y hay razones técnicas, pero con que te acostumbres basta.\n",
    "\n",
    "Esto importa muchísimo en datos: cuando un cliente no tiene fecha de última\n",
    "compra, ese hueco no es un cero. Y tratarlo como cero te cambia el promedio y\n",
    "todas las conclusiones que salgan de ahí."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que Python considera vacío"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cualquier valor de Python se puede usar como si fuera verdadero o falso. Y\n",
    "cuáles son falsos hay que sabérselo, porque de aquí sale uno de los errores más\n",
    "caros que vas a cometer con datos 🕳️"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for valor in [0, 0.0, '', '0', ' ', [], {}, None, 'n/a']:\n",
    "    print(f'{repr(valor):<8} -> {bool(valor)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los cuatro que sorprenden 👀\n",
    "\n",
    "- 0️⃣ **El cero es falso.** El entero y el decimal.\n",
    "\n",
    "- 🔤 **El texto `'0'` es verdadero**, porque es un\n",
    "texto con un carácter dentro. El cero de mentira es verdadero y el de verdad es\n",
    "falso.\n",
    "\n",
    "- ␣ **Un espacio también es verdadero.** Una celda que parece\n",
    "vacía en el Excel y trae un espacio, para Python tiene contenido.\n",
    "\n",
    "- 📭 **La lista vacía y el diccionario vacío son falsos**, y eso\n",
    "sí es cómodo: `if resultados:` se lee \"si hay resultados\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El cero que desaparece del promedio"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahora el error. Escribir `if descuento:` parece que dice \"si vino\n",
    "el descuento\". Lo que dice de verdad es \"si el descuento no es cero ni\n",
    "nulo\" 😖"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "descuentos = [15.0, 0.0, 8.5, None, 0.0, 12.0]\n",
    "\n",
    "con_if = [d for d in descuentos if d]\n",
    "con_is = [d for d in descuentos if d is not None]\n",
    "\n",
    "print('con if d            :', con_if, '->', len(con_if), 'clientes')\n",
    "print('con if d is not None:', con_is, '->', len(con_is), 'clientes')\n",
    "print('descuento promedio, primera forma :',\n",
    "      round(sum(con_if) / len(con_if), 2))\n",
    "print('descuento promedio, segunda forma :',\n",
    "      round(sum(con_is) / len(con_is), 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mira los dos promedios. No se parecen en nada 😳\n",
    "\n",
    "Los dos clientes con descuento **cero** son clientes reales a los\n",
    "que no se les dio descuento. El primer filtro los tira a la basura junto con el\n",
    "nulo, y el promedio sube de siete y pico a casi doce.\n",
    "\n",
    "Nadie va a notar ese error mirando el resultado, porque once y pico de\n",
    "descuento promedio es un número perfectamente creíble. Y estarías reportando\n",
    "un 67% más de lo que es 💸\n",
    "\n",
    "La regla, y es corta: **para preguntar si un dato vino,\n",
    "`is not None`. Para preguntar si un dato está vacío,\n",
    "`if`**. Son preguntas distintas y hay que elegir la que de\n",
    "verdad quieres."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## is no es lo mismo que =="
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ya usaste `is None` arriba y no te expliqué por qué no es\n",
    "`== None`. Aquí va, con la sorpresa incluida:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "leido = int('2500')\n",
    "escrito = 2500\n",
    "\n",
    "print('valen lo mismo:', leido == escrito)\n",
    "print('son el mismo  :', leido is escrito)\n",
    "\n",
    "leido_chico = int('250')\n",
    "escrito_chico = 250\n",
    "\n",
    "print('con 250, valen lo mismo:', leido_chico == escrito_chico)\n",
    "print('con 250, son el mismo  :', leido_chico is escrito_chico)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los mismos dos números, dos respuestas distintas 🤯\n",
    "\n",
    "`==` pregunta **¿valen lo mismo?**.\n",
    "`is` pregunta **¿son exactamente el mismo objeto en la\n",
    "memoria?**, que es otra cosa.\n",
    "\n",
    "Y el motivo de que 250 sí y 2500 no: Python guarda de antemano los enteros\n",
    "pequeños, de -5 a 256, y los reparte a quien los pida. Los grandes los crea\n",
    "nuevos cada vez. Es un detalle interno, puede cambiar entre versiones, y por eso\n",
    "mismo **nunca compares números ni textos con `is`**.\n",
    "\n",
    "Con texto pasa lo mismo:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ciudad_a = 'Lima'\n",
    "ciudad_c = ''.join(['Li', 'ma'])\n",
    "\n",
    "print('valen lo mismo:', ciudad_a == ciudad_c)\n",
    "print('son el mismo  :', ciudad_a is ciudad_c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Entonces, ¿por qué `is None` sí? Porque `None` es\n",
    "**uno solo en todo el programa**. No hay dos. Preguntar si algo es\n",
    "ese objeto es la pregunta correcta, y por eso es la forma recomendada 💜\n",
    "\n",
    "Resumido: `is` solo con `None`, `True` y\n",
    "`False`. Para todo lo demás, `==`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Redondear tampoco es lo que crees"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('round(0.5) =', round(0.5))\n",
    "print('round(1.5) =', round(1.5))\n",
    "print('round(2.5) =', round(2.5))\n",
    "print('round(3.5) =', round(3.5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El 1,5 sube a 2 y el 2,5 baja a 2. No es un fallo 🧮\n",
    "\n",
    "Se llama redondeo bancario, y la regla es: **cuando el resto es\n",
    "exactamente medio, se redondea al par más cercano**. Existe justamente\n",
    "para que los medios no se vayan todos hacia arriba y el total se infle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "montos = [0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5]\n",
    "\n",
    "print('redondeando uno a uno   :', sum(round(m) for m in montos))\n",
    "print('sumando y redondeando   :', round(sum(montos)))\n",
    "print('con el redondeo del cole:', sum(int(m + 0.5) for m in montos))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cincuenta contra cincuenta y cinco. Un 10% de diferencia en diez números, solo\n",
    "por cómo se redondea 💰\n",
    "\n",
    "Y ahí ves para qué sirve el redondeo bancario: la mitad de los medios suben y\n",
    "la otra mitad bajan, así que el total se queda donde tenía que quedarse. El\n",
    "redondeo que te enseñaron en el colegio los sube todos y desvía el total hacia\n",
    "arriba.\n",
    "\n",
    "Lo que importa en la práctica: **redondea al final, una sola vez**.\n",
    "Si redondeas en cada paso, cada redondeo mete su error y al final tienes un total\n",
    "que no cuadra con la suma de las partes. Y esa es la llamada de \"el reporte no\n",
    "cuadra por dos soles\" 📞"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Los cuatro tipos\n",
    "\n",
    "Crea una variable de cada tipo con datos de una venta e\n",
    "imprime el tipo de cada una en una sola línea."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 1\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. El total de una venta\n",
    "\n",
    "Con 8 unidades a S/34,90 cada una y 18% de IGV, imprime el\n",
    "total con dos decimales usando un f-string."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 2\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. El texto que parecía número\n",
    "\n",
    "Te llega `'250'` como texto y\n",
    "`'1.5'` también. Multiplícalos y muestra el resultado con un\n",
    "decimal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 3\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Limpiar un nombre de cliente\n",
    "\n",
    "De `'   BODEGA doña ROSA  '` saca\n",
    "`'Bodega Doña Rosa'`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 4\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Partir una fila de CSV\n",
    "\n",
    "De `'2026-04-29,Cusco,Web,154.83'` imprime la\n",
    "ciudad y el monto multiplicado por 1,18."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 5\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. La comparación que falla\n",
    "\n",
    "Comprueba que `1.1 + 2.2` no es igual a\n",
    "`3.3`, y después arregla la comparación."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 6\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Una tabla alineada\n",
    "\n",
    "Imprime tres líneas con nombre de ciudad y monto, de forma\n",
    "que los montos queden alineados a la derecha."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 7\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8. Contar con booleanos\n",
    "\n",
    "Tienes una lista de montos. Cuenta cuántos superan los\n",
    "S/1.000 usando el truco de sumar booleanos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 8\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 9. El error de tipos, provocado\n",
    "\n",
    "Provoca un `TypeError` sumando un texto y un\n",
    "número, y después arréglalo de dos formas distintas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 9\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 10. None no es cero\n",
    "\n",
    "Simula tres clientes donde uno no tiene fecha de última\n",
    "compra, y cuenta cuántos sí la tienen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 10\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 11. Cuáles de estas celdas están vacías de verdad\n",
    "\n",
    "Clasifica una fila de un Excel real, donde \"vacío\" viene de\n",
    "cinco formas distintas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 11\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 12. El promedio que se infla solo\n",
    "\n",
    "Calcula la satisfacción promedio filtrando de las dos\n",
    "maneras y mira la diferencia."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 12\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 13. Dos números iguales que no son el mismo\n",
    "\n",
    "Comprueba dónde `is` y `==` se separan."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 13\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 14. Redondear y cortar no son lo mismo\n",
    "\n",
    "Calcula el IGV de mil facturas y suma el total de tres\n",
    "maneras: exacta, redondeando cada factura, y cortando los decimales de cada\n",
    "factura."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 14\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dos números iguales que Python dice que no lo son"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de cerrar los tipos, una que parece un detalle de nada y se lleva tardes enteras. Léela despacio, porque el código de abajo no da error: da una respuesta 🧐"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Comparas dos códigos de cliente con `is`, que se lee mejor que `==`. Lo pruebas con los primeros del archivo y funciona.\n",
    "\n",
    "```\n",
    "a = int('256')\n",
    "b = int('256')\n",
    "print(a is b)      # True\n",
    "\n",
    "a = int('257')\n",
    "b = int('257')\n",
    "print(a is b)      # False\n",
    "```\n",
    "\n",
    "**¿Qué está mal?** La respuesta está en el cuaderno de soluciones. Míralo tú primero."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Lees un CSV y al sumar dos columnas te sale un TypeError. ¿Qué pasó?\n",
    "\n",
    "a) Una de las dos es texto aunque parezca un número\n",
    "\n",
    "b) Hay un valor nulo en medio\n",
    "\n",
    "c) Los números son demasiado grandes\n",
    "\n",
    "d) Falta convertir a float"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 🏷️ Una variable es una etiqueta pegada a un valor, y el `=` se lee\n",
    "de derecha a izquierda.\n",
    "\n",
    "- 🔍 Cuando algo raro pase, `type()` antes que nada.\n",
    "\n",
    "- 💸 Los decimales no son exactos: no los compares con `==` y\n",
    "redondea al final.\n",
    "\n",
    "- ✍️ Los métodos de texto devuelven una copia, no modifican la variable.\n",
    "\n",
    "- 🕳️ `None` es ausencia, no cero, y confundirlos te cambia los\n",
    "promedios.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El tipo no es un detalle del lenguaje. Es la diferencia entre sumar y\n",
    "pegar."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si vienes de Excel esto ya lo sufriste: la celda que se alinea a la\n",
    "izquierda porque el número entró como texto. Es el mismo problema y está\n",
    "contado desde ahí en la [guía de\n",
    "Excel](https://missyera.com/guias/excel-desde-cero/) 📊\n",
    "\n",
    "En el capítulo 4 vamos a guardar muchas cosas juntas: listas, tuplas y\n",
    "diccionarios. Ahí aparece el error de copiar una lista que en realidad no se\n",
    "copió, que es de los más traicioneros que hay.\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 3 de **Python desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/python-desde-cero/variables-y-tipos/\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
