# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: tf-gpu-25
#     language: python
#     name: tf-gpu-25
# ---

# +
# importing required libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# %matplotlib inline
import seaborn as sns

# Styles
plt.style.use('ggplot')
sns.set_style('whitegrid')
sns.set()

# Warning
import warnings
warnings.filterwarnings('ignore')

# Text Preprocessing
import string
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize

import spacy
nlp = spacy.load("en_core_web_sm")

# +
# loading the dataset

raw_sms = pd.read_csv('spam.csv', encoding='latin-1')

# +
# Checking top 10 rows

raw_sms.head()

# +
# Selecting First two columns

sms = raw_sms.iloc[:, :2]
# -

sms.head()

# +
# Renaming the columns

sms.rename(columns={'v1': "Label", 'v2':"Text"}, inplace=True)
# -

sms.head()

# ## Exploratory Data Analysis

# +
# Lets look at the dataset info to see if everything is alright

sms.info()

# +
# Checking the Category (SPAM vs HAM) unique count

sms['Label'].value_counts()
# -

sns.countplot(x='Label', data=sms)

# +
# Overall length of each spam and ham messages

sms['Length'] = sms['Text'].map(lambda x: len(x))

# +
# checking the histogram 

sms.hist(column='Length', bins=50, figsize=(10, 7))

# +
# Overall length of length of spam and ham messages

sms.hist(column='Length', by='Label', bins=100, figsize=(20,7))
# -

# ### Checking the Percentage of SPAM / HAM

sms["Label"].value_counts().plot(kind = 'pie', explode = [0, 0.1], figsize = (6, 6), autopct = '%1.1f%%', shadow = True)
plt.ylabel("Spam vs Ham")
plt.legend(["Ham", "Spam"])
plt.show()

# <span>
# As the dataset is imbalanced, 86.6% consists of normal message and 13.4% consists of spam message.
# </span>
#

# <span>
# We need to use Stratified sampling while splitting the dataset into training and testing set, otherwise we have a chance of our training model being skewed towards normal messages.
# </span>

# +
# let see the Top Spam / Ham sms by count

topSMS = sms.groupby('Text')['Label'] \
            .agg([len, np.max])  \
            .sort_values(by='len', ascending=False) \
            .head(n=20)

print(topSMS)

# +
# Splitting dataframe by groups formed from unique column values i.e., Spam and Ham

grouped = sms.groupby(sms.Label)
spamSMS = grouped.get_group("spam")
hamSMS = grouped.get_group("ham")

# +
# splitting the data in spam and ham

spam_sms = sms[sms["Label"] == "spam"]["Text"]
ham_sms = sms[sms["Label"] == "ham"]["Text"]

# +
# converting the series to string

spam_sms = spam_sms.to_string()
ham_sms = ham_sms.to_string()


# +
# Function to extract spam words using word_tokenizer

def extract_spam_words(spamMessage):
    words = word_tokenize(spamMessage)
    return words


# +
# Function to extract ham words using word_tokenizer

def extract_ham_words(hamMessage):
    words = word_tokenize(hamMessage)
    return words
    


# -

# ### Creating Spam and Ham word clouds
# Word Cloud is a data visualization technique used for representing text data in which the size of each word indicates its frequency or importance.

# +
# Creating corpus for spam and ham message

spam_words = extract_spam_words(spam_sms)
ham_words = extract_ham_words(ham_sms)
# -

# ### SPAM Wrod Cloud

# +
from wordcloud import WordCloud

spam_wordcloud = WordCloud(width=500, height=300).generate(" ".join(spam_words))
plt.figure( figsize=(10,8), facecolor='r')
plt.imshow(spam_wordcloud)
plt.axis("off")
plt.tight_layout(pad=0)
plt.show()
# -

# ### HAM Word Cloud

ham_wordcloud = WordCloud(width=500, height=300).generate(" ".join(ham_words))
plt.figure( figsize=(10,8), facecolor='r')
plt.imshow(ham_wordcloud)
plt.axis("off")
plt.tight_layout(pad=0)
plt.show()

# +
# Top 10 spam words

spam_words = np.array(spam_words)
print("Top 10 Spam words are :\n")
pd.Series(spam_words).value_counts().head(n = 10)
# -

# # Data pre-processing

# +
# Removing punctuations and stopwords from the text data.

import string
def text_process(text):
    text = text.translate(str.maketrans('', '', string.punctuation))
    text = [word for word in text.split() if word.lower() not in stopwords.words('english')]
    return " ".join(text)
   


# -

sms['Text'] = sms['Text'].apply(text_process)

sms.head()

# +
# Replacing ham with 0 and spam with 1

sms = sms.replace(['ham','spam'],[0, 1])
sms.head()
# -

# ###  Converting words to vectors using TFIDF Vectorizer

