"""Урок 7"""

import numpy as np
import time

# 1. Даны значения величины заработной платы заемщиков банка (zp) и значения их
# поведенческого кредитного скоринга (ks):
# zp = [35, 45, 190, 200, 40, 70, 54, 150, 120, 110],
# ks = [401, 574, 874, 919, 459, 739, 653, 902, 746, 832].
# Используя математические операции, посчитать коэффициенты линейной регрессии,
# приняв за X заработную плату (то есть, zp - признак),
# а за y - значения скорингового балла (то есть, ks - целевая переменная).
# Произвести расчет как с использованием intercept, так и без.
# Решение:
# расчет с использованием intercept
zp = [35, 45, 190, 200, 40, 70, 54, 150, 120, 110]
ks = [401, 574, 874, 919, 459, 739, 653, 902, 746, 832]
n = len(zp)
x = np.array(zp)
y = np.array(ks)
b = (np.mean(x * y) - np.mean(x) * np.mean(y)) / (
        np.mean(x ** 2) - np.mean(x) ** 2)
a = np.mean(y) - b * np.mean(x)
print('# Ответ 1:')
print(f'# Коэффициенты линейной регрессии: a={a}, b={b}')
# расчет с использованием intercept Через матрицы
X, Y = x.reshape(n, 1), y.reshape(n, 1)
X = np.hstack([np.ones((n, 1)), X])
B = np.dot(np.linalg.inv(np.dot(X.T, X)), X.T @ Y)
print(f'# Коэффициенты линейной регрессии: a={B[0][0]}, b={B[1][0]}')
# расчет без использования intercept
X, Y = x.reshape(n, 1), y.reshape(n, 1)
B = np.dot(np.linalg.inv(np.dot(X.T, X)), X.T @ Y)
print(f'# Коэффициент линейной регрессии без intercept: b={B[0][0]}')
# Ответ 1:
# Коэффициенты линейной регрессии: a=444.1773573243596, b=2.620538882402765
# Коэффициенты линейной регрессии: a=444.1773573243595, b=2.620538882402766
# Коэффициент линейной регрессии без intercept: b=5.889820420132688

# 2. Посчитать коэффициент линейной регрессии при заработной плате (zp),
# используя градиентный спуск (без intercept).
X, Y = np.array(zp), np.array(ks)
b, learning_rate = 1, 1e-6
mse_b = lambda b1: ((Y - b1 * X) ** 2).mean()
min_mse = mse_b(b)
for idx in range(10000):
    b += learning_rate * 2 * (X * (Y - b * X)).mean()
    mse_i = mse_b(b)
    if np.sign(min_mse - mse_i) < 0:
        # mse_i перестал уменьшаться и начал расти -> достигнут минимум mse
        break
    min_mse = mse_i
print('# Ответ 2:')
print(f'# Коэффициент линейной регрессии b={b} получен на итерации: {idx + 1}')
# Ответ 2:
# Коэффициент линейной регрессии b=5.889820331477086 получен на итерации: 638

# 3. В каких случаях для вычисления доверительных интервалов и проверки
# статистических гипотез используется таблица значений функции Лапласа,
# а в каких - таблица критических точек распределения Стьюдента?
# Ответ 3:
# Tаблица значений функции Лапласа используется когда известна дисперсия или
# среднее квадратической отклонение генеральной совокупности.
# Tаблица критических точек распределения Стьюдента используется когда
# НЕ известна дисперсия генеральной совокупности.
# Естественно речь идет о том, что генеральная совокупность распределена
# нормально и результаты измерений подчинены нормальному закону распределения
# вероятностей.

# *4. Произвести вычисления как в пункте 2, но с вычислением intercept.
# Учесть, что изменение коэффициентов должно производиться
# на каждом шаге одновременно (то есть изменение одного коэффициента не должно
# влиять на изменение другого во время одной итерации).
start_time = time.time()
a, b, learning_rate = 0, 1, 1e-5  # увеличил скорость обучения, долго считается
mse_ab = lambda a1, b1: ((a1 + b1 * X - Y) ** 2).mean()
min_mse = mse_ab(a, b)
for idx in range(3000000):
    y_hat = a + b * X
    error = Y - y_hat
    # градиент для коэффициента a
    a_grad = 2 * error.mean()
    # градиент для коэффициента b
    b_grad = 2 * (X * error).mean()
    # обновляем коэффициенты
    a += learning_rate * a_grad
    b += learning_rate * b_grad
    mse_i = mse_ab(a, b)
    if np.sign(min_mse - mse_i) < 0:
        # mse_i перестал уменьшаться и начал расти -> достигнут минимум mse
        break
    min_mse = mse_i
print('# Ответ 4:')
print(f'# Коэффициенты линейной регрессии: a={a}, b={b}\n'
      f'# получены на итерации: {idx + 1}')
print(f'# --- {time.time() - start_time} секунд выполнялось ---')
# Ответ 4:
# Коэффициенты линейной регрессии: a=444.17531864604894, b=2.620553887976483
# получены на итерации: 2422927
# --- 123.77007913589478 секунд выполнялось ---
