<jupyter_start><jupyter_text>Nearest Neighbor Used-based Collaborative FilteringThis notebook is a toy implementation of the classic collaborative filtering algorithm. The purpose of this implementation is to provide a small and clear numerical example. Detailed DescriptionSee [Introduction to Algorithmic Marketing](https://algorithmicweb.wordpress.com/ ) book DataWe use a small rating matrix defined inline. No external datasets are used.<jupyter_code>%matplotlib inline
import sympy as sy
import numpy as np
import matplotlib.pyplot as plt
# Input rating matrix
# X stands for unknown ratings
X = np.nan
R = np.array([
    [5, 4, X, 1, 2, 1], #  
    [4, X, 3, 1, 1, 2], #
    [X, 5, 5, X, 3, 3], # users
    [2, X, 1, 4, 5, 4], # 
    [2, 2, 2, X, 4, X], #
    [1, 2, 1, X, 5, 4]  #
   #       items 
])
# Define common varibales and helper functions
n, m = R.shape

def known(r):
    return not np.isnan(r)

def known_user_ratings(R, u):
    return [r for r in R[u, :] if known(r)]

def user_common_ratings(R, u, v):
    return np.array(list(filter(
        lambda r: all(known(val) for val in r), 
        np.column_stack((R[u, :], R[v, :]))
    ))).T

def mean_r(R, u):
    return np.mean(known_user_ratings(R, u))
# Calculate the matrix of user similarities
def similarity(R, u, v):
    I_uv = user_common_ratings(R, u, v)
    mu_u = mean_r(R, u)
    mu_v = mean_r(R, v)
    return sum([ 
          ((I_uv[0, i] - mu_u)*(I_uv[1, i] - mu_v)) / 
          (np.linalg.norm(I_uv[0, :] - mu_u) * np.linalg.norm(I_uv[1, :] - mu_v)) 
          for i in range(I_uv.shape[1])])

user_similarity = np.array([[
    similarity(R, u, v)
    for u in range(n)] for v in range(n)])
print(user_similarity)
# Predict ratings based on the user similarities
k = 2 # neighborhood size

def predict_rating(R, u, i):
    # neighbors sorted by similarity
    all_neighbors = np.argsort(user_similarity[u])[::-1]
    
    # remove neighbors without ratings for i and select top k
    neighbors = list(filter(lambda v: known(R[v,i]) and not v==u, all_neighbors))[:k] 
    mu_u = mean_r(R, u)
    score = 0 
    norm = 0
    print("user %s, item %s <- user neighbors %s" % (u, i, neighbors))
    for v in neighbors:
        mu_v = mean_r(R, v)
        score = score + user_similarity[u,v]*(R[v,i] - mu_v)
        norm = norm + abs(user_similarity[u,v])
    return mu_u + score/norm

ratings = np.array([[ R[u,i] if known(R[u,i]) else predict_rating(R, u, i)
   for i in range(m)] for u in range(n)])

print("\nComplete rating matrix:")
np.set_printoptions(precision=2)
print(ratings)<jupyter_output>user 0, item 2 <- user neighbors [2, 1]
user 1, item 1 <- user neighbors [0, 2]
user 2, item 0 <- user neighbors [0, 1]
user 2, item 3 <- user neighbors [0, 1]
user 3, item 1 <- user neighbors [5, 4]
user 4, item 3 <- user neighbors [3, 0]
user 4, item 5 <- user neighbors [5, 3]
user 5, item 3 <- user neighbors [3, 0]

Complete rating matrix:
[[5.   4.   3.5  1.   2.   1.  ]
 [4.   3.4  3.   1.   1.   2.  ]
 [6.11 5.   5.   2.59 3.   3.  ]
 [2.   2.65 1.   4.   5.   4.  ]
 [2.   2.   2.   3.63 4.   3.61]
 [1.   2.   1.   3.76 5.   4.  ]]
{'fraction_non_alphanumeric': 0.14619321817018555, 'fraction_numerical': 0.03838771593090211, 'mean_word_length': 2.7903030303030305, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 4, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '210915', 'license': 'Apache-2.0', 'n_tokens_mistral': 1300, 'n_tokens_neox': 1203, 'n_words': 406, 'path': 'recommendations/user-based-cf.ipynb', 'repo_name': 'mingjunwang88/tensor_ikasov', 'size': '3126'}