Skip to content

itemknn

ItemKNN

Bases: TopKItemSimilarityMatrixAlgorithm, PopularityPaddingMixin

Item K Nearest Neighbours model.

First described in 'Item-based top-n recommendation algorithms.' :cite:10.1145/963770.963776

This code is adapted from RecPack :cite:recpack

For each item the K most similar items are computed during fit. Similarity parameter decides how to compute the similarity between two items.

Cosine similarity between item i and j is computed as

.. math:: sim(i,j) = \frac{X_i X_j}{||X_i||_2 ||X_j||_2}

:param K: How many neigbours to use per item, make sure to pick a value below the number of columns of the matrix to fit on. Defaults to 200 :type K: int, optional

Source code in src/recnexteval/algorithms/itemknn/itemknn.py
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
class ItemKNN(TopKItemSimilarityMatrixAlgorithm, PopularityPaddingMixin):
    """Item K Nearest Neighbours model.

    First described in 'Item-based top-n recommendation algorithms.' :cite:`10.1145/963770.963776`

    This code is adapted from RecPack :cite:`recpack`

    For each item the K most similar items are computed during fit.
    Similarity parameter decides how to compute the similarity between two items.

    Cosine similarity between item i and j is computed as

    .. math::
        sim(i,j) = \\frac{X_i X_j}{||X_i||_2 ||X_j||_2}

    :param K: How many neigbours to use per item,
        make sure to pick a value below the number of columns of the matrix to fit on.
        Defaults to 200
    :type K: int, optional
    """

    ITEM_USER_BASED = ItemUserBasedEnum.ITEM

    def _fit(self, X: csr_matrix) -> Self:
        """Fit a cosine similarity matrix from item to item
        We assume that X is a binary matrix of shape (n_users, n_items)
        """
        item_similarities = compute_cosine_similarity(X)
        item_similarities = get_top_K_values(item_similarities, K=self.K)

        self.similarity_matrix_ = item_similarities
        self.X_ = X.copy()
        return self

    def _predict(self, X: PredictionMatrix) -> csr_matrix:
        predict_ui_df = X.filter_for_predict()._df  # noqa: SLF001

        # create a boolean series that is true for index in predict_ui_df.uid
        uid_to_predict = predict_ui_df[predict_ui_df.uid < self.X_.shape[0]].uid.unique()
        uid_to_predict = sorted(uid_to_predict.tolist())

        # features: csr_matrix = self.X_[uid_to_predict]
        # we try without any filtering on the feature matrix
        features: csr_matrix = self.X_
        scores = features @ self.similarity_matrix_

        if not isinstance(scores, csr_matrix):
            scores = csr_matrix(scores)

        intended_shape = (X.global_num_user, X.global_num_item)

        if scores.shape == intended_shape:
            return scores

        # there are 2 cases where the shape is different:
        # 1. The algorithm did not predict unknown user, causing shortage in rows
        # 2. The algorithm not aware of unknown items, causing shortage in columns

        # handle case 1
        if scores.shape[1] < intended_shape[1]:
            scores = self._pad_unknown_iid_with_none_strategy(
                y_pred=scores,
                current_shape=scores.shape,
                intended_shape=intended_shape,
            )

        # handle case 2
        if self.pad_with_popularity:
            scores = self._pad_unknown_uid_with_popularity_strategy(
                X_pred=scores,
                intended_shape=intended_shape,
                predict_ui_df=predict_ui_df,
            )
        else:
            scores = self._pad_unknown_uid_with_random_strategy(
                X_pred=scores,
                current_shape=scores.shape,
                intended_shape=intended_shape,
                predict_ui_df=predict_ui_df,
            )

        pred = scores[predict_ui_df["uid"].values]
        return pred

pad_with_popularity = pad_with_popularity instance-attribute

name property

Name of the object's class.

:return: Name of the object's class :rtype: str

params property

Parameters of the object.

:return: Parameters of the object :rtype: dict

