Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import math
- # ============================================================
- # ITEM-BASED рекомендательная система
- # ============================================================
- products = ['P1', 'P2', 'P3', 'P4', 'P5']
- users = ['U1', 'U2', 'U3', 'U4', 'U5']
- ratings = [
- # U1 U2 U3 U4 U5
- [5, 5, 4, 5, 4], # P1
- [5, 5, 3, 0, 3], # P2
- [5, 5, 5, 5, 0], # P3
- [5, 0, 5, 5, 5], # P4
- [0, 5, 1, 0, 2], # P5
- ]
- # Матрица косинусного подобия товаров
- item_sim_raw = {
- ('P1', 'P2'): 0.80,
- ('P1', 'P3'): 0.85,
- ('P1', 'P4'): 0.82,
- ('P1', 'P5'): 0.70,
- ('P2', 'P3'): 0.78,
- ('P2', 'P4'): 0.75,
- ('P2', 'P5'): 0.90,
- ('P3', 'P4'): 0.95,
- ('P3', 'P5'): 0.72,
- ('P4', 'P5'): 0.68,
- }
- # ============================================================
- # Вспомогательные функции
- # ============================================================
- def get_item_sim(p1, p2):
- if p1 == p2:
- return 1.0
- key = (p1, p2) if (p1, p2) in item_sim_raw else (p2, p1)
- return item_sim_raw.get(key, 0.0)
- def max_user_rating(user_idx):
- rated = [ratings[i][user_idx] for i in range(len(products)) if ratings[i][user_idx] > 0]
- return max(rated) if rated else 0
- def avg_user_rating(user_idx):
- rated = [ratings[i][user_idx] for i in range(len(products)) if ratings[i][user_idx] > 0]
- return sum(rated) / len(rated) if rated else 0.0
- def is_new_user(user_idx):
- return all(ratings[i][user_idx] == 0 for i in range(len(products)))
- def best_avg_product():
- best_p, best_avg = None, -1
- for i, p in enumerate(products):
- rated = [ratings[i][j] for j in range(len(users)) if ratings[i][j] > 0]
- avg = sum(rated) / len(rated) if rated else 0.0
- if avg > best_avg:
- best_avg = avg
- best_p = p
- return best_p, best_avg
- # ============================================================
- # Формула предсказания (ITEM-BASED):
- #
- # Pr(a, i) = sum_{j in K} r(a,j) x cos(i,j)
- # ─────────────────────────────────
- # sum_{j in K} |cos(i,j)|
- #
- # K - товары похожие на i, которые пользователь a оценил (> 0)
- # r(a,j) - оценка пользователя a для товара j из K
- # cos(i,j)- косинусное подобие товаров i и j
- #
- # Критерий рекомендации: Pr(a,i) >= max(оценок пользователя a)
- # ============================================================
- def predict(user_a_idx, item_i_idx, demo=True):
- a_name = users[user_a_idx]
- i_name = products[item_i_idx]
- K = []
- for j_idx, j_name in enumerate(products):
- if j_idx == item_i_idx:
- continue
- if ratings[j_idx][user_a_idx] > 0:
- sim = get_item_sim(i_name, j_name)
- if sim > 0:
- K.append(j_idx)
- if not K:
- if demo:
- print(f" K пуст - нет похожих оценённых товаров")
- return None
- if demo:
- print(f" K = {{ {', '.join(products[j] for j in K)} }}")
- print()
- numerator = 0.0
- denominator = 0.0
- for j_idx in K:
- j_name = products[j_idx]
- r_aj = ratings[j_idx][user_a_idx]
- cos_ij = get_item_sim(i_name, j_name)
- term = r_aj * cos_ij
- numerator += term
- denominator += abs(cos_ij)
- if demo:
- print(f" j={j_name}: r({a_name},{j_name})={r_aj}, "
- f"cos({i_name},{j_name})={cos_ij:.2f} "
- f"-> {r_aj} x {cos_ij:.2f} = {term:.4f}")
- if denominator == 0:
- return None
- pr = numerator / denominator
- if demo:
- print(f"\n Pr({a_name},{i_name}) = {numerator:.4f} / {denominator:.4f} = {pr:.4f}")
- return pr
- # ============================================================
- # Рекомендация для одного пользователя
- # ============================================================
- def recommend_for_user(user_idx, demo=True):
- a_name = users[user_idx]
- print(f"\n{'='*60}")
- print(f" Пользователь: {a_name}")
- print(f"{'='*60}")
- if is_new_user(user_idx):
- best_p, best_avg = best_avg_product()
- print(f" Новый пользователь (все оценки = 0).")
- print(f" Рекомендуем товар с наибольшим средним: {best_p} (avg={best_avg:.4f})")
- return {best_p: best_avg}
- unrated = [i for i in range(len(products)) if ratings[i][user_idx] == 0]
- if not unrated:
- print(f" {a_name} оценил все товары - нечего рекомендовать.")
- return {}
- rated_str = {products[i]: ratings[i][user_idx]
- for i in range(len(products)) if ratings[i][user_idx] > 0}
- max_a = max_user_rating(user_idx)
- avg_a = avg_user_rating(user_idx)
- print(f" Оценённые товары : {rated_str}")
- print(f" Неоценённые : {[products[i] for i in unrated]}")
- print(f" r_avg({a_name}) = {avg_a:.4f}, r_max({a_name}) = {max_a}")
- print(f" Критерий рекомендации: Pr >= r_max({a_name}) = {max_a}")
- print()
- predictions = {}
- for i_idx in unrated:
- i_name = products[i_idx]
- print(f" -> Pr({a_name}, {i_name}):")
- pr = predict(user_idx, i_idx, demo=demo)
- if pr is not None:
- predictions[i_name] = pr
- else:
- print(f" Нет данных для предсказания.")
- print()
- if not predictions:
- print(f" Не удалось предсказать оценки.")
- return {}
- print(f" Все предсказания: { {k: round(v, 4) for k, v in predictions.items()} }")
- print()
- results = {}
- for item, pr in sorted(predictions.items(), key=lambda x: -x[1]):
- if round(pr, 10) >= max_a:
- print(f" + Рекомендуем {a_name} -> {item} (Pr={pr:.4f} >= max={max_a})")
- results[item] = pr
- else:
- print(f" - Не рекомендуем {a_name} -> {item} (Pr={pr:.4f} < max={max_a})")
- return results
- # ============================================================
- # Вывод исходных данных
- # ============================================================
- print("=" * 60)
- print(" ITEM-BASED РЕКОМЕНДАТЕЛЬНАЯ СИСТЕМА")
- print("=" * 60)
- col_w = 6
- print("\n Матрица предпочтений:")
- print(" " + " " * 4 + "".join(f"{u:>{col_w}}" for u in users))
- print(" " + "-" * (4 + col_w * len(users)))
- for i, p in enumerate(products):
- row = f" {p:<4}" + "".join(f"{ratings[i][j]:>{col_w}}" for j in range(len(users)))
- print(row)
- print("\n Матрица косинусного подобия товаров:")
- print(" " + " " * 4 + "".join(f"{p:>{col_w}}" for p in products))
- print(" " + "-" * (4 + col_w * len(products)))
- for i, p1 in enumerate(products):
- row = f" {p1:<4}"
- for j, p2 in enumerate(products):
- if i == j:
- row += f"{'0':>{col_w}}"
- elif i > j:
- row += f"{'—':>{col_w}}"
- else:
- row += f"{get_item_sim(p1, p2):>{col_w}.2f}"
- print(row)
- print("\n\n" + "=" * 60)
- print(" РАСЧЁТ РЕКОМЕНДАЦИЙ")
- print("=" * 60)
- all_results = {}
- for j in range(len(users)):
- rec = recommend_for_user(j, demo=True)
- all_results[users[j]] = rec
- print("\n" + "=" * 60)
- print(" ИТОГОВЫЕ РЕКОМЕНДАЦИИ")
- print("=" * 60)
- for u, rec in all_results.items():
- if rec:
- for item, pr in rec.items():
- print(f" + {u} -> рекомендовать {item} (Pr={pr:.4f})")
- else:
- print(f" - {u} -> нечего рекомендовать")
Advertisement
Add Comment
Please, Sign In to add comment