<jupyter_start><jupyter_text>SMS Spam classification with Naive Bayes. - Libraries- Exploring the Dataset- Distribution spam and non-spam plots- Text Analytics- Feature Engineering- Predictive analysis (Multinomial Naive Bayes and Support Vector Machines)- Conclusion  Libraries<jupyter_code>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from collections import Counter
from sklearn import feature_extraction, model_selection, naive_bayes, metrics, svm
from IPython.display import Image
import warnings
warnings.filterwarnings("ignore")
%matplotlib inline<jupyter_output><empty_output><jupyter_text>Exploring the Dataset<jupyter_code>data = pd.read_csv('spam.csv', encoding='latin-1')
data.head(n=10)<jupyter_output><empty_output><jupyter_text>Distribution spam/non-spam plots<jupyter_code>count_Class=pd.value_counts(data["v1"], sort= True)
count_Class.plot(kind = 'bar',color = ["green","red"])
plt.title('Bar Plot')
plt.show();
count_Class.plot(kind = 'pie',autopct='%1.0f%%')
plt.title('Pie chart')
plt.ylabel('')
plt.show()<jupyter_output><empty_output><jupyter_text>Text Analytics We want to find the frequencies of words in the spam and non-spam messages. The words of the messages will be model features.We use the function Counter.<jupyter_code>count1 = Counter(" ".join(data[data['v1']=='ham']["v2"]).split()).most_common(20)
df1 = pd.DataFrame.from_dict(count1)
df1 = df1.rename(columns={0: "words in non-spam", 1 : "count"})
count2 = Counter(" ".join(data[data['v1']=='spam']["v2"]).split()).most_common(20)
df2 = pd.DataFrame.from_dict(count2)
df2 = df2.rename(columns={0: "words in spam", 1 : "count_"})
df1.plot.bar(legend = False)
y_pos = np.arange(len(df1["words in non-spam"]))
plt.xticks(y_pos, df1["words in non-spam"])
plt.title('More frequent words in non-spam messages')
plt.xlabel('words')
plt.ylabel('number')
plt.show()
df2.plot.bar(legend = False, color = 'orange')
y_pos = np.arange(len(df2["words in spam"]))
plt.xticks(y_pos, df2["words in spam"])
plt.title('More frequent words in spam messages')
plt.xlabel('words')
plt.ylabel('number')
plt.show()<jupyter_output><empty_output><jupyter_text>We can see that the majority of frequent words in both classes are stop words such as 'to', 'a', 'or' and so on.With stop words we refer to the most common words in a lenguage, there is no simgle, universal list of stop words.  Feature engineering Text preprocessing, tokenizing and filtering of stopwords are included in a high level component that is able to build a dictionary of features and transform documents to feature vectors. **We remove the stop words in order to improve the analytics**<jupyter_code>f = feature_extraction.text.TfidfVectorizer(stop_words = 'english')
X = f.fit_transform(data["v2"])
np.shape(X)<jupyter_output><empty_output><jupyter_text>We have created more than 8400 new features. The new feature  j  in the row  i  is equal to 1 if the word  wj appears in the text example  i . It is zero if not.  Predictive Analysis My goal is to predict if a new sms is spam or non-spam. I assume that is much worse misclassify non-spam than misclassify an spam. (I don't want to have false positives)The reason is because I normally don't check the spam messages.The two possible situations are:- New spam sms in my inbox. (False negative).  OUTCOME: I delete it.- New non-spam sms in my spam folder (False positive).  OUTCOME: I probably don't read it.I prefer the first option!!!First we transform the variable spam/non-spam into binary variable, then we split our data set in training set and test set.<jupyter_code>data["v1"]=data["v1"].map({'spam':1,'ham':0})
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, data['v1'], test_size=0.33, random_state=42)
print([np.shape(X_train), np.shape(X_test)])<jupyter_output>[(3733, 8404), (1839, 8404)]<jupyter_text>Multinomial naive bayes classifier We train different bayes models changing the regularization parameter  α .We evaluate the accuracy, recall and precision of the model with the test set.<jupyter_code>list_alpha = np.arange(1/100000, 20, 0.11)
score_train = np.zeros(len(list_alpha))
score_test = np.zeros(len(list_alpha))
recall_test = np.zeros(len(list_alpha))
precision_test= np.zeros(len(list_alpha))
count = 0
for alpha in list_alpha:
    bayes = naive_bayes.MultinomialNB(alpha=alpha)
    bayes.fit(X_train, y_train)
    score_train[count] = bayes.score(X_train, y_train)
    score_test[count]= bayes.score(X_test, y_test)
    recall_test[count] = metrics.recall_score(y_test, bayes.predict(X_test))
    precision_test[count] = metrics.precision_score(y_test, bayes.predict(X_test))
    count = count + 1<jupyter_output><empty_output><jupyter_text>Let's see the first 10 learning models and their metrics<jupyter_code>matrix = np.matrix(np.c_[list_alpha, score_train, score_test, recall_test, precision_test])
models = pd.DataFrame(data = matrix, columns = 
             ['alpha', 'Train Accuracy', 'Test Accuracy', 'Test Recall', 'Test Precision'])
models.head(n=10)<jupyter_output><empty_output><jupyter_text>I select the model with the most test precision<jupyter_code>best_index = models['Test Precision'].idxmax()
models.iloc[best_index, :]<jupyter_output><empty_output><jupyter_text>**My best model does not produce any false positive, which is our goal.**Let's see if there is more than one model with 100% precision !<jupyter_code>models[models['Test Precision']==1].head(n=5)<jupyter_output><empty_output><jupyter_text>Between these models with the highest possible precision, we are going to select which has more test accuracy.<jupyter_code>best_index = models[models['Test Precision']==1]['Test Accuracy'].idxmax()
bayes = naive_bayes.MultinomialNB(alpha=list_alpha[best_index])
bayes.fit(X_train, y_train)
models.iloc[best_index, :]<jupyter_output><empty_output><jupyter_text>Confusion matrix with naive bayes classifier<jupyter_code>m_confusion_test = metrics.confusion_matrix(y_test, bayes.predict(X_test))
pd.DataFrame(data = m_confusion_test, columns = ['Predicted 0', 'Predicted 1'],
            index = ['Actual 0', 'Actual 1'])<jupyter_output><empty_output><jupyter_text>We misclassify 44 spam messages as non-spam emails whereas we don't misclassify any non-spam message.  Finalize Model<jupyter_code>from _pickle import dump
filename = 'finalized_model.sav'
dump(bayes, open(filename, 'wb'))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1186122831692452, 'fraction_numerical': 0.015002344116268168, 'mean_word_length': 6.520564042303173, 'pattern_counts': {'":': 0, '<': 52, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '79136', 'license': 'MIT', 'n_tokens_mistral': 2243, 'n_tokens_neox': 2110, 'n_words': 607, 'path': 'SMS Spam Detection.ipynb', 'repo_name': 'ahkhalwai/Sms_Spam_Classification', 'size': '6399'}