identifier property

Identifier of the object.

Identifier is made by combining the class name with the parameters passed at construction time.

Constructed by recreating the initialisation call. Example: Algorithm(param_1=value)

:return: Identifier of the object :rtype: str

IS_BASE = True class-attribute instance-attribute

seed = 42 instance-attribute

rand_gen = np.random.default_rng(seed=(self.seed)) instance-attribute

description property

Description of the algorithm.

:return: Description of the algorithm :rtype: str

K = K instance-attribute

similarity_matrix_ instance-attribute

ITEM_USER_BASED = ItemUserBasedEnum.ITEM class-attribute instance-attribute

get_popularity_scores(X)

Compute a popularity-based scoring vector for items.

This method calculates normalized interaction counts for each item, selects the top-K most popular items, and returns a vector where only those top-K items have their normalized scores (others are 0). This is used to pad predictions for unseen users with popular items.

:param X: The interaction matrix (user-item) to compute popularity from. :type X: csr_matrix :return: A 1D array of shape (num_items,) with popularity scores for top-K items. :rtype: np.ndarray

Source code in src/recnexteval/algorithms/core/popularity_padding.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def get_popularity_scores(self, X: csr_matrix) -> np.ndarray:
    """Compute a popularity-based scoring vector for items.

    This method calculates normalized interaction counts for each item,
    selects the top-K most popular items, and returns a vector where
    only those top-K items have their normalized scores (others are 0).
    This is used to pad predictions for unseen users with popular items.

    :param X: The interaction matrix (user-item) to compute popularity from.
    :type X: csr_matrix
    :return: A 1D array of shape (num_items,) with popularity scores for top-K items.
    :rtype: np.ndarray
    """
    interaction_counts = X.sum(axis=0).A[0]
    normalized_scores = interaction_counts / interaction_counts.max()

    num_items = X.shape[1]
    if hasattr(self, "K"):
        k_value = self.K
    else:
        k_value = 100
    if num_items < k_value:
        logger.warning("K is larger than the number of items.")

    effective_k = min(k_value, num_items)
    # Get indices of top-K items by popularity
    top_k_indices = np.argpartition(normalized_scores, -effective_k)[-effective_k:]
    popularity_vector = np.zeros(num_items)
    popularity_vector[top_k_indices] = normalized_scores[top_k_indices]

    return popularity_vector

get_params() abstractmethod

Get the parameters of the object.

:return: Parameters of the object :rtype: dict

Source code in src/recnexteval/models/base.py
38
39
40
41
42
43
44
45
@abstractmethod
def get_params(self) -> dict[str, Any]:
    """Get the parameters of the object.

    :return: Parameters of the object
    :rtype: dict
    """
    ...

get_default_params() classmethod

Get default parameters without instantiation.

Uses inspect.signature to extract init parameters and their default values without instantiating the class.

Returns:

Type Description
dict

Dictionary of parameter names to default values.

dict

Parameters without defaults map to None.

Source code in src/recnexteval/algorithms/core/base.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@classmethod
def get_default_params(cls) -> dict:
    """Get default parameters without instantiation.

    Uses inspect.signature to extract __init__ parameters and their
    default values without instantiating the class.

    Returns:
        Dictionary of parameter names to default values.
        Parameters without defaults map to None.
    """
    try:
        sig = signature(cls.__init__)
    except (ValueError, TypeError):
        # Fallback for built-in types or special cases
        return {}

    params = {}
    for param_name, param in sig.parameters.items():
        if param_name == "self":
            continue

        if param.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD):
            # Skip *args, **kwargs
            continue

        # Extract the default value
        if param.default is not Parameter.empty:
            params[param_name] = param.default
        else:
            params[param_name] = None

    return params

set_params(**params)

Set the parameters of the estimator.

:param params: Estimator parameters :type params: dict

Source code in src/recnexteval/algorithms/core/base.py
 94
 95
 96
 97
 98
 99
