From 8ca3bf485cb45c5377de3e97b100982299cb3db0 Mon Sep 17 00:00:00 2001 From: martin-paz-y Date: Wed, 5 Nov 2025 19:54:33 +0100 Subject: [PATCH] =?UTF-8?q?A=C3=B1ad=C3=AD=20el=20notebook=20del=20lab=20d?= =?UTF-8?q?e=20SQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lab python sql.ipynb | 186 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 lab python sql.ipynb diff --git a/lab python sql.ipynb b/lab python sql.ipynb new file mode 100644 index 0000000..6a94cd9 --- /dev/null +++ b/lab python sql.ipynb @@ -0,0 +1,186 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a48281d4", + "metadata": {}, + "source": [ + "Establish a connection between Python and the Sakila database.\n", + "\n", + "Write a Python function called rentals_month that retrieves rental data for a given month and year (passed as parameters) from the Sakila database as a Pandas DataFrame. The function should take in three parameters:\n", + "\n", + "engine: an object representing the database connection engine to be used to establish a connection to the Sakila database.\n", + "month: an integer representing the month for which rental data is to be retrieved.\n", + "year: an integer representing the year for which rental data is to be retrieved.\n", + "The function should execute a SQL query to retrieve the rental data for the specified month and year from the rental table in the Sakila database, and return it as a pandas DataFrame.\n", + "\n", + "Develop a Python function called rental_count_month that takes the DataFrame provided by rentals_month as input along with the month and year and returns a new DataFrame containing the number of rentals made by each customer_id during the selected month and year.\n", + "\n", + "The function should also include the month and year as parameters and use them to name the new column according to the month and year, for example, if the input month is 05 and the year is 2005, the column name should be \"rentals_05_2005\".\n", + "\n", + "Hint: Consider making use of pandas groupby()\n", + "\n", + "Create a Python function called compare_rentals that takes two DataFrames as input containing the number of rentals made by each customer in different months and years. The function should return a combined DataFrame with a new 'difference' column, which is the difference between the number of rentals in the two months." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "caea990d", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import pymysql\n", + "from sqlalchemy import create_engine\n", + "import getpass # To get the password without showing the input\n", + "password = getpass.getpass()\n", + "engine = create_engine(\"mysql+pymysql://root:TUPASS@localhost/sakila\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "cf707e00", + "metadata": {}, + "outputs": [], + "source": [ + "def rentals_month(engine, month: int, year: int) -> pd.DataFrame:\n", + " sql = text(\"\"\"\n", + " SELECT\n", + " r.rental_id,\n", + " r.rental_date,\n", + " r.return_date,\n", + " r.inventory_id,\n", + " r.customer_id,\n", + " r.staff_id,\n", + " r.last_update\n", + " FROM rental r\n", + " WHERE MONTH(r.rental_date) = :m\n", + " AND YEAR(r.rental_date) = :y\n", + " \"\"\")\n", + " with engine.connect() as cn:\n", + " df = pd.read_sql(sql, cn, params={\"m\": int(month), \"y\": int(year)})\n", + " return df\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "8285965d", + "metadata": {}, + "outputs": [], + "source": [ + "def rental_count_month(df: pd.DataFrame, month: int, year: int) -> pd.DataFrame:\n", + " col = f\"rentals_{int(month):02d}_{int(year)}\"\n", + " out = (\n", + " df.groupby(\"customer_id\", as_index=False)\n", + " .size()\n", + " .rename(columns={\"size\": col})\n", + " )\n", + " return out\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "44052212", + "metadata": {}, + "outputs": [], + "source": [ + "def compare_rentals(df_a: pd.DataFrame, df_b: pd.DataFrame) -> pd.DataFrame:\n", + " col_a = [c for c in df_a.columns if c.startswith(\"rentals_\")][0]\n", + " col_b = [c for c in df_b.columns if c.startswith(\"rentals_\")][0]\n", + " merged = df_a.merge(df_b, on=\"customer_id\", how=\"inner\")\n", + " merged[\"difference\"] = merged[col_b] - merged[col_a]\n", + " return merged\n" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "da753cd0", + "metadata": {}, + "outputs": [ + { + "ename": "RuntimeError", + "evalue": "'cryptography' package is required for sha256_password or caching_sha2_password auth methods", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mRuntimeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[17]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Mayo 2005\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m may_raw = \u001b[43mrentals_month\u001b[49m\u001b[43m(\u001b[49m\u001b[43mengine\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m5\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m2005\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 3\u001b[39m may_count = rental_count_month(may_raw, \u001b[32m5\u001b[39m, \u001b[32m2005\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;66;03m# Junio 2005\u001b[39;00m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[14]\u001b[39m\u001b[32m, line 15\u001b[39m, in \u001b[36mrentals_month\u001b[39m\u001b[34m(engine, month, year)\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mrentals_month\u001b[39m(engine, month: \u001b[38;5;28mint\u001b[39m, year: \u001b[38;5;28mint\u001b[39m) -> pd.DataFrame:\n\u001b[32m 2\u001b[39m sql = text(\u001b[33m\"\"\"\u001b[39m\n\u001b[32m 3\u001b[39m \u001b[33m SELECT\u001b[39m\n\u001b[32m 4\u001b[39m \u001b[33m r.rental_id,\u001b[39m\n\u001b[32m (...)\u001b[39m\u001b[32m 13\u001b[39m \u001b[33m AND YEAR(r.rental_date) = :y\u001b[39m\n\u001b[32m 14\u001b[39m \u001b[33m \u001b[39m\u001b[33m\"\"\"\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m15\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[43mengine\u001b[49m\u001b[43m.\u001b[49m\u001b[43mconnect\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mas\u001b[39;00m cn:\n\u001b[32m 16\u001b[39m df = pd.read_sql(sql, cn, params={\u001b[33m\"\u001b[39m\u001b[33mm\u001b[39m\u001b[33m\"\u001b[39m: \u001b[38;5;28mint\u001b[39m(month), \u001b[33m\"\u001b[39m\u001b[33my\u001b[39m\u001b[33m\"\u001b[39m: \u001b[38;5;28mint\u001b[39m(year)})\n\u001b[32m 17\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m df\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\engine\\base.py:3277\u001b[39m, in \u001b[36mEngine.connect\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 3254\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mconnect\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> Connection:\n\u001b[32m 3255\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Return a new :class:`_engine.Connection` object.\u001b[39;00m\n\u001b[32m 3256\u001b[39m \n\u001b[32m 3257\u001b[39m \u001b[33;03m The :class:`_engine.Connection` acts as a Python context manager, so\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 3274\u001b[39m \n\u001b[32m 3275\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m3277\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_connection_cls\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\engine\\base.py:143\u001b[39m, in \u001b[36mConnection.__init__\u001b[39m\u001b[34m(self, engine, connection, _has_events, _allow_revalidate, _allow_autobegin)\u001b[39m\n\u001b[32m 141\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m connection \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 142\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m143\u001b[39m \u001b[38;5;28mself\u001b[39m._dbapi_connection = \u001b[43mengine\u001b[49m\u001b[43m.\u001b[49m\u001b[43mraw_connection\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 144\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m dialect.loaded_dbapi.Error \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[32m 145\u001b[39m Connection._handle_dbapi_exception_noconnection(\n\u001b[32m 146\u001b[39m err, dialect, engine\n\u001b[32m 147\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\engine\\base.py:3301\u001b[39m, in \u001b[36mEngine.raw_connection\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 3279\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mraw_connection\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> PoolProxiedConnection:\n\u001b[32m 3280\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Return a \"raw\" DBAPI connection from the connection pool.\u001b[39;00m\n\u001b[32m 3281\u001b[39m \n\u001b[32m 3282\u001b[39m \u001b[33;03m The returned object is a proxied version of the DBAPI\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 3299\u001b[39m \n\u001b[32m 3300\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m3301\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mpool\u001b[49m\u001b[43m.\u001b[49m\u001b[43mconnect\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\pool\\base.py:447\u001b[39m, in \u001b[36mPool.connect\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 439\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mconnect\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> PoolProxiedConnection:\n\u001b[32m 440\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Return a DBAPI connection from the pool.\u001b[39;00m\n\u001b[32m 441\u001b[39m \n\u001b[32m 442\u001b[39m \u001b[33;03m The connection is instrumented such that when its\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 445\u001b[39m \n\u001b[32m 446\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m447\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_ConnectionFairy\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_checkout\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\pool\\base.py:1264\u001b[39m, in \u001b[36m_ConnectionFairy._checkout\u001b[39m\u001b[34m(cls, pool, threadconns, fairy)\u001b[39m\n\u001b[32m 1256\u001b[39m \u001b[38;5;129m@classmethod\u001b[39m\n\u001b[32m 1257\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_checkout\u001b[39m(\n\u001b[32m 1258\u001b[39m \u001b[38;5;28mcls\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 1261\u001b[39m fairy: Optional[_ConnectionFairy] = \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[32m 1262\u001b[39m ) -> _ConnectionFairy:\n\u001b[32m 1263\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m fairy:\n\u001b[32m-> \u001b[39m\u001b[32m1264\u001b[39m fairy = \u001b[43m_ConnectionRecord\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcheckout\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpool\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1266\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m threadconns \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 1267\u001b[39m threadconns.current = weakref.ref(fairy)\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\pool\\base.py:711\u001b[39m, in \u001b[36m_ConnectionRecord.checkout\u001b[39m\u001b[34m(cls, pool)\u001b[39m\n\u001b[32m 709\u001b[39m rec = cast(_ConnectionRecord, pool._do_get())\n\u001b[32m 710\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m711\u001b[39m rec = \u001b[43mpool\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_do_get\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 713\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 714\u001b[39m dbapi_connection = rec.get_connection()\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\pool\\impl.py:177\u001b[39m, in \u001b[36mQueuePool._do_get\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 175\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._create_connection()\n\u001b[32m 176\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m177\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mwith\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mutil\u001b[49m\u001b[43m.\u001b[49m\u001b[43msafe_reraise\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 178\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_dec_overflow\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 179\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\util\\langhelpers.py:224\u001b[39m, in \u001b[36msafe_reraise.__exit__\u001b[39m\u001b[34m(self, type_, value, traceback)\u001b[39m\n\u001b[32m 222\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m exc_value \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 223\u001b[39m \u001b[38;5;28mself\u001b[39m._exc_info = \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;66;03m# remove potential circular references\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m224\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m exc_value.with_traceback(exc_tb)\n\u001b[32m 225\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 226\u001b[39m \u001b[38;5;28mself\u001b[39m._exc_info = \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;66;03m# remove potential circular references\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\pool\\impl.py:175\u001b[39m, in \u001b[36mQueuePool._do_get\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 173\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._inc_overflow():\n\u001b[32m 174\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m175\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_create_connection\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 176\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m:\n\u001b[32m 177\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m util.safe_reraise():\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\pool\\base.py:388\u001b[39m, in \u001b[36mPool._create_connection\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 385\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_create_connection\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> ConnectionPoolEntry:\n\u001b[32m 386\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Called by subclasses to create a new ConnectionRecord.\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m388\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_ConnectionRecord\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\pool\\base.py:673\u001b[39m, in \u001b[36m_ConnectionRecord.__init__\u001b[39m\u001b[34m(self, pool, connect)\u001b[39m\n\u001b[32m 671\u001b[39m \u001b[38;5;28mself\u001b[39m.__pool = pool\n\u001b[32m 672\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m connect:\n\u001b[32m--> \u001b[39m\u001b[32m673\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__connect\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 674\u001b[39m \u001b[38;5;28mself\u001b[39m.finalize_callback = deque()\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\pool\\base.py:899\u001b[39m, in \u001b[36m_ConnectionRecord.__connect\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 897\u001b[39m \u001b[38;5;28mself\u001b[39m.fresh = \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[32m 898\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m--> \u001b[39m\u001b[32m899\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mwith\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mutil\u001b[49m\u001b[43m.\u001b[49m\u001b[43msafe_reraise\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 900\u001b[39m \u001b[43m \u001b[49m\u001b[43mpool\u001b[49m\u001b[43m.\u001b[49m\u001b[43mlogger\u001b[49m\u001b[43m.\u001b[49m\u001b[43mdebug\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mError on connect(): \u001b[39;49m\u001b[38;5;132;43;01m%s\u001b[39;49;00m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43me\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 901\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 902\u001b[39m \u001b[38;5;66;03m# in SQLAlchemy 1.4 the first_connect event is not used by\u001b[39;00m\n\u001b[32m 903\u001b[39m \u001b[38;5;66;03m# the engine, so this will usually not be set\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\util\\langhelpers.py:224\u001b[39m, in \u001b[36msafe_reraise.__exit__\u001b[39m\u001b[34m(self, type_, value, traceback)\u001b[39m\n\u001b[32m 222\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m exc_value \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 223\u001b[39m \u001b[38;5;28mself\u001b[39m._exc_info = \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;66;03m# remove potential circular references\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m224\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m exc_value.with_traceback(exc_tb)\n\u001b[32m 225\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 226\u001b[39m \u001b[38;5;28mself\u001b[39m._exc_info = \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;66;03m# remove potential circular references\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\pool\\base.py:895\u001b[39m, in \u001b[36m_ConnectionRecord.__connect\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 893\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 894\u001b[39m \u001b[38;5;28mself\u001b[39m.starttime = time.time()\n\u001b[32m--> \u001b[39m\u001b[32m895\u001b[39m \u001b[38;5;28mself\u001b[39m.dbapi_connection = connection = \u001b[43mpool\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_invoke_creator\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 896\u001b[39m pool.logger.debug(\u001b[33m\"\u001b[39m\u001b[33mCreated new connection \u001b[39m\u001b[38;5;132;01m%r\u001b[39;00m\u001b[33m\"\u001b[39m, connection)\n\u001b[32m 897\u001b[39m \u001b[38;5;28mself\u001b[39m.fresh = \u001b[38;5;28;01mTrue\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\engine\\create.py:661\u001b[39m, in \u001b[36mcreate_engine..connect\u001b[39m\u001b[34m(connection_record)\u001b[39m\n\u001b[32m 658\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m connection \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 659\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m connection\n\u001b[32m--> \u001b[39m\u001b[32m661\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdialect\u001b[49m\u001b[43m.\u001b[49m\u001b[43mconnect\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43mcargs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mcparams\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sqlalchemy\\engine\\default.py:629\u001b[39m, in \u001b[36mDefaultDialect.connect\u001b[39m\u001b[34m(self, *cargs, **cparams)\u001b[39m\n\u001b[32m 627\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mconnect\u001b[39m(\u001b[38;5;28mself\u001b[39m, *cargs: Any, **cparams: Any) -> DBAPIConnection:\n\u001b[32m 628\u001b[39m \u001b[38;5;66;03m# inherits the docstring from interfaces.Dialect.connect\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m629\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mloaded_dbapi\u001b[49m\u001b[43m.\u001b[49m\u001b[43mconnect\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43mcargs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mcparams\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\pymysql\\connections.py:365\u001b[39m, in \u001b[36mConnection.__init__\u001b[39m\u001b[34m(self, user, password, host, database, unix_socket, port, charset, collation, sql_mode, read_default_file, conv, use_unicode, client_flag, cursorclass, init_command, connect_timeout, read_default_group, autocommit, local_infile, max_allowed_packet, defer_connect, auth_plugin_map, read_timeout, write_timeout, bind_address, binary_prefix, program_name, server_public_key, ssl, ssl_ca, ssl_cert, ssl_disabled, ssl_key, ssl_key_password, ssl_verify_cert, ssl_verify_identity, compress, named_pipe, passwd, db)\u001b[39m\n\u001b[32m 363\u001b[39m \u001b[38;5;28mself\u001b[39m._sock = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 364\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m365\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mconnect\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\pymysql\\connections.py:681\u001b[39m, in \u001b[36mConnection.connect\u001b[39m\u001b[34m(self, sock)\u001b[39m\n\u001b[32m 678\u001b[39m \u001b[38;5;28mself\u001b[39m._next_seq_id = \u001b[32m0\u001b[39m\n\u001b[32m 680\u001b[39m \u001b[38;5;28mself\u001b[39m._get_server_information()\n\u001b[32m--> \u001b[39m\u001b[32m681\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_request_authentication\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 683\u001b[39m \u001b[38;5;66;03m# Send \"SET NAMES\" query on init for:\u001b[39;00m\n\u001b[32m 684\u001b[39m \u001b[38;5;66;03m# - Ensure charaset (and collation) is set to the server.\u001b[39;00m\n\u001b[32m 685\u001b[39m \u001b[38;5;66;03m# - collation_id in handshake packet may be ignored.\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 694\u001b[39m \u001b[38;5;66;03m# - https://github.com/wagtail/wagtail/issues/9477\u001b[39;00m\n\u001b[32m 695\u001b[39m \u001b[38;5;66;03m# - https://zenn.dev/methane/articles/2023-mysql-collation (Japanese)\u001b[39;00m\n\u001b[32m 696\u001b[39m \u001b[38;5;28mself\u001b[39m.set_character_set(\u001b[38;5;28mself\u001b[39m.charset, \u001b[38;5;28mself\u001b[39m.collation)\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\pymysql\\connections.py:980\u001b[39m, in \u001b[36mConnection._request_authentication\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 978\u001b[39m \u001b[38;5;66;03m# https://dev.mysql.com/doc/internals/en/successful-authentication.html\u001b[39;00m\n\u001b[32m 979\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._auth_plugin_name == \u001b[33m\"\u001b[39m\u001b[33mcaching_sha2_password\u001b[39m\u001b[33m\"\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m980\u001b[39m auth_packet = \u001b[43m_auth\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcaching_sha2_password_auth\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mauth_packet\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 981\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._auth_plugin_name == \u001b[33m\"\u001b[39m\u001b[33msha256_password\u001b[39m\u001b[33m\"\u001b[39m:\n\u001b[32m 982\u001b[39m auth_packet = _auth.sha256_password_auth(\u001b[38;5;28mself\u001b[39m, auth_packet)\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\pymysql\\_auth.py:271\u001b[39m, in \u001b[36mcaching_sha2_password_auth\u001b[39m\u001b[34m(conn, pkt)\u001b[39m\n\u001b[32m 268\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m DEBUG:\n\u001b[32m 269\u001b[39m \u001b[38;5;28mprint\u001b[39m(conn.server_public_key.decode(\u001b[33m\"\u001b[39m\u001b[33mascii\u001b[39m\u001b[33m\"\u001b[39m))\n\u001b[32m--> \u001b[39m\u001b[32m271\u001b[39m data = \u001b[43msha2_rsa_encrypt\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconn\u001b[49m\u001b[43m.\u001b[49m\u001b[43mpassword\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconn\u001b[49m\u001b[43m.\u001b[49m\u001b[43msalt\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconn\u001b[49m\u001b[43m.\u001b[49m\u001b[43mserver_public_key\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 272\u001b[39m pkt = _roundtrip(conn, data)\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\MartínPazYáñez\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\pymysql\\_auth.py:144\u001b[39m, in \u001b[36msha2_rsa_encrypt\u001b[39m\u001b[34m(password, salt, public_key)\u001b[39m\n\u001b[32m 139\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Encrypt password with salt and public_key.\u001b[39;00m\n\u001b[32m 140\u001b[39m \n\u001b[32m 141\u001b[39m \u001b[33;03mUsed for sha256_password and caching_sha2_password.\u001b[39;00m\n\u001b[32m 142\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 143\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m _have_cryptography:\n\u001b[32m--> \u001b[39m\u001b[32m144\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\n\u001b[32m 145\u001b[39m \u001b[33m\"\u001b[39m\u001b[33m'\u001b[39m\u001b[33mcryptography\u001b[39m\u001b[33m'\u001b[39m\u001b[33m package is required for sha256_password or\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 146\u001b[39m + \u001b[33m\"\u001b[39m\u001b[33m caching_sha2_password auth methods\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 147\u001b[39m )\n\u001b[32m 148\u001b[39m message = _xor_password(password + \u001b[33mb\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;130;01m\\0\u001b[39;00m\u001b[33m\"\u001b[39m, salt)\n\u001b[32m 149\u001b[39m rsa_key = serialization.load_pem_public_key(public_key, default_backend())\n", + "\u001b[31mRuntimeError\u001b[39m: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods" + ] + } + ], + "source": [ + "# Mayo 2005\n", + "may_raw = rentals_month(engine, 5, 2005)\n", + "may_count = rental_count_month(may_raw, 5, 2005)\n", + "\n", + "# Junio 2005\n", + "jun_raw = rentals_month(engine, 6, 2005)\n", + "jun_count = rental_count_month(jun_raw, 6, 2005)\n", + "\n", + "# Comparación (clientes activos en ambos meses)\n", + "comparison = compare_rentals(may_count, jun_count)\n", + "\n", + "# Ver los primeros resultados\n", + "comparison.head()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e2734838", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "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.13.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}