src.fairreckitlib.model.algorithms.base_recommender

This module contains the base class for recommenders.

Classes:

BaseRecommender: base class for recommenders.
Recommender: implements basic shared functionality.

This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course. © Copyright Utrecht University (Department of Information and Computing Sciences)

  1"""This module contains the base class for recommenders.
  2
  3Classes:
  4
  5    BaseRecommender: base class for recommenders.
  6    Recommender: implements basic shared functionality.
  7
  8This program has been developed by students from the bachelor Computer Science at
  9Utrecht University within the Software Project course.
 10© Copyright Utrecht University (Department of Information and Computing Sciences)
 11"""
 12
 13from abc import ABCMeta, abstractmethod
 14from typing import Any, Dict, List
 15
 16import numpy as np
 17import pandas as pd
 18
 19from .base_algorithm import BaseAlgorithm
 20
 21
 22class BaseRecommender(BaseAlgorithm, metaclass=ABCMeta):
 23    """Base class for FairRecKit recommenders.
 24
 25    A recommender is used for recommender experiments. It computes a number of
 26    item recommendations for any user that it was trained on.
 27    Derived recommenders are expected to implement the abstract interface.
 28
 29    Abstract methods:
 30
 31    on_recommend
 32    on_recommend_batch (optional)
 33
 34    Public methods:
 35
 36    has_rated_items_filter
 37    recommend
 38    recommend_batch
 39    """
 40
 41    def __init__(self, rated_items_filter: bool):
 42        """Construct the recommender.
 43
 44        Args:
 45            rated_items_filter: whether to filter already rated items when
 46                producing item recommendations.
 47        """
 48        BaseAlgorithm.__init__(self)
 49        self.rated_items_filter = rated_items_filter
 50
 51    def has_rated_items_filter(self) -> bool:
 52        """Get if the recommender filters already rated items when producing recommendations.
 53
 54        Returns:
 55            whether the recommender filters already rated items.
 56        """
 57        return self.rated_items_filter
 58
 59    def recommend(self, user: int, num_items: int=10) -> pd.DataFrame:
 60        """Compute item recommendations for the specified user.
 61
 62        A recommendation is impossible when the user is not present in
 63        the unique users it was trained on and will return an empty dataframe.
 64
 65        Args:
 66            user: the user ID to compute recommendations for.
 67            num_items: the number of item recommendations to produce.
 68
 69        Raises:
 70            ArithmeticError: possibly raised by a recommender on testing.
 71            MemoryError: possibly raised by a recommender on testing.
 72            RuntimeError: when the recommender is not trained yet.
 73
 74        Returns:
 75            a dataframe with the columns: 'item' and 'score'.
 76        """
 77        if self.train_set is None:
 78            raise RuntimeError('Recommender is not trained for item recommendations')
 79
 80        if not self.train_set.knows_user(user):
 81            return pd.DataFrame(columns=['item', 'score'])
 82
 83        return self.on_recommend(user, num_items)
 84
 85    @abstractmethod
 86    def on_recommend(self, user: int, num_items: int) -> pd.DataFrame:
 87        """Compute item recommendations for the specified user.
 88
 89        The user is assumed to be present in the train set that the
 90        recommender was trained on.
 91        Derived implementations are expected to return a dataframe
 92        with the 'score' column in descending order.
 93
 94        Args:
 95            user: the user ID to compute recommendations for.
 96            num_items: the number of item recommendations to produce.
 97
 98        Raises:
 99            ArithmeticError: possibly raised by a recommender on testing.
100            MemoryError: possibly raised by a recommender on testing.
101            RuntimeError: when the recommender is not trained yet.
102
103        Returns:
104            a dataframe with the columns: 'item' and 'score'.
105        """
106        raise NotImplementedError()
107
108    def recommend_batch(self, users: List[int], num_items: int=10) -> pd.DataFrame:
109        """Compute the items recommendations for each of the specified users.
110
111        All the users that are not present in the train set that the recommender
112        was trained on are filtered before recommendations are made.
113
114        Args:
115            users: the user ID's to compute recommendations for.
116            num_items: the number of item recommendations to produce.
117
118        Raises:
119            ArithmeticError: possibly raised by a recommender on testing.
120            MemoryError: possibly raised by a recommender on testing.
121            RuntimeError: when the recommender is not trained yet.
122
123        Returns:
124            a dataframe with the columns: 'rank', 'user', 'item', 'score'.
125        """
126        if self.train_set is None:
127            raise RuntimeError('Recommender is not trained for item recommendations')
128
129        users = [u for u in users if self.train_set.knows_user(u)]
130        if len(users) == 0:
131            return pd.DataFrame(columns=['rank', 'user', 'item', 'score'])
132
133        return self.on_recommend_batch(users, num_items)
134
135    def on_recommend_batch(self, users: List[int], num_items: int) -> pd.DataFrame:
136        """Compute the items recommendations for each of the specified users.
137
138        All the users are assumed to be present in the train set that
139        the recommender was trained on.
140        A standard batch implementation is provided, but derived classes are
141        allowed to override batching with their own logic.
142
143        Args:
144            users: the user ID's to compute recommendations for.
145            num_items: the number of item recommendations to produce.
146
147        Raises:
148            ArithmeticError: possibly raised by a recommender on testing.
149            MemoryError: possibly raised by a recommender on testing.
150            RuntimeError: when the recommender is not trained yet.
151
152        Returns:
153            a dataframe with the columns: 'rank', 'user', 'item', 'score'.
154        """
155        result = pd.DataFrame()
156
157        for user in users:
158            item_scores = self.recommend(user, num_items)
159
160            item_scores['rank'] = np.arange(1, 1 + num_items)
161            item_scores['user'] = np.full(num_items, user)
162
163            result = pd.concat(
164                [result, item_scores[['rank', 'user', 'item', 'score']]],
165                ignore_index=True
166            )
167
168        return result
169
170
171class Recommender(BaseRecommender, metaclass=ABCMeta):
172    """Recommender that implements basic shared functionality."""
173
174    def __init__(self, name: str, params: Dict[str, Any], num_threads: int,
175                 rated_items_filter: bool):
176        """Construct the recommender.
177
178        Args:
179            name: the name of the recommender.
180            params: the parameters of the recommender.
181            num_threads: the max number of threads the recommender can use.
182            rated_items_filter: whether to filter already rated items when
183                producing item recommendations.
184        """
185        BaseRecommender.__init__(self, rated_items_filter)
186        self.num_threads = num_threads
187        self.recommender_name = name
188        self.params = params
189
190    def get_name(self) -> str:
191        """Get the name of the recommender.
192
193        Returns:
194            the recommender name.
195        """
196        return self.recommender_name
197
198    def get_num_threads(self) -> int:
199        """Get the max number of threads the recommender can use.
200
201        Returns:
202            the number of threads.
203        """
204        return self.num_threads
205
206    def get_params(self) -> Dict[str, Any]:
207        """Get the parameters of the recommender.
208
209        Returns:
210            the recommender parameters.
211        """
212        return dict(self.params)
 23class BaseRecommender(BaseAlgorithm, metaclass=ABCMeta):
 24    """Base class for FairRecKit recommenders.
 25
 26    A recommender is used for recommender experiments. It computes a number of
 27    item recommendations for any user that it was trained on.
 28    Derived recommenders are expected to implement the abstract interface.
 29
 30    Abstract methods:
 31
 32    on_recommend
 33    on_recommend_batch (optional)
 34
 35    Public methods:
 36
 37    has_rated_items_filter
 38    recommend
 39    recommend_batch
 40    """
 41
 42    def __init__(self, rated_items_filter: bool):
 43        """Construct the recommender.
 44
 45        Args:
 46            rated_items_filter: whether to filter already rated items when
 47                producing item recommendations.
 48        """
 49        BaseAlgorithm.__init__(self)
 50        self.rated_items_filter = rated_items_filter
 51
 52    def has_rated_items_filter(self) -> bool:
 53        """Get if the recommender filters already rated items when producing recommendations.
 54
 55        Returns:
 56            whether the recommender filters already rated items.
 57        """
 58        return self.rated_items_filter
 59
 60    def recommend(self, user: int, num_items: int=10) -> pd.DataFrame:
 61        """Compute item recommendations for the specified user.
 62
 63        A recommendation is impossible when the user is not present in
 64        the unique users it was trained on and will return an empty dataframe.
 65
 66        Args:
 67            user: the user ID to compute recommendations for.
 68            num_items: the number of item recommendations to produce.
 69
 70        Raises:
 71            ArithmeticError: possibly raised by a recommender on testing.
 72            MemoryError: possibly raised by a recommender on testing.
 73            RuntimeError: when the recommender is not trained yet.
 74
 75        Returns:
 76            a dataframe with the columns: 'item' and 'score'.
 77        """
 78        if self.train_set is None:
 79            raise RuntimeError('Recommender is not trained for item recommendations')
 80
 81        if not self.train_set.knows_user(user):
 82            return pd.DataFrame(columns=['item', 'score'])
 83
 84        return self.on_recommend(user, num_items)
 85
 86    @abstractmethod
 87    def on_recommend(self, user: int, num_items: int) -> pd.DataFrame:
 88        """Compute item recommendations for the specified user.
 89
 90        The user is assumed to be present in the train set that the
 91        recommender was trained on.
 92        Derived implementations are expected to return a dataframe
 93        with the 'score' column in descending order.
 94
 95        Args:
 96            user: the user ID to compute recommendations for.
 97            num_items: the number of item recommendations to produce.
 98
 99        Raises:
100            ArithmeticError: possibly raised by a recommender on testing.
101            MemoryError: possibly raised by a recommender on testing.
102            RuntimeError: when the recommender is not trained yet.
103
104        Returns:
105            a dataframe with the columns: 'item' and 'score'.
106        """
107        raise NotImplementedError()
108
109    def recommend_batch(self, users: List[int], num_items: int=10) -> pd.DataFrame:
110        """Compute the items recommendations for each of the specified users.
111
112        All the users that are not present in the train set that the recommender
113        was trained on are filtered before recommendations are made.
114
115        Args:
116            users: the user ID's to compute recommendations for.
117            num_items: the number of item recommendations to produce.
118
119        Raises:
120            ArithmeticError: possibly raised by a recommender on testing.
121            MemoryError: possibly raised by a recommender on testing.
122            RuntimeError: when the recommender is not trained yet.
123
124        Returns:
125            a dataframe with the columns: 'rank', 'user', 'item', 'score'.
126        """
127        if self.train_set is None:
128            raise RuntimeError('Recommender is not trained for item recommendations')
129
130        users = [u for u in users if self.train_set.knows_user(u)]
131        if len(users) == 0:
132            return pd.DataFrame(columns=['rank', 'user', 'item', 'score'])
133
134        return self.on_recommend_batch(users, num_items)
135
136    def on_recommend_batch(self, users: List[int], num_items: int) -> pd.DataFrame:
137        """Compute the items recommendations for each of the specified users.
138
139        All the users are assumed to be present in the train set that
140        the recommender was trained on.
141        A standard batch implementation is provided, but derived classes are
142        allowed to override batching with their own logic.
143
144        Args:
145            users: the user ID's to compute recommendations for.
146            num_items: the number of item recommendations to produce.
147
148        Raises:
149            ArithmeticError: possibly raised by a recommender on testing.
150            MemoryError: possibly raised by a recommender on testing.
151            RuntimeError: when the recommender is not trained yet.
152
153        Returns:
154            a dataframe with the columns: 'rank', 'user', 'item', 'score'.
155        """
156        result = pd.DataFrame()
157
158        for user in users:
159            item_scores = self.recommend(user, num_items)
160
161            item_scores['rank'] = np.arange(1, 1 + num_items)
162            item_scores['user'] = np.full(num_items, user)
163
164            result = pd.concat(
165                [result, item_scores[['rank', 'user', 'item', 'score']]],
166                ignore_index=True
167            )
168
169        return result