100
def set_params(self, **params) -> Self:
    """Set the parameters of the estimator.

    :param params: Estimator parameters
    :type params: dict
    """
    return super().set_params(**params)

fit(X)

Fit the model to the input interaction matrix.

The input data is transformed to the expected type using :meth:_transform_fit_input. The fitting is done using the :meth:_fit method. Finally the method checks that the fitting was successful using :meth:_check_fit_complete.

:param X: The interactions to fit the model on. :type X: InteractionMatrix :return: Fitted algorithm :rtype: Algorithm

Source code in src/recnexteval/algorithms/core/base.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def fit(self, X: InteractionMatrix) -> Self:
    """Fit the model to the input interaction matrix.

    The input data is transformed to the expected type using
    :meth:`_transform_fit_input`. The fitting is done using the
    :meth:`_fit` method. Finally the method checks that the fitting
    was successful using :meth:`_check_fit_complete`.

    :param X: The interactions to fit the model on.
    :type X: InteractionMatrix
    :return: Fitted algorithm
    :rtype: Algorithm
    """
    start = time.time()
    X_transformed = to_csr_matrix(X, binary=True)
    self._fit(X_transformed)

    self._check_fit_complete()
    end = time.time()
    logger.debug(f"Fitting {self.name} complete - Took {end - start:.3}s")
    return self

predict(X)

Predicts scores, given the interactions in X

The input data is transformed to the expected type using :meth:_transform_predict_input. The predictions are made using the :meth:_predict method. Finally the predictions are then padded with random items for users that are not in the training data.

:param X: interactions to predict from. :type X: InteractionMatrix :return: The recommendation scores in a sparse matrix format. :rtype: csr_matrix

Source code in src/recnexteval/algorithms/core/base.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def predict(self, X: PredictionMatrix) -> csr_matrix:
    """Predicts scores, given the interactions in X

    The input data is transformed to the expected type using
    :meth:`_transform_predict_input`. The predictions are made
    using the :meth:`_predict` method. Finally the predictions
    are then padded with random items for users that are not in the
    training data.

    :param X: interactions to predict from.
    :type X: InteractionMatrix
    :return: The recommendation scores in a sparse matrix format.
    :rtype: csr_matrix
    """
    self._check_fit_complete()
    X_pred = self._predict(X)
    return X_pred

ItemKNNIncremental

Bases: ItemKNN, IncrementalTrainingMixin

Incremental version of ItemKNN algorithm.

This class extends the ItemKNN algorithm to allow for incremental updates to the model. The incremental updates are done by updating the historical data with the new data by appending the new data to the historical data.

Source code in src/recnexteval/algorithms/itemknn/itemknn_incremental.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class ItemKNNIncremental(ItemKNN, IncrementalTrainingMixin):
    """Incremental version of ItemKNN algorithm.

    This class extends the ItemKNN algorithm to allow for incremental updates
    to the model. The incremental updates are done by updating the historical
    data with the new data by appending the new data to the historical data.
    """

    IS_BASE: bool = False

    def __init__(self, K: int = 10, pad_with_popularity: bool = True) -> None:
        PopularityPaddingMixin.__init__(self, pad_with_popularity=pad_with_popularity)
        TopKItemSimilarityMatrixAlgorithm.__init__(self, K=K)
        self.X_: None | csr_matrix = None

    def _fit(self, X: csr_matrix) -> Self:
        """Fit a cosine similarity matrix from item to item."""
        if self.X_ is not None:
            self._append_training_data(X)
            super()._fit(self.X_)
        else:
            super()._fit(X)
        return self

pad_with_popularity = pad_with_popularity instance-attribute

name property

Name of the object's class.

:return: Name of the object's class :rtype: str

params property

Parameters of the object.

:return: Parameters of the object :rtype: dict

identifier property

Identifier of the object.

Identifier is made by combining the class name with the parameters passed at construction time.

Constructed by recreating the initialisation call. Example: Algorithm(param_1=value)

