Загрузка данных


import pandas as pd
import numpy as np
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

# ---------------------------------------------------------------------
# 1. Задаём основные переменные (при необходимости измените)
# ---------------------------------------------------------------------
date_col = 'end_month_plan'
target = 'renewed_fact_rate'
pred_col = 'prediction'
weight_col = 'sum_total_out'   # или 'portfolio_share' – укажите нужный

# ---------------------------------------------------------------------
# 2. Функция агрегации (взвешенное среднее)
# ---------------------------------------------------------------------
def aggregate_weighted(df, group_col, value_col, weight_col):
    return (df.groupby(group_col)
              .apply(lambda g: pd.Series({
                  value_col: np.average(g[value_col], weights=g[weight_col]),
                  weight_col: g[weight_col].sum()
              }))
              .reset_index()
              .sort_values(group_col))

# ---------------------------------------------------------------------
# 3. Создаём comparison (факт + прогноз) для всего портфеля
# ---------------------------------------------------------------------
# Факт (взвешенный по weight_col)
fact_all = aggregate_weighted(df, date_col, target, weight_col)
# Прогноз (взвешенный по weight_col)
pred_all = aggregate_weighted(df, date_col, pred_col, weight_col)
# Объединяем
comparison = fact_all.merge(pred_all, on=date_col, suffixes=('_fact', '_pred'))

# Проверяем, что сравнение создалось
print("comparison создан, строк:", len(comparison))
print(comparison.head())

# ---------------------------------------------------------------------
# 4. Расчёт метрик
# ---------------------------------------------------------------------
y_true = comparison[target].values
y_pred = comparison[pred_col].values
weights = comparison[f'{weight_col}_fact'].values   # сумма весов по месяцам

# Обычные метрики
mae = mean_absolute_error(y_true, y_pred)
rmse = np.sqrt(mean_squared_error(y_true, y_pred))
mape = np.mean(np.abs((y_true - y_pred) / np.maximum(np.abs(y_true), 1e-6))) * 100
r2 = r2_score(y_true, y_pred)

# Взвешенные метрики (без R2)
wmae = np.average(np.abs(y_true - y_pred), weights=weights)
wrmse = np.sqrt(np.average((y_true - y_pred)**2, weights=weights))
wmape = np.average(np.abs((y_true - y_pred) / np.maximum(np.abs(y_true), 1e-6)), weights=weights) * 100

# ---------------------------------------------------------------------
# 5. Формируем DataFrame с результатами
# ---------------------------------------------------------------------
metrics_df = pd.DataFrame({
    'MAE': [mae, wmae],
    'RMSE': [rmse, wrmse],
    'MAPE (%)': [mape, wmape],
    'R²': [r2, np.nan]
}, index=['Обычные', 'Взвешенные'])

metrics_df = metrics_df.round(5)

# ---------------------------------------------------------------------
# 6. Вывод
# ---------------------------------------------------------------------
print("\nМетрики качества прогноза (вес = {})".format(weight_col))
display(metrics_df)   # в Jupyter – красивая таблица
# Если в обычном скрипте, замените на: print(metrics_df.to_string())