Base class for FairRecKit recommenders.

A recommender is used for recommender experiments. It computes a number of item recommendations for any user that it was trained on. Derived recommenders are expected to implement the abstract interface.

Abstract methods:

on_recommend on_recommend_batch (optional)

Public methods:

has_rated_items_filter recommend recommend_batch

BaseRecommender(rated_items_filter: bool)
42    def __init__(self, rated_items_filter: bool):
43        """Construct the recommender.
44
45        Args:
46            rated_items_filter: whether to filter already rated items when
47                producing item recommendations.
48        """
49        BaseAlgorithm.__init__(self)
50        self.rated_items_filter = rated_items_filter

Construct the recommender.

Args: rated_items_filter: whether to filter already rated items when producing item recommendations.

def has_rated_items_filter(self) -> bool:
52    def has_rated_items_filter(self) -> bool:
53        """Get if the recommender filters already rated items when producing recommendations.
54
55        Returns:
56            whether the recommender filters already rated items.
57        """
58        return self.rated_items_filter

Get if the recommender filters already rated items when producing recommendations.

Returns: whether the recommender filters already rated items.

def recommend(self, user: int, num_items: int = 10) -> pandas.core.frame.DataFrame:
60    def recommend(self, user: int, num_items: int=10) -> pd.DataFrame:
61        """Compute item recommendations for the specified user.
62
63        A recommendation is impossible when the user is not present in
64        the unique users it was trained on and will return an empty dataframe.
65
66        Args:
67            user: the user ID to compute recommendations for.
68            num_items: the number of item recommendations to produce.
69
70        Raises:
71            ArithmeticError: possibly raised by a recommender on testing.
72            MemoryError: possibly raised by a recommender on testing.
73            RuntimeError: when the recommender is not trained yet.
74
75        Returns:
76            a dataframe with the columns: 'item' and 'score'.
77        """
78        if self.train_set is None:
79            raise RuntimeError('Recommender is not trained for item recommendations')
80
81        if not self.train_set.knows_user(user):
82            return pd.DataFrame(columns=['item', 'score'])
83
84        return self.on_recommend(user, num_items)