:return: Identifier of the object :rtype: str

ITEM_USER_BASED = ItemUserBasedEnum.ITEM class-attribute instance-attribute

seed = 42 instance-attribute

rand_gen = np.random.default_rng(seed=(self.seed)) instance-attribute

description property

Description of the algorithm.

:return: Description of the algorithm :rtype: str

K = K instance-attribute

similarity_matrix_ instance-attribute

IS_BASE = False class-attribute instance-attribute

X_ = None instance-attribute

get_popularity_scores(X)

Compute a popularity-based scoring vector for items.

This method calculates normalized interaction counts for each item, selects the top-K most popular items, and returns a vector where only those top-K items have their normalized scores (others are 0). This is used to pad predictions for unseen users with popular items.

:param X: The interaction matrix (user-item) to compute popularity from. :type X: csr_matrix :return: A 1D array of shape (num_items,) with popularity scores for top-K items. :rtype: np.ndarray

Source code in src/recnexteval/algorithms/core/popularity_padding.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def get_popularity_scores(self, X: csr_matrix) -> np.ndarray:
    """Compute a popularity-based scoring vector for items.

    This method calculates normalized interaction counts for each item,
    selects the top-K most popular items, and returns a vector where
    only those top-K items have their normalized scores (others are 0).
    This is used to pad predictions for unseen users with popular items.

    :param X: The interaction matrix (user-item) to compute popularity from.
    :type X: csr_matrix
    :return: A 1D array of shape (num_items,) with popularity scores for top-K items.
    :rtype: np.ndarray
    """
    interaction_counts = X.sum(axis=0).A[0]
    normalized_scores = interaction_counts / interaction_counts.max()

    num_items = X.shape[1]
    if hasattr(self, "K"):
        k_value = self.K
    else:
        k_value = 100
    if num_items < k_value:
        logger.warning("K is larger than the number of items.")

    effective_k = min(k_value, num_items)
    # Get indices of top-K items by popularity
    top_k_indices = np.argpartition(normalized_scores, -effective_k)[-effective_k:]
    popularity_vector = np.zeros(num_items)
    popularity_vector[top_k_indices] = normalized_scores[top_k_indices]

    return popularity_vector

get_params() abstractmethod

Get the parameters of the object.

:return: Parameters of the object :rtype: dict

Source code in src/recnexteval/models/base.py
38
39
40
41
42
43
44
45
@abstractmethod
def get_params(self) -> dict[str, Any]:
    """Get the parameters of the object.

    :return: Parameters of the object
    :rtype: dict
    """
    ...

get_default_params() classmethod

Get default parameters without instantiation.

Uses inspect.signature to extract init parameters and their default values without instantiating the class.

Returns:

Type Description
dict

Dictionary of parameter names to default values.

dict

Parameters without defaults map to None.

Source code in src/recnexteval/algorithms/core/base.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@classmethod
def get_default_params(cls) -> dict:
    """Get default parameters without instantiation.

    Uses inspect.signature to extract __init__ parameters and their
    default values without instantiating the class.

    Returns:
        Dictionary of parameter names to default values.
        Parameters without defaults map to None.
    """
    try:
        sig = signature(cls.__init__)
    except (ValueError, TypeError):
        # Fallback for built-in types or special cases
        return {}

    params = {}
    for param_name, param in sig.parameters.items():
        if param_name == "self":
            continue

        if param.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD):
            # Skip *args, **kwargs
            continue

        # Extract the default value
        if param.default is not Parameter.empty:
            params[param_name] = param.default
        else:
            params[param_name] = None

    return params

set_params(**params)

Set the parameters of the estimator.

:param params: Estimator parameters :type params: dict

Source code in src/recnexteval/algorithms/core/base.py
 94
 95
 96
 97
 98
 99
100
def set_params(self, **params) -> Self:
    """Set the parameters of the estimator.

    :param params: Estimator parameters
    :type params: dict
    """
    return super().set_params(**params)

