8.5 KiB
8.5 KiB
In [ ]:
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.model_selection import train_test_split
DATA_PATH = Path("data/SMSSpamCollection")
RANDOM_STATE = 42
if not DATA_PATH.exists():
raise FileNotFoundError(
"Missing data/SMSSpamCollection. Follow data/README.md to download the official dataset."
)
In [ ]:
tiny_messages = [
"free prize now",
"free meeting today",
"project meeting today",
]
count_vectorizer = CountVectorizer()
tiny_bow = count_vectorizer.fit_transform(tiny_messages)
tiny_bow_df = pd.DataFrame(
tiny_bow.toarray(),
columns=count_vectorizer.get_feature_names_out(),
index=["message_1", "message_2", "message_3"],
)
tiny_bow_df
In [ ]:
df = pd.read_csv(DATA_PATH, sep="\t", header=None, names=["label", "message"])
df["label"] = df["label"].astype("string").str.strip().str.lower()
df["message"] = df["message"].astype("string").str.strip()
df = df.dropna(subset=["label", "message"])
df = df[df["label"].isin(["ham", "spam"]) & df["message"].ne("")]
conflicts = df.groupby("message")["label"].nunique()
conflict_texts = conflicts[conflicts.gt(1)].index
df = df[~df["message"].isin(conflict_texts)]
df = df.drop_duplicates(subset=["label", "message"]).reset_index(drop=True)
X = df["message"]
y = df["label"].map({"ham": 0, "spam": 1}).astype(int)
print("Clean messages:", len(df))
In [ ]:
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.20,
random_state=RANDOM_STATE,
stratify=y,
)
tfidf_vectorizer = TfidfVectorizer(lowercase=True)
X_train_tfidf = tfidf_vectorizer.fit_transform(X_train)
X_test_tfidf = tfidf_vectorizer.transform(X_test)
density = X_train_tfidf.nnz / (X_train_tfidf.shape[0] * X_train_tfidf.shape[1])
print("Training matrix shape:", X_train_tfidf.shape)
print("Test matrix shape:", X_test_tfidf.shape)
print("Training density:", f"{density:.4%}")
In [ ]:
mean_tfidf = np.asarray(X_train_tfidf.mean(axis=0)).ravel()
top_terms = pd.Series(
mean_tfidf,
index=tfidf_vectorizer.get_feature_names_out(),
name="mean_tfidf",
).sort_values(ascending=False).head(15)
top_terms
In [ ]:
ngram_vectorizer = TfidfVectorizer(ngram_range=(1, 2), min_df=1)
X_train_ngram = ngram_vectorizer.fit_transform(X_train)
ngram_features = ngram_vectorizer.get_feature_names_out()
example_bigrams = [term for term in ngram_features if " " in term][:10]
print("Unigram features:", X_train_tfidf.shape[1])
print("Unigram + bigram features:", X_train_ngram.shape[1])
print("Example bigrams:", example_bigrams)
In [ ]:
assert tiny_bow.shape == (3, len(count_vectorizer.get_feature_names_out()))
assert tiny_bow_df.loc["message_1", "free"] == 1
assert len(X_train) + len(X_test) == len(df)
assert X_train_tfidf.shape[0] == len(X_train)
assert X_test_tfidf.shape[0] == len(X_test)
assert X_train_tfidf.shape[1] == X_test_tfidf.shape[1]
assert X_train_tfidf.nnz < X_train_tfidf.shape[0] * X_train_tfidf.shape[1]
assert X_train_ngram.shape[1] >= X_train_tfidf.shape[1]
assert len(top_terms) == 15
print("✓ Lab 2 completed")