Compute item recommendations for the specified user.

A recommendation is impossible when the user is not present in the unique users it was trained on and will return an empty dataframe.

Args: user: the user ID to compute recommendations for. num_items: the number of item recommendations to produce.

Raises: ArithmeticError: possibly raised by a recommender on testing. MemoryError: possibly raised by a recommender on testing. RuntimeError: when the recommender is not trained yet.

Returns: a dataframe with the columns: 'item' and 'score'.

@abstractmethod
def on_recommend(self, user: int, num_items: int) -> pandas.core.frame.DataFrame:
 86    @abstractmethod
 87    def on_recommend(self, user: int, num_items: int) -> pd.DataFrame:
 88        """Compute item recommendations for the specified user.
 89
 90        The user is assumed to be present in the train set that the
 91        recommender was trained on.
 92        Derived implementations are expected to return a dataframe
 93        with the 'score' column in descending order.
 94
 95        Args:
 96            user: the user ID to compute recommendations for.
 97            num_items: the number of item recommendations to produce.
 98
 99        Raises:
100            ArithmeticError: possibly raised by a recommender on testing.
101            MemoryError: possibly raised by a recommender on testing.
102            RuntimeError: when the recommender is not trained yet.
103
104        Returns:
105            a dataframe with the columns: 'item' and 'score'.
106        """
107        raise NotImplementedError()

