#МНК - Метод наименьших квадратов
#У нас есть много экспериментальных точек. Через них надо провести прямую, которая как можно ближе проходила к этим точкам.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
#X - МЕСЯЦ
#У - ЦЕНА
#Проведем прямую y = kx + b через экспериментальные точки
x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
y = np.array([150, 141, 155.5, 147.3, 161.45, 148.45, 168, 170, 166.78, 170.1, 168, 159.96, 162.49, 178.76])
data = dict(zip(x, y))
print(data)

#Перепишем линейное уравнение y = mx + c как y = Rp, где A = [[ x 1 ]] и p = [[m], [c]]
#Построим R по х :
R = np.vstack([x, np.ones(len(x))]).T
#Используем lstsq для решения его относительно вектора p
k, b = np.linalg.lstsq(R, y)[0]
#print(k, b)
def show_graf(x, y, k, b):
    #Построим график полученной прямой и укажем на нем точки
    plt.plot(x, y, 'o', label='Original data', markersize=10)
    plt.plot(x, k*x + b, 'r', label='Fitted line')
    plt.legend()
    plt.show()
#show_graf(x, y, k, b)
#Рассчитаем кратчайшие расстояния от исходных данных(точек) до полученной прямой
h = abs(k*x-1*y+b)/((k**2 + (-1)**2)**0.5)
#Рассчитаем кратчайшие расстояния от исходных данных(точек) до полученной прямой
h = abs(k*x-1*y+b)/((k**2 + (-1)**2)**0.5)
print("Список полученных расстояний:","\n", h)

#Функция для отображения гистограммы для передаваемого списка(h, y(цены))
def show_hist(h):
    x1 = range(len(h))
    ax = plt.gca()
    ax.bar(x1, h, align='edge') # align='edge' - выравнивание по границе, а не по центру
    ax.set_xticks(x1)
    #ax.set_xticklabels(('first', 'second', 'third', 'fourth'))
    plt.show()


#Идея применения бутсрэпа в том, что у нас есть выборка небольшого размера и нам надо оценить, например, среднее.
# Вместо подсчета среднего самой этой выборки, мы извлекаем n_samples выборок с возвращением (то есть элементы могут повторяться) из исходной.
# У полученных выборок считаем среднее. Его уже оцениваем, вместо оценки среднего исходной выборки.
def get_bootstrap_samples(data, n_samples):
    indices = np.random.randint(0, len(data), (n_samples, len(data)))
    samples = data[indices]
    return samples
#Получим новую выборку из 1000 значений:
n_samples = 1000
number = 10
#Функция расчета доверительного интервала бутстреп методом(прогноз расстояния h):
def bootstrap_forecast(h, number, n_samples):
    #number = 10
    spisok =[]
    for i in range(0,number):
        sample = get_bootstrap_samples(h, n_samples)
        spisok1 = spisok.append(np.mean(sample))
    #print("spisok =", spisok)
    global h_min
    global h_max
    h_min = min(spisok)
    h_max = max(spisok)
    print("Минимум доверительного интервала для расстояния =", h_min)
    print("Максимум доверительного интервала для расстояния =", h_max)
#bootstrap_forecast(h, number, n_samples)
#Прогноз будущих точек:
# Так, например, двигаясь по оси Ох, значение У будет принадлжать интервалу:
#h = abs(k*x-1*y+b)/((k**2 + (-1)**2)**0.5)
#Функция для расчета цены на определенный месяц:
#Для x=15:
#x=15
def forecast(x, h_min, h_max):
    #Для x=15:
    #x=15
    #Выше прямой МНК:
    y_max1 = -h_min*((k**2 + (-1)**2)**0.5) + k*x + b
    y_min1 = -h_max*((k**2 + (-1)**2)**0.5) + k*x + b
    #Ниже прямой МНК:
    y_min2 = h_min*((k**2 + (-1)**2)**0.5) - k*x - b
    y_max2 = h_max*((k**2 + (-1)**2)**0.5) - k*x - b
    #y = h*((k**2 + (-1)**2)**0.5) - k*x - b
    print(f"Максимальная прогнозируемая цена для {x} месяца = ",y_max1)
    print(f"Минимальная прогнозируемая цена для {x} месяца = ",y_min1)
    #print("y_min2 = ",y_min2)
    #print("y_max2 = ",y_max2)

#Отображение исходных данных и прямой МНК:
show_graf(x, y, k, b)
#Отображение гистограммы известных цен:
show_hist(y)
#Отображение гистограммы известных расстояний точек до прямой:
show_hist(h)
#Прогноз бутстрап методом, где h - исходные расстояния, number - количество генерируемых выборок
# для нахождения среднего значения выборки, n_samples - размер выборки:
bootstrap_forecast(h, number, n_samples)
#Прогноз будущих цен для определенного месяца(вместо 15 - нужный месяц по порядку, на выходе интервал возможных значений прогноза
forecast(17, h_min, h_max)














