{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# First steps in pandas\n",
"\n",
"*This very short introduction to pandas is mainly based on the excellent [pandas documentation](https://pandas.pydata.org/docs/user_guide/10min.html).*\n",
"\n",
"To use this tutorial, I recommend the following procedure:\n",
"\n",
"1. On your machine, create a new folder called `pandas`\n",
"1. Download this tutorial as .ipynb (on the top right of this webpage, select the download button) and move it to your `pandas` folder\n",
"1. Open Visual Studio Code and select the \"Explorer\" symbol on the top left in the [Activity Bar](https://code.visualstudio.com/docs/getstarted/userinterface)\n",
"1. Select \"Open Folder\" and choose your folder `pandas`. This folder is now your project directory\n",
"1. In the Explorer, open the file `pandas-intro-short.ipynb`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Import pandas\n",
"\n",
"- To load the pandas package and start working with it, import the package. \n",
"- The community agreed alias for pandas is `pd`, so loading pandas as pd is assumed standard practice for all of the pandas documentation:"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Data creation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- To manually store data in a table, create a DataFrame:"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"# create the DataFrame and name it my_df\n",
"\n",
"my_df = pd.DataFrame(\n",
" { \n",
" 'name': [ \"Tom\", \"Lisa\", \"Peter\"],\n",
" 'height': [1.68, 1.93, 1.72],\n",
" 'weight': [48.4, 89.8, 84.2],\n",
" 'gender': ['male', 'female', 'male'] \n",
" }\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" height \n",
" weight \n",
" gender \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Tom \n",
" 1.68 \n",
" 48.4 \n",
" male \n",
" \n",
" \n",
" 1 \n",
" Lisa \n",
" 1.93 \n",
" 89.8 \n",
" female \n",
" \n",
" \n",
" 2 \n",
" Peter \n",
" 1.72 \n",
" 84.2 \n",
" male \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name height weight gender\n",
"0 Tom 1.68 48.4 male\n",
"1 Lisa 1.93 89.8 female\n",
"2 Peter 1.72 84.2 male"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# show my_df\n",
"my_df"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Import data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- pandas supports many different file formats or data sources out of the box (csv, excel, sql, json, parquet, …)\n",
"- each of them import data with the prefix `read_*`\n",
"- Import data, available as a CSV file in a GitHub repo:"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" Name \n",
" ID% \n",
" Height \n",
" Average Height Parents \n",
" Gender \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 1 \n",
" 162 \n",
" 161.5 \n",
" female \n",
" \n",
" \n",
" 1 \n",
" Peter \n",
" 2 \n",
" 163 \n",
" 163.5 \n",
" male \n",
" \n",
" \n",
" 2 \n",
" Stefanie \n",
" 3 \n",
" 163 \n",
" 163.2 \n",
" female \n",
" \n",
" \n",
" 3 \n",
" Manuela \n",
" 4 \n",
" 164 \n",
" 165.1 \n",
" female \n",
" \n",
" \n",
" 4 \n",
" Simon \n",
" 5 \n",
" 164 \n",
" 163.2 \n",
" male \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Name ID% Height Average Height Parents Gender\n",
"0 Stefanie 1 162 161.5 female\n",
"1 Peter 2 163 163.5 male\n",
"2 Stefanie 3 163 163.2 female\n",
"3 Manuela 4 164 165.1 female\n",
"4 Simon 5 164 163.2 male"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df = pd.read_csv(\"https://raw.githubusercontent.com/kirenz/datasets/master/height_unclean.csv\", delimiter=\";\", decimal=\",\")\n",
"\n",
"# show head\n",
"df.head()"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" Name \n",
" ID% \n",
" Height \n",
" Average Height Parents \n",
" Gender \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 1 \n",
" 162 \n",
" 161.5 \n",
" female \n",
" \n",
" \n",
" 1 \n",
" Peter \n",
" 2 \n",
" 163 \n",
" 163.5 \n",
" male \n",
" \n",
" \n",
" 2 \n",
" Stefanie \n",
" 3 \n",
" 163 \n",
" 163.2 \n",
" female \n",
" \n",
" \n",
" 3 \n",
" Manuela \n",
" 4 \n",
" 164 \n",
" 165.1 \n",
" female \n",
" \n",
" \n",
" 4 \n",
" Simon \n",
" 5 \n",
" 164 \n",
" 163.2 \n",
" male \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Name ID% Height Average Height Parents Gender\n",
"0 Stefanie 1 162 161.5 female\n",
"1 Peter 2 163 163.5 male\n",
"2 Stefanie 3 163 163.2 female\n",
"3 Manuela 4 164 165.1 female\n",
"4 Simon 5 164 163.2 male"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# same import with different style\n",
"\n",
"ROOT = \"https://raw.githubusercontent.com/kirenz/datasets/master/\"\n",
"DATA = \"height_unclean.csv\"\n",
"\n",
"df = pd.read_csv(ROOT + DATA, delimiter=\";\", decimal=\",\")\n",
"\n",
"# show head\n",
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Store data\n",
"\n",
"- pandas supports many different file formats (csv, excel, sql, json, parquet, …)\n",
"- each of them stores data with the prefix `to_*`\n",
"- The following code will save data as an Excel file in your current directory (you may need to install [OpenPyXL](https://openpyxl.readthedocs.io/en/stable/) first. \n",
"\n",
":::{note}\n",
"[Anaconda installation of OpenPyXL](https://anaconda.org/anaconda/openpyxl)):\n",
":::\n",
"\n",
"- In the example here, the `sheet_name` is named people_height instead of the default Sheet1. By setting `index=False` the row index labels are not saved in the spreadsheet:"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
"df.to_excel(\"height.xlsx\", sheet_name=\"people_height\", index=False)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- The equivalent read function `read_excel()` would reload the data to a DataFrame:"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
"# load excel file\n",
"df_new = pd.read_excel(\"height.xlsx\", sheet_name=\"people_height\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Viewing data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Overview"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" Name \n",
" ID% \n",
" Height \n",
" Average Height Parents \n",
" Gender \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 1 \n",
" 162 \n",
" 161.5 \n",
" female \n",
" \n",
" \n",
" 1 \n",
" Peter \n",
" 2 \n",
" 163 \n",
" 163.5 \n",
" male \n",
" \n",
" \n",
" 2 \n",
" Stefanie \n",
" 3 \n",
" 163 \n",
" 163.2 \n",
" female \n",
" \n",
" \n",
" 3 \n",
" Manuela \n",
" 4 \n",
" 164 \n",
" 165.1 \n",
" female \n",
" \n",
" \n",
" 4 \n",
" Simon \n",
" 5 \n",
" 164 \n",
" 163.2 \n",
" male \n",
" \n",
" \n",
" 5 \n",
" Sophia \n",
" 6 \n",
" 164 \n",
" 164.4 \n",
" female \n",
" \n",
" \n",
" 6 \n",
" Ellen \n",
" 7 \n",
" 164 \n",
" 164.0 \n",
" female \n",
" \n",
" \n",
" 7 \n",
" Emilia \n",
" 8 \n",
" 165 \n",
" 165.2 \n",
" female \n",
" \n",
" \n",
" 8 \n",
" Lina \n",
" 9 \n",
" 165 \n",
" 165.2 \n",
" female \n",
" \n",
" \n",
" 9 \n",
" Marie \n",
" 10 \n",
" 165 \n",
" 165.1 \n",
" female \n",
" \n",
" \n",
" 10 \n",
" Lena \n",
" 11 \n",
" 165 \n",
" 166.3 \n",
" female \n",
" \n",
" \n",
" 11 \n",
" Mila \n",
" 12 \n",
" 165 \n",
" 167.4 \n",
" female \n",
" \n",
" \n",
" 12 \n",
" Fin \n",
" 13 \n",
" 165 \n",
" 165.5 \n",
" male \n",
" \n",
" \n",
" 13 \n",
" Eric \n",
" 14 \n",
" 166 \n",
" 166.2 \n",
" male \n",
" \n",
" \n",
" 14 \n",
" Pia \n",
" 15 \n",
" 166 \n",
" 166.1 \n",
" female \n",
" \n",
" \n",
" 15 \n",
" Marc \n",
" 16 \n",
" 166 \n",
" 166.5 \n",
" male \n",
" \n",
" \n",
" 16 \n",
" Ralph \n",
" 17 \n",
" 166 \n",
" 166.6 \n",
" male \n",
" \n",
" \n",
" 17 \n",
" Tom \n",
" 18 \n",
" 167 \n",
" 166.2 \n",
" male \n",
" \n",
" \n",
" 18 \n",
" Steven \n",
" 19 \n",
" 167 \n",
" 167.3 \n",
" male \n",
" \n",
" \n",
" 19 \n",
" Emanuel \n",
" 20 \n",
" 168 \n",
" 168.5 \n",
" male \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Name ID% Height Average Height Parents Gender\n",
"0 Stefanie 1 162 161.5 female\n",
"1 Peter 2 163 163.5 male\n",
"2 Stefanie 3 163 163.2 female\n",
"3 Manuela 4 164 165.1 female\n",
"4 Simon 5 164 163.2 male\n",
"5 Sophia 6 164 164.4 female\n",
"6 Ellen 7 164 164.0 female\n",
"7 Emilia 8 165 165.2 female\n",
"8 Lina 9 165 165.2 female\n",
"9 Marie 10 165 165.1 female\n",
"10 Lena 11 165 166.3 female\n",
"11 Mila 12 165 167.4 female\n",
"12 Fin 13 165 165.5 male\n",
"13 Eric 14 166 166.2 male\n",
"14 Pia 15 166 166.1 female\n",
"15 Marc 16 166 166.5 male\n",
"16 Ralph 17 166 166.6 male\n",
"17 Tom 18 167 166.2 male\n",
"18 Steven 19 167 167.3 male\n",
"19 Emanuel 20 168 168.5 male"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# show df\n",
"df"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" Name \n",
" ID% \n",
" Height \n",
" Average Height Parents \n",
" Gender \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 1 \n",
" 162 \n",
" 161.5 \n",
" female \n",
" \n",
" \n",
" 1 \n",
" Peter \n",
" 2 \n",
" 163 \n",
" 163.5 \n",
" male \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Name ID% Height Average Height Parents Gender\n",
"0 Stefanie 1 162 161.5 female\n",
"1 Peter 2 163 163.5 male"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# show first 2 rows\n",
"df.head(2)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" Name \n",
" ID% \n",
" Height \n",
" Average Height Parents \n",
" Gender \n",
" \n",
" \n",
" \n",
" \n",
" 18 \n",
" Steven \n",
" 19 \n",
" 167 \n",
" 167.3 \n",
" male \n",
" \n",
" \n",
" 19 \n",
" Emanuel \n",
" 20 \n",
" 168 \n",
" 168.5 \n",
" male \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Name ID% Height Average Height Parents Gender\n",
"18 Steven 19 167 167.3 male\n",
"19 Emanuel 20 168 168.5 male"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# show last 2 rows\n",
"df.tail(2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- The `info()` method prints information about a DataFrame including the index dtype and columns, non-null values and memory usage:"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"RangeIndex: 20 entries, 0 to 19\n",
"Data columns (total 5 columns):\n",
" # Column Non-Null Count Dtype \n",
"--- ------ -------------- ----- \n",
" 0 Name 20 non-null object \n",
" 1 ID% 20 non-null int64 \n",
" 2 Height 20 non-null int64 \n",
" 3 Average Height Parents 20 non-null float64\n",
" 4 Gender 20 non-null object \n",
"dtypes: float64(1), int64(2), object(2)\n",
"memory usage: 928.0+ bytes\n"
]
}
],
"source": [
"df.info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Column names"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Index(['Name', 'ID%', 'Height', 'Average Height Parents', ' Gender'], dtype='object')"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Show columns\n",
"df.columns"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Data type"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- Show data types ([dtypes](https://pandas.pydata.org/docs/user_guide/basics.html#basics-dtypes))."
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Name object\n",
"ID% int64\n",
"Height int64\n",
"Average Height Parents float64\n",
" Gender object\n",
"dtype: object"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.dtypes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- The data types in this DataFrame are integers (int64), floats (float64) and strings (object)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Index"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"RangeIndex(start=0, stop=20, step=1)"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Only show index\n",
"df.index"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Change column names\n",
"\n",
"- Usually, we prefer to work with columns that have the following proporties:\n",
" - no leading or trailing whitespace (`\"name\"` instead of `\" name \"`, `\" name\"` or `\"name \"`)\n",
" - all lowercase (`\"name\"` instead of `\"Name\"`)\n",
" - now white spaces (`\"my_name\"` instead of `\"my name\"`)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Simple rename"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- First, we rename columns by simply using a mapping\n",
"- We rename `\"Name\"` to `\"name\"` and just print the result (we want to display errors and don't save the changes for now):"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" ID% \n",
" Height \n",
" Average Height Parents \n",
" Gender \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 1 \n",
" 162 \n",
" 161.5 \n",
" female \n",
" \n",
" \n",
" 1 \n",
" Peter \n",
" 2 \n",
" 163 \n",
" 163.5 \n",
" male \n",
" \n",
" \n",
" 2 \n",
" Stefanie \n",
" 3 \n",
" 163 \n",
" 163.2 \n",
" female \n",
" \n",
" \n",
" 3 \n",
" Manuela \n",
" 4 \n",
" 164 \n",
" 165.1 \n",
" female \n",
" \n",
" \n",
" 4 \n",
" Simon \n",
" 5 \n",
" 164 \n",
" 163.2 \n",
" male \n",
" \n",
" \n",
" 5 \n",
" Sophia \n",
" 6 \n",
" 164 \n",
" 164.4 \n",
" female \n",
" \n",
" \n",
" 6 \n",
" Ellen \n",
" 7 \n",
" 164 \n",
" 164.0 \n",
" female \n",
" \n",
" \n",
" 7 \n",
" Emilia \n",
" 8 \n",
" 165 \n",
" 165.2 \n",
" female \n",
" \n",
" \n",
" 8 \n",
" Lina \n",
" 9 \n",
" 165 \n",
" 165.2 \n",
" female \n",
" \n",
" \n",
" 9 \n",
" Marie \n",
" 10 \n",
" 165 \n",
" 165.1 \n",
" female \n",
" \n",
" \n",
" 10 \n",
" Lena \n",
" 11 \n",
" 165 \n",
" 166.3 \n",
" female \n",
" \n",
" \n",
" 11 \n",
" Mila \n",
" 12 \n",
" 165 \n",
" 167.4 \n",
" female \n",
" \n",
" \n",
" 12 \n",
" Fin \n",
" 13 \n",
" 165 \n",
" 165.5 \n",
" male \n",
" \n",
" \n",
" 13 \n",
" Eric \n",
" 14 \n",
" 166 \n",
" 166.2 \n",
" male \n",
" \n",
" \n",
" 14 \n",
" Pia \n",
" 15 \n",
" 166 \n",
" 166.1 \n",
" female \n",
" \n",
" \n",
" 15 \n",
" Marc \n",
" 16 \n",
" 166 \n",
" 166.5 \n",
" male \n",
" \n",
" \n",
" 16 \n",
" Ralph \n",
" 17 \n",
" 166 \n",
" 166.6 \n",
" male \n",
" \n",
" \n",
" 17 \n",
" Tom \n",
" 18 \n",
" 167 \n",
" 166.2 \n",
" male \n",
" \n",
" \n",
" 18 \n",
" Steven \n",
" 19 \n",
" 167 \n",
" 167.3 \n",
" male \n",
" \n",
" \n",
" 19 \n",
" Emanuel \n",
" 20 \n",
" 168 \n",
" 168.5 \n",
" male \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name ID% Height Average Height Parents Gender\n",
"0 Stefanie 1 162 161.5 female\n",
"1 Peter 2 163 163.5 male\n",
"2 Stefanie 3 163 163.2 female\n",
"3 Manuela 4 164 165.1 female\n",
"4 Simon 5 164 163.2 male\n",
"5 Sophia 6 164 164.4 female\n",
"6 Ellen 7 164 164.0 female\n",
"7 Emilia 8 165 165.2 female\n",
"8 Lina 9 165 165.2 female\n",
"9 Marie 10 165 165.1 female\n",
"10 Lena 11 165 166.3 female\n",
"11 Mila 12 165 167.4 female\n",
"12 Fin 13 165 165.5 male\n",
"13 Eric 14 166 166.2 male\n",
"14 Pia 15 166 166.1 female\n",
"15 Marc 16 166 166.5 male\n",
"16 Ralph 17 166 166.6 male\n",
"17 Tom 18 167 166.2 male\n",
"18 Steven 19 167 167.3 male\n",
"19 Emanuel 20 168 168.5 male"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.rename(columns={\"Name\": \"name\"}, errors=\"raise\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- Let`s rename Gender to gender\n",
"- Again, we just want to display the result (without saving it).\n",
"- Remove the # and run the following code:"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [],
"source": [
"# df.rename(columns={\"Gender\": \"gender\"}, errors=\"raise\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- This raises an error. Can you spot the problem? (take a look at the end of the error statement)\n",
"- The KeyError statement tells us that `\"['Gender'] not found in axis\"`\n",
"- This is because variable Gender has a white space at the beginning: `[ Gender]`\n",
"- We could fix this problem by typing `\" Gender\"` instead of `\"Gender\"`\n",
"- However, there are useful functions (regular expressions) to deal with this kind of problems"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Trailing and leading spaces (with regex)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- We use regular expressions to deal with whitespaces\n",
"- To change multiple column names at once, we use the method `.columns.str` \n",
"- To replace the spaces, we use `.replace()` with `regex=True`\n",
"- In the following function, we search for leading (line start and spaces) and trailing (spaces and line end) spaces and replace them with an empty string:"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [],
"source": [
"# replace r\"this pattern\" with empty string r\"\"\n",
"df.columns = df.columns.str.replace(r\"^ +| +$\", r\"\", regex=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Explanation for the regex (see also [Stackoverflow](https://stackoverflow.com/a/67466222)):\n",
"\n",
"- we start with `r` (for raw) which tells Python to treat all following input as raw text (without interpreting it)\n",
"- \"`^`\": is line start\n",
"- \"` +`\": (space and plus) is one or more spaces\n",
"- \"`|`\": is or\n",
"- \"`$`\": is line end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"To learn more about regular expressions (\"regex\"), visit the following sites:\n",
"\n",
"- [regular expression basics](https://www.w3schools.com/python/python_regex.asp).\n",
"- [interactive regular expressions tool](https://regex101.com/)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Replace special characters\n",
"\n",
"- Again, we use regular expressions to deal with special characters (like %, &, $ etc.)"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Index(['Name', 'ID', 'Height', 'Average Height Parents', 'Gender'], dtype='object')"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# replace r\"this pattern\" with empty string r\"\"\n",
"df.columns = df.columns.str.replace(r\"%\", r\"\", regex=True)\n",
"\n",
"df.columns"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Lowercase and whitespace\n",
"\n",
"- We can use two simple methods to convert all columns to lowercase and replace white spaces with underscores (\"_\"):"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Index(['name', 'id', 'height', 'average_height_parents', 'gender'], dtype='object')"
]
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.columns = df.columns.str.lower().str.replace(' ', '_')\n",
"\n",
"df.columns"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Change data type\n",
"\n",
"- There are several methods to [change data types in pandas](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.astype.html):\n",
"\n",
" - `.astype()`: Convert to a specific type (like \"int32\", \"float\" or \"catgeory\")\n",
" - `to_datetime`: Convert argument to datetime.\n",
" - `to_timedelta`: Convert argument to timedelta.\n",
" - `to_numeric`: Convert argument to a numeric type.\n",
" - `numpy.ndarray.astype`: Cast a numpy array to a specified type."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Categorical data\n",
"\n",
"- Categoricals are a pandas data type corresponding to categorical variables in statistics. \n",
"\n",
"- A categorical variable takes on a limited, and usually fixed, number of possible values (categories). Examples are gender, social class, blood type, country affiliation, observation time or rating via Likert scales.\n",
"\n",
"- Converting an existing column to a category dtype:"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"name object\n",
"id int64\n",
"height int64\n",
"average_height_parents float64\n",
"gender category\n",
"dtype: object"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df[\"gender\"] = df[\"gender\"].astype(\"category\")\n",
"\n",
"df.dtypes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### String data\n",
"\n",
"- In our example, id is not a number (we can't perform calculations with it)\n",
"- It is just a unique identifier so we should transform it to a simple string (object)"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"name object\n",
"id object\n",
"height int64\n",
"average_height_parents float64\n",
"gender category\n",
"dtype: object"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df['id'] = df['id'].astype(str)\n",
"\n",
"df.dtypes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Add new columns"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Constant"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" id \n",
" height \n",
" average_height_parents \n",
" gender \n",
" number \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 1 \n",
" 162 \n",
" 161.5 \n",
" female \n",
" 42 \n",
" \n",
" \n",
" 1 \n",
" Peter \n",
" 2 \n",
" 163 \n",
" 163.5 \n",
" male \n",
" 42 \n",
" \n",
" \n",
" 2 \n",
" Stefanie \n",
" 3 \n",
" 163 \n",
" 163.2 \n",
" female \n",
" 42 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name id height average_height_parents gender number\n",
"0 Stefanie 1 162 161.5 female 42\n",
"1 Peter 2 163 163.5 male 42\n",
"2 Stefanie 3 163 163.2 female 42"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# add a constant to all rows\n",
"df[\"number\"] = 42\n",
"\n",
"df.head(3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### From existing\n",
"\n",
" - Create new column from existing columns"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"# calculate height in m (from cm)\n",
"df['height_m'] = df.height/100 \n",
"\n",
"# add some random numbers\n",
"df['weight'] = round(np.random.normal(45, 5, 20) * df['height_m'],2)\n",
"\n",
"# calculate body mass index\n",
"df['bmi'] = round(df.weight / (df.height_m * df.height_m),2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Date"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- To add a date, we can use datetime and [strftime](https://strftime.org):"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" id \n",
" height \n",
" average_height_parents \n",
" gender \n",
" number \n",
" height_m \n",
" weight \n",
" bmi \n",
" date \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 1 \n",
" 162 \n",
" 161.5 \n",
" female \n",
" 42 \n",
" 1.62 \n",
" 57.25 \n",
" 21.81 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 1 \n",
" Peter \n",
" 2 \n",
" 163 \n",
" 163.5 \n",
" male \n",
" 42 \n",
" 1.63 \n",
" 68.80 \n",
" 25.89 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 2 \n",
" Stefanie \n",
" 3 \n",
" 163 \n",
" 163.2 \n",
" female \n",
" 42 \n",
" 1.63 \n",
" 73.01 \n",
" 27.48 \n",
" 2023-03-21 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name id height average_height_parents gender number height_m \\\n",
"0 Stefanie 1 162 161.5 female 42 1.62 \n",
"1 Peter 2 163 163.5 male 42 1.63 \n",
"2 Stefanie 3 163 163.2 female 42 1.63 \n",
"\n",
" weight bmi date \n",
"0 57.25 21.81 2023-03-21 \n",
"1 68.80 25.89 2023-03-21 \n",
"2 73.01 27.48 2023-03-21 "
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# add date\n",
"from datetime import datetime\n",
"\n",
"df[\"date\"] = datetime.today().strftime('%Y-%m-%d')\n",
"\n",
"df.head(3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Summary statistics"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Numeric data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- [describe()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.describe.html#pandas.DataFrame.describe) shows a quick statistic summary of your numerical data.\n",
"- We transpose the data (with `.T`) to make it more readable: "
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" count \n",
" mean \n",
" std \n",
" min \n",
" 25% \n",
" 50% \n",
" 75% \n",
" max \n",
" \n",
" \n",
" \n",
" \n",
" height \n",
" 20.0 \n",
" 165.0000 \n",
" 1.486784 \n",
" 162.00 \n",
" 164.0000 \n",
" 165.000 \n",
" 166.0000 \n",
" 168.00 \n",
" \n",
" \n",
" average_height_parents \n",
" 20.0 \n",
" 165.3500 \n",
" 1.687883 \n",
" 161.50 \n",
" 164.3000 \n",
" 165.350 \n",
" 166.3500 \n",
" 168.50 \n",
" \n",
" \n",
" number \n",
" 20.0 \n",
" 42.0000 \n",
" 0.000000 \n",
" 42.00 \n",
" 42.0000 \n",
" 42.000 \n",
" 42.0000 \n",
" 42.00 \n",
" \n",
" \n",
" height_m \n",
" 20.0 \n",
" 1.6500 \n",
" 0.014868 \n",
" 1.62 \n",
" 1.6400 \n",
" 1.650 \n",
" 1.6600 \n",
" 1.68 \n",
" \n",
" \n",
" weight \n",
" 20.0 \n",
" 73.4900 \n",
" 7.962619 \n",
" 57.25 \n",
" 68.8450 \n",
" 72.565 \n",
" 77.6275 \n",
" 90.51 \n",
" \n",
" \n",
" bmi \n",
" 20.0 \n",
" 26.9775 \n",
" 2.769862 \n",
" 21.81 \n",
" 24.9975 \n",
" 27.110 \n",
" 28.1300 \n",
" 33.25 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" count mean std min 25% 50% \\\n",
"height 20.0 165.0000 1.486784 162.00 164.0000 165.000 \n",
"average_height_parents 20.0 165.3500 1.687883 161.50 164.3000 165.350 \n",
"number 20.0 42.0000 0.000000 42.00 42.0000 42.000 \n",
"height_m 20.0 1.6500 0.014868 1.62 1.6400 1.650 \n",
"weight 20.0 73.4900 7.962619 57.25 68.8450 72.565 \n",
"bmi 20.0 26.9775 2.769862 21.81 24.9975 27.110 \n",
"\n",
" 75% max \n",
"height 166.0000 168.00 \n",
"average_height_parents 166.3500 168.50 \n",
"number 42.0000 42.00 \n",
"height_m 1.6600 1.68 \n",
"weight 77.6275 90.51 \n",
"bmi 28.1300 33.25 "
]
},
"execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.describe().T"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- Obtain summary statistics for different groups (categorical data)"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" gender \n",
" female \n",
" male \n",
" \n",
" \n",
" \n",
" \n",
" height \n",
" count \n",
" 11.000000 \n",
" 9.000000 \n",
" \n",
" \n",
" mean \n",
" 164.363636 \n",
" 165.777778 \n",
" \n",
" \n",
" std \n",
" 1.120065 \n",
" 1.563472 \n",
" \n",
" \n",
" min \n",
" 162.000000 \n",
" 163.000000 \n",
" \n",
" \n",
" 25% \n",
" 164.000000 \n",
" 165.000000 \n",
" \n",
" \n",
" 50% \n",
" 165.000000 \n",
" 166.000000 \n",
" \n",
" \n",
" 75% \n",
" 165.000000 \n",
" 167.000000 \n",
" \n",
" \n",
" max \n",
" 166.000000 \n",
" 168.000000 \n",
" \n",
" \n",
" average_height_parents \n",
" count \n",
" 11.000000 \n",
" 9.000000 \n",
" \n",
" \n",
" mean \n",
" 164.863636 \n",
" 165.944444 \n",
" \n",
" \n",
" std \n",
" 1.593909 \n",
" 1.693451 \n",
" \n",
" \n",
" min \n",
" 161.500000 \n",
" 163.200000 \n",
" \n",
" \n",
" 25% \n",
" 164.200000 \n",
" 165.500000 \n",
" \n",
" \n",
" 50% \n",
" 165.100000 \n",
" 166.200000 \n",
" \n",
" \n",
" 75% \n",
" 165.650000 \n",
" 166.600000 \n",
" \n",
" \n",
" max \n",
" 167.400000 \n",
" 168.500000 \n",
" \n",
" \n",
" number \n",
" count \n",
" 11.000000 \n",
" 9.000000 \n",
" \n",
" \n",
" mean \n",
" 42.000000 \n",
" 42.000000 \n",
" \n",
" \n",
" std \n",
" 0.000000 \n",
" 0.000000 \n",
" \n",
" \n",
" min \n",
" 42.000000 \n",
" 42.000000 \n",
" \n",
" \n",
" 25% \n",
" 42.000000 \n",
" 42.000000 \n",
" \n",
" \n",
" 50% \n",
" 42.000000 \n",
" 42.000000 \n",
" \n",
" \n",
" 75% \n",
" 42.000000 \n",
" 42.000000 \n",
" \n",
" \n",
" max \n",
" 42.000000 \n",
" 42.000000 \n",
" \n",
" \n",
" height_m \n",
" count \n",
" 11.000000 \n",
" 9.000000 \n",
" \n",
" \n",
" mean \n",
" 1.643636 \n",
" 1.657778 \n",
" \n",
" \n",
" std \n",
" 0.011201 \n",
" 0.015635 \n",
" \n",
" \n",
" min \n",
" 1.620000 \n",
" 1.630000 \n",
" \n",
" \n",
" 25% \n",
" 1.640000 \n",
" 1.650000 \n",
" \n",
" \n",
" 50% \n",
" 1.650000 \n",
" 1.660000 \n",
" \n",
" \n",
" 75% \n",
" 1.650000 \n",
" 1.670000 \n",
" \n",
" \n",
" max \n",
" 1.660000 \n",
" 1.680000 \n",
" \n",
" \n",
" weight \n",
" count \n",
" 11.000000 \n",
" 9.000000 \n",
" \n",
" \n",
" mean \n",
" 72.739091 \n",
" 74.407778 \n",
" \n",
" \n",
" std \n",
" 8.929506 \n",
" 7.013547 \n",
" \n",
" \n",
" min \n",
" 57.250000 \n",
" 66.870000 \n",
" \n",
" \n",
" 25% \n",
" 69.685000 \n",
" 68.860000 \n",
" \n",
" \n",
" 50% \n",
" 73.010000 \n",
" 71.740000 \n",
" \n",
" \n",
" 75% \n",
" 75.550000 \n",
" 78.490000 \n",
" \n",
" \n",
" max \n",
" 90.510000 \n",
" 88.350000 \n",
" \n",
" \n",
" bmi \n",
" count \n",
" 11.000000 \n",
" 9.000000 \n",
" \n",
" \n",
" mean \n",
" 26.900909 \n",
" 27.071111 \n",
" \n",
" \n",
" std \n",
" 3.098850 \n",
" 2.489942 \n",
" \n",
" \n",
" min \n",
" 21.810000 \n",
" 24.560000 \n",
" \n",
" \n",
" 25% \n",
" 25.595000 \n",
" 25.000000 \n",
" \n",
" \n",
" 50% \n",
" 27.410000 \n",
" 25.890000 \n",
" \n",
" \n",
" 75% \n",
" 27.915000 \n",
" 28.760000 \n",
" \n",
" \n",
" max \n",
" 33.250000 \n",
" 32.060000 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
"gender female male\n",
"height count 11.000000 9.000000\n",
" mean 164.363636 165.777778\n",
" std 1.120065 1.563472\n",
" min 162.000000 163.000000\n",
" 25% 164.000000 165.000000\n",
" 50% 165.000000 166.000000\n",
" 75% 165.000000 167.000000\n",
" max 166.000000 168.000000\n",
"average_height_parents count 11.000000 9.000000\n",
" mean 164.863636 165.944444\n",
" std 1.593909 1.693451\n",
" min 161.500000 163.200000\n",
" 25% 164.200000 165.500000\n",
" 50% 165.100000 166.200000\n",
" 75% 165.650000 166.600000\n",
" max 167.400000 168.500000\n",
"number count 11.000000 9.000000\n",
" mean 42.000000 42.000000\n",
" std 0.000000 0.000000\n",
" min 42.000000 42.000000\n",
" 25% 42.000000 42.000000\n",
" 50% 42.000000 42.000000\n",
" 75% 42.000000 42.000000\n",
" max 42.000000 42.000000\n",
"height_m count 11.000000 9.000000\n",
" mean 1.643636 1.657778\n",
" std 0.011201 0.015635\n",
" min 1.620000 1.630000\n",
" 25% 1.640000 1.650000\n",
" 50% 1.650000 1.660000\n",
" 75% 1.650000 1.670000\n",
" max 1.660000 1.680000\n",
"weight count 11.000000 9.000000\n",
" mean 72.739091 74.407778\n",
" std 8.929506 7.013547\n",
" min 57.250000 66.870000\n",
" 25% 69.685000 68.860000\n",
" 50% 73.010000 71.740000\n",
" 75% 75.550000 78.490000\n",
" max 90.510000 88.350000\n",
"bmi count 11.000000 9.000000\n",
" mean 26.900909 27.071111\n",
" std 3.098850 2.489942\n",
" min 21.810000 24.560000\n",
" 25% 25.595000 25.000000\n",
" 50% 27.410000 25.890000\n",
" 75% 27.915000 28.760000\n",
" max 33.250000 32.060000"
]
},
"execution_count": 42,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.groupby(['gender']).describe().T"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Categorical data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- we can also use `describe()` for categorical data"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" count \n",
" unique \n",
" top \n",
" freq \n",
" \n",
" \n",
" \n",
" \n",
" gender \n",
" 20 \n",
" 2 \n",
" female \n",
" 11 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" count unique top freq\n",
"gender 20 2 female 11"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.describe(include=\"category\").T"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- Show unique levels and count with `value_counts()`"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"female 11\n",
"male 9\n",
"Name: gender, dtype: int64"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df['gender'].value_counts()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Loop over list"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- Example of for loop to obtain statistics for specific numerical columns"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [],
"source": [
"# make a list of numerical columns\n",
"list_num = ['height', 'weight']"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Median of height equals 165.0 \n",
"\n",
"Median of weight equals 72.565 \n",
"\n"
]
}
],
"source": [
"# calculate median for our list\n",
"for i in list_num:\n",
" print(f'Median of {i} equals {df[i].median()} \\n')"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Column: height \n",
" count 20.000000\n",
"mean 165.000000\n",
"std 1.486784\n",
"min 162.000000\n",
"25% 164.000000\n",
"50% 165.000000\n",
"75% 166.000000\n",
"max 168.000000\n",
"Name: height, dtype: float64 \n",
"\n",
"Column: weight \n",
" count 20.000000\n",
"mean 73.490000\n",
"std 7.962619\n",
"min 57.250000\n",
"25% 68.845000\n",
"50% 72.565000\n",
"75% 77.627500\n",
"max 90.510000\n",
"Name: weight, dtype: float64 \n",
"\n"
]
}
],
"source": [
"# calculate summary statistics for our list\n",
"for i in list_num:\n",
" print(f'Column: {i} \\n {df[i].describe().T} \\n')"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAjsAAAHFCAYAAAAUpjivAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy88F64QAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA7jUlEQVR4nO3dfVxUZf7/8fdwNyDgeIM3oAhqiViKutqaSwq5qViIWJtpGS7WdmcWlia55s3WYlaWv7WNbbek2tzucTMDLRXNsLJCuzMN0rCyKE0RFVS4fn/4ZbYJJERwhuPr+XjM48G5zjnX+VyDw7w95zozNmOMEQAAgEV5ubsAAACApkTYAQAAlkbYAQAAlkbYAQAAlkbYAQAAlkbYAQAAlkbYAQAAlkbYAQAAlkbYAQAAlkbYARpJVlaWbDaby6Ndu3aKi4vTa6+95u7ynCIjIzVp0qRT3u/w4cOaO3eu8vLy6r1PQUGBhg4dKofDIZvNpkceeeSUj+uJXn/9dc2dO/eU97PZbA3a72R9TZkypVH6kqRdu3bJZrMpKyurSevJz8/X3LlztX///gYdB2gIwg7QyJYuXapNmzYpPz9fjz/+uLy9vZWYmKgVK1a4u7TTcvjwYc2bN++Uwk5qaqr27Nmj5557Tps2bdJVV13VdAWeQa+//rrmzZt3yvtt2rRJ1113XRNUdPpCQ0O1adMmXXrppU16nPz8fM2bN4+wgzPKx90FAFZz/vnna8CAAc7lkSNHqnXr1vrPf/6jxMREN1Z25n3yySe6/vrrlZCQ0Cj9HTt2TDabTT4+zedPlzFG5eXlCggI0KBBg9xdzknZ7XaPrg84HZzZAZqYv7+//Pz85Ovr69K+b98+3XzzzerUqZP8/PzUrVs3zZo1SxUVFZKk8vJy9evXT+ecc44OHDjg3O+7775Tx44dFRcXp8rKSknSpEmTFBQUpE8//VTDhg1TYGCg2rVrpylTpujw4cO/WmNxcbGuueYatW/fXna7XdHR0XrooYdUVVUl6cQljnbt2kmS5s2b57xMd7LLYdWX9I4fP67HHnvMuX21Tz75RElJSWrdurX8/f3Vt29fPfXUUy595OXlyWaz6ZlnntEdd9yhTp06yW63q7CwsNZjVl+GeeCBB3T//fcrMjJSAQEBiouL044dO3Ts2DHNnDlTYWFhcjgcSk5OVklJSY1+nn/+eV144YUKDAxUUFCQRowYoYKCAuf6SZMm6dFHH5Ukl0uWu3btcrZNmTJFmZmZio6Olt1ud46ttstY33zzjf70pz8pPDxcfn5+CgsL0xVXXKHvv//+JL8tV88884yio6PVokULxcTE1HrJ9IsvvtCECRNcfr/VY/jl8/fLy1j//e9/1adPH9ntdnXr1k2LFy/W3LlzXX6f9a1n7ty5mj59uiSpa9euzufuVM4WAg1iADSKpUuXGknmnXfeMceOHTNHjx41u3fvNlOnTjVeXl4mNzfXue2RI0dMnz59TGBgoHnwwQfN6tWrzezZs42Pj48ZNWqUc7sdO3aY4OBgM3bsWGOMMZWVlebiiy827du3N99++61zu5SUFOPn52e6dOli7rvvPrN69Wozd+5c4+PjYy677DKXOiMiIkxKSopzuaSkxHTq1Mm0a9fOZGZmmtzcXDNlyhQjydx0003GGGPKy8tNbm6ukWQmT55sNm3aZDZt2mQKCwtrfS5KSkrMpk2bjCRzxRVXOLc3xpjPP//cBAcHm+7du5unn37arFy50owfP95IMvfff7+zj3Xr1hlJplOnTuaKK64wr776qnnttdfM3r17az3mzp07jSQTERFhEhMTzWuvvWb+/e9/mw4dOpgePXqYiRMnmtTUVJOTk2MyMzNNUFCQSUxMdOnjvvvuMzabzaSmpprXXnvNvPLKK+bCCy80gYGB5tNPPzXGGFNYWGiuuOIKI8k5rk2bNpny8nJjjHHW3KdPH7Ns2TKzdu1a88knnzjXzZkzx3m8r7/+2oSGhpqQkBCzaNEi8+abb5rnn3/epKammm3bttU6zmqSTGRkpLngggvMCy+8YF5//XUTFxdnfHx8TFFRkXO7Tz/91DgcDtO7d2/z9NNPm9WrV5s77rjDeHl5mblz59Z4/pYuXepsy8nJMV5eXiYuLs5kZ2ebF1980fz2t781kZGR5pdvH/WpZ/fu3ebWW281kswrr7zifO4OHDhQ51iB00XYARpJddj55cNut5u///3vLttmZmYaSeaFF15wab///vuNJLN69Wpn2/PPP28kmUceecTcc889xsvLy2W9MSfCjiSzePFil/b77rvPSDIbN250tv0y7MycOdNIMu+++67LvjfddJOx2Wxm+/btxhhjfvjhhxpv1r9Gkrnllltc2q666ipjt9tNcXGxS3tCQoJp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XxMWFmauvPJK8/3339c53pMdp7bf786dO01qaqrp1KmT8fX1Ne3atTODBw829957r8s2+sXdWMYYk52dbXr37u38aIMFCxaYqVOnmtatWze4nvT0dBMWFma8vLyMJLNu3bo6xwqcLpsxxpzxhAWgUU2aNEkvvfSSysrK3F0KLO7YsWPq27evOnXqpNWrV7u7HKBeuIwFADipyZMn65JLLlFoaKi+++47ZWZmatu2bVq8eLG7SwPqjbADADipgwcP6s4779QPP/wgX19f9e/fX6+//rpzXhnQHHAZCwAAWBq3ngMAAEsj7AAAAEsj7AAAAEtjgrKkqqoqffvttwoODj5jH00PAABOjzFGBw8eVFhYWJ3fX0fY0Ynv5PnlNzADAIDmYffu3XV+qS5hR3J+TPnu3bvVsmVLN1cDAADqo7S0VOHh4b/6dSOEHf3vG4pbtmxJ2AEAoJn5tSkoTFAGAACWRtgBAACWRtgBAACWRtgBAACWRtgBAACWRtgBAACWRtgBAACWRtgBAACWRtgBAACWRtgBAACW5taws2HDBiUmJiosLEw2m03Lly+vsc22bds0evRoORwOBQcHa9CgQSouLnau/+677zRx4kR17NhRgYGB6t+/v1566aUzOAoAAODJ3Bp2Dh06pJiYGC1ZsqTW9UVFRYqNjVXPnj2Vl5enrVu3avbs2fL393duM3HiRG3fvl2vvvqqPv74Y40dO1bjxo1TQUHBmRoGAADwYDZjjHF3EdKJL/HKzs7WmDFjnG1XXXWVfH199cwzz5x0v6CgID322GOaOHGis61t27ZauHChJk+eXK9jl5aWyuFw6MCBA3wRKICzjjFG5eXl7i7jtBljVFFRIUmy2+2/+uWQzYG/v78lxtFU6vv+7bHfel5VVaWVK1dqxowZGjFihAoKCtS1a1elp6e7BKLY2Fg9//zzuvTSS9WqVSu98MILqqioUFxc3En7rqiocL4gpBNPFgCcrcrLy5WQkODuMlCLnJwcBQQEuLuMZs9jJyiXlJSorKxMCxYs0MiRI7V69WolJydr7NixWr9+vXO7559/XsePH1fbtm1lt9t1ww03KDs7W927dz9p3xkZGXI4HM5HeHj4mRgSAABwA48+syNJSUlJSktLkyT17dtX+fn5yszM1NChQyVJf/7zn/XTTz/pzTffVEhIiJYvX64//OEPeuutt9S7d+9a+05PT9e0adOcy6WlpQQeAGctf39/5eTkuLuM01ZeXq7k5GRJUnZ2tsv8zubKCmPwBB4bdkJCQuTj46NevXq5tEdHR2vjxo2STkxgXrJkiT755BOdd955kqSYmBi99dZbevTRR5WZmVlr33a7XXa7vWkHAADNhM1ms9ylEn9/f8uNCQ3nsZex/Pz8NHDgQG3fvt2lfceOHYqIiJAkHT58WJLk5eU6DG9vb+eZIQAAcHZz65mdsrIyFRYWOpd37typLVu2qE2bNurSpYumT5+ucePGaciQIYqPj1dubq5WrFihvLw8SVLPnj11zjnn6IYbbtCDDz6otm3bavny5XrjjTf02muvuWlUAADAk7g17Lz//vuKj493LlfPo0lJSVFWVpaSk5OVmZmpjIwMTZ06VVFRUXr55ZcVGxsrSfL19dXrr7+umTNnKjExUWVlZTrnnHP01FNPadSoUW4ZEwAA8Cwe8zk77sTn7ABA83fkyBHnLfTcsn12qO/7t8fO2QEAAGgMhB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBphB0AAGBpbg07GzZsUGJiosLCwmSz2bR8+fIa22zbtk2jR4+Ww+FQcHCwBg0apOLiYknSrl27ZLPZan28+OKLZ3g0AADAE7k17Bw6dEgxMTFasmRJreuLiooUGxurnj17Ki8vT1u3btXs2bPl7+8vSQoPD9eePXtcHvPmzVNgYKASEhLO5FAAAICH8nHnwRMSEuoMJbNmzdKoUaO0cOFCZ1u3bt2cP3t7e6tjx44u+2RnZ2vcuHEKCgpq/IIB4BeMMSovL3d3GZBcfg/8TjyHv7+/bDabW2twa9ipS1VVlVauXKkZM2ZoxIgRKigoUNeuXZWenq4xY8bUus8HH3ygLVu26NFHH62z74qKClVUVDiXS0tLG7N0AGeR8vJyziR7oOTkZHeXgP+Tk5OjgIAAt9bgsROUS0pKVFZWpgULFmjkyJFavXq1kpOTNXbsWK1fv77WfZ544glFR0dr8ODBdfadkZEhh8PhfISHhzfFEAAAgAfw6DM7kpSUlKS0tDRJUt++fZWfn6/MzEwNHTrUZfsjR45o2bJlmj179q/2nZ6ermnTpjmXS0tLCTwATtuS2H2yext3l3HWMkY6euKtQ35ekpuvnJzVKiptmrKxjbvLcPLYsBMSEiIfHx/16tXLpT06OlobN26ssf1LL72kw4cP69prr/3Vvu12u+x2e6PVCgCSZPc2snu7u4qzm7+7C8D/8azQ77GXsfz8/DRw4EBt377dpX3Hjh2KiIiosf0TTzyh0aNHq127dmeqRAAA0Ay49cxOWVmZCgsLncs7d+7Uli1b1KZNG3Xp0kXTp0/XuHHjNGTIEMXHxys3N1crVqxQXl6eSz+FhYXasGGDXn/99TM8AgAA4OncGnbef/99xcfHO5er59GkpKQoKytLycnJyszMVEZGhqZOnaqoqCi9/PLLio2NdennySefVKdOnTR8+PAzWj8AAPB8NmOMZ11Yc4PS0lI5HA4dOHBALVu2dHc5AJqRI0eOOG89/+fQvczZASRVVErXr28rqWlvPa/v+7fHztkBAABoDIQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaYQdAABgaW4NOxs2bFBiYqLCwsJks9m0fPnyGtts27ZNo0ePlsPhUHBwsAYNGqTi4mKXbTZt2qSLL75YgYGBatWqleLi4nTkyJEzNAoAAODJ3Bp2Dh06pJiYGC1ZsqTW9UVFRYqNjVXPnj2Vl5enrVu3avbs2fL393dus2nTJo0cOVLDhw/Xe++9p82bN2vKlCny8uKkFQAAkHzcefCEhAQlJCScdP2sWbM0atQoLVy40NnWrVs3l23S0tI0depUzZw509l27rnnNn6xZzljjMrLy91dxmkzxqiiokKSZLfbZbPZ3FzR6fP397fEOJorY4zz54pKNxYCeJCfvxZ+/hpxF7eGnbpUVVVp5cqVmjFjhkaMGKGCggJ17dpV6enpGjNmjCSppKRE7777rq6++moNHjxYRUVF6tmzp+677z7FxsaetO+KigrnG54klZaWNvVwmr3y8vI6gyncJycnRwEBAe4u46z1878lUza2dWMlgGeqqKhQixYt3FqDx17rKSkpUVlZmRYsWKCRI0dq9erVSk5O1tixY7V+/XpJ0pdffilJmjt3rq6//nrl5uaqf//+GjZsmL744ouT9p2RkSGHw+F8hIeHn5ExAQCAM8+jz+xIUlJSktLS0iRJffv2VX5+vjIzMzV06FDnNjfccIP++Mc/SpL69eunNWvW6Mknn1RGRkatfaenp2vatGnO5dLSUgLPr/D391dOTo67yzht5eXlSk5OliRlZ2e7zP9qrqwwhubMbrc7f14Su1d2bzcWA3iIisr/nen8+WvEXTw27ISEhMjHx0e9evVyaY+OjtbGjRslSaGhoZJU6za/vGPr5+x2u0c8+c2JzWaz3KUSf39/y40JZ97P50vZvUXYAX7BE+YUeuxlLD8/Pw0cOFDbt293ad+xY4ciIiIkSZGRkQoLC6tzGwAAcHZz65mdsrIyFRYWOpd37typLVu2qE2bNurSpYumT5+ucePGaciQIYqPj1dubq5WrFihvLw8SSfS4vTp0zVnzhzFxMSob9++euqpp/T555/rpZdectOoAACAJ3Fr2Hn//fcVHx/vXK6eR5OSkqKsrCwlJycrMzNTGRkZmjp1qqKiovTyyy+73Gl1++23q7y8XGlpadq3b59iYmL0xhtvqHv37md8PAAAwPO4NezExcX96v33qampSk1NrXObmTNnunzODgAAQDWPnbMDAADQGAg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0gg7AADA0hoUdrp166a9e/fWaN+/f7+6det22kUBAAA0lgaFnV27dqmysrJGe0VFhb755pvTLgoAAKCx+JzKxq+++qrz51WrVsnhcDiXKysrtWbNGkVGRjZacQAAAKfrlMLOmDFjJEk2m00pKSku63x9fRUZGamHHnqo0YoDAAA4XacUdqqqqiRJXbt21ebNmxUSEtIkRQEAADSWUwo71Xbu3NnYdQAAADSJBoUdSVqzZo3WrFmjkpIS5xmfak8++eRpFwYAANAYGhR25s2bp/nz52vAgAEKDQ2VzWZr7LoAAAAaRYPCTmZmprKysjRx4sTGrgcAAKBRNehzdo4eParBgwc3di0AAACNrkFh57rrrtOyZcsauxYAAIBGV+/LWNOmTXP+XFVVpccff1xvvvmm+vTpI19fX5dtFy1a1HgVAgAAnIZ6h52CggKX5b59+0qSPvnkE5d2JisDAABPUu+ws27duqasAwAAoEk0aM4OAABAc9GgW8+Tk5NrvVxls9nk7++vc845RxMmTFBUVFSd/WzYsEEPPPCAPvjgA+3Zs0fZ2dnO79+qtm3bNt11111av369qqqqdN555+mFF15Qly5dJElxcXFav369yz7jxo3Tc88915ChAQAAi2nQmR2Hw6G1a9fqww8/dIaegoICrV27VsePH9fzzz+vmJgYvf3223X2c+jQIcXExGjJkiW1ri8qKlJsbKx69uypvLw8bd26VbNnz5a/v7/Ldtdff7327NnjfPzjH/9oyLAAAIAFNejMTseOHTVhwgQtWbJEXl4n8lJVVZVuu+02BQcH67nnntONN96ou+66Sxs3bjxpPwkJCUpISDjp+lmzZmnUqFFauHChs61bt241tmvRooU6duzYkKGcEVVVVTpw4IC7y4Ck8vJy58/79+93WYb7OBwO59+S5qyi0ibJuLuMs5Yx0tH/+/YiPy+J+2Xc58RrwXPYjDGn/Mps166d3n77bfXo0cOlfceOHRo8eLB+/PFHffzxx7rooou0f//++hVis7lcxqqqqpLD4dCMGTO0ceNGFRQUqGvXrkpPT3e51BUXF6dPP/1Uxhh16NBBCQkJmjNnjoKDg096rIqKClVUVDiX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",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "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6v3335cxxivQ5OTkqLi42Ou/359//lk33XSTWrdurf379+uPf/yj/vWvf5VZR3FxsXJzc72+RPbt2yep/C+Wk0JDQ7V3794y7T/88IMkedVSGc52fb8OfFXl5Hpfe+01RUdHn9OyzrTmxo0b67vvvqvQOvr06aPnnntOH3zwgd5//309+OCDkk7sFUpPT9eePXtUr149r7OSGjVqpE6dOmnWrFnlLvNkoCxPaGhouQcln/wbA5yCYSaggj755BNJ8pwR1KdPHx05ckRvvvmmV7+lS5d6pp80duxYZWVl6fXXX9dzzz2nFStW6PHHHy93PS+99JLX82XLlkk6/XVh+vTpo23btunjjz8uU4tlWerdu7ekE/9VSzrn/6z79OmjdevWecLLL9cXGBho2ynBV111lfz8/LRz50517dq13MdJlfVaJCYmav369fr666/Pet4+ffrIsixNnz5dPj4+6tWrlyTpyiuv1Pr165Wenq5evXp57YUZOHCgvvjiC7Vs2bLc7TtdmImPj9e6deu89t6UlpZ6nT11tirrdQTOBntmgDPwxRdfeE5zzc3N1euvv6709HQNGTJEsbGxkqQ//OEPevrppzVq1Cjt3r1bHTt21KZNmzR79mwNGDDAMzTwt7/9TS+++KJSU1PVvn17tW/fXnfeeafuu+8+XX755V7Hufj7+2v+/Pk6cuSIunXrpoyMDP3pT39SYmKievbsecp6J06cqKVLl+rqq6/WzJkzFR0drZUrV+ovf/mLbr/9drVu3VqSFBQUpOjoaP3rX/9Snz591LBhQzVq1OisL3b28MMPe47deOihh9SwYUO99NJLWrlypebNm6eQkJCzWl5liYmJ0cyZMzVt2jR9++23nusD7d+/Xx988IHq1q3ruWhgx44dJUlz585VYmKifH191alTJ8/Q1ZmaOXOm0tLS1KtXLz3wwAPq2LGjDh06pFWrVmnSpEm68MILTzlvWFiYOnTooDVr1qh3794KDAyUdCLMHDhwQAcOHFBKSkqZ9aWnpysuLk7jx49XmzZtVFhYqN27d+s///mPFi9efMphr2nTpunf//63+vTpo2nTpikgIECLFy/Wzz//LOnshuFO6tChgyTpmWeeUVBQkNxut2JjY0+7JxE4Z3YfgQzUZOWdzRQSEmI6d+5sUlJSPKfunpSbm2vGjh1rIiMjjZ+fn4mO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AACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAoxFmAACAo9kaZoqLi/Xggw8qNjZWAQEBatGihWbOnKnS0lJPn9GjR8uyLK/HZZddZmPVAACgJvGzc+Vz587V4sWLtWTJErVv314ffvihbr75ZoWEhOjuu+/29Ovfv79SU1M9z/39/e0oFwAA1EC2hpnNmzdr8ODBuvrqqyVJMTEx+sc//qEPP/zQq5/L5VJERIQdJQIAgBrO1mGmnj176u2339b27dslSZ9++qk2bdqkAQMGePXbsGGDwsLC1Lp1a916663Kycmxo1wAAFAD2bpn5r777lNeXp4uvPBC+fr6qqSkRLNmzdKwYcM8fRITEzV06FBFR0dr165dmj59uq644gp99NFHcrlcZZZZVFSkoqIiz/P8/Pxq2RYAAGAPW8PM8uXL9eKLL2rZsmVq3769PvnkE02YMEFRUVEaNWqUJOnGG2/09O/QoYO6du2q6OhorVy5Utdee22ZZc6ZM0czZsyotm0AAAD2snWYafLkybr//vv1+9//Xh07dtRNN92kiRMnas6cOaecJzIyUtHR0frmm2/KnT516lTl5eV5HtnZ2VVVPgAAqAFs3TNTUFAgHx/vPOXr6+t1avav5ebmKjs7W5GRkeVOd7lc5Q4/AQCA2snWMDNo0CDNmjVLzZs3V/v27bVlyxalpKQoKSlJknTkyBElJyfruuuuU2RkpHbv3q0HHnhAjRo10pAhQ+wsHQAA1BC2hpmnnnpK06dP17hx45STk6OoqCiNGTNGDz30kKQTe2k+//xzLV26VIcOHVJkZKR69+6t5cuXKygoyM7SAQBADWEZY4zdRVSl/Px8hYSEKC8vT8HBwXaXAwCogKNHjyoxMVGSlJaWpoCAAJsrQlU7m+9v7s0EAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAcjTADAAAc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55EzDjOfffaZ5+e77rpL99xzj/bt26eOHTuWOaupU6dOZ1XEnj17tHbtWr3++uun7RcZGano6Gh98803p+wzdepUTZo0yfM8Pz9fzZo1O6t6AACAc5xxmOncubMsy/I64DcpKcnz88lpZ3MA8EmpqakKCwvT1Vdffdp+ubm5ys7OVmRk5Cn7uFyuUw5BAQCA2ueMw8yuXbuqpIDS0lKlpqZq1KhR8vP7XzlHjhxRcnKyrrvuOkVGRmr37t164IEH1KhRI67uCgAAPM44zERHR1dJAWvXrlVWVpbXXh5J8vX11eeff66lS5fq0KFDioyMVO/evbV8+XIFBQVVSS0AAMB5KnQA8IoVK8pttyxLbrdbF1xwgWJjY89oWf369StzrRpJCggI0OrVqytSHgAAOI9UKMxcc801ZY6fkbyPm+nZs6fefPNNNWjQoFIKBQAAKE+FLpqXnp6ubt26KT09XXl5ecrLy1N6erouvfRSvfXWW/rvf/+r3Nxc3XvvvZVdLwAAgJcK7Zm5++679cwzzyguLs7T1qdPH7ndbt12223aunWrFixYUOY4GAAAgMpWoT0zO3fuVHBwcJn24OBgffvtt5KkVq1a6aeffjq36gAAAH5DhcLMJZdcosmTJ+vHH3/0tP3444+aMmWKunXrJunELQ+aNm1aOVUCAACcQoWGmZ577jkNHjxYTZs2VbNmzWRZlrKystSiRQv961//knTiOjHTp0+v1GIBAAB+rUJhpk2bNvryyy+1evVqbd++XcYYXXjhherbt698fE7s7Lnmmmsqs04AAIByVSjMSCdOw+7fv7/69+9fmfUAAACclTMOM08++aRuu+02ud1uPfnkk6ftO378+HMuDAAA4EyccZh5/PHHNWLECLndbj3++OOn7GdZFmEGAABUmwrdaLKqbjoJAABwtip0avZJx44d09dff63i4uLKqgcAAOCsVOgA4IKCAt11111asmSJJGn79u1q0aKFxo8fr6ioKN1///2VWmRN5nV/qpLj9hUC1CS/eC+UdyNZAKhMFQozU6dO1aeffqoNGzZ4nc105ZVX6uGHHz6vwkxRUZHn56BPX7axEqBmKioqUmBgoN1lAKjFKhRm3nzzTS1fvlyXXXaZLMvytLdr1047d+6stOIAAAB+S4XCzI8//qiwsLAy7T///LNXuDkfuFwuz8+HL/q95FvH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PiUlJZo0aZLq16+v0NBQTZkyRb++W4sxRvPmzVOLFi0UEBCgiy66SK+99ppn+oYNG2RZllavXq2uXbvK5XLpnXfeqdLXBsC5I8wAqHKTJk3Su+++qxUrVig9PV3vvPOOPv74Y8/0m2++We+++65efvllffbZZxo6dKj69++vb775xtOnoKBAjz32mF544QX997//VVZWlu69917P9Pnz5+vvf/+7nnvuOW3atEkHDhzQG2+84VXHgw8+qNTUVC1atEhbt27VxIkTNXLkSG3cuNGr35QpUzRnzhx9+eWX6tSpUxW9KgAqjQGAKpSfn2/q1KljXn31VU/boUOHTGBgoLn77rvNjh07jGVZ5vvvv/ear0+fPmbq1KnGGGNSU1ONJLNjxw7P9KefftqEh4d7nkdGRpo///nPnufHjx83TZs2NYMHDzbGGHPkyBHjdrtNRkaG13puueUWM2zYMGOMMevXrzeSzJtvvlk5Gw+gWvjZHaYA1G7ffvutjh8/rksvvdTTFhISojZt2kiSPv74Yxlj1Lp1a6/5ioqKFBoa6nkeGBioli1bep5HRkYqJydHkpSXl6e9e/eqR48enul+fn7q2rWrZ6hp27ZtKiwsVN++fb3Wc+zYMXXp0sWrrWvXrueyyQCqGWEGQJU6GSYsyyq3vbS0VL6+vvroo4/k6+vr1adevXqen+vUqeM1zbKsMsfEnE5paakkaeXKlWrSpInXNJfL5fW8bt26Z7xcAPYjzACoUi1btlSdOnX0wQcfqFmzZpKk/Px8ffPNN4qPj1eXLl1UUlKinJwc/e53v6vQOkJCQhQZGan33ntPvXr1kiQVFxfro48+0sUXXyxJateunVwul7KyshQfH185GwegRiDMAKhSQUFBGjVqlCZPnqyGDRsqLCxMDz/8sHx8fGRZllq3bq0RI0boD3/4g+bPn68uXbrop59+0rp169SxY0cNGDDgjNZz9913689//rNatWqltm3bKiUlRYcOHfKq495779XEiRNVWlqqnj17Kj8/XxkZGapXr55GjRpVRa8AgKpGmAFQ5VJSUjR27FgNHDhQwcHBmjJlirKzs+V2uyVJqamp+tOf/qR77rlH33//vUJDQ9WjR48zDjKSdM8992jv3r0aPXq0fHx8lJSUpCFDhigvL8/T55FHHlFYWJjmzJmjb7/9VvXr19fFF1+sBx54oNK3GUD1sczZDDoDQCX4+eef1aRJE82fP1+33HKL3eUAcDj2zACoclu2bNFXX32lSy+9VHl5eZo5c6YkafDgwTZXBqA2IMwAqBaPPfaYvv76a/n7++uSSy7RO++8o0aNGtldFoBagGEmAADgaNzOAAAAOBphBgAAOBphBgAAOBphBgAAOBphBgAAOBphBgAAOBphBgAAOBphBgAAOBphBgAAONr/B3jPzF54MiT0AAAAAElFTkSuQmCC",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import seaborn as sns\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# obtain plots for our list\n",
"for i in list_num:\n",
" sns.boxplot(x=\"gender\", y=i, data=df)\n",
" plt.title(\"Boxplot for metric \" + i)\n",
" plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Sorting"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sorting by values:"
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" id \n",
" height \n",
" average_height_parents \n",
" gender \n",
" number \n",
" height_m \n",
" weight \n",
" bmi \n",
" date \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 1 \n",
" 162 \n",
" 161.5 \n",
" female \n",
" 42 \n",
" 1.62 \n",
" 57.25 \n",
" 21.81 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 1 \n",
" Peter \n",
" 2 \n",
" 163 \n",
" 163.5 \n",
" male \n",
" 42 \n",
" 1.63 \n",
" 68.80 \n",
" 25.89 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 2 \n",
" Stefanie \n",
" 3 \n",
" 163 \n",
" 163.2 \n",
" female \n",
" 42 \n",
" 1.63 \n",
" 73.01 \n",
" 27.48 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 3 \n",
" Manuela \n",
" 4 \n",
" 164 \n",
" 165.1 \n",
" female \n",
" 42 \n",
" 1.64 \n",
" 72.12 \n",
" 26.81 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 4 \n",
" Simon \n",
" 5 \n",
" 164 \n",
" 163.2 \n",
" male \n",
" 42 \n",
" 1.64 \n",
" 77.34 \n",
" 28.76 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 5 \n",
" Sophia \n",
" 6 \n",
" 164 \n",
" 164.4 \n",
" female \n",
" 42 \n",
" 1.64 \n",
" 61.43 \n",
" 22.84 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 6 \n",
" Ellen \n",
" 7 \n",
" 164 \n",
" 164.0 \n",
" female \n",
" 42 \n",
" 1.64 \n",
" 75.08 \n",
" 27.91 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 12 \n",
" Fin \n",
" 13 \n",
" 165 \n",
" 165.5 \n",
" male \n",
" 42 \n",
" 1.65 \n",
" 66.87 \n",
" 24.56 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 11 \n",
" Mila \n",
" 12 \n",
" 165 \n",
" 167.4 \n",
" female \n",
" 42 \n",
" 1.65 \n",
" 74.63 \n",
" 27.41 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 10 \n",
" Lena \n",
" 11 \n",
" 165 \n",
" 166.3 \n",
" female \n",
" 42 \n",
" 1.65 \n",
" 90.51 \n",
" 33.25 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 9 \n",
" Marie \n",
" 10 \n",
" 165 \n",
" 165.1 \n",
" female \n",
" 42 \n",
" 1.65 \n",
" 67.31 \n",
" 24.72 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 8 \n",
" Lina \n",
" 9 \n",
" 165 \n",
" 165.2 \n",
" female \n",
" 42 \n",
" 1.65 \n",
" 76.02 \n",
" 27.92 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 7 \n",
" Emilia \n",
" 8 \n",
" 165 \n",
" 165.2 \n",
" female \n",
" 42 \n",
" 1.65 \n",
" 72.06 \n",
" 26.47 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 13 \n",
" Eric \n",
" 14 \n",
" 166 \n",
" 166.2 \n",
" male \n",
" 42 \n",
" 1.66 \n",
" 79.49 \n",
" 28.85 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 14 \n",
" Pia \n",
" 15 \n",
" 166 \n",
" 166.1 \n",
" female \n",
" 42 \n",
" 1.66 \n",
" 80.71 \n",
" 29.29 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 15 \n",
" Marc \n",
" 16 \n",
" 166 \n",
" 166.5 \n",
" male \n",
" 42 \n",
" 1.66 \n",
" 88.35 \n",
" 32.06 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 16 \n",
" Ralph \n",
" 17 \n",
" 166 \n",
" 166.6 \n",
" male \n",
" 42 \n",
" 1.66 \n",
" 68.86 \n",
" 24.99 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 17 \n",
" Tom \n",
" 18 \n",
" 167 \n",
" 166.2 \n",
" male \n",
" 42 \n",
" 1.67 \n",
" 69.73 \n",
" 25.00 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 18 \n",
" Steven \n",
" 19 \n",
" 167 \n",
" 167.3 \n",
" male \n",
" 42 \n",
" 1.67 \n",
" 71.74 \n",
" 25.72 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 19 \n",
" Emanuel \n",
" 20 \n",
" 168 \n",
" 168.5 \n",
" male \n",
" 42 \n",
" 1.68 \n",
" 78.49 \n",
" 27.81 \n",
" 2023-03-21 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name id height average_height_parents gender number height_m \\\n",
"0 Stefanie 1 162 161.5 female 42 1.62 \n",
"1 Peter 2 163 163.5 male 42 1.63 \n",
"2 Stefanie 3 163 163.2 female 42 1.63 \n",
"3 Manuela 4 164 165.1 female 42 1.64 \n",
"4 Simon 5 164 163.2 male 42 1.64 \n",
"5 Sophia 6 164 164.4 female 42 1.64 \n",
"6 Ellen 7 164 164.0 female 42 1.64 \n",
"12 Fin 13 165 165.5 male 42 1.65 \n",
"11 Mila 12 165 167.4 female 42 1.65 \n",
"10 Lena 11 165 166.3 female 42 1.65 \n",
"9 Marie 10 165 165.1 female 42 1.65 \n",
"8 Lina 9 165 165.2 female 42 1.65 \n",
"7 Emilia 8 165 165.2 female 42 1.65 \n",
"13 Eric 14 166 166.2 male 42 1.66 \n",
"14 Pia 15 166 166.1 female 42 1.66 \n",
"15 Marc 16 166 166.5 male 42 1.66 \n",
"16 Ralph 17 166 166.6 male 42 1.66 \n",
"17 Tom 18 167 166.2 male 42 1.67 \n",
"18 Steven 19 167 167.3 male 42 1.67 \n",
"19 Emanuel 20 168 168.5 male 42 1.68 \n",
"\n",
" weight bmi date \n",
"0 57.25 21.81 2023-03-21 \n",
"1 68.80 25.89 2023-03-21 \n",
"2 73.01 27.48 2023-03-21 \n",
"3 72.12 26.81 2023-03-21 \n",
"4 77.34 28.76 2023-03-21 \n",
"5 61.43 22.84 2023-03-21 \n",
"6 75.08 27.91 2023-03-21 \n",
"12 66.87 24.56 2023-03-21 \n",
"11 74.63 27.41 2023-03-21 \n",
"10 90.51 33.25 2023-03-21 \n",
"9 67.31 24.72 2023-03-21 \n",
"8 76.02 27.92 2023-03-21 \n",
"7 72.06 26.47 2023-03-21 \n",
"13 79.49 28.85 2023-03-21 \n",
"14 80.71 29.29 2023-03-21 \n",
"15 88.35 32.06 2023-03-21 \n",
"16 68.86 24.99 2023-03-21 \n",
"17 69.73 25.00 2023-03-21 \n",
"18 71.74 25.72 2023-03-21 \n",
"19 78.49 27.81 2023-03-21 "
]
},
"execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.sort_values(by=\"height\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Selection\n",
"\n",
"### Getting []\n",
"\n",
"Selecting a single column (equivalent to `df.height`):"
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0 162\n",
"1 163\n",
"2 163\n",
"3 164\n",
"4 164\n",
"5 164\n",
"6 164\n",
"7 165\n",
"8 165\n",
"9 165\n",
"10 165\n",
"11 165\n",
"12 165\n",
"13 166\n",
"14 166\n",
"15 166\n",
"16 166\n",
"17 167\n",
"18 167\n",
"19 168\n",
"Name: height, dtype: int64"
]
},
"execution_count": 50,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df[\"height\"]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Selecting via [], which slices the rows (endpoint is not included).\n"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" id \n",
" height \n",
" average_height_parents \n",
" gender \n",
" number \n",
" height_m \n",
" weight \n",
" bmi \n",
" date \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 1 \n",
" 162 \n",
" 161.5 \n",
" female \n",
" 42 \n",
" 1.62 \n",
" 57.25 \n",
" 21.81 \n",
" 2023-03-21 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name id height average_height_parents gender number height_m \\\n",
"0 Stefanie 1 162 161.5 female 42 1.62 \n",
"\n",
" weight bmi date \n",
"0 57.25 21.81 2023-03-21 "
]
},
"execution_count": 51,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df[0:1]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### By label .loc\n",
"\n",
"The `.loc` attribute is the primary access method. The following are valid inputs:\n",
"\n",
"For getting a cross section using a label:"
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" id \n",
" height \n",
" average_height_parents \n",
" gender \n",
" number \n",
" height_m \n",
" weight \n",
" bmi \n",
" date \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 1 \n",
" 162 \n",
" 161.5 \n",
" female \n",
" 42 \n",
" 1.62 \n",
" 57.25 \n",
" 21.81 \n",
" 2023-03-21 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name id height average_height_parents gender number height_m \\\n",
"0 Stefanie 1 162 161.5 female 42 1.62 \n",
"\n",
" weight bmi date \n",
"0 57.25 21.81 2023-03-21 "
]
},
"execution_count": 52,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.loc[[0]]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Selecting on a multi-axis by label:"
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" height \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 162 \n",
" \n",
" \n",
" 1 \n",
" Peter \n",
" 163 \n",
" \n",
" \n",
" 2 \n",
" Stefanie \n",
" 163 \n",
" \n",
" \n",
" 3 \n",
" Manuela \n",
" 164 \n",
" \n",
" \n",
" 4 \n",
" Simon \n",
" 164 \n",
" \n",
" \n",
" 5 \n",
" Sophia \n",
" 164 \n",
" \n",
" \n",
" 6 \n",
" Ellen \n",
" 164 \n",
" \n",
" \n",
" 7 \n",
" Emilia \n",
" 165 \n",
" \n",
" \n",
" 8 \n",
" Lina \n",
" 165 \n",
" \n",
" \n",
" 9 \n",
" Marie \n",
" 165 \n",
" \n",
" \n",
" 10 \n",
" Lena \n",
" 165 \n",
" \n",
" \n",
" 11 \n",
" Mila \n",
" 165 \n",
" \n",
" \n",
" 12 \n",
" Fin \n",
" 165 \n",
" \n",
" \n",
" 13 \n",
" Eric \n",
" 166 \n",
" \n",
" \n",
" 14 \n",
" Pia \n",
" 166 \n",
" \n",
" \n",
" 15 \n",
" Marc \n",
" 166 \n",
" \n",
" \n",
" 16 \n",
" Ralph \n",
" 166 \n",
" \n",
" \n",
" 17 \n",
" Tom \n",
" 167 \n",
" \n",
" \n",
" 18 \n",
" Steven \n",
" 167 \n",
" \n",
" \n",
" 19 \n",
" Emanuel \n",
" 168 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name height\n",
"0 Stefanie 162\n",
"1 Peter 163\n",
"2 Stefanie 163\n",
"3 Manuela 164\n",
"4 Simon 164\n",
"5 Sophia 164\n",
"6 Ellen 164\n",
"7 Emilia 165\n",
"8 Lina 165\n",
"9 Marie 165\n",
"10 Lena 165\n",
"11 Mila 165\n",
"12 Fin 165\n",
"13 Eric 166\n",
"14 Pia 166\n",
"15 Marc 166\n",
"16 Ralph 166\n",
"17 Tom 167\n",
"18 Steven 167\n",
"19 Emanuel 168"
]
},
"execution_count": 53,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.loc[ : , [\"name\", \"height\"]]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Showing label slicing, both endpoints are included:"
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" height \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 162 \n",
" \n",
" \n",
" 1 \n",
" Peter \n",
" 163 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name height\n",
"0 Stefanie 162\n",
"1 Peter 163"
]
},
"execution_count": 54,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.loc[0:1, [\"name\", \"height\"]]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Reduction in the dimensions of the returned object:"
]
},
{
"cell_type": "code",
"execution_count": 55,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"name Stefanie\n",
"height 162\n",
"Name: 0, dtype: object"
]
},
"execution_count": 55,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.loc[0, [\"name\", \"height\"]]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For getting a scalar value:"
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0 162\n",
"Name: height, dtype: int64"
]
},
"execution_count": 56,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.loc[[0], \"height\"]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### By position .iloc\n",
"\n",
"pandas provides a suite of methods in order to get purely integer based indexing. Here, the .iloc attribute is the primary access method. "
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"name Stefanie\n",
"id 1\n",
"height 162\n",
"average_height_parents 161.5\n",
"gender female\n",
"number 42\n",
"height_m 1.62\n",
"weight 57.25\n",
"bmi 21.81\n",
"date 2023-03-21\n",
"Name: 0, dtype: object"
]
},
"execution_count": 57,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.iloc[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"By integer slices:"
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" id \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 1 \n",
" \n",
" \n",
" 1 \n",
" Peter \n",
" 2 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name id\n",
"0 Stefanie 1\n",
"1 Peter 2"
]
},
"execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.iloc[0:2, 0:2]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"By lists of integer position locations:"
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" height \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" Stefanie \n",
" 162 \n",
" \n",
" \n",
" 2 \n",
" Stefanie \n",
" 163 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name height\n",
"0 Stefanie 162\n",
"2 Stefanie 163"
]
},
"execution_count": 59,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.iloc[[0, 2], [0, 2]]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For slicing rows explicitly:"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" id \n",
" height \n",
" average_height_parents \n",
" gender \n",
" number \n",
" height_m \n",
" weight \n",
" bmi \n",
" date \n",
" \n",
" \n",
" \n",
" \n",
" 1 \n",
" Peter \n",
" 2 \n",
" 163 \n",
" 163.5 \n",
" male \n",
" 42 \n",
" 1.63 \n",
" 68.80 \n",
" 25.89 \n",
" 2023-03-21 \n",
" \n",
" \n",
" 2 \n",
" Stefanie \n",
" 3 \n",
" 163 \n",
" 163.2 \n",
" female \n",
" 42 \n",
" 1.63 \n",
" 73.01 \n",
" 27.48 \n",
" 2023-03-21 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name id height average_height_parents gender number height_m \\\n",
"1 Peter 2 163 163.5 male 42 1.63 \n",
"2 Stefanie 3 163 163.2 female 42 1.63 \n",
"\n",
" weight bmi date \n",
"1 68.80 25.89 2023-03-21 \n",
"2 73.01 27.48 2023-03-21 "
]
},
"execution_count": 60,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.iloc[1:3, :]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For slicing columns explicitly:"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" id \n",
" height \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" 1 \n",
" 162 \n",
" \n",
" \n",
" 1 \n",
" 2 \n",
" 163 \n",
" \n",
" \n",
" 2 \n",
" 3 \n",
" 163 \n",
" \n",
" \n",
" 3 \n",
" 4 \n",
" 164 \n",
" \n",
" \n",
" 4 \n",
" 5 \n",
" 164 \n",
" \n",
" \n",
" 5 \n",
" 6 \n",
" 164 \n",
" \n",
" \n",
" 6 \n",
" 7 \n",
" 164 \n",
" \n",
" \n",
" 7 \n",
" 8 \n",
" 165 \n",
" \n",
" \n",
" 8 \n",
" 9 \n",
" 165 \n",
" \n",
" \n",
" 9 \n",
" 10 \n",
" 165 \n",
" \n",
" \n",
" 10 \n",
" 11 \n",
" 165 \n",
" \n",
" \n",
" 11 \n",
" 12 \n",
" 165 \n",
" \n",
" \n",
" 12 \n",
" 13 \n",
" 165 \n",
" \n",
" \n",
" 13 \n",
" 14 \n",
" 166 \n",
" \n",
" \n",
" 14 \n",
" 15 \n",
" 166 \n",
" \n",
" \n",
" 15 \n",
" 16 \n",
" 166 \n",
" \n",
" \n",
" 16 \n",
" 17 \n",
" 166 \n",
" \n",
" \n",
" 17 \n",
" 18 \n",
" 167 \n",
" \n",
" \n",
" 18 \n",
" 19 \n",
" 167 \n",
" \n",
" \n",
" 19 \n",
" 20 \n",
" 168 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" id height\n",
"0 1 162\n",
"1 2 163\n",
"2 3 163\n",
"3 4 164\n",
"4 5 164\n",
"5 6 164\n",
"6 7 164\n",
"7 8 165\n",
"8 9 165\n",
"9 10 165\n",
"10 11 165\n",
"11 12 165\n",
"12 13 165\n",
"13 14 166\n",
"14 15 166\n",
"15 16 166\n",
"16 17 166\n",
"17 18 167\n",
"18 19 167\n",
"19 20 168"
]
},
"execution_count": 61,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.iloc[:, 1:3]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For getting a value explicitly:"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Stefanie'"
]
},
"execution_count": 62,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.iloc[0, 0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Filter (boolean indexing)\n",
"\n",
"Using a single column’s values to select data."
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" id \n",
" height \n",
" average_height_parents \n",
" gender \n",
" number \n",
" height_m \n",
" weight \n",
" bmi \n",
" date \n",
" \n",
" \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
"Empty DataFrame\n",
"Columns: [name, id, height, average_height_parents, gender, number, height_m, weight, bmi, date]\n",
"Index: []"
]
},
"execution_count": 63,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df[df[\"height\"] > 180]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Using the [isin()](https://pandas.pydata.org/docs/reference/api/pandas.Series.isin.html#pandas.Series.isin) method for filtering:"
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" name \n",
" id \n",
" height \n",
" average_height_parents \n",
" gender \n",
" number \n",
" height_m \n",
" weight \n",
" bmi \n",
" date \n",
" \n",
" \n",
" \n",
" \n",
" 17 \n",
" Tom \n",
" 18 \n",
" 167 \n",
" 166.2 \n",
" male \n",
" 42 \n",
" 1.67 \n",
" 69.73 \n",
" 25.0 \n",
" 2023-03-21 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name id height average_height_parents gender number height_m weight \\\n",
"17 Tom 18 167 166.2 male 42 1.67 69.73 \n",
"\n",
" bmi date \n",
"17 25.0 2023-03-21 "
]
},
"execution_count": 64,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df[df[\"name\"].isin([\"Tom\", \"Lisa\"])]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Grouping\n",
"\n",
"By “group by” we are referring to a process involving one or more of the following steps:\n",
"\n",
"- **Splitting** the data into groups based on some criteria\n",
"\n",
"- **Applying** a function to each group independently\n",
"\n",
"- **Combining** the results into a data structure"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Grouping and then applying the mean() function to the resulting groups."
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/var/folders/xv/mfzfdtz93_n7615m5ks0tm2m0000gn/T/ipykernel_16204/2544976795.py:1: FutureWarning: The default value of numeric_only in DataFrameGroupBy.mean is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.\n",
" df.groupby(\"gender\").mean().T\n"
]
},
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" gender \n",
" female \n",
" male \n",
" \n",
" \n",
" \n",
" \n",
" height \n",
" 164.363636 \n",
" 165.777778 \n",
" \n",
" \n",
" average_height_parents \n",
" 164.863636 \n",
" 165.944444 \n",
" \n",
" \n",
" number \n",
" 42.000000 \n",
" 42.000000 \n",
" \n",
" \n",
" height_m \n",
" 1.643636 \n",
" 1.657778 \n",
" \n",
" \n",
" weight \n",
" 72.739091 \n",
" 74.407778 \n",
" \n",
" \n",
" bmi \n",
" 26.900909 \n",
" 27.071111 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
"gender female male\n",
"height 164.363636 165.777778\n",
"average_height_parents 164.863636 165.944444\n",
"number 42.000000 42.000000\n",
"height_m 1.643636 1.657778\n",
"weight 72.739091 74.407778\n",
"bmi 26.900909 27.071111"
]
},
"execution_count": 65,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.groupby(\"gender\").mean().T"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Segment data into bins"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Use the function [cut](https://pandas.pydata.org/docs/reference/api/pandas.cut.html) when you need to segment and sort data values into bins. This function is also useful for going from a continuous variable to a categorical variable. \n",
"\n",
"In our example, we create a body mass index category. The standard weight status categories associated with BMI ranges for adults are shown in the following table:\n",
"\n",
"BMI\t| Weight Status\n",
"---| ---\n",
"Below 18.5 |\tUnderweight\n",
"18.5 - 24.9 |\tNormal or Healthy Weight\n",
"25.0 - 29.9 |\tOverweight\n",
"30.0 and Above |\tObese\n",
"\n",
"Source: [U.S. Department of Health & Human Services](https://www.cdc.gov/healthyweight/assessing/bmi/adult_bmi/index.html)\n",
"\n",
"In our function, we discretize the variable `bmi` into four bins according to the table above:\n",
"\n",
"- The bins [0, 18.5, 25, 30, float('inf')] indicate (0,18.5], (18.5,25], (25,30], (30, float('inf))\n",
"- `float('inf')` is used for setting variable with an infinitely large value"
]
},
{
"cell_type": "code",
"execution_count": 66,
"metadata": {},
"outputs": [],
"source": [
"df['bmi_category'] = pd.cut(df['bmi'], \n",
" bins=[0, 18.5, 25, 30, float('inf')], \n",
" labels=['underweight', 'normal', 'overweight', \"obese\"])"
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0 normal\n",
"1 overweight\n",
"2 overweight\n",
"3 overweight\n",
"4 overweight\n",
"5 normal\n",
"6 overweight\n",
"7 overweight\n",
"8 overweight\n",
"9 normal\n",
"10 obese\n",
"11 overweight\n",
"12 normal\n",
"13 overweight\n",
"14 overweight\n",
"15 obese\n",
"16 normal\n",
"17 normal\n",
"18 overweight\n",
"19 overweight\n",
"Name: bmi_category, dtype: category\n",
"Categories (4, object): ['underweight' < 'normal' < 'overweight' < 'obese']"
]
},
"execution_count": 67,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df['bmi_category']"
]
}
],
"metadata": {
"interpreter": {
"hash": "463226f144cc21b006ce6927bfc93dd00694e52c8bc6857abb6e555b983749e9"
},
"kernelspec": {
"display_name": "Python 3.8.2 64-bit ('base': conda)",
"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.9.16"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}