Compute item recommendations for the specified user.

The user is assumed to be present in the train set that the recommender was trained on. Derived implementations are expected to return a dataframe with the 'score' column in descending order.

Args: user: the user ID to compute recommendations for. num_items: the number of item recommendations to produce.

Raises: ArithmeticError: possibly raised by a recommender on testing. MemoryError: possibly raised by a recommender on testing. RuntimeError: when the recommender is not trained yet.

Returns: a dataframe with the columns: 'item' and 'score'.

def recommend_batch( self, users: List[int], num_items: int = 10) -> pandas.core.frame.DataFrame:
109    def recommend_batch(self, users: List[int], num_items: int=10) -> pd.DataFrame:
110        """Compute the items recommendations for each of the specified users.
111
112        All the users that are not present in the train set that the recommender
113        was trained on are filtered before recommendations are made.
114
115        Args:
116            users: the user ID's to compute recommendations for.
117            num_items: the number of item recommendations to produce.
118
119        Raises:
120            ArithmeticError: possibly raised by a recommender on testing.
121            MemoryError: possibly raised by a recommender on testing.
122            RuntimeError: when the recommender is not trained yet.
123
124        Returns:
125            a dataframe with the columns: 'rank', 'user', 'item', 'score'.
126        """
127        if self.train_set is None:
128            raise RuntimeError('Recommender is not trained for item recommendations')
129
130        users = [u for u in users if self.train_set.knows_user(u)]
131        if len(users) == 0:
132            return pd.DataFrame(columns=['rank', 'user', 'item', 'score'])
133
134        return self.on_recommend_batch(users, num_items)

Compute the items recommendations for each of the specified users.

All the users that are not present in the train set that the recommender was trained on are filtered before recommendations are made.

Args: users: the user ID's to compute recommendations for. num_items: the number of item recommendations to produce.

Raises: ArithmeticError: possibly raised by a recommender on testing. MemoryError: possibly raised by a recommender on testing. RuntimeError: when the recommender is not trained yet.

Returns: a dataframe with the columns: 'rank', 'user', 'item', 'score'.