fit(X)

Fit the model to the input interaction matrix.

The input data is transformed to the expected type using :meth:_transform_fit_input. The fitting is done using the :meth:_fit method. Finally the method checks that the fitting was successful using :meth:_check_fit_complete.

:param X: The interactions to fit the model on. :type X: InteractionMatrix :return: Fitted algorithm :rtype: Algorithm

Source code in src/recnexteval/algorithms/core/base.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def fit(self, X: InteractionMatrix) -> Self:
    """Fit the model to the input interaction matrix.

    The input data is transformed to the expected type using
    :meth:`_transform_fit_input`. The fitting is done using the
    :meth:`_fit` method. Finally the method checks that the fitting
    was successful using :meth:`_check_fit_complete`.

    :param X: The interactions to fit the model on.
    :type X: InteractionMatrix
    :return: Fitted algorithm
    :rtype: Algorithm
    """
    start = time.time()
    X_transformed = to_csr_matrix(X, binary=True)
    self._fit(X_transformed)

    self._check_fit_complete()
    end = time.time()
    logger.debug(f"Fitting {self.name} complete - Took {end - start:.3}s")
    return self

predict(X)

Predicts scores, given the interactions in X

The input data is transformed to the expected type using :meth:_transform_predict_input. The predictions are made using the :meth:_predict method. Finally the predictions are then padded with random items for users that are not in the training data.

:param X: interactions to predict from. :type X: InteractionMatrix :return: The recommendation scores in a sparse matrix format. :rtype: csr_matrix

Source code in src/recnexteval/algorithms/core/base.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def predict(self, X: PredictionMatrix) -> csr_matrix:
    """Predicts scores, given the interactions in X

    The input data is transformed to the expected type using
    :meth:`_transform_predict_input`. The predictions are made
    using the :meth:`_predict` method. Finally the predictions
    are then padded with random items for users that are not in the
    training data.

    :param X: interactions to predict from.
    :type X: InteractionMatrix
    :return: The recommendation scores in a sparse matrix format.
    :rtype: csr_matrix
    """
    self._check_fit_complete()
    X_pred = self._predict(X)
    return X_pred

ItemKNNRolling

Bases: ItemKNN

Rolling version of ItemKNN algorithm.

This class extends the ItemKNN algorithm to update the memory of the model to only keep the last window of interactions. The model is simply discarding all interactions that are older than the window size.

Source code in src/recnexteval/algorithms/itemknn/itemknn_rolling.py
 9
10
11
12
13
14
15
16
17
class ItemKNNRolling(ItemKNN):
    """Rolling version of ItemKNN algorithm.

    This class extends the ItemKNN algorithm to update the memory of the model
    to only keep the last window of interactions. The model is simply discarding
    all interactions that are older than the window size.
    """

    IS_BASE: bool = False

pad_with_popularity = pad_with_popularity instance-attribute

name property

Name of the object's class.

:return: Name of the object's class :rtype: str

params property

Parameters of the object.

:return: Parameters of the object :rtype: dict

identifier property

Identifier of the object.

Identifier is made by combining the class name with the parameters passed at construction time.

Constructed by recreating the initialisation call. Example: Algorithm(param_1=value)

:return: Identifier of the object :rtype: str

ITEM_USER_BASED = ItemUserBasedEnum.ITEM class-attribute instance-attribute

seed = 42 instance-attribute

rand_gen = np.random.default_rng(seed=(self.seed)) instance-attribute

description property

Description of the algorithm.

:return: Description of the algorithm :rtype: str

K = K instance-attribute

similarity_matrix_ instance-attribute

IS_BASE = False class-attribute instance-attribute

get_popularity_scores(X)

Compute a popularity-based scoring vector for items.

This method calculates normalized interaction counts for each item, selects the top-K most popular items, and returns a vector where only those top-K items have their normalized scores (others are 0). This is used to pad predictions for unseen users with popular items.

