Загрузка данных
# 6. Проверка диапазона прогнозов и чувствительности к new_delta
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# ------------------------------------------------------------
# 1. Проверка диапазона прогнозов
# ------------------------------------------------------------
print("=== 1. ПРОВЕРКА ДИАПАЗОНА ПРОГНОЗОВ ===")
# Проверка целевой переменной
target_min = df[target].min()
target_max = df[target].max()
print(f"Целевая переменная (renewed_fact_rate):")
print(f" Минимум: {target_min:.6f}")
print(f" Максимум: {target_max:.6f}")
print(f" В диапазоне [0, 1]: {'ДА' if 0 <= target_min and target_max <= 1 else 'НЕТ'}")
# Проверка прогнозов модели
pred_min = df[pred_col].min()
pred_max = df[pred_col].max()
print(f"\nПрогноз модели (prediction):")
print(f" Минимум: {pred_min:.6f}")
print(f" Максимум: {pred_max:.6f}")
print(f" В диапазоне [0, 1]: {'ДА' if 0 <= pred_min and pred_max <= 1 else 'НЕТ'}")
# Детальная статистика
print(f"\nСтатистика прогнозов:")
print(df[pred_col].describe().round(6))
# Проверка выбросов
outliers_low = (df[pred_col] < 0).sum()
outliers_high = (df[pred_col] > 1).sum()
print(f"\nКоличество прогнозов < 0: {outliers_low}")
print(f"Количество прогнозов > 1: {outliers_high}")
print(f"Все прогнозы в диапазоне [0, 1]: {'ДА' if outliers_low == 0 and outliers_high == 0 else 'НЕТ'}")
# Визуализация распределения прогнозов
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Гистограмма прогнозов
axes[0].hist(df[pred_col], bins=50, edgecolor='black', alpha=0.7, color='steelblue')
axes[0].axvline(x=0, color='red', linestyle='--', linewidth=1, label='0 (граница)')
axes[0].axvline(x=1, color='red', linestyle='--', linewidth=1, label='1 (граница)')
axes[0].set_xlabel('Прогноз модели')
axes[0].set_ylabel('Частота')
axes[0].set_title('Распределение прогнозов модели')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# Boxplot по сегментам
segments_plot = ['mass', 'mid', 'prem']
data_for_box = [df[df['segment'] == seg][pred_col].values for seg in segments_plot]
bp = axes[1].boxplot(data_for_box, labels=segments_plot, patch_artist=True)
for patch, color in zip(bp['boxes'], ['lightblue', 'lightgreen', 'lightcoral']):
patch.set_facecolor(color)
axes[1].axhline(y=0, color='red', linestyle='--', linewidth=1, alpha=0.7)
axes[1].axhline(y=1, color='red', linestyle='--', linewidth=1, alpha=0.7)
axes[1].set_ylabel('Прогноз модели')
axes[1].set_title('Распределение прогнозов по сегментам')
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# ------------------------------------------------------------
# 2. Чувствительность к new_delta (таблицы для prem и mid)
# ------------------------------------------------------------
print("\n=== 2. ЧУВСТВИТЕЛЬНОСТЬ ПРОГНОЗА К new_delta ===")
def create_sensitivity_table(df, segment, features, coeff_dict, F,
fixed_features, delta_range, step=1):
"""
Создаёт таблицу чувствительности прогноза к new_delta.
Параметры:
df: исходный DataFrame
segment: название сегмента ('prem' или 'mid')
features: список признаков
coeff_dict: словарь коэффициентов
F: функция CDF из KDE
fixed_features: словарь {признак: значение} для фиксации
delta_range: диапазон значений new_delta (list или array)
step: шаг для new_delta
"""
# Базовый ряд с фиксированными значениями
base_row = fixed_features.copy()
results = []
for delta in delta_range:
# Копируем фиксированные значения
row = base_row.copy()
row['new_delta'] = delta
# Если сегмент prem или mid, обнуляем сегментные new_delta
if segment == 'prem':
row['prem_new_delta'] = delta
row['mid_new_delta'] = 0
elif segment == 'mid':
row['prem_new_delta'] = 0
row['mid_new_delta'] = delta
else:
row['prem_new_delta'] = 0
row['mid_new_delta'] = 0
# Вычисляем прогноз
z = coeff_dict['intercept']
for feat in features:
z += coeff_dict[feat] * row[feat]
pred = F(np.array([z]))[0]
results.append({
'new_delta': delta,
'prediction': pred
})
return pd.DataFrame(results)
# Получаем средние значения признаков для каждого сегмента
segments_for_test = ['prem', 'mid']
for seg in segments_for_test:
print(f"\n--- Сегмент {seg.upper()} ---")
# Данные сегмента
df_seg = df[df['segment'] == seg].copy()
if len(df_seg) == 0:
print(f"Нет данных для сегмента {seg}")
continue
# Фиксируем все признаки на их средних значениях
fixed_features = {}
for feat in features:
if feat not in ['new_delta', 'prem_new_delta', 'mid_new_delta']:
fixed_features[feat] = df_seg[feat].mean()
else:
fixed_features[feat] = 0
# Для prem или mid устанавливаем соответствующий сегментный признак
if seg == 'prem':
# Для prem: prem_pr_p_n фиксируем на среднем значении prem_pr_p_n
fixed_features['prem_pr_p_n'] = df_seg['prem_pr_p_n'].mean()
fixed_features['mid_pr_p_n'] = 0
fixed_features['prem_new_delta'] = 0
fixed_features['mid_new_delta'] = 0
elif seg == 'mid':
fixed_features['prem_pr_p_n'] = 0
fixed_features['mid_pr_p_n'] = df_seg['mid_pr_p_n'].mean()
fixed_features['prem_new_delta'] = 0
fixed_features['mid_new_delta'] = 0
# Определяем диапазон new_delta для сегмента
delta_min = int(np.floor(df_seg['new_delta'].min()))
delta_max = int(np.ceil(df_seg['new_delta'].max()))
delta_range = np.arange(delta_min, delta_max + 1, 1)
print(f"Диапазон new_delta в сегменте: от {delta_min} до {delta_max}")
print(f"Фиксированные значения признаков:")
for key, val in fixed_features.items():
print(f" {key}: {val:.4f}")
# Создаём таблицу чувствительности
sensitivity_df = create_sensitivity_table(
df=df,
segment=seg,
features=features,
coeff_dict=coeff_dict,
F=F,
fixed_features=fixed_features,
delta_range=delta_range,
step=1
)
# Округляем для читаемости
sensitivity_df['prediction'] = sensitivity_df['prediction'].round(6)
print(f"\nТаблица чувствительности прогноза к new_delta (шаг 1):")
display(sensitivity_df)
# График зависимости
plt.figure(figsize=(10, 5))
plt.plot(sensitivity_df['new_delta'], sensitivity_df['prediction'],
marker='o', linestyle='-', color='steelblue', linewidth=2)
plt.axhline(y=0, color='red', linestyle='--', linewidth=1, alpha=0.5)
plt.axhline(y=1, color='red', linestyle='--', linewidth=1, alpha=0.5)
plt.xlabel('new_delta')
plt.ylabel('Прогноз модели')
plt.title(f'Зависимость прогноза от new_delta (сегмент {seg.upper()})')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# ------------------------------------------------------------
# 3. Сравнение распределения прогнозов по сегментам
# ------------------------------------------------------------
print("\n=== 3. СРАВНЕНИЕ РАСПРЕДЕЛЕНИЙ ПРОГНОЗОВ ПО СЕГМЕНТАМ ===")
seg_stats = []
for seg in ['mass', 'mid', 'prem']:
df_seg = df[df['segment'] == seg].copy()
if len(df_seg) > 0:
seg_stats.append({
'Сегмент': seg,
'Количество': len(df_seg),
'Средний прогноз': df_seg[pred_col].mean(),
'Медиана': df_seg[pred_col].median(),
'Минимум': df_seg[pred_col].min(),
'Максимум': df_seg[pred_col].max(),
'Станд. отклонение': df_seg[pred_col].std(),
'Доля < 0': (df_seg[pred_col] < 0).mean(),
'Доля > 1': (df_seg[pred_col] > 1).mean()
})
stats_df = pd.DataFrame(seg_stats).round(6)
display(stats_df)
print("\n=== ИТОГОВЫЙ ВЫВОД ПО ТЕСТУ 6 ===")
# Итоговая оценка
all_in_range = (outliers_low == 0 and outliers_high == 0)
if all_in_range:
print("Все прогнозы модели находятся в диапазоне [0, 1]")
else:
print(f"Есть прогнозы вне диапазона [0, 1]:")
print(f" < 0: {outliers_low} ({outliers_low/len(df)*100:.2f}%)")
print(f" > 1: {outliers_high} ({outliers_high/len(df)*100:.2f}%)")
print("\nРекомендации:")
if all_in_range:
print(" - Модель корректно прогнозирует вероятности в допустимом диапазоне.")
print(" - Можно использовать прогнозы напрямую без дополнительного клиппинга.")
else:
print(" - Рекомендуется применить клиппинг прогнозов к диапазону [0, 1].")
print(" - Или проверить, не возникают ли проблемы на этапе KDE-преобразования.")