def on_recommend_batch(self, users: List[int], num_items: int) -> pandas.core.frame.DataFrame:
136    def on_recommend_batch(self, users: List[int], num_items: int) -> pd.DataFrame:
137        """Compute the items recommendations for each of the specified users.
138
139        All the users are assumed to be present in the train set that
140        the recommender was trained on.
141        A standard batch implementation is provided, but derived classes are
142        allowed to override batching with their own logic.
143
144        Args:
145            users: the user ID's to compute recommendations for.
146            num_items: the number of item recommendations to produce.
147
148        Raises:
149            ArithmeticError: possibly raised by a recommender on testing.
150            MemoryError: possibly raised by a recommender on testing.
151            RuntimeError: when the recommender is not trained yet.
152
153        Returns:
154            a dataframe with the columns: 'rank', 'user', 'item', 'score'.
155        """
156        result = pd.DataFrame()
157
158        for user in users:
159            item_scores = self.recommend(user, num_items)
160
161            item_scores['rank'] = np.arange(1, 1 + num_items)
162            item_scores['user'] = np.full(num_items, user)
163
164            result = pd.concat(
165                [result, item_scores[['rank', 'user', 'item', 'score']]],
166                ignore_index=True
167            )
168
169        return result

Compute the items recommendations for each of the specified users.

All the users are assumed to be present in the train set that the recommender was trained on. A standard batch implementation is provided, but derived classes are allowed to override batching with their own logic.

Args: users: the user ID's to compute recommendations for. num_items: the number of item recommendations to produce.

Raises: ArithmeticError: possibly raised by a recommender on testing. MemoryError: possibly raised by a recommender on testing. RuntimeError: when the recommender is not trained yet.

Returns: a dataframe with the columns: 'rank', 'user', 'item', 'score'.

class Recommender(BaseRecommender):
172class Recommender(BaseRecommender, metaclass=ABCMeta):
173    """Recommender that implements basic shared functionality."""
174
175    def __init__(self, name: str, params: Dict[str, Any], num_threads: int,
176                 rated_items_filter: bool):
177        """Construct the recommender.
178
179        Args:
180            name: the name of the recommender.
181            params: the parameters of the recommender.
182            num_threads: the max number of threads the recommender can use.
183            rated_items_filter: whether to filter already rated items when
184                producing item recommendations.
185        """
186        BaseRecommender.__init__(self, rated_items_filter)
187        self.num_threads = num_threads
188        self.recommender_name = name
189        self.params = params
190
191    def get_name(self) -> str:
192        """Get the name of the recommender.
193
194        Returns:
195            the recommender name.
196        """
197        return self.recommender_name
198
199    def get_num_threads(self) -> int:
200        """Get the max number of threads the recommender can use.
201
202        Returns:
203            the number of threads.
204        """
205        return self.num_threads
206
207    def get_params(self) -> Dict[str, Any]:
208        """Get the parameters of the recommender.
209
210        Returns:
211            the recommender parameters.
212        """
213        return dict(self.params)

Recommender that implements basic shared functionality.

Recommender( name: str, params: Dict[str, Any], num_threads: int, rated_items_filter: bool)
175    def __init__(self, name: str, params: Dict[str, Any], num_threads: int,
176                 rated_items_filter: bool):
177        """Construct the recommender.
178
179        Args:
180            name: the name of the recommender.
181            params: the parameters of the recommender.
182            num_threads: the max number of threads the recommender can use.
183            rated_items_filter: whether to filter already rated items when
184                producing item recommendations.
185        """
186        BaseRecommender.__init__(self, rated_items_filter)
187        self.num_threads = num_threads
188        self.recommender_name = name
189        self.params = params

Construct the recommender.

Args: name: the name of the recommender. params: the parameters of the recommender. num_threads: the max number of threads the recommender can use. rated_items_filter: whether to filter already rated items when producing item recommendations.

def get_name(self) -> str:
191    def get_name(self) -> str:
192        """Get the name of the recommender.
193
194        Returns:
195            the recommender name.
196        """
197        return self.recommender_name

Get the name of the recommender.

Returns: the recommender name.

def get_num_threads(self) -> int:
199    def get_num_threads(self) -> int:
200        """Get the max number of threads the recommender can use.
201
202        Returns:
203            the number of threads.
204        """
205        return self.num_threads

Get the max number of threads the recommender can use.

Returns: the number of threads.

def get_params(self) -> Dict[str, Any]:
207    def get_params(self) -> Dict[str, Any]:
208        """Get the parameters of the recommender.
209
210        Returns:
211            the recommender parameters.
212        """
213        return dict(self.params)

Get the parameters of the recommender.

Returns: the recommender parameters.