From 16379f78aacdc9daaff57a4561b2d889fc311f8a Mon Sep 17 00:00:00 2001 From: Aline Date: Sat, 11 Jul 2026 10:12:06 +0100 Subject: [PATCH] Solved lab --- lab-sql-python-connection.ipynb | 849 ++++++++++++++++++++++++++++++++ 1 file changed, 849 insertions(+) create mode 100644 lab-sql-python-connection.ipynb diff --git a/lab-sql-python-connection.ipynb b/lab-sql-python-connection.ipynb new file mode 100644 index 0000000..6bb557f --- /dev/null +++ b/lab-sql-python-connection.ipynb @@ -0,0 +1,849 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "748fc874", + "metadata": {}, + "source": [ + "Challenge\n", + "In this lab, the objective is to identify the customers who were active in both May and June, and how did their activity differ between months. To achieve this, follow these steps:\n", + "\n", + "1. Establish a connection between Python and the Sakila database.\n", + "\n", + "2. 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", + "3. 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", + "4. 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": null, + "id": "2c0d619b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Note: you may need to restart the kernel to use updated packages.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "'grep' is not recognized as an internal or external command,\n", + "operable program or batch file.\n" + ] + } + ], + "source": [ + "pip show sqlalchemy | grep Version" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "342f01cf", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: sqlalchemy in C:\\Users\\ASUS\\AppData\\Local\\Python\\pythoncore-3.14-64\\Lib\\site-packages (2.0.51)\n", + "Requirement already satisfied: greenlet>=1 in C:\\Users\\ASUS\\AppData\\Local\\Python\\pythoncore-3.14-64\\Lib\\site-packages (from sqlalchemy) (3.5.3)\n", + "Requirement already satisfied: typing-extensions>=4.6.0 in C:\\Users\\ASUS\\AppData\\Local\\Python\\pythoncore-3.14-64\\Lib\\site-packages (from sqlalchemy) (4.15.0)\n" + ] + } + ], + "source": [ + "!pip install --upgrade sqlalchemy\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cf1fb955", + "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" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "a5c64d96", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Engine(mysql+pymysql://root:***@localhost/sakila)\n" + ] + } + ], + "source": [ + "from sqlalchemy import create_engine\n", + "from sqlalchemy.engine import URL\n", + "\n", + "connection_url = URL.create(\n", + " drivername=\"mysql+pymysql\",\n", + " username=\"root\",\n", + " password=password, # Raw password\n", + " host=\"localhost\",\n", + " database=\"sakila\"\n", + ")\n", + "\n", + "engine = create_engine(connection_url)\n", + "\n", + "print(engine)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "05f12966", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n" + ] + } + ], + "source": [ + "from sqlalchemy import text\n", + "\n", + "with engine.connect() as conn:\n", + " print(conn.execute(text(\"SELECT 1\")).scalar())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0870ec6c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sakila\n" + ] + } + ], + "source": [ + "with engine.connect() as conn:\n", + " print(conn.execute(text(\"SELECT DATABASE()\")).scalar())" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "550d73a6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[('actor',), ('actor_info',), ('address',), ('category',), ('city',), ('country',), ('customer',), ('customer_list',), ('film',), ('film_actor',), ('film_category',), ('film_list',), ('film_text',), ('inventory',), ('language',), ('nicer_but_slower_film_list',), ('payment',), ('rental',), ('rental_customer_information',), ('sales_by_film_category',), ('sales_by_store',), ('staff',), ('staff_list',), ('store',)]\n" + ] + } + ], + "source": [ + "with engine.connect() as conn:\n", + " tables = conn.execute(text(\"SHOW TABLES\"))\n", + " print(tables.fetchall())" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "e795c140", + "metadata": {}, + "outputs": [], + "source": [ + "def rentals_month(engine, month, year):\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "a14ff4ba", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
FieldTypeNullKeyDefaultExtra
0rental_idintNOPRINaNauto_increment
1rental_datedatetimeNOMULNaN
2inventory_idmediumint unsignedNOMULNaN
3customer_idsmallint unsignedNOMULNaN
4return_datedatetimeYESNaN
5staff_idtinyint unsignedNOMULNaN
6last_updatetimestampNOCURRENT_TIMESTAMPDEFAULT_GENERATED on update CURRENT_TIMESTAMP
\n", + "
" + ], + "text/plain": [ + " Field Type Null Key Default \\\n", + "0 rental_id int NO PRI NaN \n", + "1 rental_date datetime NO MUL NaN \n", + "2 inventory_id mediumint unsigned NO MUL NaN \n", + "3 customer_id smallint unsigned NO MUL NaN \n", + "4 return_date datetime YES NaN \n", + "5 staff_id tinyint unsigned NO MUL NaN \n", + "6 last_update timestamp NO CURRENT_TIMESTAMP \n", + "\n", + " Extra \n", + "0 auto_increment \n", + "1 \n", + "2 \n", + "3 \n", + "4 \n", + "5 \n", + "6 DEFAULT_GENERATED on update CURRENT_TIMESTAMP " + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.read_sql(\"DESCRIBE rental\", engine)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2a8d401d", + "metadata": {}, + "outputs": [], + "source": [ + "# 2. 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", + "\n", + "import pandas as pd\n", + "from sqlalchemy import text\n", + "\n", + "def rentals_month(engine, month, year):\n", + "\n", + " query = text(\"\"\"\n", + " SELECT *\n", + " FROM rental\n", + " WHERE MONTH(rental_date) = :month\n", + " AND YEAR(rental_date) = :year\n", + " \"\"\")\n", + "\n", + " df = pd.read_sql(\n", + " query,\n", + " engine,\n", + " params={\"month\": month, \"year\": year}\n", + " )\n", + "\n", + " return df" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "dd228350", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
rental_idrental_dateinventory_idcustomer_idreturn_datestaff_idlast_update
012005-05-24 22:53:303671302005-05-26 22:04:3012006-02-15 21:30:53
122005-05-24 22:54:3315254592005-05-28 19:40:3312006-02-15 21:30:53
232005-05-24 23:03:3917114082005-06-01 22:12:3912006-02-15 21:30:53
342005-05-24 23:04:4124523332005-06-03 01:43:4122006-02-15 21:30:53
452005-05-24 23:05:2120792222005-06-02 04:33:2112006-02-15 21:30:53
\n", + "
" + ], + "text/plain": [ + " rental_id rental_date inventory_id customer_id \\\n", + "0 1 2005-05-24 22:53:30 367 130 \n", + "1 2 2005-05-24 22:54:33 1525 459 \n", + "2 3 2005-05-24 23:03:39 1711 408 \n", + "3 4 2005-05-24 23:04:41 2452 333 \n", + "4 5 2005-05-24 23:05:21 2079 222 \n", + "\n", + " return_date staff_id last_update \n", + "0 2005-05-26 22:04:30 1 2006-02-15 21:30:53 \n", + "1 2005-05-28 19:40:33 1 2006-02-15 21:30:53 \n", + "2 2005-06-01 22:12:39 1 2006-02-15 21:30:53 \n", + "3 2005-06-03 01:43:41 2 2006-02-15 21:30:53 \n", + "4 2005-06-02 04:33:21 1 2006-02-15 21:30:53 " + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df = rentals_month(engine, 5, 2005)\n", + "\n", + "df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "e3ab1762", + "metadata": {}, + "outputs": [], + "source": [ + "# 3. 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", + "# 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", + "def rental_count_month(df, month, year):\n", + " \"\"\"\n", + " Returns a DataFrame with the number of rentals made by each customer\n", + " during the specified month and year.\n", + "\n", + " Parameters:\n", + " df (DataFrame): DataFrame returned by rentals_month().\n", + " month (int): Month of the rentals.\n", + " year (int): Year of the rentals.\n", + "\n", + " Returns:\n", + " DataFrame: customer_id and rental_count.\n", + " \"\"\"\n", + "\n", + " rental_counts = (\n", + " df.groupby(\"customer_id\")\n", + " .size()\n", + " .reset_index(name=\"rental_count\")\n", + " )\n", + "\n", + " # Rename the column to include the month and year\n", + " column_name = f\"rentals_{month:02d}_{year}\"\n", + " rental_counts = rental_counts.rename(columns={\"rental_count\": column_name})\n", + "\n", + " return rental_counts" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "ab37acb4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
customer_idrentals_05_2005
012
121
232
353
463
\n", + "
" + ], + "text/plain": [ + " customer_id rentals_05_2005\n", + "0 1 2\n", + "1 2 1\n", + "2 3 2\n", + "3 5 3\n", + "4 6 3" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Get rentals for May 2005\n", + "rentals = rentals_month(engine, 5, 2005)\n", + "\n", + "# Count rentals by customer\n", + "counts = rental_count_month(rentals, 5, 2005)\n", + "\n", + "counts.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "bb085f26", + "metadata": {}, + "outputs": [], + "source": [ + "# 4. 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.\n", + "\n", + "\n", + "def compare_rentals(df1, df2):\n", + " merged = pd.merge(df1, df2, on=\"customer_id\", how=\"outer\")\n", + "\n", + " merged = merged.fillna(0)\n", + "\n", + " # Get the rental count column names\n", + " col1 = df1.columns[1]\n", + " col2 = df2.columns[1]\n", + "\n", + " # Calculate the difference\n", + " merged[\"difference\"] = merged[col1] - merged[col2]\n", + "\n", + " return merged\n" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "51897a07", + "metadata": {}, + "outputs": [], + "source": [ + "# Rentals for May 2005\n", + "rentals_may = rentals_month(engine, 5, 2005)\n", + "\n", + "# Number of rentals per customer in May\n", + "count_may = rental_count_month(rentals_may, 5, 2005)\n", + "\n", + "# Rentals for June 2005\n", + "rentals_june = rentals_month(engine, 6, 2005)\n", + "\n", + "# Number of rentals per customer in June\n", + "count_june = rental_count_month(rentals_june, 6, 2005)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "45026fb8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Index(['customer_id', 'rentals_05_2005'], dtype='str')\n", + "Index(['customer_id', 'rentals_06_2005'], dtype='str')\n" + ] + } + ], + "source": [ + "print(count_may.columns)\n", + "print(count_june.columns)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "6c4634b6", + "metadata": {}, + "outputs": [], + "source": [ + "comparison = compare_rentals(count_may, count_june)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "39dc09b9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
customer_idrentals_05_2005rentals_06_2005difference
012.07.0-5.0
121.01.00.0
232.04.0-2.0
340.06.0-6.0
453.05.0-2.0
...............
5935951.02.0-1.0
5945966.02.04.0
5955972.03.0-1.0
5965980.01.0-1.0
5975991.04.0-3.0
\n", + "

598 rows × 4 columns

\n", + "
" + ], + "text/plain": [ + " customer_id rentals_05_2005 rentals_06_2005 difference\n", + "0 1 2.0 7.0 -5.0\n", + "1 2 1.0 1.0 0.0\n", + "2 3 2.0 4.0 -2.0\n", + "3 4 0.0 6.0 -6.0\n", + "4 5 3.0 5.0 -2.0\n", + ".. ... ... ... ...\n", + "593 595 1.0 2.0 -1.0\n", + "594 596 6.0 2.0 4.0\n", + "595 597 2.0 3.0 -1.0\n", + "596 598 0.0 1.0 -1.0\n", + "597 599 1.0 4.0 -3.0\n", + "\n", + "[598 rows x 4 columns]" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "comparison" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6ede6e3f", + "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.14.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}