{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tags": [
"remove_input"
]
},
"outputs": [],
"source": [
"path_data = '../../data/'\n",
"\n",
"import numpy as np\n",
"import pandas as pd\n",
"\n",
"%matplotlib inline\n",
"import matplotlib.pyplot as plt\n",
"plt.style.use('fivethirtyeight')\n",
"\n",
"import warnings\n",
"warnings.filterwarnings('ignore')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# A \"More Likely Than Not\" Binary Classifier\n",
"Let's try to use data to classify a point into one of two categories, choosing the category that we think is more likely than not. To do this, we not only need the data but also a clear description of how chances are involved.\n",
"\n",
"We will start out in a simple artifical setting just to develop the main technique, and then move to a more intriguing example.\n",
"\n",
"Suppose there is a university class with the following composition:\n",
"- 60% of the students are Second Years and the remaining 40% are Third Years\n",
"- 50% of the Second Years have declared their major\n",
"- 80% of the Third Years have declared their major\n",
"\n",
"Now suppose **I pick a student at random from the class**. Can you classify the student as Second Year or Third Year, using our \"more likely than not\" criterion?\n",
"\n",
"You can, because the student is picked at random and so you know that the chance that the student is a Second Year is 60%. That's greater than the 40% chance of being a Third Year, so you would classify the student as Second Year.\n",
"\n",
"The information about the majors is irrelevant, as we already know the proportions of Second and Third Years in the class.\n",
"\n",
"We have a pretty simple classifier! But now suppose I give you some additional information about the student who was picked:\n",
"\n",
"**The student has declared a major.**\n",
"\n",
"Would this knowledge change your classification?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Updating the Prediction Based on New Information\n",
"Now that we know the student has declared a major, it becomes important to look at the relation between year and major declaration. It's still true that more students are Second Years than Third Years. But it's also true that among the Third Years, a much higher percent have declared their major than among the Second Years. Our classifier has to take both of these observations into account.\n",
"\n",
"To visualize this, we will use a table `students` that consists of one row for each of 100 students whose years and majors have the same proportions as given in the data."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"tags": [
"remove_input"
]
},
"outputs": [],
"source": [
"year = np.array(['Second']*60 + ['Third']*40)\n",
"major = np.array(['Undeclared']*30+['Declared']*30+['Undeclared']*8+['Declared']*32)\n",
"students = pd.DataFrame(\n",
" {'Year':year,\n",
" 'Major':major}\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" Year | \n",
" Major | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" Second | \n",
" Undeclared | \n",
"
\n",
" \n",
" 1 | \n",
" Second | \n",
" Undeclared | \n",
"
\n",
" \n",
" 2 | \n",
" Second | \n",
" Undeclared | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Year Major\n",
"0 Second Undeclared\n",
"1 Second Undeclared\n",
"2 Second Undeclared"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"students.head(3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To check that the proportions are correct, let's use `pivot` to cross-classify each student according to the two variables."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" Major | \n",
" Declared | \n",
" Undeclared | \n",
"
\n",
" \n",
" Year | \n",
" | \n",
" | \n",
"
\n",
" \n",
" \n",
" \n",
" Second | \n",
" 30 | \n",
" 30 | \n",
"
\n",
" \n",
" Third | \n",
" 32 | \n",
" 8 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
"Major Declared Undeclared\n",
"Year \n",
"Second 30 30\n",
"Third 32 8"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"students_pivot = students.groupby(['Year', 'Major']).Major.agg('count').to_frame('count').reset_index()\n",
"\n",
"pd.pivot_table(students_pivot, values='count', index=['Year'], columns=['Major'], aggfunc=np.sum).fillna(0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The total count is 100 students, of whom 60 are Second Years and 40 are Third Years. Among the Second Years, 50% are in each of the Major categories. Among the 40 Third Years, 20% are Undeclared and 80% Declared. So this population of 100 students has the same proportions as the class in our problem, and we can assume that our student has been picked at random from among all 100 students.\n",
"\n",
"We have to pick which row the student is most likely to be in. When we knew nothing more about the student, he or she could be in any of the four cells, and therefore were more likely to be in the top row (Second Year) because that contains more students.\n",
"\n",
"But now we know that the student has declared a major, so the space of possible outcomes has decreased: now the student can only be in one of the two Declared cells. \n",
"\n",
"There are 62 students in those cells, and 32 out of the 62 are Third Years. That's more than half, even though not by much. \n",
"\n",
"So, in the light of the new information about the student's major, we have to update our prediction and now classify the student as a Third Year.\n",
"\n",
"What is the chance that our classification is correct? We will be right for all the 32 Third Years who are Declared, and wrong for the 30 Second Years who are Declared. The chance that we are correct is therefore about 0.516.\n",
"\n",
"In other words, the chance that we are correct is **the proportion of Third Years among the students who have Declared**."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.5161290322580645"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"32/(30+32)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Tree Diagram\n",
"The proportion that we have just calculated was based on a class of 100 students. But there's no reason the class couldn't have had 200 students, for example, as long as all the proportions in the cells were correct. Then our calculation would just have been 64/(60 + 64) which is 0.516 as before.\n",
"\n",
"So the calculation depends only on the proportions in the different categories, not on the counts. The proportions can be visualized in a *tree diagram*, shown directly below the pivot table for ease of comparison."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" Major | \n",
" Declared | \n",
" Undeclared | \n",
"
\n",
" \n",
" Year | \n",
" | \n",
" | \n",
"
\n",
" \n",
" \n",
" \n",
" Second | \n",
" 30 | \n",
" 30 | \n",
"
\n",
" \n",
" Third | \n",
" 32 | \n",
" 8 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
"Major Declared Undeclared\n",
"Year \n",
"Second 30 30\n",
"Third 32 8"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pd.pivot_table(students_pivot, values='count', index=['Year'], columns=['Major'], aggfunc=np.sum).fillna(0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![Students Tree Diagram](../../images/tree_students.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Like the pivot table, this diagram *partitions* the students into four distinct groups known as \"branches\". Notice that the \"Third Year, Declared\" branch contains the proportion 0.4 x 0.8 = 0.32 of the students, corresponding to the 32 students in the \"Third Year, Declared\" cell of the pivot table. The \"Second Year, Declared\" branch contains 0.6 x 0.5 = 0.3 of the students, corresponding to the 30 in the \"Second Year, Declared\" cell of the pivot table.\n",
"\n",
"We know that the student who was picked belongs to a \"Declared\" branch; that is, the student is either in the top branch or the third from top. Those two branches now form our reduced space of possibilities, and all chances have to be calculated relative to the total chance of this reduced space.\n",
"\n",
"So, given that the student is Declared, the chance of them being a Third Year can be calculated directly from the tree. The answer is the proportion in the \"Third Year, Declared\" branch relative to the total proportion in the two \"Declared\" branches.\n",
"\n",
"That is, the answer is **the proportion of Third Years among students who are Declared**, as before."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.5161290322580645"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(0.4 * 0.8)/(0.6 * 0.5 + 0.4 * 0.8)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Bayes' Rule\n",
"The method that we have just used is due to the Reverend [Thomas Bayes](https://en.wikipedia.org/wiki/Thomas_Bayes) (1701-1761). His method solved what was called an \"inverse probability\" problem: given new data, how can you update chances you had found earlier? Though Bayes lived three centuries ago, his method is [widely used now](https://en.wikipedia.org/wiki/Naive_Bayes_classifier) in machine learning.\n",
"\n",
"We will state the rule in the context of our population of students. First, some terminology:\n",
"\n",
"**Prior probabilities.** Before we knew the chosen student's major declaration status, the chance that the student was a Second Year was 60% and the chance that the student was a Third Year was 40%. These are the *prior* probabilities of the two categories.\n",
"\n",
"**Likelihoods.** These are the chances of the Major status, given the category of student; thus they can be read off the tree diagram. For example, the likelihood of Declared status given that the student is a Second Year is 0.5.\n",
"\n",
"**Posterior probabilities.** These are the chances of the two Year categories, *after* we have taken into account information about the Major declaration status. We computed one of these:\n",
"\n",
"The *posterior probability* that the student is a Third Year, given that the student has Declared, is denoted $P(\\text{Third Year} ~\\big{\\vert}~ Declared)$ and is calculated as follows."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$$\n",
"\\begin{align*}\n",
"P(Third Year ~\\big{\\vert}~ Declared) \n",
"~ &=~ \\frac{ 0.4 \\times 0.8}{0.6 \\times 0.5 ~+~ 0.4 \\times 0.8} \\\\ \\\\\n",
"&=~ \\frac{\\mbox{(prior probability of Third Year)} \\times\n",
"\\mbox{(likelihood of Declared given Third Year)}}\n",
"{\\mbox{total probability of Declared}}\n",
"\\end{align*}\n",
"$$\n",
"\n",
"The other posterior probability is\n",
"\n",
"$$\n",
"\\begin{align*}\n",
"P(\\mbox{Second Year} ~\\big{\\vert}~ \\mbox{Declared})\n",
"~ &=~ \\frac{ 0.6 \\times 0.5}{0.6 \\times 0.5 ~+~ 0.4 \\times 0.8} \\\\ \\\\\n",
"&=~ \\frac{\\mbox{(prior probability of Second Year)} \\times\n",
"\\mbox{(likelihood of Declared given Second Year)}}\n",
"{\\mbox{total probability of Declared}}\n",
"\\end{align*}\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.4838709677419354"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(0.6 * 0.5)/(0.6 * 0.5 + 0.4 * 0.8)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"That's about 0.484, which is less than half, consistent with our classification of Third Year. \n",
"\n",
"Notice that both the posterior probabilities have the same denominator: the chance of the new information, which is that the student has Declared.\n",
"\n",
"Because of this, Bayes' method is sometimes summarized as a statement about proportionality:\n",
"\n",
"$$\n",
"\\mbox{posterior} ~ \\propto ~ \\mbox{prior} \\times \\mbox{likelihood}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Formulas are great for efficiently describing calculations. But in settings like our example about students, it is simpler not to think in terms of formulas. Just use the tree diagram."
]
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.12"
}
},
"nbformat": 4,
"nbformat_minor": 2
}