{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Python and MySQL" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "# First step is always import the necessary packages\n", "import pymysql" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Connections and Cursors\n", "With SQL, just as with Mongo, we need to first create a **connection** to our database. Through this connection we can retreive database meta data, but can also instantiate a **curosr**.\n", " - Just as with Mongo, the **cursor** is used to interact with the database and will store the results of any executed queries" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "scrolled": true, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "import pymysql.cursors\n", "\n", "# Connect to the database\n", "connection = pymysql.connect(host='gwumysql-restore.cazdwdlcg6dm.us-east-2.rds.amazonaws.com',\n", " user='EMSE6992',\n", " port=3306,\n", " db='EMSE6992',\n", " password='MySQLpass')\n", "\n", "#From our connection we need a cursor, which acts as our interface into the database\n", "cur = connection.cursor()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "SQL **cursor** objects have a basic functional loop:\n", " 1. **cursor**.execute(_query_)\n", " - The **cursor** must first execute a _query_ whoes response may be stored within the **cursor**\n", " 2. **cursor**._fetch_() or *iterate through the cursor*\n", " - Once executed, the results of a _query_ can be retreived from the **cursor**, if any results exist\n", " \n", "*NOTE: This is a different functional loop from __Mongo's__ cursor loop*" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The number of rows returned: 5\n" ] } ], "source": [ "#From a cursor we can execute SQL directly\n", "res = cur.execute(\"Select * From users LIMIT 5\")\n", "print(f\"The number of rows returned: {res}\") #the output from execute() will be the number of records returned" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can *fetch* the results (*__fetchall()__ will return all records*):" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(datetime.date(2018, 5, 25), \"creator of knowyourmeme's meme of the day 15th november 2019 - @AetherionArt did the avatar & @macagilart did the header\", 18739, 1188, '1000026776872144897', 21, None, 'fooly', 'mashcore4mums', 20970, None, None)\n" ] } ], "source": [ "users = cur.fetchall()\n", "for user in users:\n", " print(user) # We can now take a look at what was returned\n", " break" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(datetime.date(2018, 5, 25), \"creator of knowyourmeme's meme of the day 15th november 2019 - @AetherionArt did the avatar & @macagilart did the header\", 18739, 1188, '1000026776872144897', 21, None, 'fooly', 'mashcore4mums', 20970, None, None)\n" ] } ], "source": [ "res = cur.execute(\"Select * From users LIMIT 5\")\n", "for user in users:\n", " print(user)\n", " break" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Each **execute()** refreshes the information stored in the **cursor**, meaning we can continously query the database.\n", "\n", "Here we see that by calling **fetchone()** rather than **fetchall()** we won't get a list, but rather directly access the top _tuple_" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(datetime.date(2018, 5, 25),\n", " \"creator of knowyourmeme's meme of the day 15th november 2019 - @AetherionArt did the avatar & @macagilart did the header\",\n", " 18739,\n", " 1188,\n", " '1000026776872144897',\n", " 21,\n", " None,\n", " 'fooly',\n", " 'mashcore4mums',\n", " 20970,\n", " None,\n", " None)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cur.execute(\"Select * From users\")\n", "\n", "data = cur.fetchone()\n", "data" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "86202" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cur.execute(\"Select * From users\")\n", "\n", "data = cur.fetchall()\n", "len(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Dict Cursor\n", "We can define types of cursors when connecting to a database, which alters the format of the returned data\n", " - Done by adding a _cursorclass_ param to our connection" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "connection = pymysql.connect(host='gwumysql-restore.cazdwdlcg6dm.us-east-2.rds.amazonaws.com',\n", " user='EMSE6992',\n", " port=3306,\n", " db='EMSE6992',\n", " password='MySQLpass',\n", " cursorclass=pymysql.cursors.DictCursor) # This is the significant line!\n", "\n", "cur = connection.cursor()" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'created_date': datetime.date(2018, 5, 25),\n", " 'description': \"creator of knowyourmeme's meme of the day 15th november 2019 - @AetherionArt did the avatar & @macagilart did the header\",\n", " 'favorites_count': 18739,\n", " 'friends_count': 1188,\n", " 'user_id': '1000026776872144897',\n", " 'listed_count': 21,\n", " 'location': None,\n", " 'name': 'fooly',\n", " 'screen_name': 'mashcore4mums',\n", " 'statuses_count': 20970,\n", " 'url': None,\n", " 'verified': None}" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cur.execute(\"SELECT * FROM users LIMIT 1;\")\n", "cur.fetchone()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# In Class Work\n", "Create a connection to the **EMSE6992 database** and run a query to determine how many users have tweeted more than 1,000 times, using the **statuses table**:\n", "\n", "*Expected Result: 8 users*" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Space for work\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How Could we implement this in Mongo?\n", "\n", "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## SQL - Dataframe Compatibility\n", "Given that both SQL and Dataframes both work with tabular data, it makes these two technologies easily integratable." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "\n", "res = cur.execute(\"Select * From users LIMIT 100\")\n", "users = cur.fetchall()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since the cursor returns a list of dictionaries, we are setup to dump the data into a pandas DataFrame" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>created_date</th>\n", " <th>description</th>\n", " <th>favorites_count</th>\n", " <th>friends_count</th>\n", " <th>user_id</th>\n", " <th>listed_count</th>\n", " <th>location</th>\n", " <th>name</th>\n", " <th>screen_name</th>\n", " <th>statuses_count</th>\n", " <th>url</th>\n", " <th>verified</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>2018-05-25</td>\n", " <td>creator of knowyourmeme's meme of the day 15th...</td>\n", " <td>18739.0</td>\n", " <td>1188.0</td>\n", " <td>1000026776872144897</td>\n", " <td>21.0</td>\n", " <td>None</td>\n", " <td>fooly</td>\n", " <td>mashcore4mums</td>\n", " <td>20970</td>\n", " <td>None</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>2018-05-25</td>\n", " <td>-_-</td>\n", " <td>5281.0</td>\n", " <td>120.0</td>\n", " <td>1000073751457824768</td>\n", " <td>NaN</td>\n", " <td>uncomfortable</td>\n", " <td>Cas</td>\n", " <td>Amai_Neo</td>\n", " <td>1260</td>\n", " <td>None</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>2018-05-25</td>\n", " <td>Lifelong learner. \\n\\nD2C brands + iOS & Shopi...</td>\n", " <td>4240.0</td>\n", " <td>385.0</td>\n", " <td>1000084374468128768</td>\n", " <td>10.0</td>\n", " <td>Earth</td>\n", " <td>Max Grev</td>\n", " <td>max_grev</td>\n", " <td>855</td>\n", " <td>https://t.co/J7KuZFVRRI</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>2009-12-28</td>\n", " <td>Be an encourager. The World has plenty of crit...</td>\n", " <td>4566.0</td>\n", " <td>477.0</td>\n", " <td>100009243</td>\n", " <td>1.0</td>\n", " <td>Leicester, England</td>\n", " <td>Aakash Jabbar</td>\n", " <td>mangianees</td>\n", " <td>3816</td>\n", " <td>https://t.co/Afy69InVdg</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>2009-12-28</td>\n", " <td>Culture Writer | I Get It In Ohio - Just using...</td>\n", " <td>106836.0</td>\n", " <td>2069.0</td>\n", " <td>100009746</td>\n", " <td>26.0</td>\n", " <td>Columbus, OH</td>\n", " <td>Stimmy Lovato</td>\n", " <td>MikeishaDache</td>\n", " <td>178768</td>\n", " <td>https://t.co/IILZF0YLqw</td>\n", " <td>NaN</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " created_date description \\\n", "0 2018-05-25 creator of knowyourmeme's meme of the day 15th... \n", "1 2018-05-25 -_- \n", "2 2018-05-25 Lifelong learner. \\n\\nD2C brands + iOS & Shopi... \n", "3 2009-12-28 Be an encourager. The World has plenty of crit... \n", "4 2009-12-28 Culture Writer | I Get It In Ohio - Just using... \n", "\n", " favorites_count friends_count user_id listed_count \\\n", "0 18739.0 1188.0 1000026776872144897 21.0 \n", "1 5281.0 120.0 1000073751457824768 NaN \n", "2 4240.0 385.0 1000084374468128768 10.0 \n", "3 4566.0 477.0 100009243 1.0 \n", "4 106836.0 2069.0 100009746 26.0 \n", "\n", " location name screen_name statuses_count \\\n", "0 None fooly mashcore4mums 20970 \n", "1 uncomfortable Cas Amai_Neo 1260 \n", "2 Earth Max Grev max_grev 855 \n", "3 Leicester, England Aakash Jabbar mangianees 3816 \n", "4 Columbus, OH Stimmy Lovato MikeishaDache 178768 \n", "\n", " url verified \n", "0 None NaN \n", "1 None NaN \n", "2 https://t.co/J7KuZFVRRI NaN \n", "3 https://t.co/Afy69InVdg NaN \n", "4 https://t.co/IILZF0YLqw NaN " ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = pd.DataFrame.from_dict(users)\n", "df[:5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This obviously allows us to work with the data with a more robust API" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " favorites_count friends_count listed_count statuses_count\n", "created_date \n", "2007-11-06 947.000000 420.000000 21112.000000 363488.000000\n", "2009-12-28 30468.818182 607.545455 243.222222 41672.636364\n", "2009-12-29 3275.250000 1223.750000 513.750000 72326.000000\n", "2012-12-09 22373.500000 723.000000 NaN 6708.000000\n", "2012-12-10 18302.000000 744.666667 20.714286 12219.666667\n", "2018-05-25 10281.000000 461.250000 11.666667 6263.250000\n", "2018-05-26 33416.266667 946.400000 29.666667 10082.000000\n", "2018-05-27 20586.333333 230.555556 7.200000 18210.333333\n", "2018-05-28 20539.866667 1392.200000 33.545455 9590.800000\n", "2018-05-29 33173.933333 577.200000 28.545455 14812.666667\n", "2018-05-30 20223.615385 541.538462 44.777778 12599.230769\n", "2018-05-31 36709.000000 1259.000000 45.000000 30776.000000\n" ] } ], "source": [ "df.drop('verified', axis=1, inplace=True)\n", "print(df.groupby(['created_date']).mean())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### We can also read data directly into pandas\n", "\n", "By passing a connection object into pandas' **read_sql(_query_, _conn_)** we can create DataFrame's directly from a query." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>screen_name</th>\n", " <th>user_id</th>\n", " <th>favorites_count</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>mashcore4mums</td>\n", " <td>1000026776872144897</td>\n", " <td>18739</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>mashcore4mums</td>\n", " <td>1000026776872144897</td>\n", " <td>18739</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>mashcore4mums</td>\n", " <td>1000026776872144897</td>\n", " <td>18739</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>mashcore4mums</td>\n", " <td>1000026776872144897</td>\n", " <td>18739</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>Amai_Neo</td>\n", " <td>1000073751457824768</td>\n", " <td>5281</td>\n", " </tr>\n", " <tr>\n", " <th>5</th>\n", " <td>Amai_Neo</td>\n", " <td>1000073751457824768</td>\n", " <td>5281</td>\n", " </tr>\n", " <tr>\n", " <th>6</th>\n", " <td>max_grev</td>\n", " <td>1000084374468128768</td>\n", " <td>4240</td>\n", " </tr>\n", " <tr>\n", " <th>7</th>\n", " <td>mangianees</td>\n", " <td>100009243</td>\n", " <td>4566</td>\n", " </tr>\n", " <tr>\n", " <th>8</th>\n", " <td>MikeishaDache</td>\n", " <td>100009746</td>\n", " <td>106836</td>\n", " </tr>\n", " <tr>\n", " <th>9</th>\n", " <td>MikeishaDache</td>\n", " <td>100009746</td>\n", " <td>106836</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " screen_name user_id favorites_count\n", "0 mashcore4mums 1000026776872144897 18739\n", "1 mashcore4mums 1000026776872144897 18739\n", "2 mashcore4mums 1000026776872144897 18739\n", "3 mashcore4mums 1000026776872144897 18739\n", "4 Amai_Neo 1000073751457824768 5281\n", "5 Amai_Neo 1000073751457824768 5281\n", "6 max_grev 1000084374468128768 4240\n", "7 mangianees 100009243 4566\n", "8 MikeishaDache 100009746 106836\n", "9 MikeishaDache 100009746 106836" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "conn = pymysql.connect(host='gwumysql-restore.cazdwdlcg6dm.us-east-2.rds.amazonaws.com',\n", " user='EMSE6992',\n", " port=3306,\n", " db='EMSE6992',\n", " password='MySQLpass')\n", "\n", "df = pd.read_sql(\"SELECT users.screen_name, users.user_id, users.favorites_count FROM users JOIN statuses On users.user_id = statuses.user_id limit 10\", conn)\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# In Class Work\n", "Using a SQL query, populate a dataframe with the total number of statuses made per location. Using the dataframe, create a histogram and bar plot. Limit your results to the top 25 results.\n", "\n", "*Note: location is a user field*\n", "\n", "*Hint: Dataframes have a `Dataframe.plot.bar()` and a `Dataframe.plot.hist()` method built into them.*" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "<AxesSubplot:xlabel='location'>" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "<Figure size 432x288 with 1 Axes>" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# Space for work\n", "import matplotlib.pyplot as plt\n", "\n", "df_class = pd.read_sql(\"\"\"\n", "SELECT location, count(*) as num FROM statuses \n", " INNER JOIN users\n", " On users.user_id = statuses.user_id \n", "GROUP BY location \n", "ORDER BY num DESC\n", "\"\"\", conn)\n", "\n", "df_class['location'] = df_class['location'].astype('str')\n", "df_class[1:25].plot.bar(x='location', y='num')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Table Alterations\n", "Whenever executing a query that will alter the information in a database (creating/removing/altering tables/records) we need to commit those changes" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "query = \"INSERT INTO statuses (list of cols) VALUES (place for values)\"\n", "cur.execute(query)\n", "\n", "# NOTE: We commit using the connection!\n", "conn.commit()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## IN Class Work\n", "Insert yourself as a **movie** into the **movies** table\n", "\n", "*Note: You must include a title and year**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Space for work\n", "cur = " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Extra - Input Sanitzation\n", "This is more a consideration for developing applications, but an important principal of working with databases is sanitization\n", "\n", "<img src='https://imgs.xkcd.com/comics/exploits_of_a_mom.png'>" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "title = \"Robert'); Drop Table movie_data;\"\n", "{cursor_object}.execute(f\"INSERT INTO movie_data (title, year) VALUES ({name}, '2020')\")" ] } ], "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.8.5" } }, "nbformat": 4, "nbformat_minor": 4 }