@HitGirl

Как оптимизировать алгоритм SlopeOne в python?

Здравствуйте!
У меня есть дата фрейм со следующими колонками: 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]))
  • Вопрос задан
  • 109 просмотров
Пригласить эксперта
Ответы на вопрос 1
Maksim_64
@Maksim_64
Data Analyst
По скольку нет полного датафрейма я не могу полностью сформировать рабочий ответ. Но основываясь на вашем коде вот какая главная мысль.
Не каких for в pandas абсолютно любой функционал выполним без циклов. (либо напрямую) любой массив в pandas уже векторизован. либо посредством функций apply или agg (первая поэлементно выполнит вами написанную функцию) , вторая агрегирует, то есть на вход получит массив на выход одно число например (mean, std) и.т.д. Но главный вывод не каких циклов pandas устроен так что ты никогда не используешь циклы для обработки данных. (только для работы с индексами иногда применяют циклы), но никогда с данными. Это очень сильно ускорит код.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы