diff --git a/test.ipynb b/test.ipynb
deleted file mode 100644
index b2cb7d5..0000000
--- a/test.ipynb
+++ /dev/null
@@ -1 +0,0 @@
-{"cells":[{"cell_type":"markdown","metadata":{"id":"QUdOQKBjalTs"},"source":["**# Install the library Urduhack: A Python NLP library for Urdu language**"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"7hDKYcogbFzg"},"outputs":[],"source":["!pip install Urduhack"]},{"cell_type":"markdown","metadata":{"id":"hsILf-3dbdIV"},"source":["**# Import the necessary libraries**"]},{"cell_type":"code","execution_count":2,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":3055,"status":"ok","timestamp":1702895856151,"user":{"displayName":"Khubaib Ahmad","userId":"13992953376961780846"},"user_tz":-300},"id":"t3JWEU9kaJhS","outputId":"9e92627f-cfc4-4fc1-d4b5-e06b692e9b24"},"outputs":[{"name":"stderr","output_type":"stream","text":["/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/tensorflow_addons/utils/tfa_eol_msg.py:23: UserWarning: \n","\n","TensorFlow Addons (TFA) has ended development and introduction of new features.\n","TFA has entered a minimal maintenance and release mode until a planned end of life in May 2024.\n","Please modify downstream libraries to take dependencies from other repositories in our TensorFlow community (e.g. Keras, Keras-CV, and Keras-NLP). \n","\n","For more information see: https://github.com/tensorflow/addons/issues/2807 \n","\n"," warnings.warn(\n","[nltk_data] Downloading package punkt to\n","[nltk_data] /Users/danial.shabbir/nltk_data...\n","[nltk_data] Package punkt is already up-to-date!\n"]}],"source":["from tqdm import tqdm\n","import os\n","import re\n","import calendar\n","import numpy as np\n","import pandas as pd\n","import pickle\n","from collections import Counter\n","import urduhack\n","import nltk\n","from nltk.tokenize import word_tokenize\n","nltk.download('punkt')\n","import json\n","import itertools\n","from typing import List\n","from sklearn.metrics import confusion_matrix, precision_score, recall_score\n","# Download UrduHack resources\n","urduhack.download()\n","from urduhack.normalization import normalize\n","from urduhack.preprocessing import normalize_whitespace, remove_punctuation, remove_accents, replace_urls, replace_emails, replace_numbers, replace_currency_symbols, remove_english_alphabets"]},{"cell_type":"markdown","metadata":{"id":"lf6xJ8mOaRRz"},"source":["**# Load the dataset from your google drive directory**"]},{"cell_type":"code","execution_count":78,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":67280,"status":"ok","timestamp":1702891465908,"user":{"displayName":"Khubaib Ahmad","userId":"13992953376961780846"},"user_tz":-300},"id":"UrJmeCOrIyd2","outputId":"1fd7d5ac-43db-4649-ce33-aa89d9789d56"},"outputs":[],"source":["# from google.colab import drive\n","# drive.mount('/content/drive')\n","# os.chdir('/content/drive/MyDrive/Colab Notebooks/Khubaib')\n","# import pandas as pd\n","\n","\n","data = pd.read_csv('data/urdu-news-dataset-1M.csv', index_col='Index', encoding='unicode_escape')"]},{"cell_type":"markdown","metadata":{"id":"GPVSV7XkK1vW"},"source":["**# Data preprocessing**"]},{"cell_type":"code","execution_count":79,"metadata":{"id":"TIeey5R-PH8C"},"outputs":[],"source":["# Drop null values\n","data = data.dropna()\n","data.reset_index(drop=True, inplace=True)"]},{"cell_type":"code","execution_count":80,"metadata":{"id":"Xm0w7zAbKwP2"},"outputs":[],"source":["# Function to convert encodings\n","def encodings_change(series):\n"," try:\n"," return [text.encode('latin1').decode('utf-8') for text in series]\n"," except:\n"," return series"]},{"cell_type":"code","execution_count":81,"metadata":{"id":"RMC_roiALETh"},"outputs":[],"source":["# Function to update date format\n","english_month_name = [i.lower() for i in list(calendar.month_name)]\n","urdu_month_map = ['',\n"," 'جنوری',\n"," 'فروری',\n"," 'مارچ',\n"," 'اپريل',\n"," 'مئی',\n"," 'جون',\n"," 'جولائی',\n"," 'اگست',\n"," 'ستمبر',\n"," 'اکتوبر',\n"," 'نومبر',\n"," 'دسمبر',\n"," '','','',\n"," 'اپریل',\n"," 'مئ','',\n"," 'جولائ',\n"," '','','','',''\n"," ]\n","\n","short_month_name = ['','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug',\n"," 'Sep','Oct','Nov','Dec']\n","\n","def date_encoding(date,urdu_month_map = urdu_month_map,\n"," english_month_name = english_month_name,\n"," short_month_name = short_month_name):\n","\n"," month = None\n"," if bool(re.match('\\d\\d\\d\\d\\-\\d\\d\\-\\d\\d',str(date))):\n"," return date\n"," if '/'in date:\n"," date = date.split('/')\n"," year = date[-1]\n"," month = date[0]\n"," day = date[1]\n"," date = f'{year}-{month}-{day}'\n"," return date\n","\n"," for i in date.encode('latin1').decode('utf-8').split():\n"," if ',' in i:\n"," i = i.replace(',','')\n"," if i in urdu_month_map:\n"," month = urdu_month_map.index(i)\n"," if month > 12:\n"," month = month - 12\n","\n"," elif i.lower() in english_month_name:\n"," month = english_month_name.index(i.lower())\n","\n"," elif i.lower() in short_month_name:\n"," month = short_month_name.index(i.lower())\n","\n"," if len(i)==4:\n"," year = i\n","\n"," if len(i)<=2:\n"," day = i\n","\n"," date = f'{year}-{month}-{day}'\n"," return date"]},{"cell_type":"code","execution_count":82,"metadata":{"id":"5baU_vQoLghD"},"outputs":[],"source":["def date_update(series):\n"," all_dates = []\n","\n"," for i,date in enumerate(series):\n"," if 'hours' in str(date):\n"," all_dates.append(np.nan)\n"," continue\n"," all_dates.append(date_encoding(date))\n"," return all_dates"]},{"cell_type":"code","execution_count":83,"metadata":{"id":"GHZcX0YALmpD"},"outputs":[],"source":["# Apply encodings_change and date_encoding to relevant columns\n","data['News Text'] = encodings_change(data['News Text'])\n","data['Headline'] = encodings_change(data['Headline'])\n","data['Date'] = date_update(data['Date'])"]},{"cell_type":"code","execution_count":84,"metadata":{},"outputs":[],"source":["# data = data.query('Headline.str.contains(\"خیبرپختونخوا\")')\\\n","# # .groupby('Category',as_index = False,group_keys=False).apply(lambda s: s.sample(2))\n","# #data.head()\n","# # data.head()\n"]},{"cell_type":"code","execution_count":85,"metadata":{"id":"ChzPzRiCR2AI"},"outputs":[],"source":["#save clean dataset\n","data_file = open('data/Urdu_News', 'wb')\n","pickle.dump(data, data_file)\n","data_file.close()"]},{"cell_type":"code","execution_count":86,"metadata":{"id":"IGHRNmsrV0NY"},"outputs":[],"source":[" # remove stop words\n","stop_words = frozenset(\"\"\"\n","آ آئی آئیں آئے آتا آتی آتے آداب آدھ آدھا آدھی آدھے آس\n"," آمدید آنا آنسہ آنی آنے آپ آگے آہ آہا آیا اب ابھی ابے\n"," اتوار ارب اربویں ارے اس اسکا اسکی اسکے اسی اسے اف افوہ الاول البتہ\n"," الثانی الحرام السلام الف المکرم ان اندر انکا انکی انکے انہوں انہی انہیں\n"," اوئے اور اوپر اوہو اپ اپنا اپنوں اپنی اپنے اپنےآپ اکبر اکثر اگر اگرچہ\n"," اگست اہاہا ایسا ایسی ایسے ایک بائیں بار بارے بالکل باوجود باہر بج بجے\n"," بخیر برسات بشرطیکہ بعض بغیر بلکہ بن بنا بناؤ بند بڑی بھر بھریں\n"," بھی بہار بہت بہتر بیگم تاکہ تاہم تب تجھ تجھی تجھے ترا تری\n"," تلک تم تمام تمہارا تمہاروں تمہاری تمہارے تمہیں تو تک تھا تھی تھیں تھے\n"," تہائی تیرا تیری تیرے تین جا جاؤ جائیں جائے جاتا جاتی جاتے جانی جانے\n"," جب جبکہ جدھر جس جسے جن جناب جنہوں جنہیں جو جہاں جی جیسا\n"," جیسوں جیسی جیسے جیٹھ حالانکہ حالاں حصہ حضرت خاطر خالی خدا خزاں خواہ خوب\n"," خود دائیں درمیان دریں دو دوران دوسرا دوسروں دوسری دوشنبہ دوں دکھائیں دگنا دی\n"," دیئے دیا دیتا دیتی دیتے دیر دینا دینی دینے دیکھو دیں دیے دے ذریعے\n"," رکھا رکھتا رکھتی رکھتے رکھنا رکھنی رکھنے رکھو رکھی رکھے رہ رہا رہتا\n"," رہتی رہتے رہنا رہنی رہنے رہو رہی رہیں رہے ساتھ سامنے ساڑھے سب سبھی\n"," سراسر سلام سمیت سوا سوائے سکا سکتا سکتے سہ سہی سی سے شام شاید\n"," شکریہ صاحب صاحبہ صرف ضرور طرح طرف طور علاوہ عین فروری فقط فلاں\n"," فی قبل قطا لائی لائے لاتا لاتی لاتے لانا لانی لایا لو لوجی لوگوں\n"," لگ لگا لگتا لگتی لگی لگیں لگے لہذا لی لیا لیتا لیتی لیتے لیکن\n"," لیں لیے لے ماسوا مت مجھ مجھی مجھے محترم محترمی محض مرا مرحبا\n"," مری مرے مزید مس مسز مسٹر مطابق مطلق مل منٹ منٹوں مکرمی مگر\n"," مگھر مہربانی میرا میروں میری میرے میں نا نزدیک نما نو نومبر نہ نہیں\n"," نیز نیچے نے و وار واسطے واقعی والا والوں والی والے واہ وجہ ورنہ\n"," وعلیکم وغیرہ ولے وگرنہ وہ وہاں وہی وہیں ویسا ویسے ویں پاس\n"," پایا پر پس پلیز پون پونا پونی پونے پھاگن پھر پہ پہر پہلا پہلی\n"," پہلے پیر پیچھے چاہئے چاہتے چاہیئے چاہے چلا چلو چلیں چلے چناچہ چند چونکہ\n"," چوگنی چکی چکیں چکے چہارشنبہ چیت ڈالنی ڈالنے ڈالے کئے کا کاتک کاش کب\n"," کبھی کدھر کر کرتا کرتی کرتے کرم کرنا کرنے کرو کریں کرے کس\n"," کسی کسے کل کم کن کنہیں کو کوئی کون کونسا کونسے کچھ کہ کہا\n"," کہاں کہہ کہی کہیں کہے کی کیا کیسا کیسے کیونکر کیونکہ کیوں کیے کے\n"," گئی گئے گا گرما گرمی گنا گو گویا گھنٹا گھنٹوں گھنٹے گی گیا\n"," ہائیں ہائے ہاڑ ہاں ہر ہرچند ہرگز ہزار ہفتہ ہم ہمارا ہماری ہمارے ہمی\n"," ہمیں ہو ہوئی ہوئیں ہوئے ہوا ہوبہو ہوتا ہوتی ہوتیں ہوتے ہونا ہونگے ہونی\n"," وہاں یہاں کہاں ہم ہر ہوۓ ہوئ ہوا ہوگا ہوگی ہوں گے میں کو تھا تھی تھے ہے سے اور اس ان اسے انہوں انہیں تک تم تو کا کی کے نا نے گا گی گے آئ آۓ گۓ گئ گیا جاتی جاتے جاتا چکا چکی چکے دیا دیۓ دیتا دیتے رک رکی رکا رکے سکی سکا سکے ہونے ہوں ہی ہیلو ہیں ہے یا یات یعنی یک یہ یہاں یہی یہیں\n","\"\"\".split())"]},{"cell_type":"code","execution_count":87,"metadata":{"id":"oodPMLHzWUcQ"},"outputs":[],"source":["def remove_stopwords(text: str):\n"," return \" \".join(word for word in text.split() if word not in stop_words)\n","def preprocess_stopwords(text):\n"," text = remove_stopwords(text)\n"," return text\n","def apply_stopwords_preprocess(series):\n"," preprocessed_stopword = []\n"," for text in series:\n"," preprocessed_stopword.append(preprocess_stopwords(text))\n"," return preprocessed_stopword"]},{"cell_type":"code","execution_count":88,"metadata":{"id":"TMu1_zWAWopr"},"outputs":[],"source":["data['Headline'] = apply_stopwords_preprocess(data['Headline'])\n","data['Headline'] = data['Headline'].apply(normalize)\n","data['Headline'] = data['Headline'].apply(remove_accents)\n","data['Headline'] = data['Headline'].apply(replace_urls)\n","data['Headline'] = data['Headline'].apply(replace_emails)\n","data['Headline'] = data['Headline'].apply(replace_currency_symbols)\n","data['Headline'] = data['Headline'].apply(normalize_whitespace)\n","data['Headline'] = data['Headline'].apply(remove_punctuation)\n","data['Headline'] = data['Headline'].apply(replace_numbers)\n","data['Headline'] = data['Headline'].apply(remove_english_alphabets)"]},{"cell_type":"code","execution_count":89,"metadata":{"id":"Uz0hUOjoXyGw"},"outputs":[],"source":["def removing_unwanted_data(text):\n","\n"," # Format words and remove unwanted characters from news headlines\n"," text = re.sub(r'https?:\\/\\/.*[\\r\\n]*', '', text, flags=re.MULTILINE)\n"," text = re.sub(r'\\', ' ', text)\n"," text = re.sub(r'\\'', ' ', text)\n"," return text\n","data['Headline']= list(map(removing_unwanted_data,data.Headline))"]},{"cell_type":"markdown","metadata":{"id":"DodGGrB6dqoO"},"source":["**# Divide dataset into headlines that are related to user query and those headlines that are not related to user query**"]},{"cell_type":"code","execution_count":90,"metadata":{"id":"xnYPgI07Yk9t"},"outputs":[],"source":["#consider the entertainment as negative topic and the other as positive docs.\n","positive_headlines = []\n","business_positive_headlines = data['Headline'][(data['Category'] == 'Business & Economics')][0:19309]\n","science_positive_headlines = data['Headline'][(data['Category'] == 'Science & Technology')][0:6400]\n","sports_positive_headlines = data['Headline'][(data['Category'] == 'Sports')][0:35870]\n","negative_headlines = data['Headline'][data['Category'] == 'Entertainment'][0:27930]\n","#concatenate them into 80% training data\n","positive_headlines = pd.concat([business_positive_headlines, science_positive_headlines, sports_positive_headlines], axis=0)"]},{"cell_type":"markdown","metadata":{"id":"YKekSrF3dMQz"},"source":["**# Features extraction**"]},{"cell_type":"code","execution_count":91,"metadata":{"id":"hM1TPi-mc6NN"},"outputs":[],"source":["positive_unique_words = set()\n","positive_headlines.str.lower().str.split().apply(positive_unique_words.update)\n","positive_unique_words = list(positive_unique_words)"]},{"cell_type":"markdown","metadata":{"id":"8L-5ZtaUlMLU"},"source":["**# Apply pattern taxonomy model (PTM)**"]},{"cell_type":"code","execution_count":92,"metadata":{"id":"aAZ4hLHlfotM"},"outputs":[],"source":["#assign each head a number.\n","positive_titles_refs = [f'dp{i+1}' for i in positive_headlines.index]"]},{"cell_type":"code","execution_count":93,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":441326,"status":"ok","timestamp":1702896320269,"user":{"displayName":"Khubaib Ahmad","userId":"13992953376961780846"},"user_tz":-300},"id":"QWmZUqQBf33c","outputId":"f787e68a-bda6-46bb-a97f-663149f598f7"},"outputs":[{"name":"stderr","output_type":"stream","text":["100%|██████████| 61579/61579 [01:35<00:00, 645.96it/s] \n"]}],"source":["#assign each ref to its words.\n","#example:- dp1: ['he', 'is', 'mad']\n","set_of_paragraphs = {}\n","for i in tqdm(range(len(positive_headlines))):\n"," #tokenize headline\n"," title = word_tokenize(positive_headlines.iloc[i])\n"," terms = []\n"," for j in range(len(title)):\n"," if title[j] in positive_unique_words:\n"," terms.append(positive_unique_words[positive_unique_words.index(title[j])])\n"," set_of_paragraphs[positive_titles_refs[i]] = terms"]},{"cell_type":"code","execution_count":94,"metadata":{"id":"SZt2yUnkh-B6"},"outputs":[],"source":["#get tokenized terms\n","positive_terms = list(set_of_paragraphs.values())\n","#get dps\n","positive_dp = list(set_of_paragraphs.keys())"]},{"cell_type":"code","execution_count":95,"metadata":{"id":"iND12vDYiEwC"},"outputs":[],"source":["#save positive paragraphs refs to use them later in pattern depolying step\n","model_file = open('Model/Urdu Positive dp', 'wb')\n","pickle.dump(positive_dp, model_file)\n","model_file.close()\n","\n","#save positive terms to use them later in pattern depolying step\n","model_file = open('Model/Urdu Positive terms', 'wb')\n","pickle.dump(positive_terms, model_file)\n","model_file.close()"]},{"cell_type":"markdown","metadata":{"id":"v7VjULuPjxCR"},"source":["**# Create patterns from positive terms**"]},{"cell_type":"code","execution_count":96,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"UbmI43nWiZMb","outputId":"19330bc2-6f0b-491a-ac04-fe3fdce0dab2"},"outputs":[{"name":"stderr","output_type":"stream","text":["100%|██████████| 61579/61579 [06:03<00:00, 169.36it/s] \n"]}],"source":["def create_combinations_of_terms(terms):\n"," #combinations list.\n"," all_combinations = []\n"," for i in tqdm(range(len(terms))):\n"," combinations = []\n"," for j in range(1, len(terms[i])+1):\n"," combinations.append(list(itertools.combinations(terms[i], j)))\n"," #flatten it since it's 2d list.\n"," combinations = [item for sublist in combinations for item in sublist]\n"," all_combinations.append(combinations)\n"," return all_combinations\n","positive_combinations = create_combinations_of_terms(positive_terms)\n","len(positive_combinations)\n","#flatten the whole list of patterns. 2d -> 1d\n","positive_patterns = [item for sublist in positive_combinations for item in sublist]"]},{"cell_type":"markdown","metadata":{"id":"Hqx3FiSajK4B"},"source":["**# Create covering sets for the positive patterns**"]},{"cell_type":"code","execution_count":97,"metadata":{"id":"D-tjWnCWii3G"},"outputs":[{"name":"stderr","output_type":"stream","text":[" 0%| | 0/335673740 [00:00, ?it/s]"]},{"name":"stderr","output_type":"stream","text":[" 0%| | 6973/335673740 [02:35<2170:35:29, 42.96it/s]"]}],"source":["def create_covering_sets(patterns: List, terms: List, dp: List, min_support: int) -> dict:\n"," covering_sets = {}\n"," for i in tqdm(range(len(patterns))):\n"," count = 0\n"," terms_dp = []\n"," for j in range(len(terms)):\n"," #if pattern in the terms list\n"," if set(patterns[i]) <= set(terms[j]):\n"," count += 1\n"," terms_dp.append(dp[j])\n"," #if the support of the pattern is greater than min_support then its frequent pattern\n"," if count / len(terms) >= min_support:\n"," covering_sets[tuple(patterns[i])] = terms_dp\n"," return covering_sets\n","min_support = 0.0001\n","positive_covering_sets = create_covering_sets(positive_patterns, positive_terms, positive_dp, min_support)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Ua7E4dO7kjcZ"},"outputs":[],"source":["#Save PTM dictionary.\n","model_file = open('Model/Urdu Positive Doc PTM', 'wb')\n","pickle.dump(positive_covering_sets, model_file)\n","model_file.close()"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"YTX4VcMhklFN"},"outputs":[],"source":["#Open PTM dictionary.\n","with open('Model/Urdu Positive Doc PTM', 'rb') as f:\n"," positive_covering_sets = pickle.load(f)"]},{"cell_type":"markdown","metadata":{"id":"z4dkYhbWlw-X"},"source":["**# Apply closed sequential pattern mining algorithm to extract closed sequential patterns**"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"AlicapQzmKlI"},"outputs":[{"name":"stderr","output_type":"stream","text":["100%|██████████| 5398/5398 [00:05<00:00, 1003.15it/s]\n"]}],"source":["from tqdm import tqdm\n","closed_covering_sets = {}\n","covering_sets_keys = list(positive_covering_sets.keys())\n","# Create a tqdm instance with the total number of iterations\n","for i in tqdm(range(len(covering_sets_keys))):\n"," current_pattern = covering_sets_keys[i]\n"," is_closed = True\n"," for j in range(len(covering_sets_keys)):\n"," if i != j:\n"," other_pattern = covering_sets_keys[j]\n"," if set(current_pattern).issubset(other_pattern) and len(positive_covering_sets[current_pattern]) <= len(positive_covering_sets[other_pattern]):\n"," is_closed = False\n"," break\n"," if is_closed:\n"," closed_covering_sets[current_pattern] = positive_covering_sets[current_pattern]\n","\n","# Now, closed_covering_sets should contain the closed covering sets"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"gcG2WLtwmSFZ"},"outputs":[],"source":["#save clospan dictionary.\n","model_file = open('Model/Urdu Positive Doc Closed PTM', 'wb')\n","pickle.dump(closed_covering_sets, model_file)\n","model_file.close()"]},{"cell_type":"markdown","metadata":{"id":"_EQb32BPmlKK"},"source":["**# Now, features extraction from negative docs**"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"9B1-sUBFnMXE"},"outputs":[{"name":"stderr","output_type":"stream","text":["100%|██████████| 2/2 [00:00<00:00, 9754.20it/s]\n"]}],"source":["#Negative docs title refrences and negative docs titles dictionaries.\n","negative_titles_refs = [f'dp{i+1}' for i in negative_headlines.index]\n","titles = []\n","for i in tqdm(range(len(negative_headlines))):\n"," title = word_tokenize(negative_headlines.iloc[i])\n"," titles.append(title)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"I5JKrcmUnWiq"},"outputs":[],"source":["negative_doc_weights = {}\n","negative_doc_titles = {}\n","for i in range(len(titles)):\n"," summ = []\n"," for word in titles[i]:\n"," summ.append(sum([row.count(word) for row in titles]) / len(titles))\n"," negative_doc_weights[negative_titles_refs[i]] = sum(summ)\n"," negative_doc_titles[negative_titles_refs[i]] = titles[i]"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Ln1L3Gg-oOXS"},"outputs":[],"source":["#save weights of the negative docs.\n","model_file = open('Model/Urdu Negative_doc_weights', 'wb')\n","pickle.dump(negative_doc_weights, model_file)\n","model_file.close()\n","model_file = open('Model/Urdu Negative_doc_titles', 'wb')\n","pickle.dump(negative_doc_titles, model_file)\n","model_file.close()"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"JQV_T0iLo0m9"},"outputs":[],"source":["# Load required data and files\n","with open('data/Urdu_News', 'rb') as f:\n"," test_data = pickle.load(f)\n","with open('Model/Urdu Positive Doc Closed PTM', 'rb') as f:\n"," closed_positive_covering_sets = pickle.load(f)\n","with open('Model/Urdu Positive dp', 'rb') as f:\n"," positive_dp = pickle.load(f)\n","with open('Model/Urdu Positive terms', 'rb') as f:\n"," positive_terms = pickle.load(f)\n","with open('Model/Urdu Negative_doc_weights', 'rb') as f:\n"," negative_doc_weights = pickle.load(f)\n","with open('Model/Urdu Negative_doc_titles', 'rb') as f:\n"," negative_doc_titles = pickle.load(f)"]},{"cell_type":"markdown","metadata":{"id":"03jejSIJSaaF"},"source":["**# Patterns deployment**"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"cmTjvwauQniZ"},"outputs":[],"source":["positive_temp_covering_set = {}\n","def create_empty_covering_set(list_dps):\n"," temp_covering_set = {}\n"," for i in range(len(list_dps)):\n"," temp_covering_set[list_dps[i]] = []\n"," return temp_covering_set\n","positive_temp_covering_set = create_empty_covering_set(positive_dp)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"GIEIaWcaTuah"},"outputs":[],"source":["# d_patterns creation\n","def create_d_patterns(closed_covering_sets, empty_covering_set):\n","\n"," for key, value in closed_covering_sets.items():\n"," for i in range(len(value)):\n"," empty_covering_set[value[i]].append(key)\n"," return empty_covering_set\n","positive_temp_covering_set = create_d_patterns(closed_positive_covering_sets, positive_temp_covering_set)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"DIxR2O-iT-JH"},"outputs":[],"source":["def get_support_of_each_term(temp_covering_set):\n"," temp_values = []\n"," new_temp_covering_set = {}\n"," for key, value in temp_covering_set.items():\n"," if value != []:\n"," tmp_lst = [j for i in value for j in i]\n"," new_temp_covering_set[key] = dict(Counter(tmp_lst))\n"," temp_values.append(new_temp_covering_set[key])\n"," return new_temp_covering_set, temp_values\n","positive_terms_supports, positive_temp_values = get_support_of_each_term(positive_temp_covering_set)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"8eUZ4XAzUHpg"},"outputs":[],"source":["import copy\n","#create a deep copy of the positive terms support.\n","none_normalized_positive_d_pattern = copy.deepcopy(positive_terms_supports)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"i21jHXM0UTQB"},"outputs":[],"source":["# normalize d_patterns\n","def create_normalized_d_patterns(closed_covering_Set,terms_supports):\n"," \"\"\"\n"," This function is used to normalize the support of each pattern.\n"," ex:- {dp1: {'t1':1, 't2':2, 't3':3}} -> {dp1: {'t1':1/6, 't2':2/6, 't3':3/6}}\n"," \"\"\"\n"," for i in range(len(terms_supports)):\n"," summ = sum(list(terms_supports[i].values()))\n"," for key, value in terms_supports[i].items():\n"," terms_supports[i][key] = value * (1 / summ)\n"," i = 0\n"," for key, value in closed_covering_Set.items():\n"," closed_covering_Set[key] = terms_supports[i]\n"," i += 1\n"," return closed_covering_Set\n","normalized_positive_d_pattern = create_normalized_d_patterns(positive_terms_supports, positive_temp_values)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"082FgzYzUkYi"},"outputs":[],"source":["supports = list(none_normalized_positive_d_pattern.values())\n","sum_of_supports = []\n","for key, value in none_normalized_positive_d_pattern.items():\n"," sum_of_supports.append(sum(list(value.values())))\n","Threshold = min(sum_of_supports)"]},{"cell_type":"markdown","metadata":{"id":"0ih3RIrjVAvp"},"source":["**# Shuffling Algorithm**"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ZGYccRmFVK78"},"outputs":[],"source":["def shuffling_algorithm(nd: dict, norm_d_patterns:dict, mu:int) -> dict:\n"," \"\"\"\n"," This function is used to shuffle the supports of terms in dp if there are a noise term in the dp or remove the whole dp if all terms in the negative pattern.\n"," Params:-\n"," nd:- similar terms between positive and negative patterns.\n"," norm_d_patterns:- normalized positive d-pattern.\n"," mu:- constant value.\n"," \"\"\"\n"," keys = list(norm_d_patterns.keys())\n"," for key in keys:\n"," if key in nd:\n"," offering = 0\n"," base = 0\n"," if sorted(nd[key]) == sorted(list(norm_d_patterns[key].keys())):\n","\n"," del norm_d_patterns[key]\n"," else:\n"," supports = np.array(list(norm_d_patterns[key].values()))\n","\n"," dps = list(norm_d_patterns[key].keys())\n"," in_indices = [i for i, x in enumerate(dps) if x in nd[key]]\n"," out_indices = [i for i, x in enumerate(dps) if x not in nd[key]]\n"," sum_of_offering_supports = np.sum(supports[in_indices])\n"," sum_of_base_supports = np.sum(supports[out_indices])\n"," offering = (1 - (1/mu)) * sum_of_offering_supports\n"," base = sum_of_base_supports\n","\n"," for term in norm_d_patterns[key].keys():\n"," if term in nd[key]:\n"," norm_d_patterns[key][term] = (1/ mu) * norm_d_patterns[key][term]\n"," else:\n"," norm_d_patterns[key][term] = (1 + offering / base) * norm_d_patterns[key][term]\n"," return norm_d_patterns"]},{"cell_type":"markdown","metadata":{"id":"ULXDrvGfViAX"},"source":["**# IPEvolving Algorithm**"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"3A9NL3E9Vqul"},"outputs":[],"source":["negative_doc_titles_keys = list(negative_doc_titles.keys())\n","for key in negative_doc_titles_keys:\n"," #if the weight of the negaitve pattern >= TAHRESHOLD\n"," if negative_doc_weights[key] >= Threshold:\n"," nd = {}\n"," #get the terms in each dp\n"," normalized_positive_d_pattern_keys = list(normalized_positive_d_pattern.keys())\n"," for key_2 in normalized_positive_d_pattern_keys:\n"," #if there similar terms between positive patterns and negative patterns.\n"," if list(set(normalized_positive_d_pattern[key_2].keys()) & set(negative_doc_titles[key])) != []:\n"," #add these similar terms to the nd.\n"," nd[key_2] = list(set(normalized_positive_d_pattern[key_2].keys()) & set(negative_doc_titles[key]))\n"," #do shuffling.\n"," normalized_positive_d_pattern = shuffling_algorithm(nd, normalized_positive_d_pattern, 5)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"HRL_RrKhV4IB"},"outputs":[],"source":["# save updated d patterns\n","model_file = open('Model/d_patterns', 'wb')\n","pickle.dump(normalized_positive_d_pattern, model_file)\n","model_file.close()"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"flx5Kq9nWP5C"},"outputs":[],"source":["# open updated d patterns\n","with open('Model/d_patterns', 'rb') as f:\n"," data_patterns = pickle.load(f)"]},{"cell_type":"markdown","metadata":{"id":"IS0l7sW2XFah"},"source":["**# Test pattern mining algorithms**"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Ujth4cqKWu4K"},"outputs":[],"source":["# function to get headlines from testing data that are related to user query and contain updated d patterns\n","def search_for_query(query, data_patterns):\n"," \"\"\"\n"," This function searches for query in the normalized_positive_d_pattern and if it exists it will return the top paragraphs indecis.\n"," \"\"\"\n"," all_supports = {}\n"," words = word_tokenize(query)\n"," for key, value in data_patterns.items():\n"," dp_support = 0\n"," for word in words:\n"," if word in list(value.keys()):\n"," dp_support += value[word]\n"," all_supports[key] = dp_support\n"," #sort supports\n"," all_supports = dict(sorted(all_supports.items(), key=lambda item: item[1], reverse = True))\n"," results = list(all_supports.keys())\n"," final_supports = list(all_supports.values())\n"," #check if final supports has supports > 0\n"," check = all(v == 0 for v in final_supports)\n"," top_paragraphs = []\n"," #if all final supports has 0 values\n"," if check:\n"," print(\"ہم معزرت خواہ ہیں۔ آپ کے استفسار کے مطابق سسٹم میں ڈاکومنٹس موجود نہیں ہیں۔ یہ سسٹم محدود ڈیٹا سیٹ پر تیار کیا گیا ہے۔\")\n"," else:\n","\n"," for i in range(len(results)):\n"," if final_supports[i] >= 0.3:\n"," top_paragraphs.append(int(results[i][results[i].index('p')+1:]) -1)\n"," return top_paragraphs"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"xFmfCMYlYxf4"},"outputs":[],"source":["# function to print the retrieved result\n","def print_retrieved_queries(query, test_data,top_paragraphs):\n"," if top_paragraphs == []:\n"," print(\"ہم معزرت خواہ ہیں۔ آپ کے استفسار کے مطابق سسٹم میں ڈاکومنٹس موجود نہیں ہیں۔ یہ سسٹم محدود ڈیٹا سیٹ پر تیار کیا گیا ہے۔\")\n"," else:\n"," print(f\"Documents retrieved for query: {query}\")\n"," for i in range(len(top_paragraphs)):\n"," print(f\"Page {i+1}, Date {test_data['Date'].iloc[top_paragraphs[i]]}\")\n"," print(f\"Category: {test_data['Category'].iloc[top_paragraphs[i]]}\")\n"," print(f\"Source: {test_data['Source'].iloc[top_paragraphs[i]]}, {data['URL'].iloc[top_paragraphs[i]]}\")\n"," print(\"------------------------------------------------------Headline--------------------------------------------------------\")\n"," print(test_data['Headline'].iloc[top_paragraphs[i]])\n"," print(\"------------------------------------------------------News Text--------------------------------------------------------\")\n"," print(test_data['News Text'].iloc[top_paragraphs[i]])\n"," print(\"\\n\\n\")"]},{"cell_type":"markdown","metadata":{"id":"LqOaD2rkZbCG"},"source":["**# Function to calculate precision and recall**"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"exhWlc-GZulj"},"outputs":[],"source":["def compute_precision_recall(retrieved_categories, intended_category, total_relevant):\n"," # Count the number of retrieved documents that are relevant\n"," relevant_retrieved = sum([1 for cat in retrieved_categories if cat == intended_category])\n"," #print(relevant_retrieved)\n"," # Precision: fraction of retrieved documents that are relevant\n"," precision = relevant_retrieved / len(retrieved_categories) if retrieved_categories else 0\n"," # Recall: fraction of the total relevant documents that were retrieved\n"," recall = relevant_retrieved / total_relevant if total_relevant else 0\n"," return precision, recall"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["metrics = {\n"," \"Business & Economics\":{},\n"," \"Science & Technology\":{},\n"," \"Sports\":{}\n","}"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"3mWBxHObZUKD"},"outputs":[{"name":"stdout","output_type":"stream","text":["Documents retrieved for query: خیبرپختونخوا\n"]},{"ename":"IndexError","evalue":"single positional indexer is out-of-bounds","output_type":"error","traceback":["\u001b[0;31m---------------------------------------------------------------------------\u001b[0m","\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)","Cell \u001b[0;32mIn[77], line 4\u001b[0m\n\u001b[1;32m 2\u001b[0m query \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mخیبرپختونخوا\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;66;03m#input(\"Enter query: \")\u001b[39;00m\n\u001b[1;32m 3\u001b[0m top_paragraphs \u001b[38;5;241m=\u001b[39m search_for_query(query, data_patterns)\n\u001b[0;32m----> 4\u001b[0m \u001b[43mprint_retrieved_queries\u001b[49m\u001b[43m(\u001b[49m\u001b[43mquery\u001b[49m\u001b[43m \u001b[49m\u001b[43m,\u001b[49m\u001b[43mtest_data\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtop_paragraphs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 5\u001b[0m query_intended_category \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mBusiness & Economics\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;66;03m# This should be set based on the query's intended category\u001b[39;00m\n\u001b[1;32m 6\u001b[0m retrieved_categories \u001b[38;5;241m=\u001b[39m [test_data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mCategory\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39miloc[i] \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m top_paragraphs]\n","Cell \u001b[0;32mIn[73], line 8\u001b[0m, in \u001b[0;36mprint_retrieved_queries\u001b[0;34m(query, test_data, top_paragraphs)\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDocuments retrieved for query: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mquery\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[38;5;28mlen\u001b[39m(top_paragraphs)):\n\u001b[0;32m----> 8\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPage \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mi\u001b[38;5;241m+\u001b[39m\u001b[38;5;241m1\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m, Date \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[43mtest_data\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mDate\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43miloc\u001b[49m\u001b[43m[\u001b[49m\u001b[43mtop_paragraphs\u001b[49m\u001b[43m[\u001b[49m\u001b[43mi\u001b[49m\u001b[43m]\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCategory: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mtest_data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mCategory\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39miloc[top_paragraphs[i]]\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mSource: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mtest_data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mSource\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39miloc[top_paragraphs[i]]\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m, \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mdata[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mURL\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39miloc[top_paragraphs[i]]\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n","File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/pandas/core/indexing.py:1103\u001b[0m, in \u001b[0;36m_LocationIndexer.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 1100\u001b[0m axis \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39maxis \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m 1102\u001b[0m maybe_callable \u001b[38;5;241m=\u001b[39m com\u001b[38;5;241m.\u001b[39mapply_if_callable(key, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mobj)\n\u001b[0;32m-> 1103\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_getitem_axis\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmaybe_callable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43maxis\u001b[49m\u001b[43m)\u001b[49m\n","File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/pandas/core/indexing.py:1656\u001b[0m, in \u001b[0;36m_iLocIndexer._getitem_axis\u001b[0;34m(self, key, axis)\u001b[0m\n\u001b[1;32m 1653\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCannot index by location index with a non-integer key\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 1655\u001b[0m \u001b[38;5;66;03m# validate the location\u001b[39;00m\n\u001b[0;32m-> 1656\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_validate_integer\u001b[49m\u001b[43m(\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1658\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mobj\u001b[38;5;241m.\u001b[39m_ixs(key, axis\u001b[38;5;241m=\u001b[39maxis)\n","File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/pandas/core/indexing.py:1589\u001b[0m, in \u001b[0;36m_iLocIndexer._validate_integer\u001b[0;34m(self, key, axis)\u001b[0m\n\u001b[1;32m 1587\u001b[0m len_axis \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mobj\u001b[38;5;241m.\u001b[39m_get_axis(axis))\n\u001b[1;32m 1588\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m key \u001b[38;5;241m>\u001b[39m\u001b[38;5;241m=\u001b[39m len_axis \u001b[38;5;129;01mor\u001b[39;00m key \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m-\u001b[39mlen_axis:\n\u001b[0;32m-> 1589\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mIndexError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msingle positional indexer is out-of-bounds\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n","\u001b[0;31mIndexError\u001b[0m: single positional indexer is out-of-bounds"]}],"source":["# precision and recall for Business & Economics\n","query = \"آسٹریلیا میں کھیلوں کا انفراسٹرکچر\"#input(\"Enter query: \")\n","top_paragraphs = search_for_query(query, data_patterns)\n","print_retrieved_queries(query ,test_data, top_paragraphs)\n","query_intended_category = \"Business & Economics\" # This should be set based on the query's intended category\n","retrieved_categories = [test_data['Category'].iloc[i] for i in top_paragraphs]\n","#print(retrieved_categories)\n","total_relevant_docs = len(test_data[test_data['Category'] == query_intended_category])\n","#print(total_relevant_docs)\n","precision, recall = compute_precision_recall(retrieved_categories, query_intended_category, total_relevant_docs)\n","print(f\"Precision for the query: {precision:.7f}\")\n","print(f\"Recall for the query: {recall:.7f}\")\n","print(\"---------------------------------------------------------------\")\n","\n","# gather metrics category wise\n","metrics['Business & Economics']['precision'] = precision\n","metrics['Business & Economics']['recall'] = recall\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"4NE7CuKXaips"},"outputs":[{"name":"stdout","output_type":"stream","text":["ہم معزرت خواہ ہیں۔ آپ کے استفسار کے مطابق سسٹم میں ڈاکومنٹس موجود نہیں ہیں۔ یہ سسٹم محدود ڈیٹا سیٹ پر تیار کیا گیا ہے۔\n","ہم معزرت خواہ ہیں۔ آپ کے استفسار کے مطابق سسٹم میں ڈاکومنٹس موجود نہیں ہیں۔ یہ سسٹم محدود ڈیٹا سیٹ پر تیار کیا گیا ہے۔\n","Precision for the query: 0.0000000\n","Recall for the query: 0.0000000\n","---------------------------------------------------------------\n"]}],"source":["# precision and recall for Science & Technology\n","query = \"آسٹریلیا میں کھیلوں کا انفراسٹرکچر\"#input(\"Enter query: \")\n","top_paragraphs = search_for_query(query, data_patterns)\n","print_retrieved_queries(query ,test_data, top_paragraphs)\n","query_intended_category = \"Science & Technology\" # This should be set based on the query's intended category\n","retrieved_categories = [test_data['Category'].iloc[i] for i in top_paragraphs]\n","#print(retrieved_categories)\n","total_relevant_docs = len(test_data[test_data['Category'] == query_intended_category])\n","#print(total_relevant_docs)\n","precision, recall = compute_precision_recall(retrieved_categories, query_intended_category, total_relevant_docs)\n","print(f\"Precision for the query: {precision:.7f}\")\n","print(f\"Recall for the query: {recall:.7f}\")\n","print(\"---------------------------------------------------------------\")\n","\n","# gather metrics category wise\n","metrics['Science & Technology']['precision'] = precision\n","metrics['Science & Technology']['recall'] = recall\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"OosySb_Cavxp"},"outputs":[{"name":"stdout","output_type":"stream","text":["ہم معزرت خواہ ہیں۔ آپ کے استفسار کے مطابق سسٹم میں ڈاکومنٹس موجود نہیں ہیں۔ یہ سسٹم محدود ڈیٹا سیٹ پر تیار کیا گیا ہے۔\n","ہم معزرت خواہ ہیں۔ آپ کے استفسار کے مطابق سسٹم میں ڈاکومنٹس موجود نہیں ہیں۔ یہ سسٹم محدود ڈیٹا سیٹ پر تیار کیا گیا ہے۔\n","Precision for the query: 0.0000000\n","Recall for the query: 0.0000000\n","---------------------------------------------------------------\n"]}],"source":["# precision and recall for Sports\n","query = \"آسٹریلیا میں کھیلوں کا انفراسٹرکچر \" #input(\"Enter query: \")\n","top_paragraphs = search_for_query(query, data_patterns)\n","print_retrieved_queries(query ,test_data, top_paragraphs)\n","query_intended_category = \"Sports\" # This should be set based on the query's intended category\n","retrieved_categories = [test_data['Category'].iloc[i] for i in top_paragraphs]\n","#print(retrieved_categories)\n","total_relevant_docs = len(test_data[test_data['Category'] == query_intended_category])\n","#print(total_relevant_docs)\n","precision, recall = compute_precision_recall(retrieved_categories, query_intended_category, total_relevant_docs)\n","print(f\"Precision for the query: {precision:.7f}\")\n","print(f\"Recall for the query: {recall:.7f}\")\n","print(\"---------------------------------------------------------------\")\n","\n","\n","# gather metrics category wise\n","metrics['Sports']['precision'] = precision\n","metrics['Sports']['recall'] = recall\n"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["Metrics for categories\n"]},{"data":{"text/html":["
"],"text/plain":[" categories precision recall occurence weighted_precision \\\n","0 Business & Economics 0.0 0.0 10 0.0 \n","1 Science & Technology 0.0 0.0 10 0.0 \n","2 Sports 0.0 0.0 10 0.0 \n","\n"," weighted_recall \n","0 0.0 \n","1 0.0 \n","2 0.0 "]},"metadata":{},"output_type":"display_data"},{"name":"stdout","output_type":"stream","text":["Overal Precision: 0.0\n","Overal Recall: 0.0\n"]}],"source":["import pandas as pd \n","print(\"Metrics for categories\")\n","df = pd.DataFrame(metrics).T.head().reset_index().rename(columns = {'index':'categories'})\n","display(df.head())\n","\n","# get total headlines which belongs to this category\n","def total_instance(category):\n"," return data[data['Category'] == category].shape[0]\n","\n","df['occurence'] = df['categories'].apply(total_instance)\n","df['weighted_precision'] = df['occurence'] * df['precision']\n","df['weighted_recall'] = df['occurence'] * df['recall']\n","display(df.head())\n","overal_precision = round(df['weighted_precision'].sum() / df['occurence'].sum(), 2)\n","overal_recall = round(df['weighted_recall'].sum() / df['occurence'].sum(), 2)\n","\n","print(f\"Overal Precision: {overal_precision}\")\n","print(f\"Overal Recall: {overal_recall}\")\n","# total instances of each category \n","\n"]},{"cell_type":"markdown","metadata":{"id":"SbYimIpxfdDq"},"source":["**# precision line graph for single word queries**\n","\n","* first calculate precision for 25 queries one by one\n","* then enter calculated precision values one by one in y list\n","\n","* repeate the same process for recall line graph\n","\n","* remember its a manual line graph not system generated\n","\n","\n","\n","\n","\n"]},{"cell_type":"code","execution_count":83,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":472},"executionInfo":{"elapsed":749,"status":"ok","timestamp":1702929430728,"user":{"displayName":"Khubaib Ahmad","userId":"13992953376961780846"},"user_tz":-300},"id":"o6g8R5kUbrLp","outputId":"d63dd061-9ff2-4948-a354-60aeaee018a2"},"outputs":[{"data":{"image/png":"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UL0xh4aGwsbGBgkJCbh7965av7rOBPz1119qP5uYmKguqZSWltY4JjAwEC4uLkhOTlbrs3PnTpw5cwaDBg3SaNs00bJlS3h6euKjjz5CeXk5evbsCeDeB++FCxewefNmPPnkk/XeP6TqfkEPfpA11IP7z9raGn5+frXuO22YmpoiJCQEW7duxbVr11Tt58+fx86dO+sdP2LECBw8eBC7du2q9rtbt26hoqKizvG67PuBAwciJycHKSkpqraKigosW7YM1tbW6Nu3b73bHBYWhq1btyIzM1PVfubMmRq340GBgYFwdnZGcnIyysrKVO1r1qyp9revKlrun3tUWVmJlStXqvULCAhA69atsXjxYhQVFVVbZ35+fr151WTo0KEwNTXFvHnzqr1GhRCqY6uwsLDa36pz584wMTHRy3FGxodnbkjy+vbti8mTJyMhIQEnTpxAaGgozMzMkJGRgU2bNuHjjz/G888/D1tbW3z00UeYMGECunXrprrHxsmTJ1FSUlLrJaYJEybg5s2b6NevH1q0aIErV65g2bJl6NKli2qexIPMzMywcOFCREdHo2/fvhg5cqTqq+C+vr547bXX9LoPevfujY0bN6Jz586qf2137doVVlZWOHfunEbzbarOLkyfPh1hYWEwNTXFCy+80ODcHnvsMTz11FMICAiAg4MDjhw5gs2bN2Pq1KkNjg3c+6r5Tz/9hJ49e+KVV15BZWUlli9fjk6dOuHEiRN1jp05cya2b9+Of/3rXxg7diwCAgJQXFyM06dPY/Pmzbh8+TKcnJzqjKHtvp80aRI+++wzjB07FkePHoWvry82b96MAwcOIDExETY2NvVu87x585CamorevXtjypQpquKoY8eOOHXqVJ1jzczM8N5772Hy5Mno168fIiMjcenSJaxevbranJuOHTviySefRFxcHG7evAkHBwds3LixWiFhYmKCL774AuHh4ejYsSOio6Ph6emJ7Oxs7N27F7a2tvj+++/r3a4HtW7dGu+99x7i4uJw+fJlREREwMbGBpcuXcJ3332HSZMm4fXXX8d//vMfTJ06FcOHD0fbtm1RUVGBr7/+Gqamphg2bJjW66UmwIDf1CLSSNXXTQ8fPlxnv6ioKGFlZVXr71euXCkCAgKEhYWFsLGxEZ07dxZvvPGGuHbtmlq/7du3ix49eggLCwtha2srunfv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VIuXl5dWeRIREVHTolNxU1xcjHHjxsHd3R19+vRBnz594OHhgfHjx6OkpETfOaoolUq4uLhg5cqVCAgIQGRkJN5++20kJyfXOiYuLg4FBQWqJSsr66HlR0RERIanU3ETGxuLn3/+Gd9//z1u3bqluoz0888/49///rdGMZycnGBqaorc3Fy19tzcXLi5udU4xt3dHW3btlW7BNWhQwfk5OSgrKysxjEKhQK2trZqCxEREUmXTsXNt99+iy+//BLh4eGqgmHgwIH4/PPPsXnzZo1iyOVyBAQEIC0tTdWmVCqRlpaG4ODgGsf07NkT58+fV3vcw7lz5+Du7g65XK7LphAREZHE6FTclJSUVJsrAwAuLi5aXZaKjY3F559/jv/7v//DmTNn8Morr6C4uFj17akxY8YgLi5O1f+VV17BzZs3MWPGDJw7dw4//vgj3n//fcTExOiyGURERCRBOn0VPDg4GPHx8fjqq69gbm4OALhz5w7mzZtX61mXmkRGRiI/Px9z5sxBTk4OunTpgtTUVFXhlJmZCROTf+ovLy8v7Nq1C6+99hoef/xxeHp6YsaMGXjzzTd12QwiIiKSIJ2+Cv7bb78hLCwMpaWl8Pf3BwCcPHkS5ubm2LVrFzp27Kj3RPVFm6+SERERkXHQ5vNb5/vclJSUYN26dfjzzz8B3JvYO3r0aFhYWOgSrtGwuCEiImp6tPn81umyFABYWlpi4sSJug4nIiIieig0Lm62b9+O8PBwmJmZYfv27XX2ffbZZxucGBEREZEuNL4sZWJigpycHLi4uKhN8q0WUCZDZWWl3hLUN16WIiIianoeymWp++8tc///ExERERmTBj1b6n63bt3SVygiIiIinelU3CxcuBApKSmqn4cPHw4HBwd4enri5MmTekuOiIiISFs6FTfJycmqp2vv3r0be/bsQWpqKsLDwzFz5ky9JkhERESkDZ2+Cp6Tk6Mqbn744QeMGDECoaGh8PX1RVBQkF4TJCIiItKGTmdumjdvjqysLABAamoqQkJCAABCCKP+phQRERFJn05nboYOHYpRo0ahTZs2+OuvvxAeHg4AOH78OPz8/PSaIBEREZE2dCpuPvroI/j6+iIrKwsffvghrK2tAQDXr1/HlClT9JogERERkTZ0frZUU8Wb+BERETU9D+Umfnz8AhERETUFfPwCERERGT0+foGI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","text/plain":["