#495·Surprise

Cosine similarity ZeroDivisionError

Author: Gabriel-KissinCreated Dec 2, 2025Updated Dec 2, 2025

Firstly, thanks for the great library!

I came across an issue while doing a large RandomizedSearchCV which was raising a ZeroDivisionError. This turned out to be because one of the users has all zero ratings, and cosine similarity was being used in that iteration, so obviously the denominator was zero. However it was tricky to debug because it wasn't immediately clear which similarity measure was being used when the error was raised. Because RandomizedSearchCV is parallelised, the traceback wasn't helpful.

MRE:

python
from surprise import Dataset, Reader, KNNBasic
import pandas as pd

df = pd.DataFrame([
    ['u1', 'i1', 0],  # user u1 only has 0 ratings
    ['u2', 'i1', 1],
    ['u2', 'i2', 0],
])
# df[2] = df[2] + 1 # uncommenting this line fixes the issue

reader = Reader(rating_scale=(df[2].min(), df[2].max()))
data = Dataset.load_from_df(df, reader)
trainset = data.build_full_trainset()

algo = KNNBasic(sim_options={'name': 'cosine'})
algo.fit(trainset)

Suggestion 1: clear error message

Catch the case that the denominator is zero and give a clear error message:

change similarities.pyx L76C1-L86C1 from

python
    for xi in range(n_x):
        sim[xi, xi] = 1
        for xj in range(xi + 1, n_x):
            if freq[xi, xj] < min_sprt:
                sim[xi, xj] = 0
            else:
                denum = sqrt(sqi[xi, xj] * sqj[xi, xj])
                sim[xi, xj] = prods[xi, xj] / denum

to

python
    for xi in range(n_x):
        sim[xi, xi] = 1
        for xj in range(xi + 1, n_x):
            if freq[xi, xj] < min_sprt:
                sim[xi, xj] = 0
            elif (sqi[xi, xj]==0) or (sqj[xi, xj]==0):
                raise ZeroDivisionError(
                   "Cosine similarity failed: at least one user (or item) has only zero "
                   "ratings. Rescale your ratings, or remove all-zero users/items."
               )
            else:
                denum = sqrt(sqi[xi, xj] * sqj[xi, xj])
                sim[xi, xj] = prods[xi, xj] / denum

Suggestion 2: set similarity to zero

Catch the case that the denominator is zero and set the similarity as equal to zero:

python
    for xi in range(n_x):
        sim[xi, xi] = 1
        for xj in range(xi + 1, n_x):
            if freq[xi, xj] < min_sprt:
                sim[xi, xj] = 0
            elif (sqi[xi, xj]==0) or (sqj[xi, xj]==0):
                sim[xi, xj] = 0
            else:
                denum = sqrt(sqi[xi, xj] * sqj[xi, xj])
                sim[xi, xj] = prods[xi, xj] / denum

that would be similar to scikit-learn's approach:

python
import sklearn.metrics
X = [[0, 0, 0]]
sklearn.metrics.pairwise.cosine_similarity(X, X)
# returns array([[0.]]), no error

Intuitively, if cosine is the criteria for similarity, then when that is undefined we interpret this as zero similarity. Maybe there should be a warning in the elif to make the user aware