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


# 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-преобразования.")