import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
# ----------------------------
# 1. Функция взвешенной агрегации (если ещё не определена)
# ----------------------------
def aggregate_weighted(df, group_col, value_col, weight_col):
"""
Возвращает DataFrame со взвешенным средним value_col по group_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))
# ----------------------------
# 2. Агрегируем факт и прогноз
# ----------------------------
date_col = 'end_month_plan'
target = 'renewed_fact_rate'
weight_col = 'portfolio_share' # или 'sum_total_out' – используйте тот же, что в модели
fact_agg = aggregate_weighted(df, date_col, target, weight_col)
pred_agg = aggregate_weighted(df, date_col, 'prediction', weight_col)
# Объединяем для сравнения
comparison = fact_agg.merge(pred_agg, on=date_col, suffixes=('_fact', '_pred'))
# ----------------------------
# 3. График фактической и прогнозной доли
# ----------------------------
plt.figure(figsize=(12, 5))
plt.plot(comparison[date_col], comparison[target], marker='o', label='Факт (взвешенная доля)')
plt.plot(comparison[date_col], comparison['prediction'], marker='x', label='Прогноз (linear+KDE)')
plt.title('Сравнение фактической и прогнозной доли пролонгаций по месяцам')
plt.xlabel('Дата')
plt.ylabel('Доля пролонгаций')
plt.legend()
plt.grid(True)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
# ----------------------------
# 4. Ключевые метрики (обычные и взвешенные)
# ----------------------------
y_true = comparison[target].values
y_pred = comparison['prediction'].values
weights = comparison[f'{weight_col}_fact'].values # или _pred – они одинаковы
# Обычные метрики
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)
# Взвешенные метрики
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
print("=== Метрики на агрегированных по месяцам данных ===")
print(f"MAE : {mae:.5f}")
print(f"RMSE : {rmse:.5f}")
print(f"MAPE : {mape:.2f}%")
print(f"R² : {r2:.5f}")
print()
print("=== Взвешенные метрики (по portfolio_share) ===")
print(f"wMAE : {wmae:.5f}")
print(f"wRMSE : {wrmse:.5f}")
print(f"wMAPE : {wmape:.2f}%")