Здравствуйте!
У меня есть дата фрейм со следующими колонками: userId, movieId, rating. Это оценки пользователей для фильмов. Дата фрейм очень большой.
Пытаюсь реализовать следующий алгоритм (Slope One). Алгоритм заполняет недостающие оценки пользователей.
Алгоритм:
Для каждого пользователя t:
Для каждого неоценённого фильма m_j пользователя t:
Для каждого оценённого фильма m_i пользователя t:
Найти пользователей оценивших ОБА фильма (m_j и m_i)
Если пользователи найдены
Для каждого пользователя k оценивших оба фильма
Найти разницу между оценкой пользователя для фильма m_j и m_i
Сумму разниц оценок разделить на количество пользователей (средняя разница оценок)
Прибавить к сумме разниц оценку текущего пользователя для фильма m_i
Разделить полученную сумму на количество фильмов m_i для которых были найдены пользователи
Данное значение и будет оценкой пользователя t фильму m_j
Мой код ниже. Я оставил только одного пользователя с id==5, но даже в таком варианте расчёт одной оценки занимает больше 30 секунд. Помогите, пожалуйста, оптимизировать код.
t=5
user_movies = pd.Series(pd.unique(movie_data.loc[movie_data['userId']==5, 'movieId']))
not_user_movies = pd.Series(pd.unique(movie_data[~movie_data['movieId'].isin(user_movies)]['movieId']))
users_ratings_prediction = pd.DataFrame([],columns=['user_id','movie_id','rating'])
users_mi_dict = {}
for _, mi in user_movies.iteritems():
users_mi_dict[mi] = movie_data.loc[movie_data['movieId'] == mi, 'userId']
# для всех неоценённых пользователем фильмов
for _, mj in tqdm(not_user_movies.iteritems()):
R_j=0
dev_j_i_sum=0
users_mj = movie_data.loc[movie_data['movieId'] == mj,'userId']
# для всех оцененных пользователем фильмов
for _, mi in user_movies.iteritems():
users_mi = users_mi_dict[mi]
# ищем пользователем которые оценили оба фильма
intersect_users = np.intersect1d(users_mj,users_mi)
# если такие пользователи нашлись
if (intersect_users.size > 0):
r_k_j = movie_data.loc[(movie_data['movieId'] == mj) & (movie_data['userId'].isin(intersect_users)),'rating'].sum()
r_k_i = movie_data.loc[(movie_data['movieId'] == mi) & (movie_data['userId'].isin(intersect_users)),'rating'].sum()
# считаем среднюю разницу между оценками этих фильмов
dev_j_i = (r_k_j-r_k_i)/intersect_users.size
# и прибавляем оценку текущего пользователя
dev_j_i_sum = dev_j_i + movie_data.loc[(movie_data['movieId'] == mi) & (movie_data['userId'] == t),'rating'].item()
R_j+=1
# получаем среднюю оценку для неоценённого фильма по оценённым фильмам пользователя
P_j = dev_j_i_sum/R_j
users_ratings_prediction.loc[users_ratings_prediction.shape[0]] = [t, mj, P_j]
print(users_ratings_prediction.sort_values(['rating'], ascending=[0]))