:param X: The interaction matrix (user-item) to compute popularity from. :type X: csr_matrix :return: A 1D array of shape (num_items,) with popularity scores for top-K items. :rtype: np.ndarray

Source code in src/recnexteval/algorithms/core/popularity_padding.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def get_popularity_scores(self, X: csr_matrix) -> np.ndarray:
    """Compute a popularity-based scoring vector for items.

    This method calculates normalized interaction counts for each item,
    selects the top-K most popular items, and returns a vector where
    only those top-K items have their normalized scores (others are 0).
    This is used to pad predictions for unseen users with popular items.

    :param X: The interaction matrix (user-item) to compute popularity from.
    :type X: csr_matrix
    :return: A 1D array of shape (num_items,) with popularity scores for top-K items.
    :rtype: np.ndarray
    """
    interaction_counts = X.sum(axis=0).A[0]
    normalized_scores = interaction_counts / interaction_counts.max()

    num_items = X.shape[1]
    if hasattr(self, "K"):
        k_value = self.K
    else:
        k_value = 100
    if num_items < k_value:
        logger.warning("K is larger than the number of items.")

    effective_k = min(k_value, num_items)
    # Get indices of top-K items by popularity
    top_k_indices = np.argpartition(normalized_scores, -effective_k)[-effective_k:]
    popularity_vector = np.zeros(num_items)
    popularity_vector[top_k_indices] = normalized_scores[top_k_indices]

    return popularity_vector

get_params() abstractmethod

Get the parameters of the object.

:return: Parameters of the object :rtype: dict

Source code in src/recnexteval/models/base.py
38
39
40
41
42
43
44
45
@abstractmethod
def get_params(self) -> dict[str, Any]:
    """Get the parameters of the object.

    :return: Parameters of the object
    :rtype: dict
    """
    ...

get_default_params() classmethod

Get default parameters without instantiation.

Uses inspect.signature to extract init parameters and their default values without instantiating the class.

Returns:

Type Description
dict

Dictionary of parameter names to default values.

dict

Parameters without defaults map to None.

Source code in src/recnexteval/algorithms/core/base.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@classmethod
def get_default_params(cls) -> dict:
    """Get default parameters without instantiation.

    Uses inspect.signature to extract __init__ parameters and their
    default values without instantiating the class.

    Returns:
        Dictionary of parameter names to default values.
        Parameters without defaults map to None.
    """
    try:
        sig = signature(cls.__init__)
    except (ValueError, TypeError):
        # Fallback for built-in types or special cases
        return {}

    params = {}
    for param_name, param in sig.parameters.items():
        if param_name == "self":
            continue

        if param.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD):
            # Skip *args, **kwargs
            continue

        # Extract the default value
        if param.default is not Parameter.empty:
            params[param_name] = param.default
        else:
            params[param_name] = None

    return params

set_params(**params)

Set the parameters of the estimator.

:param params: Estimator parameters :type params: dict

Source code in src/recnexteval/algorithms/core/base.py
 94
 95
 96
 97
 98
 99
100
def set_params(self, **params) -> Self:
    """Set the parameters of the estimator.

    :param params: Estimator parameters
    :type params: dict
    """
    return super().set_params(**params)

fit(X)

Fit the model to the input interaction matrix.

The input data is transformed to the expected type using :meth:_transform_fit_input. The fitting is done using the :meth:_fit method. Finally the method checks that the fitting was successful using :meth:_check_fit_complete.

:param X: The interactions to fit the model on. :type X: InteractionMatrix :return: Fitted algorithm :rtype: Algorithm

