{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Pandas"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `Pandas` module is Python's fundamental data analytics library and it provides high-performance, easy-to-use data structures and tools for data analysis. Pandas allows for creating pivot tables, computing new columns based on other columns, etc. Pandas also facilitates grouping rows by column values and joining tables as in SQL. \n",
"\n",
"A good cheat sheet for Pandas can be found [here](https://pandas.pydata.org/Pandas_Cheat_Sheet.pdf). \n",
"\n",
"Pandas is a very comprehensive and mature module that can be used for advanced data analytics, and this tutorial presents just a very basic overview of Pandas' capabilities. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Table of Contents\n",
" * [Data Structures](#Data-Structures)\n",
" * [Loading Data: Course Grades](#Loading-Data:-Course-Grades)\n",
" * [Data Exploration](#Data-Exploration)\n",
" + [Preview](#Preview)\n",
" + [Sorting](#Sort)\n",
" + [Summary](#Summary)\n",
" + [Selecting specific columns](#Selecting-specific-columns)\n",
" + [Filtering for particular values](#Filtering-for-particular-values)\n",
" + [Group By](#Group-By)\n",
" * [Data Manipulation](#Data-Manipulation)\n",
" + [Handling missing values](#Handling-missing-values)\n",
" + [Handling irregular cardinality](#Handling-irregular-cardinality)\n",
" + [Detecting outliers](#Detecting-outliers)\n",
" * [Saving a DataFrame](#Saving-a-DataFrame)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Before we start, let's suppress warnings as they can get annoying sometimes."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import warnings\n",
"warnings.filterwarnings('ignore')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's import `Pandas` with the usual convention as `pd` and `NumPy` as `np`."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Series and Data Frames"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are two main data structures in Pandas: `Series` and `DataFrame`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`Series` is like a Python list: it is a one-dimensional data structure that can store values with labels (or index). We can initialize a Series with a list as below."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 34\n",
"1 23\n",
"2 -5\n",
"3 0\n",
"dtype: int64\n"
]
}
],
"source": [
"x = pd.Series([34, 23, -5, 0])\n",
"print(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you would like to see the list of methods that you can use with a Series, use the code completion feature in Jupyter Notebook: type \"x.\" in a cell and then hit the Tab button."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A `DataFrame` is just a table with rows and columns, much like an Excel spreadsheet.\n",
"\n",
"An easy way to create a DataFrame object is by using a dictionary:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
name
\n",
"
state
\n",
"
birthyear
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
Mary
\n",
"
VIC
\n",
"
1980
\n",
"
\n",
"
\n",
"
1
\n",
"
David
\n",
"
NSW
\n",
"
1992
\n",
"
\n",
"
\n",
"
2
\n",
"
Jack
\n",
"
VIC
\n",
"
2000
\n",
"
\n",
"
\n",
"
3
\n",
"
John
\n",
"
SA
\n",
"
1980
\n",
"
\n",
"
\n",
"
4
\n",
"
Robin
\n",
"
QLD
\n",
"
1995
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name state birthyear\n",
"0 Mary VIC 1980\n",
"1 David NSW 1992\n",
"2 Jack VIC 2000\n",
"3 John SA 1980\n",
"4 Robin QLD 1995"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"data = {'name': ['Mary', 'David', 'Jack', 'John', 'Robin'],\n",
" 'state': ['VIC', 'NSW', 'VIC', 'SA', 'QLD'],\n",
" 'birthyear': [1980, 1992, 2000, 1980, 1995]}\n",
"df = pd.DataFrame(data)\n",
"df"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An alternative is to first define an empty data frame with column names and then append rows to it as dictionaries whose keys are the columns."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
name
\n",
"
state
\n",
"
birthyear
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
Mary
\n",
"
VIC
\n",
"
1980
\n",
"
\n",
"
\n",
"
1
\n",
"
David
\n",
"
NSW
\n",
"
1992
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name state birthyear\n",
"0 Mary VIC 1980\n",
"1 David NSW 1992"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df = pd.DataFrame(columns=['name', 'state', 'birthyear'])\n",
"\n",
"# Add rows using loc with dictionaries\n",
"df.loc[len(df)] = {'name': 'Mary', 'state': 'VIC', 'birthyear': 1980}\n",
"df.loc[len(df)] = {'name': 'David', 'state': 'NSW', 'birthyear': 1992}\n",
"\n",
"df"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A second alternative, which is a bit shorter, is to append rows as lists at the end of the data frame."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
name
\n",
"
state
\n",
"
birthyear
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
Mary
\n",
"
VIC
\n",
"
1980
\n",
"
\n",
"
\n",
"
1
\n",
"
David
\n",
"
NSW
\n",
"
1992
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name state birthyear\n",
"0 Mary VIC 1980\n",
"1 David NSW 1992"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df = pd.DataFrame(columns=['name', 'state', 'birthyear'])\n",
"df.loc[len(df)] = ['Mary', 'VIC', 1980]\n",
"df.loc[len(df)] = ['David', 'NSW', 1992]\n",
"\n",
"df"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This tutorial will mostly focus on data frames, since real-world datasets are generally multi-dimensional tables rather than just one dimensional arrays."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Loading Data: Course Grades"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This sample data contains course assessment results and students' final grades (Pass or Fail) for a class with 40 students. \n",
"\n",
"- `Student ID`: Unique ID number for each student in the data.\n",
"\n",
"\n",
"- `Gender`: The gender of student.\n",
"\n",
"\n",
"- `Project Phase 1`: The mark student received by completing the first part of project. The marks are out of 20.\n",
"\n",
"\n",
"- `Project Phase 2`: The mark student received by completing the second part of project. The marks are out of 30.\n",
"\n",
"\n",
"- `Mid-Semester Test`: The mark student received from the mid-semester test. The marks are out of 100.\n",
"\n",
"\n",
"- `Final Exam`: The mark student received from the final exam. The marks are out of 100.\n",
"\n",
"\n",
"- `Grade`: The grade student received indicating whether they passed or failed.\n",
"\n",
"Now let's load the data into a DataFrame object from the Cloud."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"import warnings\n",
"warnings.filterwarnings(\"ignore\")\n",
"\n",
"import io\n",
"import requests\n",
"\n",
"# so that we can see all the columns\n",
"pd.set_option('display.max_columns', None) \n",
"\n",
"# how to read a csv file from a github account\n",
"url_name = 'https://raw.githubusercontent.com/akmand/datasets/master/sample_grades.csv'\n",
"url_content = requests.get(url_name, verify=False).content\n",
"grades = pd.read_csv(io.StringIO(url_content.decode('utf-8')))\n",
"\n",
"# alternatively, you can download this CSV file to \n",
"# where this notebook is on your computer\n",
"# and then use the read_csv() method:\n",
"# ### grades = pd.read_csv('sample_grades.csv', header=0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Data Exploration"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Preview"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are different approaches to viewing the data we are interested in exploring."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" The method `head()` prints the first five rows by default."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Student ID
\n",
"
Gender
\n",
"
Project Phase 1
\n",
"
Project Phase 2
\n",
"
Mid-Semester Test
\n",
"
Final Exam
\n",
"
Grade
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
101
\n",
"
Male
\n",
"
18.25
\n",
"
15.5
\n",
"
94
\n",
"
61.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
1
\n",
"
102
\n",
"
Female
\n",
"
17.75
\n",
"
30.0
\n",
"
79
\n",
"
62.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
2
\n",
"
103
\n",
"
Male
\n",
"
0.00
\n",
"
0.0
\n",
"
78
\n",
"
15.0
\n",
"
NN
\n",
"
\n",
"
\n",
"
3
\n",
"
104
\n",
"
Male
\n",
"
20.00
\n",
"
25.0
\n",
"
69
\n",
"
65.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
4
\n",
"
105
\n",
"
Male
\n",
"
18.75
\n",
"
30.0
\n",
"
96
\n",
"
51.0
\n",
"
PA
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Student ID Gender Project Phase 1 Project Phase 2 Mid-Semester Test \\\n",
"0 101 Male 18.25 15.5 94 \n",
"1 102 Female 17.75 30.0 79 \n",
"2 103 Male 0.00 0.0 78 \n",
"3 104 Male 20.00 25.0 69 \n",
"4 105 Male 18.75 30.0 96 \n",
"\n",
" Final Exam Grade \n",
"0 61.0 PA \n",
"1 62.0 PA \n",
"2 15.0 NN \n",
"3 65.0 PA \n",
"4 51.0 PA "
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Alternatively, we can define the number of header rows we want to print."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Student ID
\n",
"
Gender
\n",
"
Project Phase 1
\n",
"
Project Phase 2
\n",
"
Mid-Semester Test
\n",
"
Final Exam
\n",
"
Grade
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
101
\n",
"
Male
\n",
"
18.25
\n",
"
15.5
\n",
"
94
\n",
"
61.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
1
\n",
"
102
\n",
"
Female
\n",
"
17.75
\n",
"
30.0
\n",
"
79
\n",
"
62.0
\n",
"
PA
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Student ID Gender Project Phase 1 Project Phase 2 Mid-Semester Test \\\n",
"0 101 Male 18.25 15.5 94 \n",
"1 102 Female 17.75 30.0 79 \n",
"\n",
" Final Exam Grade \n",
"0 61.0 PA \n",
"1 62.0 PA "
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.head(2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" `tail()` prints the last five rows by default."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Student ID
\n",
"
Gender
\n",
"
Project Phase 1
\n",
"
Project Phase 2
\n",
"
Mid-Semester Test
\n",
"
Final Exam
\n",
"
Grade
\n",
"
\n",
" \n",
" \n",
"
\n",
"
35
\n",
"
136
\n",
"
Male
\n",
"
18.50
\n",
"
22.0
\n",
"
26
\n",
"
68.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
36
\n",
"
137
\n",
"
Female
\n",
"
20.00
\n",
"
26.0
\n",
"
89
\n",
"
63.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
37
\n",
"
138
\n",
"
Male
\n",
"
18.75
\n",
"
30.0
\n",
"
59
\n",
"
52.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
38
\n",
"
139
\n",
"
Male
\n",
"
19.00
\n",
"
30.0
\n",
"
70
\n",
"
NaN
\n",
"
PA
\n",
"
\n",
"
\n",
"
39
\n",
"
140
\n",
"
Male
\n",
"
20.00
\n",
"
29.0
\n",
"
84
\n",
"
77.0
\n",
"
PA
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Student ID Gender Project Phase 1 Project Phase 2 Mid-Semester Test \\\n",
"35 136 Male 18.50 22.0 26 \n",
"36 137 Female 20.00 26.0 89 \n",
"37 138 Male 18.75 30.0 59 \n",
"38 139 Male 19.00 30.0 70 \n",
"39 140 Male 20.00 29.0 84 \n",
"\n",
" Final Exam Grade \n",
"35 68.0 PA \n",
"36 63.0 PA \n",
"37 52.0 PA \n",
"38 NaN PA \n",
"39 77.0 PA "
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.tail()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`sample()` randomly selects rows from the entire data."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Student ID
\n",
"
Gender
\n",
"
Project Phase 1
\n",
"
Project Phase 2
\n",
"
Mid-Semester Test
\n",
"
Final Exam
\n",
"
Grade
\n",
"
\n",
" \n",
" \n",
"
\n",
"
25
\n",
"
126
\n",
"
Female
\n",
"
20.00
\n",
"
22.5
\n",
"
83
\n",
"
56.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
36
\n",
"
137
\n",
"
Female
\n",
"
20.00
\n",
"
26.0
\n",
"
89
\n",
"
63.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
29
\n",
"
130
\n",
"
Male
\n",
"
19.50
\n",
"
13.0
\n",
"
62
\n",
"
39.0
\n",
"
NN
\n",
"
\n",
"
\n",
"
22
\n",
"
123
\n",
"
Male
\n",
"
19.75
\n",
"
30.0
\n",
"
74
\n",
"
61.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
28
\n",
"
129
\n",
"
Male
\n",
"
20.00
\n",
"
30.0
\n",
"
64
\n",
"
86.0
\n",
"
PA
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Student ID Gender Project Phase 1 Project Phase 2 Mid-Semester Test \\\n",
"25 126 Female 20.00 22.5 83 \n",
"36 137 Female 20.00 26.0 89 \n",
"29 130 Male 19.50 13.0 62 \n",
"22 123 Male 19.75 30.0 74 \n",
"28 129 Male 20.00 30.0 64 \n",
"\n",
" Final Exam Grade \n",
"25 56.0 PA \n",
"36 63.0 PA \n",
"29 39.0 NN \n",
"22 61.0 PA \n",
"28 86.0 PA "
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.sample(5, random_state=99)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sorting"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can sort the data with respect to a particular column by calling `sort_values()`. Let's sort the data by Final Exam scores in descending order. \n",
"\n",
"If you want the sorting to be permanent, you must specifically set the `inplace` argument to `True`. This is a **fundamental rule** in Pandas: in most cases, if you don't set the `inplace` argument to `True`, you will need to set the output of the command to another variable to save its effect.\n",
"\n",
"Another nice feature of Pandas is **method chaining**: notice below how we chain two methods together."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Student ID
\n",
"
Gender
\n",
"
Project Phase 1
\n",
"
Project Phase 2
\n",
"
Mid-Semester Test
\n",
"
Final Exam
\n",
"
Grade
\n",
"
\n",
" \n",
" \n",
"
\n",
"
27
\n",
"
128
\n",
"
Female
\n",
"
20.0
\n",
"
30.00
\n",
"
84
\n",
"
91.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
28
\n",
"
129
\n",
"
Male
\n",
"
20.0
\n",
"
30.00
\n",
"
64
\n",
"
86.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
26
\n",
"
127
\n",
"
Female
\n",
"
20.0
\n",
"
35.00
\n",
"
84
\n",
"
83.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
14
\n",
"
115
\n",
"
Male
\n",
"
19.5
\n",
"
26.00
\n",
"
100
\n",
"
79.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
13
\n",
"
114
\n",
"
Male
\n",
"
20.0
\n",
"
22.75
\n",
"
85
\n",
"
78.0
\n",
"
PA
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Student ID Gender Project Phase 1 Project Phase 2 Mid-Semester Test \\\n",
"27 128 Female 20.0 30.00 84 \n",
"28 129 Male 20.0 30.00 64 \n",
"26 127 Female 20.0 35.00 84 \n",
"14 115 Male 19.5 26.00 100 \n",
"13 114 Male 20.0 22.75 85 \n",
"\n",
" Final Exam Grade \n",
"27 91.0 PA \n",
"28 86.0 PA \n",
"26 83.0 PA \n",
"14 79.0 PA \n",
"13 78.0 PA "
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.sort_values(by='Final Exam', ascending=False).head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Summary"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The function `shape` counts the number of rows and columns. Thus, number of rows and columns can be obtained as `grade.shape[0]` and `grade.shape[1]` respectively."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(40, 7)"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`info()` provides a concise summary of the columns."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"RangeIndex: 40 entries, 0 to 39\n",
"Data columns (total 7 columns):\n",
" # Column Non-Null Count Dtype \n",
"--- ------ -------------- ----- \n",
" 0 Student ID 40 non-null int64 \n",
" 1 Gender 37 non-null object \n",
" 2 Project Phase 1 40 non-null float64\n",
" 3 Project Phase 2 37 non-null float64\n",
" 4 Mid-Semester Test 40 non-null int64 \n",
" 5 Final Exam 36 non-null float64\n",
" 6 Grade 40 non-null object \n",
"dtypes: float64(3), int64(2), object(2)\n",
"memory usage: 2.3+ KB\n"
]
}
],
"source": [
"grades.info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`describe()` generates descriptive statistics. Keep in mind that this function excludes *null* values."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
"
],
"text/plain": [
" Student ID Gender Project Phase 1 Project Phase 2 \\\n",
"count 40.000000 37 40.000000 37.000000 \n",
"unique NaN 4 NaN NaN \n",
"top NaN Male NaN NaN \n",
"freq NaN 22 NaN NaN \n",
"mean 120.500000 NaN 16.987500 23.750000 \n",
"std 11.690452 NaN 5.964626 7.509716 \n",
"min 101.000000 NaN 0.000000 0.000000 \n",
"25% 110.750000 NaN 17.687500 20.000000 \n",
"50% 120.500000 NaN 19.500000 25.500000 \n",
"75% 130.250000 NaN 20.000000 30.000000 \n",
"max 140.000000 NaN 20.000000 35.000000 \n",
"\n",
" Mid-Semester Test Final Exam Grade \n",
"count 40.000000 36.000000 40 \n",
"unique NaN NaN 2 \n",
"top NaN NaN PA \n",
"freq NaN NaN 29 \n",
"mean 72.100000 56.055556 NaN \n",
"std 19.664885 20.520296 NaN \n",
"min 26.000000 6.000000 NaN \n",
"25% 59.750000 45.500000 NaN \n",
"50% 76.000000 60.000000 NaN \n",
"75% 86.000000 71.750000 NaN \n",
"max 100.000000 91.000000 NaN "
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.describe(include='all')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also use the following functions to summarize the data. All these methods exclude null values by default.\n",
"- `count()` to count the number of elements.\n",
"- `value_counts()` to get a frequency distribution of unique values.\n",
"- `nunique()` to get the number of unique values.\n",
"- `mean()` to calculate the arithmetic mean of a given set of numbers.\n",
"- `std()` to calculate the sample standard deviation of a given set of numbers.\n",
"- `max()` to return the maximum of the provided values.\n",
"- `min()` to return minimum of the provided values."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Student ID 40\n",
"Gender 37\n",
"Project Phase 1 40\n",
"Project Phase 2 37\n",
"Mid-Semester Test 40\n",
"Final Exam 36\n",
"Grade 40\n",
"dtype: int64"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.count()"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Gender\n",
"Male 22\n",
"Female 13\n",
"M 1\n",
"F 1\n",
"Name: count, dtype: int64"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades['Gender'].value_counts()"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"4"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades['Gender'].nunique()"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"np.float64(56.05555555555556)"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades['Final Exam'].mean()"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"np.float64(19.66488475195551)"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades['Mid-Semester Test'].std()"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"np.float64(35.0)"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades['Project Phase 2'].max()"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"np.float64(0.0)"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades['Project Phase 1'].min()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Selecting specific columns"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When there are many columns, we may prefer to select only the ones we are interested in. Let's say we want to select the \"Gender\" and the \"Grade\" columns only."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Gender
\n",
"
Grade
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
Male
\n",
"
PA
\n",
"
\n",
"
\n",
"
1
\n",
"
Female
\n",
"
PA
\n",
"
\n",
"
\n",
"
2
\n",
"
Male
\n",
"
NN
\n",
"
\n",
"
\n",
"
3
\n",
"
Male
\n",
"
PA
\n",
"
\n",
"
\n",
"
4
\n",
"
Male
\n",
"
PA
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Gender Grade\n",
"0 Male PA\n",
"1 Female PA\n",
"2 Male NN\n",
"3 Male PA\n",
"4 Male PA"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# notice the double brackets\n",
"grades[['Gender', 'Grade']].head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also get a single column as a Series object from a data frame."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
"# notice the single bracket\n",
"gender_series = grades['Gender']"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"pandas.core.series.Series"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(gender_series)"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0 Male\n",
"1 Female\n",
"2 Male\n",
"3 Male\n",
"4 Male\n",
"Name: Gender, dtype: object"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gender_series.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Filtering for particular values"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can subset the data based on a particular criterion. Let's say we want the list of students who have failed this class."
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Student ID
\n",
"
Gender
\n",
"
Project Phase 1
\n",
"
Project Phase 2
\n",
"
Mid-Semester Test
\n",
"
Final Exam
\n",
"
Grade
\n",
"
\n",
" \n",
" \n",
"
\n",
"
2
\n",
"
103
\n",
"
Male
\n",
"
0.00
\n",
"
0.0
\n",
"
78
\n",
"
15.0
\n",
"
NN
\n",
"
\n",
"
\n",
"
8
\n",
"
109
\n",
"
M
\n",
"
18.00
\n",
"
23.0
\n",
"
50
\n",
"
33.0
\n",
"
NN
\n",
"
\n",
"
\n",
"
12
\n",
"
113
\n",
"
Female
\n",
"
0.00
\n",
"
NaN
\n",
"
67
\n",
"
NaN
\n",
"
NN
\n",
"
\n",
"
\n",
"
16
\n",
"
117
\n",
"
NaN
\n",
"
15.75
\n",
"
10.0
\n",
"
81
\n",
"
34.0
\n",
"
NN
\n",
"
\n",
"
\n",
"
17
\n",
"
118
\n",
"
Male
\n",
"
12.50
\n",
"
10.0
\n",
"
30
\n",
"
22.0
\n",
"
NN
\n",
"
\n",
"
\n",
"
18
\n",
"
119
\n",
"
Male
\n",
"
17.50
\n",
"
20.0
\n",
"
61
\n",
"
31.0
\n",
"
NN
\n",
"
\n",
"
\n",
"
21
\n",
"
122
\n",
"
Female
\n",
"
20.00
\n",
"
23.0
\n",
"
37
\n",
"
25.0
\n",
"
NN
\n",
"
\n",
"
\n",
"
29
\n",
"
130
\n",
"
Male
\n",
"
19.50
\n",
"
13.0
\n",
"
62
\n",
"
39.0
\n",
"
NN
\n",
"
\n",
"
\n",
"
30
\n",
"
131
\n",
"
Male
\n",
"
0.00
\n",
"
NaN
\n",
"
60
\n",
"
NaN
\n",
"
NN
\n",
"
\n",
"
\n",
"
31
\n",
"
132
\n",
"
Female
\n",
"
17.50
\n",
"
20.0
\n",
"
42
\n",
"
47.0
\n",
"
NN
\n",
"
\n",
"
\n",
"
34
\n",
"
135
\n",
"
Male
\n",
"
20.00
\n",
"
30.0
\n",
"
61
\n",
"
6.0
\n",
"
NN
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Student ID Gender Project Phase 1 Project Phase 2 Mid-Semester Test \\\n",
"2 103 Male 0.00 0.0 78 \n",
"8 109 M 18.00 23.0 50 \n",
"12 113 Female 0.00 NaN 67 \n",
"16 117 NaN 15.75 10.0 81 \n",
"17 118 Male 12.50 10.0 30 \n",
"18 119 Male 17.50 20.0 61 \n",
"21 122 Female 20.00 23.0 37 \n",
"29 130 Male 19.50 13.0 62 \n",
"30 131 Male 0.00 NaN 60 \n",
"31 132 Female 17.50 20.0 42 \n",
"34 135 Male 20.00 30.0 61 \n",
"\n",
" Final Exam Grade \n",
"2 15.0 NN \n",
"8 33.0 NN \n",
"12 NaN NN \n",
"16 34.0 NN \n",
"17 22.0 NN \n",
"18 31.0 NN \n",
"21 25.0 NN \n",
"29 39.0 NN \n",
"30 NaN NN \n",
"31 47.0 NN \n",
"34 6.0 NN "
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades[grades['Grade'] == 'NN']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We could also filter based on multiple conditions. Let's see who failed the class even though their final exam scores were higher than 45, but let's only look at a few of the columns using the `loc` method."
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Student ID
\n",
"
Final Exam
\n",
"
Grade
\n",
"
\n",
" \n",
" \n",
"
\n",
"
31
\n",
"
132
\n",
"
47.0
\n",
"
NN
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Student ID Final Exam Grade\n",
"31 132 47.0 NN"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.loc[(grades['Grade'] == 'NN') & (grades['Final Exam'] > 45), ['Student ID', 'Final Exam', 'Grade']]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's select the last 2 columns and all the rows between 10 and 15 using the `iloc` method. Notice the use of the negative sign for negative indexing."
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Final Exam
\n",
"
Grade
\n",
"
\n",
" \n",
" \n",
"
\n",
"
10
\n",
"
52.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
11
\n",
"
NaN
\n",
"
PA
\n",
"
\n",
"
\n",
"
12
\n",
"
NaN
\n",
"
NN
\n",
"
\n",
"
\n",
"
13
\n",
"
78.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
14
\n",
"
79.0
\n",
"
PA
\n",
"
\n",
"
\n",
"
15
\n",
"
52.0
\n",
"
PA
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Final Exam Grade\n",
"10 52.0 PA\n",
"11 NaN PA\n",
"12 NaN NN\n",
"13 78.0 PA\n",
"14 79.0 PA\n",
"15 52.0 PA"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.iloc[10:16, -2:]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Group By"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pandas allows aggregation of data into groups to run calculations over each group. Let's group the data by `Grade`:"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
"
],
"text/plain": [
" Student ID Project Phase 1 Project Phase 2 Mid-Semester Test \\\n",
"Gender Grade \n",
"F PA 1 1 1 1 \n",
"Female NN 3 3 2 3 \n",
" PA 10 10 9 10 \n",
"M NN 1 1 1 1 \n",
"Male NN 6 6 5 6 \n",
" PA 16 16 16 16 \n",
"\n",
" Final Exam \n",
"Gender Grade \n",
"F PA 1 \n",
"Female NN 2 \n",
" PA 9 \n",
"M NN 1 \n",
"Male NN 5 \n",
" PA 15 "
]
},
"execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.groupby(['Gender','Grade']).count()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notice that there are unexpected multiple levels defining the same category (e.g., *F* also represents *Female*). We will take care of this in Data Manipulation section.\n",
"\n",
"So, how do we make the above grouping a data frame? For this, we use the `reset_index` method."
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Gender
\n",
"
Grade
\n",
"
Student ID
\n",
"
Project Phase 1
\n",
"
Project Phase 2
\n",
"
Mid-Semester Test
\n",
"
Final Exam
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
F
\n",
"
PA
\n",
"
1
\n",
"
1
\n",
"
1
\n",
"
1
\n",
"
1
\n",
"
\n",
"
\n",
"
1
\n",
"
Female
\n",
"
NN
\n",
"
3
\n",
"
3
\n",
"
2
\n",
"
3
\n",
"
2
\n",
"
\n",
"
\n",
"
2
\n",
"
Female
\n",
"
PA
\n",
"
10
\n",
"
10
\n",
"
9
\n",
"
10
\n",
"
9
\n",
"
\n",
"
\n",
"
3
\n",
"
M
\n",
"
NN
\n",
"
1
\n",
"
1
\n",
"
1
\n",
"
1
\n",
"
1
\n",
"
\n",
"
\n",
"
4
\n",
"
Male
\n",
"
NN
\n",
"
6
\n",
"
6
\n",
"
5
\n",
"
6
\n",
"
5
\n",
"
\n",
"
\n",
"
5
\n",
"
Male
\n",
"
PA
\n",
"
16
\n",
"
16
\n",
"
16
\n",
"
16
\n",
"
15
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Gender Grade Student ID Project Phase 1 Project Phase 2 \\\n",
"0 F PA 1 1 1 \n",
"1 Female NN 3 3 2 \n",
"2 Female PA 10 10 9 \n",
"3 M NN 1 1 1 \n",
"4 Male NN 6 6 5 \n",
"5 Male PA 16 16 16 \n",
"\n",
" Mid-Semester Test Final Exam \n",
"0 1 1 \n",
"1 3 2 \n",
"2 10 9 \n",
"3 1 1 \n",
"4 6 5 \n",
"5 16 15 "
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.groupby(['Gender','Grade']).count().reset_index()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Data Manipulation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Handling missing values"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dealing with missing values is a time consuming but crucial task. We should first identify the missing values and then try to determine why they are missing.\n",
"\n",
"There are two basic strategies to handle missing values:\n",
"1. Remove rows and columns with missing values.\n",
"2. Impute missing values, replacing them with predefined values.\n",
"\n",
"Missing values are a bit complicated in Python as they can be denoted by either \"na\" or \"null\" in Pandas (both mean the same thing). Furthermore, `NumPy` denotes missing values as \"NaN\" (that is, \"not a number\")."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First, let's count the number of missing values in each column."
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Student ID 0\n",
"Gender 3\n",
"Project Phase 1 0\n",
"Project Phase 2 3\n",
"Mid-Semester Test 0\n",
"Final Exam 4\n",
"Grade 0\n",
"dtype: int64"
]
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.isna().sum()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The function `dropna()` drops rows with at least one missing value."
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(33, 7)"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades_no_na = grades.dropna()\n",
"grades_no_na.shape"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Student ID 0\n",
"Gender 0\n",
"Project Phase 1 0\n",
"Project Phase 2 0\n",
"Mid-Semester Test 0\n",
"Final Exam 0\n",
"Grade 0\n",
"dtype: int64"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades_no_na.isna().sum()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's look at the `Gender` column. "
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Gender\n",
"Male 22\n",
"Female 13\n",
"M 1\n",
"F 1\n",
"Name: count, dtype: int64"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades['Gender'].value_counts()"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"np.int64(3)"
]
},
"execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades['Gender'].isna().sum()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So this column has 3 missing values. Let's use the `fillna()` method to replace these missing values with \"Unknown\":"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"Gender\n",
"Male 22\n",
"Female 13\n",
"Unknown 3\n",
"M 1\n",
"F 1\n",
"Name: count, dtype: int64"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades['Gender'].fillna('Unknown', inplace=True)\n",
"\n",
"# this also works:\n",
"# ## grades[['Gender']] = grades[['Gender']].fillna('Unknown')\n",
"\n",
"grades['Gender'].value_counts()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's go back and check the missing values in our modified data."
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Student ID 0\n",
"Gender 0\n",
"Project Phase 1 0\n",
"Project Phase 2 3\n",
"Mid-Semester Test 0\n",
"Final Exam 4\n",
"Grade 0\n",
"dtype: int64"
]
},
"execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.isna().sum()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Some assessments have missing values. We know for a fact that these missing values are due to students missing these assessments, and therefore we set these missing values to zero."
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {},
"outputs": [],
"source": [
"grades = grades.fillna(0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can now confirm that there are no more missing values."
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Student ID 0\n",
"Gender 0\n",
"Project Phase 1 0\n",
"Project Phase 2 0\n",
"Mid-Semester Test 0\n",
"Final Exam 0\n",
"Grade 0\n",
"dtype: int64"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.isna().sum()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Handling irregular cardinality"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The cardinality is the number of different values we have for a particular feature. Sometimes we might have unexpected number of distinct values for a feature and this is called **irregular cardinality**. Let's start by counting the unique number of observations for each column by using the `nunique()` method:"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Student ID 40\n",
"Gender 5\n",
"Project Phase 1 15\n",
"Project Phase 2 19\n",
"Mid-Semester Test 34\n",
"Final Exam 31\n",
"Grade 2\n",
"dtype: int64"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades.nunique()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First, it is clear that all the values in the `Student ID` column are unique for each student. Therefore, we will remove it since **ID-type columns are not useful in statistical modeling**."
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [],
"source": [
"grades.drop(columns=['Student ID'], inplace=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, we notice `Gender` has a cardinality of 4, which is more than expected. This issue often arises when multiple levels are used to represent the same thing (e.g., *M, male, MALE, Male* all represent the \"male\" gender). For Gender, in this case, there should be only three different values: `Female`, `Male`, and `Unknown`. Let's print the unique elements in Gender using the `value_counts()` method:"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Gender\n",
"Male 22\n",
"Female 13\n",
"Unknown 3\n",
"M 1\n",
"F 1\n",
"Name: count, dtype: int64"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades['Gender'].value_counts()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's use the `replace()` method to replace all the problematic levels to a standard set of levels."
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [],
"source": [
"grades.replace(['M', 'male'], 'Male', inplace=True)"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [],
"source": [
"grades.replace(['F', 'female'], 'Female', inplace=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's check again the unique elements in Gender."
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Gender\n",
"Male 23\n",
"Female 14\n",
"Unknown 3\n",
"Name: count, dtype: int64"
]
},
"execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"grades['Gender'].value_counts()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Detecting outliers"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Outliers are values that significantly differ from other values and they lie far away from the central tendency of a variable. The best way to visually detect outliers is by using boxplots. Pandas allows for direct visualization of a data frame's columns.\n",
"\n",
"First, let's prepare the plotting environment as Pandas' plotting functions actually use Matplotlib."
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"%matplotlib inline \n",
"%config InlineBackend.figure_format = 'retina'\n",
"plt.style.use(\"seaborn-v0_8\")"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABUEAAAOkCAYAAACIwfWrAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/TGe4hAAAACXBIWXMAAB7CAAAewgFu0HU+AAB5uklEQVR4nOzdeZyVdf3//+cs7MOqkAqftE+fSHEps6w0NdQsM0tMrTQXUhNTv2am6cds1cLKyoWi+iiKGbnkvptKkuZWmSjuZAouoOwgwzAzvz/4MUlsKgPnzNv7/XbzxuGc6zrzOmfOXB4ec53rqmltbW0NAAAAAEChais9AAAAAADA2iSCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFq6/0AKWaPn1upUfgLaBv3+6pr6/L4sXNmTlzQaXHAVhjtmtAaWzXgNLYrrEu9e/fs93uy56g0IHV1NQs8ydAR2e7BpTGdg0oje0aHZUICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICirfMIetppp+Xd7353rrjiitUu29TUlLFjx2afffbJ1ltvnfe+973ZY4898rOf/SyzZs1a7fqPP/54TjjhhOy4447ZYost8pGPfCQjRozInXfe2Q6PBAAAAADoCOrX5Rf74x//mIsvvvh1LdvY2JjDDjss99133zLXP/XUU3nqqadyxRVX5LzzzsvgwYNXuP5tt92WY489Nk1NTW3XTZ8+PXfccUfuuOOOHHjggfnmN7/55h8MAAAAANAhrLM9QW+//fZ89atfTUtLy+ta/uSTT859992XTp065bjjjsttt92WCRMm5LTTTkvv3r0zbdq0jBgxIgsWLFhu3UmTJuVrX/tampqasuWWW+aiiy7KPffck8svvzy77rprkuSiiy563UEWAAAAAOi41noEbWlpydlnn52jjjpqmb0yV2XixIm5/vrrkySnnHJKRowYkUGDBmXAgAHZd999c8EFF6RTp06ZOnVqxo4du9z6Z511VhYuXJiNN944F154Ybbddtv07ds3W265Zc4999x84hOfSJKcffbZmTdvXvs9WAAAAACg6qzVCDphwoR85jOfyahRo9LS0pLNN9/8da03ZsyYJMmgQYOy3377LXf7kCFDstdeeyVJLrvssmVue/rppzN+/PgkyRFHHJEePXosc3tNTU1OOumk1NbWZtasWbn11lvf4KMCAAAAADqStRpBDzvssDzxxBPp1KlTjjnmmPz85z9f7Tqtra2ZMGFCkmTo0KGpq6tb4XK77LJLkmTKlCl57LHH2q5fum5NTU2GDh26wnU33HDDbLbZZkmWHKcUAAAAACjXWo2gNTU12W233XL11Vfn6KOPTm3t6r/clClTMmfOnCRZ5Z6jQ4YMabv88MMPt11+9NFHkyQbbbRR+vXrt9r1H3nkkdXOBAAAAAB0XGv17PA33nhj3vGOd7yhdaZOndp2edCgQStdrn///unUqVOampoyZcqU5dZf1brJkkiaJC+++GIWL16c+vq1+lQAAAAAABWyVsvfGw2gSTJz5sy2y7169VrpcrW1tenRo0dmzZrVtufoa9fv3bv3Kr9Oz549kyz5+P2cOXNWudfom9G3b/fU1NS0633Cf6qtrWn7s1+/HqtZGqD62a4B7eHyyy/Ld77zncydO7fSo2RRU3NebVycbl3q07nTig/1tS717Nkz3/3ud/PZz+5T6VGADsr7NTqqqtv9sbGxse1y165dV7lsly5dlltn6eWlt63Ma+970aJFb3jO1amvr/wbHN46ampqUlcnugPlsF0D1sSZZ565zHkDqsG8Sg/wGmeeeeYKT0AL8EZ4v0ZHU3URdGUnQlpX67eXxYub7QnKWldbW5Oampq0trampaW10uMArDHbNaA9fP3rX8+3v/3tqtgTdObcxrS0tKa2tiZ9e656R411oWfPnjn++OPT3NxS6VGADsr7Ndalurr2O51R1UXQbt26tV1+7R6eK7L09tfu1bl0/dXt3blw4cK2y6vba/TNmDlzQbvfJ/ynfv16pK6uJi0trZkxY36lxwFYY7ZrQHsYOnT3DB26e6XHSJKc8Mu788rshVmvd9f8+MjtKj1OG9tY4M3yfo11qX//nu12X2v17PBvxmuPA7qq39y2tLRk/vwlP2x9+/Ztu37psT5X91vfpccRraurW+3xQwEAAACAjqvqIugmm2zSdvn5559f6XLTp09PU1NTkmTDDTdsu37pyZhWtW6SvPDCC0mSt73tbamtrbqnAQAAAABoJ1VX/wYMGJA+ffokSSZNmrTS5R555JG2y0OGDGm7PHjw4CTJc889l3nzVn748aX3vdlmm63JuAAAAABAlau6CJokO+20U5Jk/PjxaW1d8UF2b7/99iRJ//79s+mmmy63bnNzc8aPH7/CdV944YU8+uijSZIddtihvcYGAAAAAKpQVUbQYcOGJUkmT56c3/3ud8vdPmnSpFx11VVJkoMPPniZs7D/13/9V7bZZpskyTnnnLPcsUFbW1szcuTItLS0pG/fvvnMZz6zlh4FAAAAAFANqjKCfvjDH87OO++cJDn99NPzs5/9LM8991ymT5+eyy+/PMOHD09TU1MGDRqUL3zhC8utf/LJJ6e2tjbPPPNM9t9///z5z3/OjBkz8sgjj+SYY47JTTfdlCQ55phj0r1793X62AAAAACAdau+0gOszMiRI3PooYdm4sSJGT16dEaPHr3M7euvv37OP//8NDQ0LLfulltumdNPPz2nnnpqnnjiiRx66KHLLTN8+PAccMABa21+AAAAAKA6VG0E7d27d8aNG5dx48bl2muvzdNPP51FixZl4MCBGTp0aA4//PCst956K11/7733zuabb57zzjsv9957b1555ZV07949W2yxRfbff//suuuu6/DRAAAAAACVUtO6sjMPsUamT5+7+oVgDfXr1yN1dbVpbm7JjBnzKz0OwBqzXQNKc8Iv784rsxdmvd5d8+Mjt6v0OABrzPs11qX+/Xu2231V5TFBAQAAAADaiwgKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAULT6Sg8AAAAAwMpdc82VOeOM0zNv3rxKj5LFzS15tXFxunWpT31d5feta2hoyEknfTN77rlXpUehyomgAAAAAFVs1Kiz8uSTT1R6jGVUPsf+26hRZ4mgrJYICgAAAFDFjj76qxk58rSq2BN09vxFaWlpTW1tTXr36FzpcdLQ0JCjjjq20mPQAYigAAAAAFVszz33qpo9HU/45d15ZfbCrNe7a3585HaVHgdet8ofvAEAAAAAYC0SQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDR6is9AAAA0PHd/9i0XDVhchYuaq70KFVl9rzGJMnMOQtz/Ki7KjxNdenauS7DdvjvvH/TAZUeBYC3ABEUAABYY1dNmJwXXllQ6TGqVktrMnNuY6XHqDpXTpgsggKwToigAADAGlu6B2hNTdKnoUuFp6keTc0tWdi4OF271KdTnaORLTVrXmNaW2PPYQDWGREUAABoN30auuTMo7av9BhVo1+/Hqmrq01zc0tmzJhf6XGqxvGj7rJnLADrlF9FAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFq6/0AKvzl7/8Jb/97W/zj3/8I7NmzUqPHj2y6aabZtiwYfn0pz+d2toVd9ympqaMGzcu11xzTZ5++um0trZm4MCB2XXXXTN8+PD06dNn3T4QAAAAAKAiqjqCnnHGGTn//POXuW7WrFm55557cs899+Taa6/NqFGj0rVr12WWaWxszGGHHZb77rtvmeufeuqpPPXUU7niiity3nnnZfDgwWv9MQAAAAAAlVW1H4e/7LLL2gLoe9/73lxwwQW566678oc//CGf+tSnkiR//vOf873vfW+5dU8++eTcd9996dSpU4477rjcdtttmTBhQk477bT07t0706ZNy4gRI7JgwYJ1+pgAAAAAgHWvaiPor3/96yTJ4MGDM3bs2Hz4wx/O+uuvny222CJnnnlmWwi94oor8tJLL7WtN3HixFx//fVJklNOOSUjRozIoEGDMmDAgOy777654IIL0qlTp0ydOjVjx45d9w8MAAAAAFinqjKCzpo1K88++2yS5NOf/nS6dOmy3DJf+MIXkiStra156KGH2q4fM2ZMkmTQoEHZb7/9lltvyJAh2WuvvZIs2dsUAAAAAChbVUbQ157saPHixStcplOnTsst39ramgkTJiRJhg4dmrq6uhWuu8suuyRJpkyZkscee6xdZgYAAAAAqlNVRtBevXplk002SZJcf/31WbRo0XLL/OEPf0iyJIZuueWWSZZEzTlz5iRJNt9885Xe/5AhQ9ouP/zww+01NgAAAABQhaoygibJ8ccfn9ra2jz55JMZPnx47rnnnrzyyit57LHHcuqpp+aSSy5Jkhx55JEZMGBAkmTq1Klt6w8aNGil992/f/+2PUmnTJmyFh8FAAAAAFBp9ZUeYGV22223nHvuufnRj36UBx54IAcffPAyt2+44Yb56le/2nZ8zySZOXNm2+VevXqt9L5ra2vTo0ePzJo1q23PUQAAAACgTFUbQZNk3rx56d69+wpve+WVV/K3v/0tO+64Y/r165ckaWxsbLu9a9euq7zvpSdbeu067alv3+6pqalZK/cNS9XW1rT92a9fjwpPA7DmbNeg4/Lzu2KelxXzvEAZ/PzSkVRtBD3ttNNy0UUXJVlyJvgDDzww//Vf/5WZM2fmlltuyc9//vNccskleeCBBzJ27Nisv/76Kz0RUiXU11fPLJSvpqYmdXWiO1AO2zXo2OrqqvaoWxVju7ZyXi/Qcfn5pSOpygh69913twXQ448/Pl/+8pfbbnvb296WAw88MB/4wAfyhS98IU8//XR++tOf5gc/+EG6devWttzq9vBcevvq9hh9sxYvbrYnKGtdbW1Nampq0trampaW1kqPA7DGbNegDM3NLZUeoWrYrq2e1wt0XH5+WdvaM7RXZQS99NJLkyw57uehhx66wmU23XTTfP7zn8/555+fq6++OqeeeuoyxwGdO3fuSu+/paUl8+fPT5L07du3HSf/t5kzF6yV+4XX6tevR+rqatLS0poZM+ZXehyANWa7Bh3X0sDn53dZtmsr5vUCZfDzy9rWv3/Pdruvqtxv+ZlnnkmSvOc971nlR9y33XbbJMnixYvz7LPPZpNNNmm77fnnn1/petOnT09TU1OSJaEVAAAAAChXVUbQpYFy0aJFr3udRYsWZcCAAenTp0+SZNKkSStd9pFHHmm7PGTIkDc3JAAAAADQIVRlBH3HO96RJPnb3/62yhD6wAMPJEnq6+uz8cYbJ0l22mmnJMn48ePT2rriY+7cfvvtSZL+/ftn0003bbe5AQAAAIDqU5UR9JOf/GSSZNasWfnZz362wmWeeuqp/O53v0uS7Ljjjm3HAx02bFiSZPLkyW23v9akSZNy1VVXJUkOPvhgJy8CAAAAgMJVZQTdfffd86EPfShJcv755+eYY47J/fffnxkzZmTKlCm56KKLsv/++2fBggXp2bNnTjzxxLZ1P/zhD2fnnXdOkpx++un52c9+lueeey7Tp0/P5ZdfnuHDh6epqSmDBg3KF77whYo8PgAAAABg3anKs8PX1NTknHPOybHHHpu77747t9xyS2655Zblllt//fVz9tlnt318fqmRI0fm0EMPzcSJEzN69OiMHj16ufXOP//8NDQ0rNXHAQAAAABUXlVG0CTp1atXzjvvvNxyyy256qqr8vDDD2fWrFnp2rVrNtlkk+y888454IAD0rt37+XW7d27d8aNG5dx48bl2muvzdNPP51FixZl4MCBGTp0aA4//PCst956FXhUAAAAAMC6VrURNElqa2vziU98Ip/4xCfe8LqdOnXKQQcdlIMOOmgtTAYAAAAAdBRVeUxQAAAAAID2IoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAotVXegAAAKAMH5g5KR/816OZfMLllR6lajxTU5PUJGlNWlpbKz1O1Thg3qLc22uzPNVz60qPAsBbhAgKAAC0iy4ti9LQND+LZ86v9ChUuYYseb0AwLoiggIAAO2isbZz5nXqkT4NnSs9StWotSfoCs2atyiNtV4nAKw7IigAANAu7u87JE+9feucedT2lR6lavTr1yN1dbVpbm7JjBn2kF3q+FF3ZebcxvSt9CAAvGU4MRIAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAilZf6QEAAAAAqs39j03LVRMmZ+Gi5kqPUlVmz2tMksycszDHj7qrwtNUl66d6zJsh//O+zcdUOlRWAERFAAAAOA/XDVhcl54ZUGlx6haLa3JzLmNlR6j6lw5YbIIWqVEUAAAAID/sHQP0JqapE9DlwpPUz2amluysHFxunapT6c6R1lcata8xrS2xp7DVUwEBQAAAFiJPg1dcuZR21d6jKrRr1+P1NXVprm5JTNmzK/0OFXj+FF32TO2ykn2AAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEWrr/QAqzNv3ryMHTs2f/zjH/Pss8+msbExG220UXbaaacceuihedvb3rbSdRcsWJAxY8bkpptuyrPPPpu6urpsvPHG2X333XPQQQela9eu6/CRAAAAAACVUNUR9LHHHsvhhx+eadOmLXP9M888k2eeeSZXX311fvOb32SrrbZabt2ZM2fmgAMOyNNPP73M9ZMmTcqkSZNy5ZVX5oILLlhlRAUAAAAAOr6q/Tj89OnTc/DBB2fatGnp2bNnvvWtb+X222/PLbfckpNPPjndunXLrFmzctRRR2XevHnLrNvS0pIjjzwyTz/9dHr06JFvf/vbufPOO3PHHXfkhBNOSJcuXTJ58uQcffTRaWlpqdAjBAAAAADWhaqNoCNHjsysWbPSvXv3jBkzJgcccEAGDhyYjTfeOIccckh+/vOfJ0mmTZuWq6++epl1b7nllvz9739Pkvz85z/P/vvvn7e97W3ZaKONcthhh+Xss89Okjz00EO5/vrr1+njAgAAAADWraqMoC+//HJuvPHGJMmRRx6ZLbfccrllPvrRj2aTTTZJp06d8sgjjyxz25gxY5IkH/jAB7LjjjuucN3tttsuSXLppZe29/gAAAAAQBWpymOC3nzzzWlubk63bt3yxS9+caXLXXPNNenSpcsy182aNSv/+Mc/kiS77LLLStfdZZddcvfdd+eBBx7I7Nmz07t37/YZHgAAAACoKlW5J+hDDz2UJNlyyy3TvXv3ZW5rampqu/yfATRZcjKl1tbWJMnmm2++0q+x2WabJVly/NBJkyat8cwAAAAAQHWqyj1Bn3zyySTJJptskiS57bbb8tvf/jYPPvhgFixYkP79+2fXXXfNkUceudzZ3adOndp2edCgQSv9GgMHDmy7PGXKlHacHgAAAACoJlUZQadNm5Yk6d27d771rW/lkksuWeb26dOnZ9y4cbnxxhszevTobL311m23zZw5s+1yr169Vvo1Ghoa2i7PmTOnvUZv07dv99TU1LT7/cJr1dbWtP3Zr1+PCk8DsOZs16Dj8vO7Yp6XFfO80BF4na6Y52XFPC/Vryoj6Pz585MkV111VaZPn573v//9Oe6447Lllltm/vz5ufHGG/OTn/wks2bNyle+8pVcffXVGTBgQJKksbGx7X66du260q/x2tteu057qa+va/f7hJWpqalJXZ3oDpTDdg06trq6qjzqVkXZrq2c1wsdgdfp8mzXVs7rpTpVZQRduHBhkiV7fH7wgx/Meeedl06dOiVZchzQAw44IIMHD85BBx2UGTNm5Ne//nW++c1vJknq6qojPi5e3GxPUNa62tqa1NTUpLW1NS0trZUeB2CN2a5Bx/XK7IVtfx7yvZsrPE31aGxqzquNi9OtS326dKqOf6tUg5lzFrZdbm5uqeAk8Pp4nf6b92ur5/XSftozKFdlBO3atWsWLFiQJDnppJPaAuhrfeADH8hOO+2UO+64I7fccktbBO3WrVvbMo2NjamvX/FDXBpal3699jZz5oJ2v0/4T/369UhdXU1aWlozY8b8So8DsMZs16AMS4Mo/zZvQVPmpWn1C77FdK6vtb2nai0NfN6XLMv7tRXzelk7+vfv2W73VZURtEePHlmwYEF69uyZIUOGrHS5bbfdNnfccUdeeumlzJs3Lw0NDcscB3TevHnp0WPFx2GYO3du2+W+ffu23/AAAPAWtO1mA3Lfo0uO7d+3Z5cKT1M9Zs9rTEtrUluT9G7wvLxW1851GbbDf1d6DADeIqoygg4aNCjTp09Ply6rfpPw2pMbLVy4MA0NDW1nlE+WnCn+P88ev9Tzzz/fdnnDDTdcs4EBAOAtbsRntsiIz1R6iupzwi/vziuzF6Zvr6758ZHbVXocAHjLqsojtW622WZJkhkzZmTevHkrXe7ll19OknTq1Cn9+vVLkrzrXe9qOxbno48+utJ1J02alGTJgXw33XTTdpkbAAAAAKg+VRlBP/rRjyZJWlpa8sc//nGly911111Jkq222iq1tUseSkNDQ7bZZpskye23377SdZfettVWW6VPnz7tMDUAAAAAUI2qMoJuv/32GThwYJLkZz/7Wdsen69100035YEHHkiSDBs2bJnb9tprryTJn//854wfP365dcePH5+77747SXLIIYe03+AAAAAAQNWpyghaX1+f733ve6mtrc2LL76Y/fbbL1dffXVeeumlTJ06NaNHj87Xv/71JMl73/ve7L333susv/fee7edUOnYY4/N+eefnxdffDEvvvhizj///Bx77LFJkve85z35xCc+sW4fHAAAAACwTlXliZGS5CMf+Uh+8pOf5H//938zderUnHjiicsts/nmm+ess85KXV3dMtfX1dXl3HPPzcEHH5znnnsuZ5xxRs4444xllnnHO96R0aNHt32MHgAAAAAoU9VG0CTZY489svXWW2fMmDG588478+KLL6ZLly55xzvekU9/+tP57Gc/m65du65w3YEDB+bqq6/OBRdckJtvvjnPPfdcmpubs/HGG+fjH/94hg8fnh49eqzjRwQAAAAArGtVHUGTZKONNsopp5ySU0455Q2v26NHjxx11FE56qij1sJkAAAAAEBH4LPgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGj1lR4AAAAAoBp9YOakfPBfj2byCZdXepSq8UxNTVKTpDVpaW2t9DhV44B5i3Jvr83yVM+tKz0KKyGCAgAAAKxAl5ZFaWian8Uz51d6FKpcQ5a8XqheIigAAADACjTWds68Tj3Sp6FzpUepGrX2BF2hWfMWpbHW66SaiaAAAAAAK3B/3yF56u1b58yjtq/0KFWjX78eqaurTXNzS2bMsIfsUsePuisz5zamb6UHYaWcGAkAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNFEUAAAAACgaCIoAAAAAFA0ERQAAAAAKJoICgAAAAAUTQQFAAAAAIomggIAAAAARRNBAQAAAICiiaAAAAAAQNE6XARdsGBBPv7xj+fd7353zjnnnJUu19TUlLFjx2afffbJ1ltvnfe+973ZY4898rOf/SyzZs1adwMDAAAAABVVX+kB3qiRI0fmmWeeWeUyjY2NOeyww3Lfffctc/1TTz2Vp556KldccUXOO++8DB48eC1OCgAAAABUgw61J+j48eNzySWXrHa5k08+Offdd186deqU4447LrfddlsmTJiQ0047Lb179860adMyYsSILFiwYB1MDQAAAABUUoeJoDNmzMgpp5yy2uUmTpyY66+/PklyyimnZMSIERk0aFAGDBiQfffdNxdccEE6deqUqVOnZuzYsWt7bAAAAACgwjpMBP3mN7+Zl19+OXvvvfcqlxszZkySZNCgQdlvv/2Wu33IkCHZa6+9kiSXXXZZu88JAAAAAFSXDhFBL7vsstx2220ZOHDgKvcGbW1tzYQJE5IkQ4cOTV1d3QqX22WXXZIkU6ZMyWOPPdb+AwMAAAAAVaPqI+izzz6bH/zgB6mpqckPf/jDNDQ0rHTZKVOmZM6cOUmSzTfffKXLDRkypO3yww8/3H7DAgAAAABVp6ojaHNzc0488cQsWLAgBx10UD74wQ+ucvmpU6e2XR40aNBKl+vfv386deqUZEk4BQAAAADKVdUR9Fe/+lX+/ve/553vfGeOP/741S4/c+bMtsu9evVa6XK1tbXp0aNHkrTtOQoAAAAAlKm+0gOszMMPP5xf/OIXqa+vz49+9KN06dJltes0Nja2Xe7atesql116f69dpz317ds9NTU1a+W+Yana2pq2P/v161HhaQDWnO0aUDLbNehYvC9ZMc/Linleql9VRtCFCxfmhBNOSFNTU4455phsscUWr2u9lZ0IqRLq66tnFspXU1OTujrRHSiH7RpQorq6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jPmVHgcAAChE5XexAAAAAABYi0RQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKKJoAAAAABA0URQAAAAAKBoIigAAAAAUDQRFAAAAAAomggKAAAAABRNBAUAAAAAiiaCAgAAAABFE0EBAAAAgKLVV3qAtenxxx/P//3f/+Xee+/NjBkz0qdPn2yxxRbZf//9s+OOO1Z6PFgjf//7XzN+/K158cUXssEGG+ajH/1Ytt56m0qPBfCm2a4BAABrS01ra2trpYdYG2677bYce+yxaWpqWuHtBx54YL75zW+uta8/ffrctXbfvHUtXrw4w4cfkFtvvTktLS3L3V5bW5uPfezjGTPm4tTXF/07DqAQtmtA6fr165G6uto0N7dkxoz5lR4HYI2d8Mu788rshVmvd9f8+MjtKj0Ohevfv2e73VeRH4efNGlSvva1r6WpqSlbbrllLrrootxzzz25/PLLs+uuuyZJLrroolx88cUVnhRev29847hstFG/3HzzjSsMBUnS0tKSm2++MRtt1C/f+MZx63hCgDfGdg0AAFhXioygZ511VhYuXJiNN944F154Ybbddtv07ds3W265Zc4999x84hOfSJKcffbZmTdvXoWnhdX73OeGZcyY85a7vr6+Pl27dl3h3lFjxpyXz31u2LoYD+ANs10DAADWpeIi6NNPP53x48cnSY444oj06NFjmdtrampy0kknpba2NrNmzcqtt95agSnh9fvGN47LHXfctsx1W2yxVR566IksXNiYV199NQsXNuahh57I5ptvscxyd9xxmz2ngKpjuwYAAKxrxUXQCRMmJFkSO4cOHbrCZTbccMNsttlmSZI//vGP62w2eKMWL168zJ5StbW1eeCBibn99j9ngw02WGbZDTbYIHfccXceeGBiamr+/aM9Zsx5Wbx48TqbGWBVbNcAAIBKKC6CPvroo0mSjTbaKP369VvpckOGDEmSPPLII+tkLngzhg8/YJm/33ffP/L2t2+8ynXe/vaNc//9/1jmukMPPbDdZwN4M2zXAACASigugk6dOjVJMmjQoFUut9FGGyVJXnzxRXuTULVuvfXmtsubb77FakPBUm9/+8YZMmTztr/ffPNN7T4bwJthuwYAAFRCcRF05syZSZLevXuvcrmePXsmSVpbWzNnzpy1Phe8UX//+1+XOVvyuHFXvKH1f//7K9sut7Q05+9//2u7zQbwZtiuAQAAlbL8qVc7uMbGxiRJly5dVrlc165d2y4vWrSo3efo27d7ampq2v1+eesYP/7fJ+2qr6/PkCHvXG6Z2tqatj/79Vv2JGD9+r0z9fX1bXs6T5hwW3bZZce1ODHAqtmuAW9Fq9quAbxel19+Wb7zne9k7ty5lR4lM+c2pqWlNbW1Nfnjr1fdXtaFnj175rvf/W4++9l9Kj0KVa64CFpXV1fpEZIk9fXVMQcd14svvtB2ub6+PnV1K99xu6amJnV1y0f3urq6tlgwderUVd4HwNpmuwa8la1suwbwepx55pl57LHHKj3GchZUyQdrzzzzzOy3336VHoMqV1wE7datW5LV7925cOHCtsur22v0zVi8uNmeoKyRDTbYsO3y4sWL09zcstwytbU1qampSWtra1paWpe7vbm5ue3ywIEDV3gfAOuK7RrwVrS67RrA6/H1r3893/72t6tiT9BFTc15tXFxunWpT+dOld8BrGfPnjn++OO9LyxUe+70UFwEXXqsz9VtGJYeB7Surm61xw99M2bOXNDu98lby0c/+rH88Ic/SLIkFkya9HQ22GCDZZbp169H6upq0tLSmhkz5i9z23+e9GuHHXZZbhmAdcl2DXgrWtV2DeD1Gjp09wwdunulx0iydLtWm+bmlqrarlXTLLSf/v17ttt9FfcZsne84x1Jkueff36Vy73wwpKP5L3tbW9LbW1xTwMF2HrrbZZ5bX7hC3u/ofU///lhbZdra+uy9dbbtNtsAG+G7RoAAFApxdW/wYMHJ0mee+65zJs3b6XLTZo0KUmy2WabrZO54M342Mc+3nb5kUcezrPP/ut1rffPf07OpEmPtP394x//RLvPBvBm2K4BAACVUFwE3WmnnZIsOWbY+PHjV7jMCy+8kEcffTRJssMOO6yr0eANGzPm4mX+/oEPvGe1weDZZ/+VD33ofctcd955F7X7bABvhu0aAABQCcVF0P/6r//KNtss+XjcOeecs9yxQVtbWzNy5Mi0tLSkb9+++cxnPlOJMeF1qa+vz/Dhh7b9vbW1Je9//5YZOnS7vPjii8ss++KLL+ajH/1w3v/+LdPa+u8DQg8ffmjq64s7/C/QQdmuAQAAlVDT2tpa3CkKJ06cmP322y8tLS0ZPHhwvvGNb2TIkCF54YUX8stf/jK33nprkuRb3/pWDjjggLUyw/TplT9jG+X43OeG5Y47blvu+vr6+tTV1aW5uXmZk4UsNXToLrnkkivXxYgAb4jtGvBWUa0nEAF4s2zXWJfa88RIRUbQJLniiity6qmnrvAfUEkyfPjwnHTSSWvt64ugtLdvfOO4jBlz3utefvjwQ3PGGT9bixMBrBnbNeCtQCwASmO7xrokgr5Ojz/+eM4777zce++9eeWVV9K9e/dsscUW2X///bPrrruu1a8tgrI2LF68OIceemBuvvnGtLS0LHd7bW1dPv7xT+S88y7yUVGgQ7BdA0onFgClsV1jXRJBOwARlLXt73//ayZMuC1Tp07NwIEDs8MOu2Trrbep9FgAb5rtGlAisQAoje0a65II2gGIoKwL/ucDlMZ2DSiN7RpQGts11qX2jKDFnR0eAAAAAOC1RFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGg1ra2trZUeAgAAAABgbbEnKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAAAAFE0EBQAAAACKJoICAAAAAEUTQQEAAACAoomgAAAAAEDRRFAAAAAAoGgiKAAAAABQNBEUAAAAACiaCAoAAFCglpaWSo9QhObm5kqPAEA7qK/0AMCSN1Z1dXWVHgOg3diuQft597vf3Xa5pqYmf/rTn/K2t73tda37la98Jbfddlvb3x9//PEV3vfRRx+dY4455g3Ndc455+Tcc8/NwIEDc/vtt7+hdf/TbbfdlltvvTV///vfM3369CxevDj9+vXLxhtvnO233z7Dhg1L//791+hrvJX861//yne+8518//vfz6BBgyo9TpJ/v17erLFjx+aDH/xgO060ei0tLfnd736XZ555Jt/85jfX6dem+hx44IG577773tA6w4YNy8iRI5Mk9957bw466KAklXk9r8iUKVOyyy67JEl++MMfZu+9937d65500km58sor3/DX3HbbbXPRRRe94fWgPYigrLGdd945U6dOXentnTp1Svfu3bPRRhvl/e9/fz73uc/lXe961zqc8N/W5I3+2jBv3rz89Kc/zVZbbZW99trrDa3bXs/70v95tcc/YEr2/PPP51Of+lT69OnjeXoLsF1782zXqteMGTPy29/+NuPHj8+//vWvNDY2pm/fvnnPe96TfffdNzvttFOlR+R1aG1tzY033phDDjlktcvOnTs3d95559ofag28+OKLOfbYY/Pggw8ud9sLL7yQF154Iffcc09++ctf5vjjj88Xv/jFdT9kB/PYY49lv/32S2NjY6VH6fBOOOGEXHfddRk2bFilRwGgHYigrHVNTU2ZPXt2Zs+enUcffTQXX3xxTjzxxAwfPrzSo1Xc7rvvnmnTpmWLLbZo9/v2vLefhQsX5utf/3rmz5+fPn36VHocqoCfr5WzXatODzzwQI4++ujMnDlzmeunTZuWW2+9NbfeemuGDRuW008/3R68HcBNN930uiLoLbfckqamplUu8/a3vz1J0rt37/YY7Q1ZsGBBvvSlL+Xpp59OQ0NDhg8fno9+9KPZaKONUldXl5dffjn3339/fv3rX2fq1Kn5/ve/n/r6+nz+859f57N2JLNnz67KAHrEEUfkS1/60gpve9/73pck2XPPPfPd7353hct07dp1rc22MtOmTVvnX5Pqt9FGG+W66657Xct26tSp7XLXrl3btrmVeD2vTX/7299e97LeZ1BJIijtZptttslvfvOb5a5vaWnJ3Llzc9ddd+XMM8/MzJkzM3LkyLzrXe/KRz7ykXU649KPjjU0NKzTr7sy7fHGqiM87x3Z/Pnzc8wxx+Svf/1rpUehAjrCz5ftmu3a6rz44osZMWJE5s6dmz59+uTYY4/NjjvumC5duuTJJ5/MqFGj8sADD+TKK6/M+uuvn69//euVHpmVGDx4cJ544ok8+OCDefHFF7PBBhuscvkbbrghSdK/f/9Mnz59hcvceuut7T7n63XppZfm6aefTqdOnXLRRRdlyJAhy9zeu3fvvPOd78xuu+2Wz372s3n++edz5pln5lOf+lTVbPN4/Tp37pzOnTuvcpn6+vr06NFjHU0Eb05NTc2bep2+5z3vqeg2d23yc0tH4cRItJu6urr06NFjuf969uyZjTbaKPvuu29+8YtfpKamJsmS4wKta3feeWfuvPPOovYa6gjPe0f15JNPZp999sldd91V6VGokI7w82W7Zru2OqNHj87cuXPTpUuXjB07Nvvvv38GDRqU/v37Z7vttstvf/vbfOxjH0uSXHDBBfZ8qmI77bRTevTokdbW1tx0002rXHbGjBm55557Ul9f3/b9rTZLD1fxkY98ZLkA+lr9+vXLiSeemCSZM2dO/vznP6+T+QCAstgTlHXqfe97Xz74wQ/mnnvuyYMPPphXXnkl6623XqXHKp7n/Y2ZPXt2fvGLX+Tiiy9OU1NTunfvng022CCTJ0+u9GhUIT9fleF5f/1uvvnmJMkee+yxzAl2lqqpqclXv/rV3HrrrWlqaspdd93l+HdVqkuXLtl5551z7bXXrvYj8TfeeGMWL16cj3zkI+nXr99Kl1vdcYWffPLJnH/++XnggQfy0ksvZf3118/OO++cr3zlK2v8eJbunfp6Prq9/fbbZ/DgwenTp0/q61f8T5impqZcfvnlueGGG/LEE09k/vz5WW+99fL+978/Bx54YN773vcut85rTwpy2223pbW1Nb/61a8yYcKEzJgxI+uvv36GDh2ao446qm0bc9VVV+X3v/99nnjiibS2tmbw4ME55JBDsvvuu690/qeeeioXXnhh/vKXv2TatGnp3LlzNtlkk3z84x/PF7/4xXTr1m2F673wwgsZO3ZsJkyYkOeeey5Jst566+V973tf9ttvv2y77bYrfCxLLf37ir6/f//733PxxRfngQceyCuvvJJu3bpl8ODB2WOPPbLPPvss8zHepZYeu/m0007LhhtumB/96EeZPHlyevfunQ996EM588wzV/octIc38z1O/n0s3auuuioPP/xwZs+enYaGhrzjHe/I0KFDs//++6dnz55ty//nCV+uvPLKtr//58nF4PVa1YmRlm6Lx44dmyFDhuT//u//csstt+T5559P586ds9lmm+Vzn/tc9thjj5Xe/9SpU/P73/8+f/nLXzJlypTMnTu37Vjq2223XQ4++ODVfoJgXbvgggvywx/+MElywAEH5Fvf+tZyy9xwww057rjjkiw5OdV/nqTsySefzCWXXJL7778/L7zwQubPn5+Ghoa8/e1vz4477pgDDzxwuUOarattP9VHBGWd22yzzXLPPfckWbKhXrpRWXq2vREjRmS33XbL9773vUyaNCndu3fPFltskV/+8pdtH6FpbGzMH/7wh9x4441tb4D69u2brbfeOvvss0923HHHFX7t1b3RnzlzZi688MLccccdee6559Lc3JwNN9wwO+ywQ770pS9lww03XOnjamlpyR//+Mf84Q9/yOOPP56XX345vXr1ylZbbZX9999/mZn+8+QfJ598ck4++eS1eqa8lT3v/+mGG27IJZdckkcffTSNjY3ZaKONsttuu+XQQw9Nr169VrjOokWLcvXVV+e2227LpEmTMmvWrNTW1qZv377Zaqutsu+++670o6pv5M39f1qT79eqjB07NhdccEGSZMiQIRk5cmTGjBkjgrJStmu2a69VTdu1pccArampyVZbbbXS5ZYeoyxxDLxqt8cee+Taa69d7Ufil34U/lOf+lSmTJnypr7WH/7wh3zrW9/K4sWL266bOnVqLrrootx000358Ic//Kbud6lBgwZl8uTJuffee3PnnXeudDuXJL169cq111670ttfeOGFfPnLX84TTzyxzPUvvvhirrvuulx33XU5/PDDc/zxx7ftRf6fHnjggXz/+9/PvHnz2q57/vnnc/HFF2fChAm58sor853vfGe5OR588MF89atfzcsvv5wDDzxwufsdM2ZMfvzjH6e5ubntusbGxkycODETJ07MuHHj8utf/zr/8z//s8x6EydOzJe+9KXMmTNnmeunTp2aqVOn5tprr82hhx7atpfs69XS0pIf/ehHGTNmzDLXL1q0KPfff3/uv//+XHrppRk9enTbIVf+04MPPpjvfve7bcecffnll9f6x2HX5Ht8wgknLPd9mzVrVv7+97+3xeCxY8dmk002WauPAVZnypQp+cY3vpEXXnih7bqFCxfm3nvvzb333pt77rkn3//+95db77LLLlvmZ3KpOXPmZM6cOXnsscdy+eWX58ILL1zlnvfr2kEHHZRbb701DzzwQMaNG5dPf/rTy/wyY9q0aW3HCX7Xu96VE044YZn1zz333Jx77rlpbW1d5vpZs2Zl1qxZeeihh/KHP/wh48aNW+l7qLW17ac6iaCsc699U7KigyI/99xzOfjggzN37twkS96QJWkLBc8880yOOuqoPPXUU8usN23atNx88825+eab88lPfjIjR45Mly5dXvdc99xzT/7f//t/mT179jLX//Of/8w///nPXHrppfnRj36Uj3/848utO3v27Hzta19b7uNZr7zySu64447ccccd+eIXv5hTTz31dc/T3lb3vC9evDjHH3/8cgf5njx5ckaPHp3rrrsul1566XKR4bnnnsthhx2WZ555Zrn7fPXVV/P888/npptuWmGgWZM392vy/Xo9+vfvnyOPPDKf+9znVrrHCSxlu1YZtmur17dv3/zlL3/JokWLlvsHwms9++yzbZdXFoapDttvv3169eqVOXPmrHRv0Jdeeil//etf07lz53zsYx9bLna9Hvfee2/+93//N8mSY5F+/etfz5ZbbpmZM2fm8ssvz5gxY3LNNdes0WMZNmxY7rzzzjQ3N+fLX/5ydtxxx+y+++7ZfvvtM2DAgNd9P0tPsDR58uR07949Rx55ZHbbbbf06dMn//rXv3LhhRfm+uuvz29+85v06tUrX/7yl1d4P9/61rfSrVu3/OAHP8hHPvKRzJ07N6NGjcoNN9yQZ599Nvvss0/++c9/Zu+9984hhxySAQMG5KGHHsp3vvOdPP/88znrrLOy7777LnPCk8suuywjR45Mkmy77bYZMWJENttsszQ2NubPf/5zzjrrrEydOjWHHnporrzyyra9dltbW3PiiSdmzpw52WSTTXLCCSdkyJAh6dKlS5544on89Kc/zUMPPZTzzjsvu+66a973vvdl4MCB+dvf/pYHHnig7TFef/312XDDDZfZq/Pss89ue03stttuOeSQQ/LOd74zc+fOzR//+Mece+65mTRpUo444ohccsklK/z/zuWXX54NNtggP/jBD7LpppvmwQcfXOaXKe1tTb7H1113XVu8OPjgg/PZz342AwYMyOzZs3PTTTflnHPOyUsvvZTvfe97Of/885Mk3/ve93Lqqafm8MMPz1//+tdVnrAJ2tP3v//9tLS05Nhjj80ee+yRnj175sEHH8zpp5+eKVOm5NJLL82nP/3pfOADH2hb56GHHsqpp56a1tbWbLHFFjnmmGMyePDgdO7cOc8991x+//vf56qrrsqcOXMycuTIjB07toKPcFm1tbX54Q9/mM985jNZsGBBTj311Fx55ZVt//765je/mVmzZqVTp075yU9+ssz2aOnPb7Lk/41f/vKX8453vCPJkvdOY8aMyfjx4/PCCy/k7LPPbtvj9D+tjW0/1csxQVnnJk6cmGTJgc833njj5W6//vrr09ramrPOOit33313LrjggraPXM2cOTOHHnponnrqqXTq1CkjRozIDTfckHvvvTeXXHJJ2z8Mb7jhhpx88smve6YnnngiRxxxRGbPnp1BgwblRz/6Ue6888785S9/ya9//etsscUWWbhwYb72ta+t8AQ5rw0Fn//853PVVVflL3/5S37/+99nu+22S5L89re/zWWXXdb2GF97Br3vfve7+dvf/rbCE4G0l9U97y+99FKuu+66fPCDH8wFF1yQu+++O1dccUXbczplypT85Cc/WWad5ubmHH300XnmmWfSvXv3nHzyybnxxhtzzz335LrrrsvJJ5/c9tGDX/ziF217RCXLv7kfNWpU7rjjjrbv+dI9ls4777zlzja4pt+v1Rk2bFhuv/32HHDAAQIor4vtmu1aUt3btc6dO68yoP/+979vu7zNNtu84ftn3encuXN23XXXJFnpcUFvuOGGtLa2ZqeddnrTJxA6/fTTkySbbLJJfve732WnnXZKv3798s53vjPf+MY33tD2aGU++clPZr/99kuy5OfnT3/6U0466aTssMMO2W233XLyySfnqquuyssvv7zK+/nNb36TyZMnp1OnTrngggvy5S9/OZtsskn69OmT97zn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4Pz587h79y4qKipgaWmJoUOHwsPDA87Ozmq3s7OzA6D5Bq+oqAjHjh3D5cuXkZubi6qqKpiZmcHe3h5z586Fi4uLxmOqra3Ft99+i9jYWGRmZqK8vBxisRiOjo5YvHix2tG2srIybN26FQkJCaioqED37t0RHx/fpMrArXXe/f39cfbs2QYDFWfOnMHnn38OQHX0u6ioCAcOHEBCQgIeP34MkUgECwsLvPPOO5g1axYmTZokVCM2NDTEpUuXlEbgnjx50uzOveJ7t/R6vY7zRLqD7ZoM27X2365t2bJFbTGQhjAISkRERES6iEFQ+l2RBwt8fHywevXqt3w0RETaY7tGRERERETUOBZGot8NxeIV+vr6b/FIiIhaB9s1IiIiIiKipmEQlH43fv31V+FxeyrSQUSkCds1IiIiIiKipmFhJNJ5aWlpkEqliIqKEp7r37//WzwiIiLtsF0jIiIiIiJqHuYEJZ03ffp0SCQS4d8DBw7E2bNnoafHidBE1D6xXSMiIiIiImoe3i2RTqupqUF1dTX09fXRpUsXuLm5ITw8nIECImq32K4RERERERE1H2eCEhERERERERERkU7jtBEiIiIiIiIiIiLSaQyCEhERERERERERkU5jEJSIiIiIiIiIiIh0GoOgREREREREREREpNMYBCUiIiIiIiIiIiKdxiAoERERERERERER6TQGQYmIiIiIiIiIiEinMQhKREREREREREREOo1BUCIiIiIiIiIiItJpDIISERERERERERGRTmMQlIiIiIiIiIiIiHQag6BERERERERERESk0xgEJSIiIiIiIiIiIp3GICgRERERERERERHpNAZBiYiIiIiIiIiISKcxCEpEREREREREREQ6jUFQIiIiIiIiIiIi0mkMghIREREREREREZFO+z9sD9eGXO6UQQAAAABJRU5ErkJggg==",
"text/plain": [
"
"
]
},
"metadata": {
"image/png": {
"height": 486,
"width": 665
}
},
"output_type": "display_data"
}
],
"source": [
"grades.hist(column=['Project Phase 1', 'Project Phase 2', 'Mid-Semester Test', 'Final Exam']);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Saving a DataFrame"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pandas allows for saving DataFrame objects in various formats, including CSV, Excel, JSON, HTML, and SQL. Let's save `grades` as a new file in CSV format with all the modifications we performed. Before saving, let's shorten the long column names and then take a peak at the final data."
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"