{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# default_exp models.vsknn"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# VSKNN\n",
    "> Vector Multiplication Session-Based kNN\n",
    "\n",
    "The idea of this variant is to put more emphasis on the more recent events of a session when computing the similarities. Instead of encoding a session as a binary vector, we use real-valued vectors to encode the current session. Only the very last element of the session obtains a value of “1”; the weights of the other elements are determined using a linear decay function that depends on the position of the element within the session, where elements appearing earlier in the session obtain a lower weight. As a result, when using the dot product as a similarity function between the current weight-encoded session and a binary-encoded past session, more emphasis is given to elements that appear later in the sessions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#hide\n",
    "from nbdev.showdoc import *\n",
    "from fastcore.nb_imports import *\n",
    "from fastcore.test import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "from operator import itemgetter\n",
    "from math import sqrt\n",
    "import time\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from math import log10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "class VMContextKNN:\n",
    "    '''\n",
    "    VMContextKNN( k, sample_size=1000, similarity='cosine', weighting='div', weighting_score='div_score', session_key = 'SessionId', item_key= 'ItemId')\n",
    "    Parameters\n",
    "    -----------\n",
    "    k : int\n",
    "        Number of neighboring session to calculate the item scores from. (Default value: 200)\n",
    "    sample_size : int\n",
    "        Defines the length of a subset of all training sessions to calculate the nearest neighbors from. (Default value: 2000)\n",
    "    similarity : string\n",
    "        String to define the method for the similarity calculation (jaccard, cosine, binary, tanimoto). (default: cosine)\n",
    "    weighting : string\n",
    "        Decay function to determine the importance/weight of individual actions in the current session (linear, same, div, log, quadratic). (default: div)\n",
    "    weighting_score : string\n",
    "        Decay function to lower the score of candidate items from a neighboring sessions that were selected by less recently clicked items in the current session. (linear, same, div, log, quadratic). (default: div_score)\n",
    "    session_key : string\n",
    "        Header of the session ID column in the input file. (default: 'SessionId')\n",
    "    item_key : string\n",
    "        Header of the item ID column in the input file. (default: 'ItemId')\n",
    "    '''\n",
    "    def __init__( self, k=200, sample_size=0, similarity='cosine', weighting='div', weighting_score='div_score', session_key = 'SessionId', item_key= 'ItemId'):\n",
    "       \n",
    "        self.k = k\n",
    "        self.sample_size = sample_size\n",
    "        self.weighting = weighting\n",
    "        self.weighting_score = weighting_score\n",
    "        self.similarity = similarity\n",
    "        self.session_key = session_key\n",
    "        self.item_key = item_key\n",
    "        \n",
    "        #updated while recommending\n",
    "        self.session = -1\n",
    "        self.session_items = []\n",
    "        self.relevant_sessions = set()\n",
    "\n",
    "        # cache relations once at startup\n",
    "        self.session_item_map = dict() \n",
    "        self.item_session_map = dict()\n",
    "        self.session_time = dict()\n",
    "        self.min_time = -1\n",
    "        \n",
    "        self.sim_time = 0\n",
    "        \n",
    "    def fit(self, train, items=None):\n",
    "        '''\n",
    "        Trains the predictor.\n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        data: pandas.DataFrame\n",
    "            Training data. It contains the transactions of the sessions. It has one column for session IDs, one for item IDs and one for the timestamp of the events (unix timestamps).\n",
    "            It must have a header. Column names are arbitrary, but must correspond to the ones you set during the initialization of the network (session_key, item_key, time_key properties).\n",
    "        '''\n",
    "        self.items_ids = list(train[self.item_key].unique())\n",
    "        train[self.item_key] = train[self.item_key].astype('category')\n",
    "        self.new_old = dict(enumerate(train[self.item_key].cat.categories))\n",
    "        self.old_new = {y:x for x,y in self.new_old.items()}\n",
    "        train[[self.item_key]] = train[[self.item_key]].apply(lambda x: x.cat.codes)\n",
    "        \n",
    "        self.freqs = dict(train[self.item_key].value_counts())\n",
    "        \n",
    "        self.num_items = train[self.item_key].max()\n",
    "        index_session = train.columns.get_loc( self.session_key )\n",
    "        index_item = train.columns.get_loc( self.item_key )\n",
    "        \n",
    "        session = -1\n",
    "        session_items = set()\n",
    "        for row in train.itertuples(index=False):\n",
    "            # cache items of sessions\n",
    "            if row[index_session] != session:\n",
    "                if len(session_items) > 0:\n",
    "                    self.session_item_map.update({session : session_items})\n",
    "                session = row[index_session]\n",
    "                session_items = set()\n",
    "            session_items.add(row[index_item])\n",
    "            \n",
    "            # cache sessions involving an item\n",
    "            map_is = self.item_session_map.get( row[index_item] )\n",
    "            if map_is is None:\n",
    "                map_is = set()\n",
    "                self.item_session_map.update({row[index_item] : map_is})\n",
    "            map_is.add(row[index_session])\n",
    "            \n",
    "        # Add the last tuple    \n",
    "        self.session_item_map.update({session : session_items})\n",
    "        self.predict_for_item_ids = list(range(1, self.num_items+1))\n",
    "        \n",
    "        \n",
    "    def predict_next(self, session_items, k):\n",
    "        '''\n",
    "        Gives predicton scores for a selected set of items on how likely they be the next item in the session.\n",
    "                \n",
    "        Parameters\n",
    "        --------\n",
    "        session_items : List\n",
    "            Items IDs in current session.\n",
    "        k : Integer\n",
    "            How many items to recommend\n",
    "        Returns\n",
    "        --------\n",
    "        out : pandas.Series\n",
    "            Prediction scores for selected items on how likely to be the next item of this session. \n",
    "            Indexed by the item IDs.\n",
    "        '''\n",
    "            \n",
    "        all_len = len(self.predict_for_item_ids)\n",
    "        input_item_id = session_items[-1]\n",
    "        neighbors = self.find_neighbors(input_item_id, session_items)\n",
    "        scores = self.score_items(neighbors, session_items)\n",
    "        \n",
    "        # Create things in the format ..\n",
    "        preds = np.zeros(all_len)\n",
    "        scores_keys = list(scores.keys())\n",
    "        for i in range(all_len):\n",
    "            if i+1 in scores_keys:\n",
    "                preds[i] = scores[i+1]\n",
    "                \n",
    "        series = pd.Series(data = preds, index = self.predict_for_item_ids)\n",
    "        series = series / series.max()\n",
    "        return series.nlargest(k).index.values\n",
    "    \n",
    "    def items_for_session(self, session):\n",
    "        '''\n",
    "        Returns all items in the session\n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        session: Id of a session\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : set           \n",
    "        '''\n",
    "        return self.session_item_map.get(session);\n",
    "    \n",
    "    def vec_for_session(self, session):\n",
    "        '''\n",
    "        Returns all items in the session\n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        session: Id of a session\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : set           \n",
    "        '''\n",
    "        return self.session_vec_map.get(session);\n",
    "    \n",
    "    def sessions_for_item(self, item_id):\n",
    "        '''\n",
    "        Returns all session for an item\n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        item: Id of the item session\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : set           \n",
    "        '''\n",
    "        return self.item_session_map.get( item_id ) if item_id in self.item_session_map else set()\n",
    "        \n",
    "        \n",
    "    def most_recent_sessions( self, sessions, number ):\n",
    "        '''\n",
    "        Find the most recent sessions in the given set\n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        sessions: set of session ids\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : set           \n",
    "        '''\n",
    "        sample = set()\n",
    "\n",
    "        tuples = list()\n",
    "        for session in sessions:\n",
    "            time = self.session_time.get( session )\n",
    "            if time is None:\n",
    "                print(' EMPTY TIMESTAMP!! ', session)\n",
    "            tuples.append((session, time))\n",
    "            \n",
    "        tuples = sorted(tuples, key=itemgetter(1), reverse=True)\n",
    "        #print 'sorted list ', sortedList\n",
    "        cnt = 0\n",
    "        for element in tuples:\n",
    "            cnt = cnt + 1\n",
    "            if cnt > number:\n",
    "                break\n",
    "            sample.add( element[0] )\n",
    "        #print 'returning sample of size ', len(sample)\n",
    "        return sample\n",
    "        \n",
    "        \n",
    "    def possible_neighbor_sessions(self, input_item_id):\n",
    "        '''\n",
    "        Find a set of session to later on find neighbors in.\n",
    "        A self.sample_size of 0 uses all sessions in which any item of the current session appears. \n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        sessions: set of session ids\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : set           \n",
    "        '''\n",
    "        \n",
    "        self.relevant_sessions = self.relevant_sessions | self.sessions_for_item( input_item_id )\n",
    "               \n",
    "        if self.sample_size == 0: #use all session as possible neighbors\n",
    "            return self.relevant_sessions\n",
    "\n",
    "        else: #sample some sessions\n",
    "            if len(self.relevant_sessions) > self.sample_size:    \n",
    "                return self.relevant_sessions[-self.sample_size:]\n",
    "            else: \n",
    "                return self.relevant_sessions\n",
    "                        \n",
    "    def calc_similarity(self, session_items, sessions):\n",
    "        '''\n",
    "        Calculates the configured similarity for the items in session_items and each session in sessions.\n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        session_items: set of item ids\n",
    "        sessions: list of session ids\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : list of tuple (session_id,similarity)           \n",
    "        '''\n",
    "        pos_map = {}\n",
    "        length = len(session_items)\n",
    "        \n",
    "        count = 1\n",
    "        for item in session_items:\n",
    "            if self.weighting is not None: \n",
    "                pos_map[item] = getattr(self, self.weighting)(count, length)\n",
    "                count += 1\n",
    "            else:\n",
    "                pos_map[item] = 1\n",
    "        #print('POS MAP: ', pos_map, session_items)\n",
    "        items = set(session_items)\n",
    "        neighbors = []\n",
    "        for session in sessions: \n",
    "            n_items = self.items_for_session(session)\n",
    "            similarity = self.vec(items, n_items, pos_map)        \n",
    "            if similarity > 0:\n",
    "                neighbors.append((session, similarity))\n",
    "        return neighbors\n",
    "\n",
    "    #-----------------\n",
    "    # Find a set of neighbors, returns a list of tuples (sessionid: similarity) \n",
    "    #-----------------\n",
    "    def find_neighbors( self, input_item_id, session_items):\n",
    "        '''\n",
    "        Finds the k nearest neighbors for the given session_id and the current item input_item_id. \n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        session_items: list of item ids in current session\n",
    "        input_item_id: int\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : list of tuple (session_id, similarity)           \n",
    "        '''\n",
    "        #print('SESSION ITEMS1:', session_items)\n",
    "        possible_neighbors = self.possible_neighbor_sessions(input_item_id)\n",
    "        possible_neighbors = self.calc_similarity(session_items, possible_neighbors)\n",
    "        \n",
    "        possible_neighbors = sorted( possible_neighbors, reverse=True, key=lambda x: x[1] )\n",
    "        possible_neighbors = possible_neighbors[:self.k]\n",
    "        \n",
    "        return possible_neighbors\n",
    "    \n",
    "            \n",
    "    def score_items(self, neighbors, current_session):\n",
    "        '''\n",
    "        Compute a set of scores for all items given a set of neighbors.\n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        neighbors: set of session ids\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : list of tuple (item, score)           \n",
    "        '''\n",
    "        # now we have the set of relevant items to make predictions\n",
    "        scores = dict()\n",
    "        # iterate over the sessions\n",
    "        for session in neighbors:\n",
    "            # get the items in this session\n",
    "            items = self.items_for_session( session[0] )\n",
    "            step = 1\n",
    "            \n",
    "            for item in reversed( current_session ):\n",
    "                if item in items:\n",
    "                    decay = getattr(self, self.weighting_score)(step)\n",
    "                    break\n",
    "                step += 1\n",
    "                                    \n",
    "            for item in items:\n",
    "                old_score = scores.get( item )\n",
    "                similarity = session[1]\n",
    "                \n",
    "                if old_score is None:\n",
    "                    scores.update({item : ( similarity * decay ) })\n",
    "                else: \n",
    "                    new_score = old_score + ( similarity * decay )\n",
    "                    scores.update({item : new_score})\n",
    "                    \n",
    "        return scores\n",
    "    \n",
    "    \n",
    "    def linear_score(self, i):\n",
    "        return 1 - (0.1*i) if i <= 100 else 0\n",
    "    \n",
    "    def same_score(self, i):\n",
    "        return 1\n",
    "    \n",
    "    def div_score(self, i):\n",
    "        return 1/i\n",
    "    \n",
    "    def log_score(self, i):\n",
    "        return 1/(log10(i+1.7))\n",
    "    \n",
    "    def quadratic_score(self, i):\n",
    "        return 1/(i*i)\n",
    "    \n",
    "    def linear(self, i, length):\n",
    "        return 1 - (0.1*(length-i)) if i <= 10 else 0\n",
    "    \n",
    "    def same(self, i, length):\n",
    "        return 1\n",
    "    \n",
    "    def div(self, i, length):\n",
    "        return i/length\n",
    "    \n",
    "    def log(self, i, length):\n",
    "        return 1/(log10((length-i)+1.7))\n",
    "    \n",
    "    def quadratic(self, i, length):\n",
    "        return (i/length)**2\n",
    "\n",
    "\n",
    "    def jaccard(self, first, second):\n",
    "        '''\n",
    "        Calculates the jaccard index for two sessions\n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        first: Id of a session\n",
    "        second: Id of a session\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : float value           \n",
    "        '''\n",
    "        sc = time.clock()\n",
    "        intersection = len(first & second)\n",
    "        union = len(first | second )\n",
    "        res = intersection / union\n",
    "        \n",
    "        self.sim_time += (time.clock() - sc)\n",
    "        \n",
    "        return res \n",
    "    \n",
    "    def cosine(self, first, second):\n",
    "        '''\n",
    "        Calculates the cosine similarity for two sessions\n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        first: Id of a session\n",
    "        second: Id of a session\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : float value           \n",
    "        '''\n",
    "        li = len(first&second)\n",
    "        la = len(first)\n",
    "        lb = len(second)\n",
    "        result = li / sqrt(la) * sqrt(lb)\n",
    "\n",
    "        return result\n",
    "    \n",
    "    def tanimoto(self, first, second):\n",
    "        '''\n",
    "        Calculates the cosine tanimoto similarity for two sessions\n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        first: Id of a session\n",
    "        second: Id of a session\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : float value           \n",
    "        '''\n",
    "        li = len(first&second)\n",
    "        la = len(first)\n",
    "        lb = len(second)\n",
    "        result = li / ( la + lb -li )\n",
    "\n",
    "        return result\n",
    "    \n",
    "    def binary(self, first, second):\n",
    "        '''\n",
    "        Calculates the ? for 2 sessions\n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        first: Id of a session\n",
    "        second: Id of a session\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : float value           \n",
    "        '''\n",
    "        a = len(first&second)\n",
    "        b = len(first)\n",
    "        c = len(second)\n",
    "        \n",
    "        result = (2 * a) / ((2 * a) + b + c)\n",
    "\n",
    "        return result\n",
    "    \n",
    "    def vec(self, first, second, map):\n",
    "        '''\n",
    "        Calculates the ? for 2 sessions\n",
    "        \n",
    "        Parameters\n",
    "        --------\n",
    "        first: Id of a session\n",
    "        second: Id of a session\n",
    "        \n",
    "        Returns \n",
    "        --------\n",
    "        out : float value           \n",
    "        '''\n",
    "        a = first & second\n",
    "        sum = 0\n",
    "        for i in a:\n",
    "            sum += map[i]\n",
    "        \n",
    "        result = sum / len(map)\n",
    "\n",
    "        return result    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import gc\n",
    "import os\n",
    "import time\n",
    "import argparse\n",
    "import subprocess\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from recohut.utils.common_utils import download_url"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Downloading https://github.com/RecoHut-Datasets/yoochoose/raw/v4/yoochoose_train.txt\n",
      "Downloading https://github.com/RecoHut-Datasets/yoochoose/raw/v4/yoochoose_valid.txt\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'/content/data/yoochoose_valid.txt'"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_root = '/content/data'\n",
    "download_url('https://github.com/RecoHut-Datasets/yoochoose/raw/v4/yoochoose_train.txt', data_root)\n",
    "download_url('https://github.com/RecoHut-Datasets/yoochoose/raw/v4/yoochoose_valid.txt', data_root)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Finished Reading Data \n",
      "Start Model Fitting...\n",
      "End Model Fitting\n",
      " Start Predictions...\n",
      "Finished Prediction for  5000 items.\n",
      "Recall: [0.5305537459283388]\n",
      "\n",
      "MRR: [0.1676372920865109]\n",
      "\n",
      "Coverage: [0.2715548471236053]\n",
      "\n",
      "Popularity: [0.06588592053880978]\n",
      "\n",
      "train_time: 4.415358543395996\n",
      "\n",
      "test_time: 2822.0330970287323\n",
      "End Model Predictions\n"
     ]
    }
   ],
   "source": [
    "parser = argparse.ArgumentParser()\n",
    "parser.add_argument('--K', type=int, default=20, help=\"K items to be used in Recall@K and MRR@K\")\n",
    "parser.add_argument('--neighbors', type=int, default=200, help=\"K neighbors to be used in KNN\")\n",
    "parser.add_argument('--sample', type=int, default=0, help=\"Max Number of steps to walk back from the currently viewed item\")\n",
    "parser.add_argument('--weight_score', type=str, default='div_score', help=\"Decay function to lower the score of candidate items from a neighboring sessions that were selected by less recently clicked items in the current session. (linear, same, div, log, quadratic)_score\")\n",
    "parser.add_argument('--weighting', type=str, default='div', help=\"Decay function to determine the importance/weight of individual actions in the current session(linear, same, div, log, qudratic)\")\n",
    "parser.add_argument('--similarity', type=str, default='cosine', help=\"String to define the method for the similarity calculation (jaccard, cosine, binary, tanimoto). (default: cosine)\")\n",
    "parser.add_argument('--itemid', default='sid', type=str)\n",
    "parser.add_argument('--sessionid', default='uid', type=str)\n",
    "parser.add_argument('--valid_data', default='yoochoose_valid.txt', type=str)\n",
    "parser.add_argument('--train_data', default='yoochoose_train.txt', type=str)\n",
    "parser.add_argument('--data_folder', default=data_root, type=str)\n",
    "args = parser.parse_args([])\n",
    "\n",
    "# Get the arguments\n",
    "train_data = os.path.join(args.data_folder, args.train_data)\n",
    "x_train = pd.read_csv(train_data)\n",
    "x_train.sort_values(args.sessionid, inplace=True)\n",
    "distinct_train = x_train[args.itemid].nunique()\n",
    "\n",
    "valid_data = os.path.join(args.data_folder, args.valid_data)\n",
    "x_valid = pd.read_csv(valid_data)\n",
    "x_valid.sort_values(args.sessionid, inplace=True)\n",
    "\n",
    "print('Finished Reading Data \\nStart Model Fitting...')\n",
    "# Fitting Model\n",
    "t1 = time.time()\n",
    "model = VMContextKNN(k = args.neighbors, sample_size = args.sample, similarity = args.similarity, \n",
    "\t\t\t\t\t weighting = args.weighting, weighting_score = args.weight_score,\n",
    "\t\t\t\t\t session_key = args.sessionid, item_key = args.itemid)\n",
    "model.fit(x_train)\n",
    "#memory_task.kill()\n",
    "train_time = time.time() - t1\n",
    "print('End Model Fitting\\n Start Predictions...')\n",
    "\n",
    "# Test Set Evaluation\n",
    "test_size = 0.0\n",
    "hit = [0.0]\n",
    "MRR = [0.0]\n",
    "cov = [[]]\n",
    "pop = [[]]\n",
    "Ks = [args.K]\n",
    "cur_length = 0\n",
    "cur_session = -1\n",
    "last_items = []\n",
    "t1 = time.time()\n",
    "index_item = x_valid.columns.get_loc(args.itemid)\n",
    "index_session = x_valid.columns.get_loc(args.sessionid)\n",
    "train_items = model.items_ids\n",
    "counter = 0\n",
    "for row in x_valid.itertuples( index=False ):\n",
    "\tcounter += 1\n",
    "\tif counter % 5000 == 0:\n",
    "\t\tprint('Finished Prediction for ', counter, 'items.')\n",
    "\tsession_id, item_id = row[index_session], row[index_item]\n",
    "\tif session_id != cur_session:\n",
    "\t\tcur_session = session_id\n",
    "\t\tlast_items = []\n",
    "\t\tcur_length = 0\n",
    "\t\n",
    "\tif not item_id in last_items and item_id in train_items:\n",
    "\t\t#print(item_id, item_id in train_items)\n",
    "\t\titem_id = model.old_new[item_id]\n",
    "\t\tif len(last_items) > cur_length: #make prediction\n",
    "\t\t\tcur_length += 1\n",
    "\t\t\ttest_size += 1\n",
    "\t\t\t# Predict the most similar items to items\n",
    "\t\t\tfor k in range(len(Ks)):\n",
    "\t\t\t\tpredictions = model.predict_next(last_items, k = Ks[k])\n",
    "\t\t\t\t# Evaluation\n",
    "\t\t\t\trank = 0\n",
    "\t\t\t\tfor predicted_item in predictions:\n",
    "\t\t\t\t\tif predicted_item not in cov[k]:\n",
    "\t\t\t\t\t\tcov[k].append(predicted_item)\n",
    "\t\t\t\t\tpop[k].append(model.freqs[predicted_item])\n",
    "\t\t\t\t\trank += 1\n",
    "\t\t\t\t\tif predicted_item == item_id:\n",
    "\t\t\t\t\t\thit[k] += 1.0\n",
    "\t\t\t\t\t\tMRR[k] += 1/rank\n",
    "\t\t\t\t\t\tbreak\n",
    "\t\t\n",
    "\t\tlast_items.append(item_id)\n",
    "  \n",
    "#memory_task.kill()\n",
    "hit[:] = [x / test_size for x in hit]\n",
    "MRR[:] = [x / test_size for x in MRR]\n",
    "cov[:] = [len(x) / distinct_train for x in cov]\n",
    "maxi = max(model.freqs.values())\n",
    "pop[:] = [np.mean(x) / maxi for x in pop]\n",
    "test_time = (time.time() - t1)\n",
    "print('Recall:', hit)\n",
    "print ('\\nMRR:', MRR)\n",
    "print ('\\nCoverage:', cov)\n",
    "print ('\\nPopularity:', pop)\n",
    "print ('\\ntrain_time:', train_time)\n",
    "print ('\\ntest_time:', test_time)\n",
    "print('End Model Predictions')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **References:-**\n",
    "- [https://arxiv.org/pdf/1803.09587.pdf](https://arxiv.org/pdf/1803.09587.pdf)\n",
    "- [https://github.com/mmaher22/iCV-SBR/tree/master/Source Codes/VSKNN_Python](https://github.com/mmaher22/iCV-SBR/tree/master/Source%20Codes/VSKNN_Python)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Author: Sparsh A.\n",
      "\n",
      "Last updated: 2022-01-01 06:12:09\n",
      "\n",
      "Compiler    : GCC 7.5.0\n",
      "OS          : Linux\n",
      "Release     : 5.4.144+\n",
      "Machine     : x86_64\n",
      "Processor   : x86_64\n",
      "CPU cores   : 2\n",
      "Architecture: 64bit\n",
      "\n",
      "pandas  : 1.1.5\n",
      "numpy   : 1.19.5\n",
      "argparse: 1.1\n",
      "IPython : 5.5.0\n",
      "\n"
     ]
    }
   ],
   "source": [
    "#hide\n",
    "%reload_ext watermark\n",
    "%watermark -a \"Sparsh A.\" -m -iv -u -t -d"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
