# -*- coding: utf-8 -*-
"""
@author: Aghiles Salah
"""
import numpy as np
from ..utils.util_functions import which_
#todo: take into account 'm' parameter
[docs]class Ndcg:
"""Normalized Discount Cumulative Gain.
Parameters
----------
m: int, optional, default: None
The number of items in the top@m list, \
if None then all items are considered to compute NDCG.
name: string, value: 'NDCG'
Name of the measure.
type: string, value: 'ranking'
Type of the metric, e.g., "ranking".
"""
def __init__(self, m= None):
self.name = 'NDCG'
self.type = 'ranking'
self.m = m
#Compute nDCG for a single user i
def compute(self,data_test,reclist):
#Compute Ideal DCG for user i
irankTest_i = np.array(range(1,len(which_(data_test,'>',0))+1))
irankTest_i = irankTest_i + 1
irankTest_i = np.log2(irankTest_i)
idcg_i = sum(np.divide(1,irankTest_i))
#Compute DCG for user i
rankTest_i = np.where(np.in1d(reclist,which_(data_test,'>',0)))[0]
rankTest_i = rankTest_i +1 +1 # the second +1 because indices starst from 0 in python
rankTest_i = np.log2(rankTest_i)
dcg_i = sum(np.divide(1,rankTest_i))
#Compute nDCG for user i
ndcg_i = dcg_i/idcg_i
return ndcg_i
#todo: take into account 'm' parameter
[docs]class Ncrr:
"""Normalized Cumulative Reciprocal Rank.
Parameters
----------
m: int, optional, default: None
The number of items in the top@m list, \
if None then all items are considered to compute NDCG.
name: string, value: 'NCRR'
Name of the measure.
type: string, value: 'ranking'
Type of the metric, e.g., "ranking".
"""
def __init__(self, m= None):
self.name = 'NCRR'
self.m = m
self.type = 'ranking'
#Compute nCRR for a single user i
def compute(self,data_test,reclist):
#Compute Ideal DCG for user i
irankTest_i = np.array(range(1,len(which_(data_test,'>',0))+1))
irankTest_i = irankTest_i
icrr_i = sum(np.divide(1,irankTest_i))
#### Compute DCG for user i
rankTest_i = np.where(np.in1d(reclist,which_(data_test,'>',0)))[0]
rankTest_i = rankTest_i +1 # the +1 because indices starst from 0 in python
crr_i = sum(np.divide(1,rankTest_i))
#Compute nDCG for user i
ncrr_i = crr_i/icrr_i
return ncrr_i
[docs]class Mrr:
"""Mean Reciprocal Rank.
Parameters
----------
name: string, value: 'MRR'
Name of the measure.
type: string, value: 'ranking'
Type of the metric, e.g., "ranking".
"""
def __init__(self):
self.name = 'MRR'
self.type = 'ranking'
#Compute MRR for a single user i
def compute(self,data_test,reclist):
rankTest_i = np.where(np.in1d(reclist,which_(data_test,'>',0)))[0]
#if rankTest_i:
mrr_i = np.divide(1,(rankTest_i[0]+1)) # +1 beacause indeces start from 0 in python
#else:
# mrr_i = 0
# print('Error! only users with at least one heldout item should be evaluated')
return mrr_i
class MeasureAtM:
def __init__(self, m = 20, name=None):
self.name = name
self.m = m
self.type = 'ranking'
self.tp = None
self.tp_fn = None
self.tp_fp = None
#Evaluate TopMlist for a single user: Precision@M, Recall@M, F-meansure@M (F1)
def measures_at_m(self,data_test,reclist):
data_test_bin = np.full(len(data_test), 0)
data_test_bin[which_(data_test,'>',0)] = 1
pred = np.full(len(data_test), 0)
pred[reclist[range(0,self.m)]] = 1
self.tp = np.sum(pred * data_test_bin)
self.tp_fn = np.sum(data_test_bin)
self.tp_fp = np.sum(pred)
[docs]class Precision(MeasureAtM):
"""Precision@M.
Parameters
----------
m: int, optional, default: 20
The number of items in the top@m list.
name: string, value: 'Precision@m'
Name of the measure.
type: string, value: 'ranking'
Type of the metric, e.g., "ranking".
"""
def __init__(self, m = 20):
MeasureAtM.__init__(self,m = m, name="Precision@"+str(m))
#Compute Precision@M for a single user i
def compute(self,data_test,reclist):
self.measures_at_m(data_test,reclist)
prec = self.tp/self.tp_fp
return prec
[docs]class Recall(MeasureAtM):
"""Recall@M.
Parameters
----------
m: int, optional, default: 20
The number of items in the top@m list.
name: string, value: 'Recall@m'
Name of the measure.
type: string, value: 'ranking'
Type of the metric, e.g., "ranking".
"""
def __init__(self, m = 20):
MeasureAtM.__init__(self,m = m, name="Recall@"+str(m))
#Compute Precision@M for a single user i
def compute(self,data_test,reclist):
self.measures_at_m(data_test,reclist)
rec = self.tp/self.tp_fn
return rec
[docs]class Fmeasure(MeasureAtM):
"""F-measure@M.
Parameters
----------
m: int, optional, default: 20
The number of items in the top@m list.
name: string, value: 'F1@m'
Name of the measure.
type: string, value: 'ranking'
Type of the metric, e.g., "ranking".
"""
def __init__(self, m = 20):
MeasureAtM.__init__(self,m = m, name="F1@"+str(m))
#Compute Precision@M for a single user i
def compute(self,data_test,reclist):
self.measures_at_m(data_test,reclist)
prec = self.tp/self.tp_fp
rec = self.tp/self.tp_fn
if (prec+rec):
f1 = 2*(prec*rec)/(prec+rec)
else:
f1 = 0
return f1