{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"
\n",
"*This notebook contains an excerpt from the book [Machine Learning for OpenCV](https://www.packtpub.com/big-data-and-business-intelligence/machine-learning-opencv) by Michael Beyeler.\n",
"The code is released under the [MIT license](https://opensource.org/licenses/MIT),\n",
"and is available on [GitHub](https://github.com/mbeyeler/opencv-machine-learning).*\n",
"\n",
"*Note that this excerpt contains only the raw code - the book is rich with additional explanations and illustrations.\n",
"If you find this content useful, please consider supporting the work by\n",
"[buying the book](https://www.packtpub.com/big-data-and-business-intelligence/machine-learning-opencv)!*"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"< [Implementing Our First Bayesian Classifier](07.01-Implementing-Our-First-Bayesian-Classifier.ipynb) | [Contents](../README.md) | [Discovering Hidden Structures with Unsupervised Learning](08.00-Discovering-Hidden-Structures-with-Unsupervised-Learning.ipynb) >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Classifying Emails Using the Naive Bayes Classifier\n",
"\n",
"The final task of this chapter will be to apply our newly gained skills to a real spam filter!\n",
"Naive Bayes classifiers are actually a very popular model for email filtering. Their naivety\n",
"lends itself nicely to the analysis of text data, where each feature is a word (or a **bag of\n",
"words**), and it would not be feasible to model the dependence of every word on every other\n",
"word.\n",
"\n",
"A bunch of interesting email datasets are mentioned in the book.\n",
"\n",
"In this section, we will be using the Enrom-Spam dataset, which can be downloaded for free\n",
"from the given website. However, if you followed the installation instructions at the\n",
"beginning of this book and have downloaded the latest code from GitHub, you are already\n",
"good to go!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Loading the dataset\n",
"\n",
"If you downloaded the latest code from GitHub, you will find a number of `.zip` files in the\n",
"`notebooks/data/chapter7` directory. These files contain raw email data (with fields for\n",
"To:, Cc:, and text body) that are either classified as spam (with the `SPAM = 1` class label) or\n",
"not (also known as ham, the `HAM = 0` class label).\n",
"\n",
"We build a variable called sources, which contains all the raw data files:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"HAM = 0\n",
"SPAM = 1\n",
"datadir = 'data/chapter7'\n",
"sources = [\n",
" ('beck-s.tar.gz', HAM),\n",
" ('farmer-d.tar.gz', HAM),\n",
" ('kaminski-v.tar.gz', HAM),\n",
" ('kitchen-l.tar.gz', HAM),\n",
" ('lokay-m.tar.gz', HAM),\n",
" ('williams-w3.tar.gz', HAM),\n",
" ('BG.tar.gz', SPAM),\n",
" ('GP.tar.gz', SPAM),\n",
" ('SH.tar.gz', SPAM)\n",
"]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The first step is to extract these files into subdirectories. For this, we can use the\n",
"`extract_tar` function we wrote in the previous chapter:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def extract_tar(datafile, extractdir):\n",
" try:\n",
" import tarfile\n",
" except ImportError:\n",
" raise ImportError(\"You do not have tarfile installed. \"\n",
" \"Try unzipping the file outside of Python.\")\n",
"\n",
" tar = tarfile.open(datafile)\n",
" tar.extractall(path=extractdir)\n",
" tar.close()\n",
" print(\"%s successfully extracted to %s\" % (datafile, extractdir))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In order to apply the function to all data files in the sources, we need to run a loop. The\n",
"`extract_tar` function expects a path to the `.tar.gz` file—which we build from `datadir`\n",
"and an entry in sources—and a directory to extract the files to (`datadir`). This will extract\n",
"all emails in, for example, `data/chapter7/beck-s.tar.gz` to the\n",
"`data/chapter7/beck-s/` subdirectory:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"data/chapter7/beck-s.tar.gz successfully extracted to data/chapter7\n",
"data/chapter7/farmer-d.tar.gz successfully extracted to data/chapter7\n",
"data/chapter7/kaminski-v.tar.gz successfully extracted to data/chapter7\n",
"data/chapter7/kitchen-l.tar.gz successfully extracted to data/chapter7\n",
"data/chapter7/lokay-m.tar.gz successfully extracted to data/chapter7\n",
"data/chapter7/williams-w3.tar.gz successfully extracted to data/chapter7\n",
"data/chapter7/BG.tar.gz successfully extracted to data/chapter7\n",
"data/chapter7/GP.tar.gz successfully extracted to data/chapter7\n",
"data/chapter7/SH.tar.gz successfully extracted to data/chapter7\n"
]
}
],
"source": [
"for source, _ in sources:\n",
" datafile = '%s/%s' % (datadir, source)\n",
" extract_tar(datafile, datadir)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now here's the tricky bit. Every one of these subdirectories contains a number of other\n",
"directories, wherein the text files reside. So we need to write two functions:\n",
"- `read_single_file(filename)`: This is a function that extracts the relevant content from a single file called `filename`\n",
"- `read_files(path)`: This is a function that extracts the relevant content from all files in a particular directory called `path`"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import os\n",
"def read_single_file(filename):\n",
" past_header, lines = False, []\n",
" if os.path.isfile(filename):\n",
" f = open(filename, encoding=\"latin-1\")\n",
" for line in f:\n",
" if past_header:\n",
" lines.append(line)\n",
" elif line == '\\n':\n",
" past_header = True\n",
" f.close()\n",
" content = '\\n'.join(lines)\n",
" return filename, content"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def read_files(path):\n",
" for root, dirnames, filenames in os.walk(path):\n",
" for filename in filenames:\n",
" filepath = os.path.join(root, filename)\n",
" yield read_single_file(filepath)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Building a data matrix using Pandas\n",
"\n",
"Now it's time to introduce another essential data science tool that comes preinstalled with\n",
"Python Anaconda: **Pandas**. Pandas is built on NumPy and provides a number of useful\n",
"tools and methods to deal with data structures in Python. Just as we generally import\n",
"NumPy under the alias `np`, it is common to import Pandas under the `pd` alias:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pandas provides a useful data structure called `DataFrame`, which can be understood as a\n",
"generalization of a 2D NumPy array, as shown here:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
| \n", " | class | \n", "model | \n", "
|---|---|---|
| 0 | \n", "cv2.ml.NormalBayesClassifier_create() | \n", "Normal Bayes | \n", "
| 1 | \n", "sklearn.naive_bayes.MultinomialNB() | \n", "Multinomial Bayes | \n", "
| 2 | \n", "sklearn.naive_bayes.BernoulliNB() | \n", "Bernoulli Bayes | \n", "