Source code in src/recnexteval/algorithms/core/base.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def fit(self, X: InteractionMatrix) -> Self:
    """Fit the model to the input interaction matrix.

    The input data is transformed to the expected type using
    :meth:`_transform_fit_input`. The fitting is done using the
    :meth:`_fit` method. Finally the method checks that the fitting
    was successful using :meth:`_check_fit_complete`.

    :param X: The interactions to fit the model on.
    :type X: InteractionMatrix
    :return: Fitted algorithm
    :rtype: Algorithm
    """
    start = time.time()
    X_transformed = to_csr_matrix(X, binary=True)
    self._fit(X_transformed)

    self._check_fit_complete()
    end = time.time()
    logger.debug(f"Fitting {self.name} complete - Took {end - start:.3}s")
    return self

predict(X)

Predicts scores, given the interactions in X

The input data is transformed to the expected type using :meth:_transform_predict_input. The predictions are made using the :meth:_predict method. Finally the predictions are then padded with random items for users that are not in the training data.

:param X: interactions to predict from. :type X: InteractionMatrix :return: The recommendation scores in a sparse matrix format. :rtype: csr_matrix

Source code in src/recnexteval/algorithms/core/base.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def predict(self, X: PredictionMatrix) -> csr_matrix:
    """Predicts scores, given the interactions in X

    The input data is transformed to the expected type using
    :meth:`_transform_predict_input`. The predictions are made
    using the :meth:`_predict` method. Finally the predictions
    are then padded with random items for users that are not in the
    training data.

    :param X: interactions to predict from.
    :type X: InteractionMatrix
    :return: The recommendation scores in a sparse matrix format.
    :rtype: csr_matrix
    """
    self._check_fit_complete()
    X_pred = self._predict(X)
    return X_pred

ItemKNNStatic

Bases: ItemKNN

Static version of ItemKNN algorithm.

This class extends the ItemKNN algorithm to only fit the model once. fit will only fit the model once and will not update the model with new data. The purpose is to make the training data static and not update the model with new data.

Source code in src/recnexteval/algorithms/itemknn/itemknn_static.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class ItemKNNStatic(ItemKNN):
    """Static version of ItemKNN algorithm.

    This class extends the ItemKNN algorithm to only fit the model once. `fit` will only
    fit the model once and will not update the model with new data. The purpose
    is to make the training data static and not update the model with new data.
    """

    IS_BASE: bool = False

    def __init__(self, K: int = 10) -> None:
        self._is_fitted = False
        super().__init__(K)

    def fit(self, X: InteractionMatrix) -> Self:
        if self._is_fitted:
            return self

        super().fit(X)
        self._is_fitted = True
        return self

pad_with_popularity = pad_with_popularity instance-attribute

name property

Name of the object's class.

:return: Name of the object's class :rtype: str

params property

Parameters of the object.

:return: Parameters of the object :rtype: dict

identifier property

Identifier of the object.

Identifier is made by combining the class name with the parameters passed at construction time.

Constructed by recreating the initialisation call. Example: Algorithm(param_1=value)

:return: Identifier of the object :rtype: str

ITEM_USER_BASED = ItemUserBasedEnum.ITEM class-attribute instance-attribute

seed = 42 instance-attribute

rand_gen = np.random.default_rng(seed=(self.seed)) instance-attribute

description property

Description of the algorithm.

:return: Description of the algorithm :rtype: str

K = K instance-attribute

similarity_matrix_ instance-attribute

IS_BASE = False class-attribute instance-attribute

get_popularity_scores(X)

Compute a popularity-based scoring vector for items.

This method calculates normalized interaction counts for each item, selects the top-K most popular items, and returns a vector where only those top-K items have their normalized scores (others are 0). This is used to pad predictions for unseen users with popular items.

:param X: The interaction matrix (user-item) to compute popularity from. :type X: csr_matrix :return: A 1D array of shape (num_items,) with popularity scores for top-K items. :rtype: np.ndarray

