Slope One是一种基于物品的协同过滤算法，在2005年的paper《Slope One Predictors for Online Rating-Based Collaborative Filtering》被提出，用于预测用户对某一给定的物品的评分。

```import pandas as pd
import numpy as np
data_url = 'https://gist.githubusercontent.com/guerbai/3f4964350678c84d359e3536a08f6d3a/raw/f62f26d9ac24d434b1a0be3b5aec57c8a08e7741/user_book_ratings.txt'
df = pd.read_csv(data_url, sep = ',', header = None, names = ['user_id', 'book_id', 'rating'])
user_count = df['user_id'].unique().shape[0]
item_count = df['book_id'].unique().shape[0]
user_id_index_series = pd.Series(range(user_count), index=['user_001', 'user_002', 'user_003', 'user_004', 'user_005', 'user_006'])
item_id_index_series = pd.Series(range(item_count), index=['book_001', 'book_002', 'book_003', 'book_004', 'book_005', 'book_006'])
def construct_user_item_matrix(df):
user_item_matrix = np.zeros((user_count, item_count), dtype=np.int8)
for row in df.itertuples():
user_id = row[1]
book_id = row[2]
rating = row[3]
user_item_matrix[user_id_index_series[user_id], item_id_index_series[book_id]] = rating
return user_item_matrix
user_item_matrix = construct_user_item_matrix(df)
print (user_item_matrix)```

```[[4 3 0 0 5 0]
[5 0 4 0 4 0]
[4 0 5 3 4 0]
[0 3 0 0 0 5]
[0 4 0 0 0 4]
[0 0 2 4 0 5]]```

## 构造物品评分差异矩阵

`weight_matrix`

`((4-4)+(5-4))/2 = 0.5`
，故在`differential_matrix[2][4]`

```def compute_differential(ratings):
item_count = ratings.shape[1]
differential_matrix = np.zeros((item_count, item_count))
weight_matrix = np.zeros((item_count, item_count))
for i in range(item_count):
for j in range(i+1, item_count):
differential = 0
i_rating_user_indexes = ratings[:, i].nonzero()[0]
j_rating_user_indexes = ratings[:, j].nonzero()[0]
rating_i_j_user = set(i_rating_user_indexes).intersection(set(j_rating_user_indexes))
user_count = len(rating_i_j_user)
if user_count == 0:
continue
for user_index in rating_i_j_user:
differential += ratings[user_index][i] - ratings[user_index][j]
weight_matrix[i][j] = user_count
weight_matrix[j][i] = user_count
differential_matrix[i][j] = round(differential/user_count, 2)
differential_matrix[j][i] = -differential_matrix[i][j]
return differential_matrix, weight_matrix
differential_matrix, weight_matrix = compute_differential(user_item_matrix)
print ('differential_matrix')
print (differential_matrix)
print ('-----')
print ('weight_matrix')
print (weight_matrix)```

```differential_matrix
[[ 0.   1.   0.   1.   0.   0. ]
[-1.   0.   0.   0.  -2.  -1. ]
[-0.   0.   0.   0.   0.5 -3. ]
[-1.   0.  -0.   0.  -1.  -1. ]
[-0.   2.  -0.5  1.   0.   0. ]
[ 0.   1.   3.   1.   0.   0. ]]
-----
weight_matrix
[[ 0.  1.  2.  1.  3.  0.]
[ 1.  0.  0.  0.  1.  2.]
[ 2.  0.  0.  2.  2.  1.]
[ 1.  0.  2.  0.  1.  1.]
[ 3.  1.  2.  1.  0.  0.]
[ 0.  2.  1.  1.  0.  0.]]```

## 进行评分预测

;

，则利用`item_001`

，这便是加权的含义；

```def predict(ratings, differential_matrix, weight_matrix, user_index, item_index):
if ratings[user_index][item_index] != 0: return ratings[user_index][item_index]
fenzi = 0
fenmu = 0
for rated_item_index in ratings[user_index].nonzero()[0]:
fenzi += weight_matrix[item_index][rated_item_index] * \
(differential_matrix[item_index][rated_item_index] + ratings[user_index][rated_item_index])
fenmu += weight_matrix[rated_item_index][item_index]
return round(fenzi/fenmu, 2)```

`predict(user_book_matrix, book_differential, weight_matrix, 1, 3)`

`3.75`

## 新的评分数据

```def update_matrices(user_index, item_index, rating):
rated_item_indexes = user_item_matrix[user_index].nonzero()[0]
user_item_matrix[user_index][item_index] = rating
for rated_item_index in rated_item_indexes:
old_weight = weight_matrix[rated_item_index][item_index]
weight_matrix[rated_item_index][item_index] += 1
weight_matrix[item_index][rated_item_index] += 1
differential_matrix[rated_item_index][item_index] = (differential_matrix[rated_item_index][item_index] \
* old_weight + (user_item_matrix[user_index][rated_item_index] - rating)) / (old_weight + 1)
differential_matrix[item_index][rated_item_index] = (differential_matrix[item_index][rated_item_index] \
* old_weight + (rating - user_item_matrix[user_index][rated_item_index])) / (old_weight + 1)```