{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "5838c9f8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: pandas in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (1.4.4)\n",
      "Requirement already satisfied: numpy in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (1.24.3)\n",
      "Requirement already satisfied: scikit-learn in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (1.0.2)\n",
      "Requirement already satisfied: matplotlib in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (3.5.2)\n",
      "Requirement already satisfied: python-dateutil>=2.8.1 in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (from pandas) (2.8.2)\n",
      "Requirement already satisfied: pytz>=2020.1 in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (from pandas) (2022.1)\n",
      "Requirement already satisfied: joblib>=0.11 in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (from scikit-learn) (1.1.0)\n",
      "Requirement already satisfied: scipy>=1.1.0 in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (from scikit-learn) (1.9.1)\n",
      "Requirement already satisfied: threadpoolctl>=2.0.0 in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (from scikit-learn) (2.2.0)\n",
      "Requirement already satisfied: fonttools>=4.22.0 in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (from matplotlib) (4.25.0)\n",
      "Requirement already satisfied: kiwisolver>=1.0.1 in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (from matplotlib) (1.4.2)\n",
      "Requirement already satisfied: pyparsing>=2.2.1 in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (from matplotlib) (3.0.9)\n",
      "Requirement already satisfied: pillow>=6.2.0 in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (from matplotlib) (9.2.0)\n",
      "Requirement already satisfied: packaging>=20.0 in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (from matplotlib) (21.3)\n",
      "Requirement already satisfied: cycler>=0.10 in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (from matplotlib) (0.11.0)\n",
      "Requirement already satisfied: six>=1.5 in c:\\users\\raed.tattan\\anaconda3\\lib\\site-packages (from python-dateutil>=2.8.1->pandas) (1.16.0)\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "pip install pandas numpy scikit-learn matplotlib"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "47a61000",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import required libraries \n",
    "import re  # For text cleaning using regular expressions\n",
    "import numpy as np  # For numerical operations\n",
    "import pandas as pd  # For reading and handling tabular data\n",
    "\n",
    "from sklearn.model_selection import train_test_split  # To split data into train/val/test\n",
    "from sklearn.preprocessing import LabelEncoder  # To convert labels (text) to numbers\n",
    "\n",
    "from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer  # Text -> numbers\n",
    "from sklearn.linear_model import LogisticRegression  # Model 1\n",
    "from sklearn.svm import LinearSVC  # Model 2 (SVM)\n",
    "from sklearn.naive_bayes import MultinomialNB  # Model 3 (Naive Bayes)\n",
    "\n",
    "from sklearn.metrics import accuracy_score, classification_report, confusion_matrix  # Evaluation\n",
    "\n",
    "import matplotlib.pyplot as plt  # Plot confusion matrix\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d46ef31c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dataset shape: (2600, 2)\n",
      "Columns: ['text', 'label']\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>text</th>\n",
       "      <th>label</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Java Concurrency in Practice is probably the b...</td>\n",
       "      <td>positive</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>haha aww hun i bet you are more creative tha...</td>\n",
       "      <td>positive</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>_pickle lol, thank you very much Hope you`re h...</td>\n",
       "      <td>positive</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Out for an evening on the town with jeremy. Sa...</td>\n",
       "      <td>negative</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>- just took over the #1 Most Endorsed spot on...</td>\n",
       "      <td>positive</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                                text     label\n",
       "0  Java Concurrency in Practice is probably the b...  positive\n",
       "1    haha aww hun i bet you are more creative tha...  positive\n",
       "2  _pickle lol, thank you very much Hope you`re h...  positive\n",
       "3  Out for an evening on the town with jeremy. Sa...  negative\n",
       "4   - just took over the #1 Most Endorsed spot on...  positive"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Load the dataset \n",
    "\n",
    "data_path = data_path = r\"C:\\Users\\Raed.tattan\\unbalanceddataset.csv\"  # Your dataset file name\n",
    "#data_path = data_path = data_path = \"C:/Users/Raed.tattan/f70c25fa-bf85-4802-a2b3-19fc13087be7.csv\"\n",
    "\n",
    "df = pd.read_csv(data_path)  # Read CSV file into a DataFrame\n",
    "\n",
    "# Choose the correct columns (change these if your file has different names)\n",
    "text_col = \"text\"        # Column that contains text\n",
    "label_col = \"sentiment\"  # Column that contains labels (classes)\n",
    "\n",
    "# Keep only the needed columns and rename them to standard names\n",
    "df = df[[text_col, label_col]].rename(columns={text_col: \"text\", label_col: \"label\"})\n",
    "\n",
    "# Display basic info\n",
    "print(\"Dataset shape:\", df.shape)\n",
    "print(\"Columns:\", df.columns.tolist())\n",
    "df.head()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "a8e898d8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Label classes: ['negative', 'positive']\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>text</th>\n",
       "      <th>label</th>\n",
       "      <th>text_clean</th>\n",
       "      <th>label_id</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Java Concurrency in Practice is probably the b...</td>\n",
       "      <td>positive</td>\n",
       "      <td>java concurrency in practice is probably the b...</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>haha aww hun i bet you are more creative tha...</td>\n",
       "      <td>positive</td>\n",
       "      <td>haha aww hun i bet you are more creative than me</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>_pickle lol, thank you very much Hope you`re h...</td>\n",
       "      <td>positive</td>\n",
       "      <td>pickle lol thank you very much hope you re hav...</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Out for an evening on the town with jeremy. Sa...</td>\n",
       "      <td>negative</td>\n",
       "      <td>out for an evening on the town with jeremy sad...</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>- just took over the #1 Most Endorsed spot on...</td>\n",
       "      <td>positive</td>\n",
       "      <td>just took over the 1 most endorsed spot on twi...</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                                text     label  \\\n",
       "0  Java Concurrency in Practice is probably the b...  positive   \n",
       "1    haha aww hun i bet you are more creative tha...  positive   \n",
       "2  _pickle lol, thank you very much Hope you`re h...  positive   \n",
       "3  Out for an evening on the town with jeremy. Sa...  negative   \n",
       "4   - just took over the #1 Most Endorsed spot on...  positive   \n",
       "\n",
       "                                          text_clean  label_id  \n",
       "0  java concurrency in practice is probably the b...         1  \n",
       "1   haha aww hun i bet you are more creative than me         1  \n",
       "2  pickle lol thank you very much hope you re hav...         1  \n",
       "3  out for an evening on the town with jeremy sad...         0  \n",
       "4  just took over the 1 most endorsed spot on twi...         1  "
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Text preprocessing functions \n",
    "def clean_text(s):\n",
    "    \"\"\"Clean a single text string (student-friendly).\"\"\"\n",
    "    if not isinstance(s, str):\n",
    "        s = \"\"  # Handle missing or non-string values\n",
    "    s = s.lower()  # Convert to lowercase\n",
    "    s = re.sub(r\"http\\S+|www\\.\\S+\", \" \", s)  # Remove URLs\n",
    "    s = re.sub(r\"@\\w+\", \" \", s)  # Remove @mentions\n",
    "    s = re.sub(r\"[^a-z0-9\\s]\", \" \", s)  # Keep only letters, numbers, and spaces\n",
    "    s = re.sub(r\"\\s+\", \" \", s).strip()  # Remove extra spaces\n",
    "    return s\n",
    "# Apply cleaning\n",
    "df[\"text\"] = df[\"text\"].fillna(\"\")  # Fill missing texts\n",
    "df[\"text_clean\"] = df[\"text\"].apply(clean_text)  # Create a cleaned text column\n",
    "# Remove empty rows after cleaning\n",
    "df = df[df[\"text_clean\"].str.len() > 0].reset_index(drop=True)\n",
    "# Encode labels (convert class names to integers)\n",
    "le = LabelEncoder()\n",
    "df[\"label_id\"] = le.fit_transform(df[\"label\"].astype(str))\n",
    "print(\"Label classes:\", list(le.classes_))\n",
    "df.head()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "2526ddb7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train size: 1559\n",
      "Val size: 520\n",
      "Test size: 520\n"
     ]
    }
   ],
   "source": [
    "# Split the dataset into train/val/test \n",
    "X = df[\"text_clean\"].tolist()       # Clean texts\n",
    "y = df[\"label_id\"].to_numpy()       # Numeric labels\n",
    "# 1) Split into Train+Val and Test (80% / 20%)\n",
    "X_trval, X_test, y_trval, y_test = train_test_split(\n",
    "    X, y, test_size=0.2, random_state=42, stratify=y\n",
    ")\n",
    "# 2) Split Train+Val into Train and Val (75% of 80% = 60%, 25% of 80% = 20%)\n",
    "X_train, X_val, y_train, y_val = train_test_split(\n",
    "    X_trval, y_trval, test_size=0.25, random_state=42, stratify=y_trval\n",
    ")\n",
    "print(\"Train size:\", len(X_train))\n",
    "print(\"Val size:\", len(X_val))\n",
    "print(\"Test size:\", len(X_test))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "beabd925",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Evaluation helper \n",
    "def evaluate_model(model_name, y_true, y_pred, class_names):\n",
    "    \"\"\"Print basic evaluation metrics.\"\"\"\n",
    "    acc = accuracy_score(y_true, y_pred)\n",
    "    print(f\"\\n=== {model_name} ===\")\n",
    "    print(\"Accuracy:\", round(acc, 4))\n",
    "    print(\"\\nClassification Report:\\n\")\n",
    "    print(classification_report(y_true, y_pred, target_names=class_names, zero_division=0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "3493e888",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "=== BOW + Logistic Regression (VAL) ===\n",
      "Accuracy: 0.8192\n",
      "\n",
      "Classification Report:\n",
      "\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "    negative       0.66      0.44      0.53       120\n",
      "    positive       0.85      0.93      0.89       400\n",
      "\n",
      "    accuracy                           0.82       520\n",
      "   macro avg       0.76      0.69      0.71       520\n",
      "weighted avg       0.80      0.82      0.81       520\n",
      "\n",
      "\n",
      "=== BOW + Linear SVM (VAL) ===\n",
      "Accuracy: 0.8096\n",
      "\n",
      "Classification Report:\n",
      "\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "    negative       0.59      0.56      0.58       120\n",
      "    positive       0.87      0.89      0.88       400\n",
      "\n",
      "    accuracy                           0.81       520\n",
      "   macro avg       0.73      0.72      0.73       520\n",
      "weighted avg       0.81      0.81      0.81       520\n",
      "\n",
      "\n",
      "=== BOW + Naive Bayes (VAL) ===\n",
      "Accuracy: 0.8327\n",
      "\n",
      "Classification Report:\n",
      "\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "    negative       0.72      0.45      0.55       120\n",
      "    positive       0.85      0.95      0.90       400\n",
      "\n",
      "    accuracy                           0.83       520\n",
      "   macro avg       0.79      0.70      0.73       520\n",
      "weighted avg       0.82      0.83      0.82       520\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 1) Bag of Words representation\n",
    "bow = CountVectorizer(ngram_range=(1, 2), min_df=2, max_df=0.95)\n",
    "\n",
    "# Fit on train and transform train/val/test\n",
    "X_train_bow = bow.fit_transform(X_train)\n",
    "X_val_bow   = bow.transform(X_val)\n",
    "X_test_bow  = bow.transform(X_test)\n",
    "\n",
    "class_names = list(le.classes_)\n",
    "\n",
    "# Model A: Logistic Regression\n",
    "logreg = LogisticRegression(max_iter=2000, random_state=42)\n",
    "logreg.fit(X_train_bow, y_train)\n",
    "val_pred_logreg = logreg.predict(X_val_bow)\n",
    "evaluate_model(\"BOW + Logistic Regression (VAL)\", y_val, val_pred_logreg, class_names)\n",
    "\n",
    "# Model B: Linear SVM\n",
    "svm = LinearSVC(random_state=42)\n",
    "svm.fit(X_train_bow, y_train)\n",
    "val_pred_svm = svm.predict(X_val_bow)\n",
    "evaluate_model(\"BOW + Linear SVM (VAL)\", y_val, val_pred_svm, class_names)\n",
    "\n",
    "# Model C: Naive Bayes\n",
    "nb = MultinomialNB()\n",
    "nb.fit(X_train_bow, y_train)\n",
    "val_pred_nb = nb.predict(X_val_bow)\n",
    "evaluate_model(\"BOW + Naive Bayes (VAL)\", y_val, val_pred_nb, class_names)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "78589809",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "=== TFIDF + Logistic Regression (VAL) ===\n",
      "Accuracy: 0.8\n",
      "\n",
      "Classification Report:\n",
      "\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "    negative       0.94      0.14      0.25       120\n",
      "    positive       0.79      1.00      0.88       400\n",
      "\n",
      "    accuracy                           0.80       520\n",
      "   macro avg       0.87      0.57      0.57       520\n",
      "weighted avg       0.83      0.80      0.74       520\n",
      "\n",
      "\n",
      "=== TFIDF + Linear SVM (VAL) ===\n",
      "Accuracy: 0.825\n",
      "\n",
      "Classification Report:\n",
      "\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "    negative       0.68      0.46      0.55       120\n",
      "    positive       0.85      0.94      0.89       400\n",
      "\n",
      "    accuracy                           0.82       520\n",
      "   macro avg       0.77      0.70      0.72       520\n",
      "weighted avg       0.81      0.82      0.81       520\n",
      "\n",
      "\n",
      "=== TFIDF + Naive Bayes (VAL) ===\n",
      "Accuracy: 0.775\n",
      "\n",
      "Classification Report:\n",
      "\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "    negative       0.71      0.04      0.08       120\n",
      "    positive       0.78      0.99      0.87       400\n",
      "\n",
      "    accuracy                           0.78       520\n",
      "   macro avg       0.75      0.52      0.48       520\n",
      "weighted avg       0.76      0.78      0.69       520\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 2) TF-IDF representation\n",
    "tfidf = TfidfVectorizer(ngram_range=(1, 2), min_df=2, max_df=0.95)\n",
    "\n",
    "# Fit on train and transform train/val/test\n",
    "X_train_tfidf = tfidf.fit_transform(X_train)\n",
    "X_val_tfidf   = tfidf.transform(X_val)\n",
    "X_test_tfidf  = tfidf.transform(X_test)\n",
    "\n",
    "# Model A: Logistic Regression\n",
    "logreg_t = LogisticRegression(max_iter=2000, random_state=42)\n",
    "logreg_t.fit(X_train_tfidf, y_train)\n",
    "val_pred_logreg_t = logreg_t.predict(X_val_tfidf)\n",
    "evaluate_model(\"TFIDF + Logistic Regression (VAL)\", y_val, val_pred_logreg_t, class_names)\n",
    "\n",
    "# Model B: Linear SVM\n",
    "svm_t = LinearSVC(random_state=42)\n",
    "svm_t.fit(X_train_tfidf, y_train)\n",
    "val_pred_svm_t = svm_t.predict(X_val_tfidf)\n",
    "evaluate_model(\"TFIDF + Linear SVM (VAL)\", y_val, val_pred_svm_t, class_names)\n",
    "\n",
    "# Model C: Naive Bayes\n",
    "nb_t = MultinomialNB()\n",
    "nb_t.fit(X_train_tfidf, y_train)\n",
    "val_pred_nb_t = nb_t.predict(X_val_tfidf)\n",
    "evaluate_model(\"TFIDF + Naive Bayes (VAL)\", y_val, val_pred_nb_t, class_names)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "ed0e5ade",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "=== FINAL: TFIDF + Linear SVM (TEST) ===\n",
      "Accuracy: 0.8288\n",
      "\n",
      "Classification Report:\n",
      "\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "    negative       0.69      0.47      0.56       120\n",
      "    positive       0.86      0.94      0.89       400\n",
      "\n",
      "    accuracy                           0.83       520\n",
      "   macro avg       0.77      0.71      0.73       520\n",
      "weighted avg       0.82      0.83      0.82       520\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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xMSK87OrFOSIiIiYmPG/I799FEVaXSUiIiKmpEyOiIiIialcJSIiIuZUjOtVKleJiIiIKSmTIyIiYmZOKFehcpWIiIjcbm71jse3E5WrRERExJSUyRERETExra4SERERczIs+Z9TU0SDHJWrRERExJSUyRERETGx4jzxWEGOiIiImWkzQBERERFzUSZHRETExLS6SkRERMyriJab8kvlKhERETElZXJERERMTOUqERERMSetrhIRERExF2VyRERETM3yvyO/fRQ9CnJERETMTOUqEREREXNRJkdERMTMinEmR0GOiIiImRmWS0d++yiCVK4SERERU1ImR0RExMQM49KR3z6KIgU5IiIiZlaM5+SoXCUiIiKmpEyOiIiImRXjiccKckREREzMYlw68ttHUaRylYiIiJiSMjkiIiJmVownHivIERERMbNiPCfnpspVixcv5r777iMsLIxjx44BMHPmTD777DOnDk5ERETkZuU5yJkzZw7PPPMMHTp04Ny5c2RnZwNQokQJZs6c6ezxiYiISH4YTjqKoDwHObNnz+b9999n/PjxuLq62s83aNCAPXv2OHVwIiIikk8KcnLvyJEj1KtXL8d5q9VKamqqUwYlIiIikl95DnIqVqzIrl27cpz/+uuvqVGjhjPGJCIiIs5SjDM5eV5d9eyzzzJ8+HDS09MxDIOff/6ZDz/8kClTpvDBBx8UxBhFRETkZhXj1VV5DnIef/xxLl68yHPPPceFCxfo3bs3ZcqUYdasWTz88MMFMUYRERGRPLupfXIGDRrEoEGDOH36NDabjaCgIGePS0RERJygOD/WIV+bAZYuXdpZ4xAREZGCUIx3PL6piceVKlW67iEiIiLF15QpU7j33nvx8/MjKCiIbt26ceDAAYc2hmEwceJEwsLC8PLyonnz5uzbt8+hTUZGBiNGjKB06dL4+PjQpUsXTpw4kaex5DmTM2rUKIfXWVlZ7Ny5k7Vr1/Lss8/mtTsRERExkS1btjB8+HDuvfdeLl68yPjx42nbti379+/Hx8cHgGnTpjF9+nSioqKoVq0akyZNok2bNhw4cAA/Pz/gUrzxxRdfsHz5ckqVKsXo0aPp1KkTO3bscNin7+/kOch5+umnr3n+P//5D9u3b89rdyIiIlKALDhhTk4e2q5du9bh9YIFCwgKCmLHjh00bdoUwzCYOXMm48ePp3v37gAsXLiQ4OBgli1bxpAhQ0hKSmLevHksXryY1q1bA7BkyRLCw8NZv3497dq1y9VYnPaAzvbt2zN27FgWLFjgrC6LrYvH/wSLe2EPQ+SW+ebkrsIegsgtl3zeRslqhT2KvElOTnZ4bbVasVqtf3tPUlISAIGBgcClTYXj4+Np27atQz/NmjVj27ZtDBkyhB07dpCVleXQJiwsjFq1arFt27ZcBzk39YDOa/n000/tH0BERERuE5f3ycnvAYSHhxMQEGA/pkyZ8vdvbRg888wz/OMf/6BWrVoAxMfHAxAcHOzQNjg42H4tPj4eDw8PSpYsed02uZHnTE69evWwWP4/cWUYBvHx8Zw6dYp33nknr92JiIhIQXLi6qrY2Fj8/f3tp2+UxXnqqafYvXs3W7duzXHtylgCLsUTV5/LMYxctLlSnoOcbt26Obx2cXHhjjvuoHnz5tx555157U5ERESKCH9/f4cg5++MGDGCzz//nO+++46yZcvaz4eEhACXsjWhoaH28wkJCfbsTkhICJmZmSQmJjpkcxISEoiMjMz1ePMU5Fy8eJEKFSrQrl07+yBFRETkNnaL98kxDIMRI0awatUqNm/eTMWKFR2uV6xYkZCQENatW2d/4HdmZiZbtmzh9ddfB6B+/fq4u7uzbt06evbsCUBcXBx79+5l2rRpuR5LnoIcNzc3nnzySX777be83CYiIiKF5FbveDx8+HCWLVvGZ599hp+fn30OTUBAAF5eXlgsFkaNGsXkyZOpWrUqVatWZfLkyXh7e9O7d2972wEDBjB69GhKlSpFYGAgY8aMoXbt2vbVVrmR53JVo0aN2LlzJ+XLl8/rrSIiInKr3eJMzpw5cwBo3ry5w/kFCxbQv39/AJ577jnS0tIYNmwYiYmJNGrUiG+//da+Rw7AjBkzcHNzo2fPnqSlpdGqVSuioqJyvUcO3ESQM2zYMEaPHs2JEyeoX7++fWOfy+rUqZPXLkVERMQkDOPGEZHFYmHixIlMnDjxum08PT2ZPXs2s2fPvumx5DrIeeKJJ5g5cya9evUCYOTIkQ6DvTzjOTs7+6YHIyIiIk5WjJ9dlesgZ+HChUydOpUjR44U5HhERETEifQU8ly4nH7SXBwREREpCvI0JycvG/CIiIjIbeCKHYvz1UcRlKcgp1q1ajcMdM6ePZuvAYmIiIgTaU5O7rz88ssEBAQU1FhEREREnCZPQc7DDz9MUFBQQY1FREREnEwTj3NB83FERESKoGJcrnLJbcPcbO4jIiIicrvIdSbHZrMV5DhERESkIDihXFVUMzl5fqyDiIiIFCEqV4mIiIiYizI5IiIiZlaMMzkKckREREysOC8hV7lKRERETElBjoiIiJiSylUiIiJmVozn5CiTIyIiIqakTI6IiIiJFeeJxwpyREREzK6IBin5pXKViIiImJIyOSIiImZWjCceK8gRERExseI8J0flKhERETElZXJERETMTOUqERERMSOVq0RERERMRpkcERERM1O5SkREREypGAc5KleJiIiIKSmTIyIiYmLFeeKxghwREREzU7lKRERExFyUyRERETGzYpzJUZAjIiJiYsV5To7KVSIiImJKyuSIiIiYmcpVIiIiYkYqV4mIiIiYjDI5IiIiZqZylYiIiJhSMQ5yVK4SERERU1ImR0RExMQs/zvy20dRpCBHRETEzFSuEhEREXGO7777js6dOxMWFobFYmH16tUO1/v374/FYnE4Gjdu7NAmIyODESNGULp0aXx8fOjSpQsnTpzI0zgU5IiIiJjY5X1y8nvkRWpqKnXr1uXtt9++bpv777+fuLg4+7FmzRqH66NGjWLVqlUsX76crVu3kpKSQqdOncjOzs71OFSuEhERMTMnlquSk5MdTlutVqxWa47m7du3p3379n/bpdVqJSQk5JrXkpKSmDdvHosXL6Z169YALFmyhPDwcNavX0+7du1yNWxlckRERCRXwsPDCQgIsB9Tpky56b42b95MUFAQ1apVY9CgQSQkJNiv7dixg6ysLNq2bWs/FxYWRq1atdi2bVuu30OZHBEREbNz0sTh2NhY/P397a+vlcXJjfbt29OjRw/Kly/PkSNHePHFF2nZsiU7duzAarUSHx+Ph4cHJUuWdLgvODiY+Pj4XL+PghwRERETc+azq/z9/R2CnJvVq1cv+3/XqlWLBg0aUL58eb766iu6d+9+3fsMw8Biyf2CdpWrREREpFCFhoZSvnx5Dh48CEBISAiZmZkkJiY6tEtISCA4ODjX/SrIERERMTPDSUcBOnPmDLGxsYSGhgJQv3593N3dWbdunb1NXFwce/fuJTIyMtf9qlwlIiJiYs4sV+VWSkoKhw4dsr8+cuQIu3btIjAwkMDAQCZOnMiDDz5IaGgo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M5x05MF3331H586dCQsLw2KxsHr1aschGQYTJ04kLCwMLy8vmjdvzr59+xzaZGRkMGLECEqXLo2Pjw9dunThxIm8bUipIEdEREScKjU1lbp16/L2229f8/q0adOYPn06b7/9NjExMYSEhNCmTRvOn///zVhHjRrFqlWrWL58OVu3biUlJYVOnTrlam+7y27LcpWIiIg4hwUnTDzOY/v27dvbnwpwNcMwmDlzJuPHj6d79+7ApYdyBwcHs2zZMoYMGUJSUhLz5s1j8eLFtG7dGoAlS5YQHh7O+vXradeuXa7GoUyOiIiImV3e8Ti/B5eWpV95ZGRk5Hk4R44cIT4+nrZt29rPWa1WmjVrxrZt2wDYsWMHWVlZDm3CwsKoVauWvU1uKMgRERGRXAkPDycgIMB+TJkyJc99xMfHAxAc7LhxZXBwsP1afHw8Hh4elCxZ8rptckPlKhERERNz5j45sbGxDpsB5uch05cfqn3ZlQ/avp7ctLmSMjkiIiJm5sTVVf7+/g7HzQQ5ISEhADkyMgkJCfbsTkhICJmZmSQmJl63TW4oyBERETExi2E45XCWihUrEhISwrp16+znMjMz2bJlC5GRkQDUr18fd3d3hzZxcXHs3bvX3iY3VK4SERERp0pJSeHQoUP210eOHGHXrl0EBgZSrlw5Ro0axeTJk6latSpVq1Zl8uTJeHt707t3bwACAgIYMGAAo0ePplSpUgQGBjJmzBhq165tX22VGwpyREREzMz2vyO/feTB9u3badGihf31M888A0C/fv2IioriueeeIy0tjWHDhpGYmEijRo349ttv8fPzs98zY8YM3Nzc6NmzJ2lpabRq1YqoqChcXV1zPY7b8inkxZWeQi7FlZ5CLsXRrXoKedMmL+Hmls+nkF9M57vvXylyTyHXnBwRERExJZWrREREzOwmnj11zT6KIAU5IiIiZnbFjsX56qMIUrlKRERETEmZHBERERNz5o7HRY2CHBERETNTuUpERETEXJTJERERMTGL7dKR3z6KIgU5IiIiZqZylYiIiIi5KJMjIiJiZtoMUERERMzIYhhY8lluyu/9hUXlKhERETElZXJERETMrBhPPFaQIyIiYmYGkN8l4EUzxlG5SkRERMxJmRwRERETK84TjxXkiIiImJmBE+bkOGUkt5zKVSIiImJKyuSIiIiYmVZXiYiIiCnZ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wYM89thjJCQk8MMPPxASEpKjDwX3IjemIEcKxdUThC9cuEB0dDR33303sbGxdO/enZEjR/L000+zaNEiBg8eTEREBNOnT6devXqFOHIR55kwYQJz585l8eLFRERE4Ovra7927Ngxevbsyblz59i0aRNhYWH2a5pgL5I7mqUpt9yVP6CXLFnC8ePH8fb2JiIigsDAQL777jsqVKhAv379gEuPdujQoQNly5albt26hTl0Eaf5448/WLFiBXPnzqVNmzakp6eza9cupk2bxscff0z58uX59NNPyc7O5p///KfDvQpwRHJHuU65pa5M0+/atYs33niDRYsWsXjxYoKDg7HZbPz111+kpqZy+vRpPD09+fLLL+nSpQsDBgzI0YdIUeXq6oqHhwdJSUmsX7+eDz/8kF9++YWMjAwuXLhAYmIiQ4YMYfPmzYSGhhb2cEWKJP2mkFvGMAx7cDJ16lRef/11MjMz2bRpE3379uXPP//ExcWFrl278t///pfOnTtTo0YN/vjjDx577LEcfYgUFTabLce50NBQwsLCmD59Ou3atcPf35+pU6eybds2qlSpwtmzZwEoW7Ysrq6uZGdn3+phixR5yuTILXM5xf7vf/+b1157jRUrVlC2bFm++uorVqxYQd++fVm4cCH33nsv69ev54cffsDFxYUnn3wSNzc3TbSUIunKzON3331HSkoK7u7utGnThs8++4yYmBhcXFxo2LCh/Z709PQcE+o1wV4k7zTxWG6p9PR0evbsSa1atewbmQEsW7aMV199lXLlyjF//nzKlCnjMHdHq6ikqHv22WdZunQpvr6+HD58mA4dOvDPf/6Tli1bAnD+/HlOnTrF8OHDiYuLY/v27QrqRfJJeX+5pTw9PXFzc+PAgQMO53v37k2zZs1Yt24dAwYM4OTJk1gsFvvzqBTgSFE2b948Fi1axOrVq/nxxx/55ZdfOHPmDNOnT2fr1q0ALF68mH79+pGZmUlMTAxubm4qUYnkk4IcKTDXmodgGAYNGzbk0KFDbNmyxf4gToC7776bLl264OrqyrRp08jKytIqEjGF3bt306RJExo2bEjJkiWpW7cuH3zwAYcPH2bRokUADBw4kGeeeYZvv/0Wd3d3Ll68qOBeJJ9UrpICceU8hG+++YbExEQAunbtipubGy1atCAzM5OJEycSGRmJu7s7jz76KE2aNOH06dN8+umnREdHExgYWJgfQyTPrl79ZxgGAwYMIC4ujq+//hqbzUZ2djbu7u58+OGHPPnkk+zevdv+/CpQeVbEWZTJkQJx+Yf8888/z8CBA/nggw8YM2YM7dq1IyYmhvXr1+Pt7c0LL7xArVq1aNy4Mfv27eOZZ56hVatWGIZBRkZGIX8Kkby5MsA5dOgQJ0+exDAM+vfvzzfffMOKFStwcXGxP2/Nzc2NypUrExAQ4NCPAhwR59CsNikw8+fPZ8mSJXz++efUr1+fuXPnMnz4cBITE/H09OTbb79ly5Yt7Nu3D39/f/vmf5988glhYWH4+fkV8icQyb0rtzd44YUXWLVqFWfOnKFmzZr06NGDN954g0cffZSUlBTatm2Lq6srCxYsIDg4GH9//0IevYg5qVwlBea5554jIyODWbNm8dFHHzFkyBCmTJnCk08+yfnz58nOzqZEiRL29j///DOLFy9m6dKlbN68mTp16hTe4EXy4MoMzvLly3nmmWeYM2cO586dY//+/bz11lsMHjyYu+66i6effprg4GC8vLzw9fUlOjoad3d3bXIpUgCUyRGnuPpZOjabjQMHDtC0aVN++eUXBg4cyBtvvMHQoUOx2WxERUXh7+/Po48+ak/N//HHH/zyyy8KcKTIuRycbN68mQ0bNvDss8/StWtXAJKTkylXrhwvvPACy5cvZ8+ePfz3v//Fzc2Ndu3a4erqqj2gRAqIMjmSb1f+BfrHH3/g6+tLUFAQy5YtY+DAgaSnp7N06VIeeeQRAFJSUujevTsNGzZk0qRJDn0lJycrdS9FUnx8PP/4xz9ISEjg+eefZ/z48fZrZ8+e5YknniA8PJzZs2c73KdJxiIFR7lRybfLAc64cePo0qULNWrU4LnnnsNqtTJgwABCQ0MJDg4mLS2NQ4cO0aNHD86ePcvEiRNz9KUAR4qqkJAQVq5cSVBQECtXrmTnzp32a4GBgZQuXZqDBw/muE8BjkjBUZAjN+3KfXA++eQTFi1axKRJkxg5ciRbt27lk08+oWzZsvTo0YN27dpx55138uCDD5KSksKPP/6ozc7EdOrUqcPKlSvJzs5m1qxZ7Nq1C7i0m/F///tfwsPDC3eAIsWMylWSb9999x0rVqygbt26PPHEEwB8/vnnzJ49m5IlSzJo0CDCwsLYv38/d9xxB02bNsXFxUXzEMS0du7cyaOPPsqZM2e499578fDw4MiRI0RHR+Ph4ZFjDpuIFAxlciRf4uPjeeKJJ4iKiiI5Odl+vkuXLowcOZIzZ87wzjvvcP78eXr06EHz5s1xcXEhOztbAY6YVr169fjoo4/w9vYmKSmJNm3a8Msvv+Dh4aGdvEVuIQU5ki+X5yGEhISwZs0a9uzZY7/WuXNnRo8ezaFDh/jss88A9CwqKTZq1arFypUryczM5JdffuHQoUMA9o0ARaTgqVwlTvHrr7/y+OOP06BBA55++mlq1qxpv7Zt2zYaNWqkwEaKpZ07dzJ06FAqVarEhAkTuPPOOwt7SCLFhjI54hR169Zl3rx57Nixg1mzZrF//377tcjISFxdXTXJWIqlevXq8fbbbxMXF5fj8Q0iUrCUyRGn2rlzJ0OGDKF8+fJMmzaNihUrFvaQRG4L6enpeHp6FvYwRIoVZXLEqS7/1ern50f58uULezgitw0FOCK3njI5UiAuL5HV83hERKSwKMiRAqO9QEREpDDpT2wpMApwRESkMCnIEREREVNSkCMiIiKmpCBHRERETElBjoiIiJiSghwRERExJQU5InJLTZw4kbvvvtv+un///nTr1u2Wj+Po0aNYLBZ27dp1y99bRG4NBTkiAlwKNiwWCxaLBXd3dypVqsSYMWNITU0t0PedNWsWUVFRuWqrwERE8sKtsAcgIreP+++/nwULFpCVlcX333/PwIEDSU1NZc6cOQ7tsrKycHd3d8p76qGVIlJQlMkRETur1UpISAjh4eH07t2bPn36sHr1anuJaf78+VSqVAmr1YphGCQlJTF48GCCgoLw9/enZcuW/Prrrw59Tp06leDgYPz8/BgwYADp6ekO168uV9lsNl5//XWqVKmC1WqlXLlyvPbaawD2B77Wq1cPi8VC8+bN7fctWLCAu+66C09PT+68807eeecdh/f5+eefqVevHp6enjRo0ICdO3c68SsnIrcjZXJE5Lq8vLzIysoC4NChQ3z88cesWLECV1dXADp27EhgYCBr1qwhICCAuXPn0qpVK37//XcCAwP5+OOPmTBhAv/5z39o0qQJixcv5q233qJSpUrXfc+xY8fy/vvvM2PGDP7xj38QFxfHf//7X+BSoNKwYUPWr19PzZo18fDwAOD9999nwoQJvP3229SrV4+dO3cyaNAgfHx86NevH6mpqXTq1ImWLVuyZMkSjhw5wtNPP13AXz0RKXSGiIhhGP369TO6du1qf/3TTz8ZpUqVMnr27GlMmDDBcHd3NxISEuzXN2zYYPj7+xvp6ekO/VSuXNmYO3euYRiGERERYQwdOtTheqNGjYy6dete832Tk5MNq9VqvP/++9cc45EjRwzA2Llzp8P58PBwY9myZQ7nXn31VSMiIsIwDMOYO3euERgYaKSmptqvz5kz55p9iYh5qFwlInZffvklvr6+eHp6EhERQdOmTZk9ezYA5cuX54477rC33bFjBykpKZQqVQpfX1/7ceTIEQ4fPgzAb7/9RkREhMN7XP36Sr/99hsZGRm0atUq12M+deoUsbGxDBgwwGEckyZNchhH3bp18fb2ztU4RMQcVK4SEbsWLVowZ84c3N3dCQsLc5hc7OPj49DWZrMRGhrK5s2bc/RTokSJm3p/Ly+vPN9js9mASyWrRo0aOVy7XFYzDOOmxiMiRZuCHBGx8/HxoUqVKrlqe8899xAfH4+bmxsVKlS4Zpu77rqL6OhoHnvsMfu56Ojo6/ZZtWpVvLy82LBhAwMHDsxx/fIcnOzsbPu54OBgypQpwx9//EGfPn2u2W+NGjVYvHgxaWlp9kDq78YhIuagcpWI3JTWrVsTERFBt27d+Oabbzh69Cjbtm3jX//6F9u3bwfg6aefZv78+cyfP5/ff/+dCRMmsG/fvuv26enpyfPPP89zzz3HokWLOHz4MNHR0cybNw+AoKAgvLy8WLt2LX/99RdJSUnApQ0Gp0yZwqxZs/j999/Zs2cPCxYsYPr06QD07t0bFxcXBgwYwP79+1mzZg3//ve/C/grJCKFTUGOiNwUi8XCmjVraNq0KU888QTVqlXj4Ycf5ujRowQHBwPQq1cvXnrpJZ5//nnq16/PsWPHePLJJ/+23xdffJHRo0fz0ksvcdddd9GrVy8SEhIAcHNz46233mLu3LmEhYXRtWtXAAYOHMgHH3xAVFQUtWvXplmzZkRFRdmXnPv6+vLFF1+wf/9+6tWrx/jx43n99dcL8KsjIrcDi6FitYiIiJiQMjkiIiJiSgpyRERExJQU5IiIiIgpKcgRERERU1KQIyIiIqakIEdERERMSUGOiIiImJKCHBERETElBTkiIiJiSgpyRERExJQU5IiIiIgp/R+XHJ5oJ0IW7gAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 600x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Choose one final model \n",
    "# Here we select: TFIDF + Linear SVM (common strong baseline)\n",
    "final_vectorizer = tfidf\n",
    "final_model = svm_t\n",
    "\n",
    "# Predict on test set\n",
    "test_pred = final_model.predict(X_test_tfidf)\n",
    "evaluate_model(\"FINAL: TFIDF + Linear SVM (TEST)\", y_test, test_pred, class_names)\n",
    "\n",
    "# Confusion matrix\n",
    "cm = confusion_matrix(y_test, test_pred)\n",
    "\n",
    "plt.figure(figsize=(6, 5))\n",
    "plt.imshow(cm, interpolation=\"nearest\")\n",
    "plt.title(\"Confusion Matrix (Test)\")\n",
    "plt.colorbar()\n",
    "ticks = np.arange(len(class_names))\n",
    "plt.xticks(ticks, class_names, rotation=45, ha=\"right\")\n",
    "plt.yticks(ticks, class_names)\n",
    "plt.xlabel(\"Predicted\")\n",
    "plt.ylabel(\"True\")\n",
    "\n",
    "# Add numbers in cells\n",
    "for i in range(cm.shape[0]):\n",
    "    for j in range(cm.shape[1]):\n",
    "        plt.text(j, i, str(cm[i, j]), ha=\"center\", va=\"center\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "58a805f9",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.9.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