Source code in src/recnexteval/algorithms/core/popularity_padding.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def get_popularity_scores(self, X: csr_matrix) -> np.ndarray:
    """Compute a popularity-based scoring vector for items.

    This method calculates normalized interaction counts for each item,
    selects the top-K most popular items, and returns a vector where
    only those top-K items have their normalized scores (others are 0).
    This is used to pad predictions for unseen users with popular items.

    :param X: The interaction matrix (user-item) to compute popularity from.
    :type X: csr_matrix
    :return: A 1D array of shape (num_items,) with popularity scores for top-K items.
    :rtype: np.ndarray
    """
    interaction_counts = X.sum(axis=0).A[0]
    normalized_scores = interaction_counts / interaction_counts.max()

    num_items = X.shape[1]
    if hasattr(self, "K"):
        k_value = self.K
    else:
        k_value = 100
    if num_items < k_value:
        logger.warning("K is larger than the number of items.")

    effective_k = min(k_value, num_items)
    # Get indices of top-K items by popularity
    top_k_indices = np.argpartition(normalized_scores, -effective_k)[-effective_k:]
    popularity_vector = np.zeros(num_items)
    popularity_vector[top_k_indices] = normalized_scores[top_k_indices]

    return popularity_vector

get_params() abstractmethod

Get the parameters of the object.

:return: Parameters of the object :rtype: dict

Source code in src/recnexteval/models/base.py
38
39
40
41
42
43
44
45
@abstractmethod
def get_params(self) -> dict[str, Any]:
    """Get the parameters of the object.

    :return: Parameters of the object
    :rtype: dict
    """
    ...

get_default_params() classmethod

Get default parameters without instantiation.

Uses inspect.signature to extract init parameters and their default values without instantiating the class.

Returns:

Type Description
dict

Dictionary of parameter names to default values.

dict

Parameters without defaults map to None.

Source code in src/recnexteval/algorithms/core/base.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@classmethod
def get_default_params(cls) -> dict:
    """Get default parameters without instantiation.

    Uses inspect.signature to extract __init__ parameters and their
    default values without instantiating the class.

    Returns:
        Dictionary of parameter names to default values.
        Parameters without defaults map to None.
    """
    try:
        sig = signature(cls.__init__)
    except (ValueError, TypeError):
        # Fallback for built-in types or special cases
        return {}

    params = {}
    for param_name, param in sig.parameters.items():
        if param_name == "self":
            continue

        if param.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD):
            # Skip *args, **kwargs
            continue

        # Extract the default value
        if param.default is not Parameter.empty:
            params[param_name] = param.default
        else:
            params[param_name] = None

    return params

set_params(**params)

Set the parameters of the estimator.

:param params: Estimator parameters :type params: dict

Source code in src/recnexteval/algorithms/core/base.py
 94
 95
 96
 97
 98
 99
100
def set_params(self, **params) -> Self:
    """Set the parameters of the estimator.

    :param params: Estimator parameters
    :type params: dict
    """
    return super().set_params(**params)

predict(X)

Predicts scores, given the interactions in X

The input data is transformed to the expected type using :meth:_transform_predict_input. The predictions are made using the :meth:_predict method. Finally the predictions are then padded with random items for users that are not in the training data.

:param X: interactions to predict from. :type X: InteractionMatrix :return: The recommendation scores in a sparse matrix format. :rtype: csr_matrix

Source code in src/recnexteval/algorithms/core/base.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def predict(self, X: PredictionMatrix) -> csr_matrix:
    """Predicts scores, given the interactions in X

    The input data is transformed to the expected type using
    :meth:`_transform_predict_input`. The predictions are made
    using the :meth:`_predict` method. Finally the predictions
    are then padded with random items for users that are not in the
    training data.

    :param X: interactions to predict from.
    :type X: InteractionMatrix
    :return: The recommendation scores in a sparse matrix format.
    :rtype: csr_matrix
    """
    self._check_fit_complete()
    X_pred = self._predict(X)
    return X_pred

fit(X)

Source code in src/recnexteval/algorithms/itemknn/itemknn_static.py
25
26
27
28
29
30
31
def fit(self, X: InteractionMatrix) -> Self:
    if self._is_fitted:
        return self

    super().fit(X)
    self._is_fitted = True
    return self