from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()
vectors = vectorizer.fit_transform(sms['Text'])

X = vectors
y = sms['Label']

X.shape

y.shape

# ### Splitting into training and test set

# +
from sklearn.model_selection import train_test_split

# using stratify sampling as we have imbalace dataset
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, test_size=0.2)
# -

print (X_train.shape)
print (X_test.shape)
print (y_train.shape)
print (y_test.shape)

# ### Training using Naive Bayes MultinomialNB

from sklearn.naive_bayes import MultinomialNB
spam_detect_model = MultinomialNB()
spam_detect_model.fit(X_train, y_train)

# Let's check the outputs of the model
# I'll store them in y_hat_test as this is the 'theoretical' name of the predictions
y_hat_test = spam_detect_model.predict(X_test)
y_hat_test

# ### Evaluation
# <span> We can check precision,recall,f1-score using classification report!</span>

from sklearn.metrics import classification_report

print(classification_report(y_test, y_hat_test))

from sklearn import metrics
print("Accuracy ", metrics.accuracy_score(y_test, y_hat_test))

# ###  Hyper parameter tuning
#
# Finding best possible hyper-parameters

from sklearn.model_selection import GridSearchCV

# creating classifier
clf = MultinomialNB()

clf.get_params().keys()

# +
 # creating Hyper-parameter search space

# paramter for smoothing
alphas = [0.01, 0.1, 1.0]
# final Hyperparameter Options
hyperparameters = dict(alpha = alphas)

# +
# define search

# Creating GridSearch using 10-fold cross validation
search = GridSearchCV(clf, hyperparameters, cv=10, scoring='accuracy', verbose=3, n_jobs=-1)
# -

# Execute search
best_model = search.fit(X_train,y_train)

print("Tuned Hyperparameters : (best parameters) ", best_model.best_params_)

print("Accuracy : ", best_model.best_score_)

# ### Training the model with best_parameters obtained by hyerparameters

classifier = MultinomialNB(alpha=0.1)
model = classifier.fit(X_train, y_train)

# ### Testing

y_hat = model.predict(X_test)
y_hat

from sklearn import metrics
print("Accuracy ", metrics.accuracy_score(y_test, y_hat))

# Creating the dataframe of the predicted values
df = pd.DataFrame(y_hat, columns=['Prediction'])
df.head()

# We can include the test targets in data frame (so we can manually compare them)
df['Target'] = y_test
df

# +
# In y_test, old indexes are preserved 

# Therefore, to get a proper result, we must reset the index and drop the old indexing
y_test = y_test.reset_index(drop=True)

# Check the result
y_test.head()
# -

# Let's overwrite the 'Target' column with the appropriate values
df['Target'] = y_test
df


# +
# Replacing the ham with 0 and spam with 1
# df = df.replace([0,1],['ham', 'spam'])
# df.head(20)
# -

# function to display text message
def find(p):
    if p == 1:
        print("SMS is SPAM")
    else:
        print("SMS is NOT spam")


# +
text1 = ["Free tones Hope you enjoyed your new content"]
text2 = ["No. I meant the calculation is the same. That I'll call later"]
text3 = ["Had your contract mobile 11 Mnths? Latest Motorola Now"]
text4 = ["WINNER!! You just won a free ticket to Bahamas. Send your Details"]
text5 = ["Hey there, How are you?"]

integers1 = vectorizer.transform(text1)
integers2 = vectorizer.transform(text2)
integers3 = vectorizer.transform(text3)
integers4 = vectorizer.transform(text4)
integers5 = vectorizer.transform(text5)

# +
p1 = best_model.predict(integers1)[0]
p2 = best_model.predict(integers2)[0]
p3 = best_model.predict(integers3)[0]
p4 = best_model.predict(integers4)[0]
p5 = best_model.predict(integers5)[0]


find(p1)
find(p2)
find(p3)
find(p4)
find(p5)

# +
text11 = ["Free tones Hope you enjoyed your new content"]
text21 = ["No. I meant the calculation is the same. That I'll call later."]
text31 = ["Had your contract mobile 11 Mnths? Latest Motorola Now"]
text41 = ["WINNER!! You just won a free ticket to Bahamas. Send your Details"]
text51 = ["Hey there, How are you?"]

integers11 = vectorizer.transform(text11)
integers21 = vectorizer.transform(text21)
integers31 = vectorizer.transform(text31)
integers41 = vectorizer.transform(text41)
integers51 = vectorizer.transform(text51)

# +
p11 = model.predict(integers11)[0]
p21 = model.predict(integers21)[0]
p31 = model.predict(integers31)[0]
p41 = model.predict(integers41)[0]
p51 = best_model.predict(integers51)[0]

find(p11)
find(p21)
find(p31)
find(p41)
find(p51)
# -



StarcoderdataJupyterScriptsDedupFiltered