diff --git a/app/menu_load.py b/app/menu_load.py deleted file mode 100644 index 45f9129..0000000 --- a/app/menu_load.py +++ /dev/null @@ -1,178 +0,0 @@ -from app.utils.system import clear_console -from app.zadachi.zadacha1 import zadacha1 -from app.zadachi.zadacha2 import zadacha2 -from app.zadachi.zadacha3 import zadacha3 -from app.zadachi.zadacha4 import zadacha4 -from app.zadachi.zadacha5 import zadacha5 - -class menu: - - def __init__(self): - pass # Конструктор пока пустой, при необходимости можно добавить параметры - - def main_menu(self): - clear_console() - while True: - print('\nMain Menu:') - print('1. Задача 1') - print('2. Задача 2') - print('3. Задача 3') - print('4. Задача 4') - print('5. Задача 5') - print('0. Exit') - - choice = input('Select an option: ') - - clear_console() - if choice == '1': - self.zadacha1_menu() # Переход к подменю Задачи 1 - elif choice == '2': - self.zadacha2_menu() # Переход к подменю Задачи 2 - elif choice == '3': - self.zadacha3_menu() # Переход к подменю Задачи 3 - elif choice == '4': - self.zadacha4_menu() # Добавлен вызов меню задачи 4 - elif choice == '5': - self.zadacha5_menu() # Добавлен вызов меню задачи 5 - elif choice == '0': - print('Exiting program...') - break - else: - print('Invalid option') - - def zadacha1_menu(self): - """Меню для задачи 1 с подкатегориями.""" - clear_console() - while True: - print('\nЗадача 1:') - print('1. Линейно сгенерировать массив Y, зависящий от x') - print('2. Квадратично сгенерировать массив Y, зависящий от x') - print('0. Назад') - - choice = input('Выберите подкатегорию: ') - - clear_console() - z1 = zadacha1() # создаем объект для задачи 1 - - if choice == '1': - z1.plot_lin_data() # Выполнить первую подзадачу - #return # Вернуться в основное меню после выполнения - elif choice == '2': - z1.plot_sqr_data() # Выполнить вторую подзадачу - #return # Вернуться в основное меню после выполнения - elif choice == '0': - break # Вернуться в основное меню - else: - print('Неверный выбор, попробуйте снова.') - - def zadacha2_menu(self): - """Меню для задачи 2 (расчет коэффициентов методом наименьших квадратов).""" - clear_console() - z1 = zadacha1() # Используем объект задачи 1 для генерации данных - z2 = zadacha2() # Создаем объект для задачи 2 - - while True: - print('\nЗадача 2:') - print('1. Рассчитать коэффициенты b0 и b1 для линейной модели') - print('2. Сделать прогноз по рассчитанным коэффициентам') - print('0. Назад') - - choice = input('Выберите подкатегорию: ') - - clear_console() - - if choice == '1': - # Генерация линейных данных с помощью функции из задачи 1 - x_data, y_data = z1.create_lin_array() - - # Рассчет коэффициентов b0 и b1 методом наименьших квадратов - b0, b1 = z2.calculate_coefficients(x_data, y_data) - - print(f'Рассчитанные коэффициенты:') - print(f'b0 (свободный член) = {b0}') - print(f'b1 (коэффициент наклона) = {b1}') - elif choice == '2': - # Пример предсказания на новых данных - x_data, y_data = z1.create_lin_array() # Сначала генерируем данные - b0, b1 = z2.calculate_coefficients(x_data, y_data) # Рассчитываем коэффициенты - - # Выполняем прогноз - predicted_y = z2.predict(x_data, b0, b1) - - print(f'Предсказанные значения Y на основе рассчитанных коэффициентов:') - print(predicted_y) - elif choice == '0': - break # Вернуться в основное меню - else: - print('Неверный выбор, попробуйте снова.') - - def zadacha3_menu(self): - """Меню для задачи 3 (построение графика регрессии и расчет метрик).""" - clear_console() - z1 = zadacha1() # Используем объект задачи 1 для генерации данных - z2 = zadacha2() # Используем объект задачи 2 для расчета коэффициентов - - while True: - print('\nЗадача 3:') - print('1. Построить график регрессии и рассчитать метрики') - print('0. Назад') - - choice = input('Выберите подкатегорию: ') - - clear_console() - - if choice == '1': - x_data, y_data = z1.create_lin_array() - b0, b1 = z2.calculate_coefficients(x_data, y_data) - - # Создаем объект задачи 3 и выполняем построение графика - z3 = zadacha3(x_data, y_data, b0, b1) - z3.plot_regression() - elif choice == '0': - break # Вернуться в основное меню - else: - print('Неверный выбор, попробуйте снова.') - - def zadacha4_menu(self): - """Меню для задачи 4""" - clear_console() - #z4 = zadacha4() # Создаем объект задачи 4 - - while True: - print('\nЗадача 4:') - print('1. Провести визуальный анализ остатков') - print('0. Назад') - - choice = input('Выберите подкатегорию: ') - - clear_console() - - if choice == '1': - z4 = zadacha4() - z4.analyze_residuals() - elif choice == '0': - break - else: - print('Неверный выбор, попробуйте снова.') - - def zadacha5_menu(self): - """Меню для задачи 5""" - clear_console() - - while True: - print('\nЗадача 5:') - print('1. Рассчитать статистики для определения выполнения условий применения линейной регрессии') - print('0. Назад') - - choice = input('Выберите подкатегорию: ') - - clear_console() - - if choice == '1': - z5 = zadacha5() - z5.calculate_spearman_rank_test() - z5.calculate_gq_test() - elif choice == '0': - break - else: - print('Неверный выбор, попробуйте снова.') diff --git a/app/utils/config.py b/app/utils/config.py deleted file mode 100644 index c5b5026..0000000 --- a/app/utils/config.py +++ /dev/null @@ -1 +0,0 @@ -debug=0 diff --git a/app/utils/system.py b/app/utils/system.py deleted file mode 100644 index b2b3aac..0000000 --- a/app/utils/system.py +++ /dev/null @@ -1,10 +0,0 @@ -import os -import platform - -def clear_console(): - """Очистить консоль в зависимости от операционной системы.""" - os_name = platform.system() - if os_name == 'Windows': - os.system('cls') - else: - os.system('clear') diff --git a/app/zadachi/zadacha1.py b/app/zadachi/zadacha1.py deleted file mode 100644 index 90c196f..0000000 --- a/app/zadachi/zadacha1.py +++ /dev/null @@ -1,60 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - -class zadacha1: - - def __init__(self): - pass # Конструктор пока пустой, при необходимости можно добавить параметры - - # Метод для генерации линейного массива данных - def create_lin_array(self, b0=2, b1=4, N=500, noise_std=10): - """ - Генерация массива данных по линейной зависимости Y = b0 + b1 * X + error - :param b0: свободный коэффициент - :param b1: коэффициент при X - :param N: количество точек данных - :param noise_std: стандартное отклонение для случайной ошибки - :return: массивы X и Y - """ - X = np.linspace(1, 500, N) # создаем массив X с равномерным шагом - error = np.random.normal(0, noise_std, N) # генерируем шум - Y = b0 + b1 * X + error # генерируем Y по линейному закону - return X, Y - - # Метод для генерации квадратичного массива данных - def create_sqr_array(self, a=1, b=3, c=2, N=500, noise_std=10): - """ - Генерация массива данных по квадратичной зависимости Y = a * X^2 + b * X + c + error - :param a: коэффициент при X^2 - :param b: коэффициент при X - :param c: свободный коэффициент - :param N: количество точек данных - :param noise_std: стандартное отклонение для случайной ошибки - :return: массивы X и Y - """ - X = np.linspace(1, 500, N) # создаем массив X - error = np.random.normal(0, noise_std, N) # генерируем шум - Y = a * X**2 + b * X + c + error # генерируем Y по квадратичному закону - return X, Y - - # Метод для отображения графика линейных данных - def plot_lin_data(self, b0=2, b1=4, N=500, noise_std=10): - """ - Отображение сгенерированных линейных данных и теоретической линии - """ - X, Y = self.create_lin_array(b0, b1, N, noise_std) # Генерация данных - plt.scatter(X, Y) # График рассеяния - plt.plot(X, b0 + b1 * X, color='red') # Линейная функция без шума - plt.title("Линейная зависимость с шумом") - plt.show() - - # Метод для отображения графика квадратичных данных - def plot_sqr_data(self, a=1, b=3, c=2, N=500, noise_std=10): - """ - Отображение сгенерированных квадратичных данных и теоретической кривой - """ - X, Y = self.create_sqr_array(a, b, c, N, noise_std) # Генерация данных - plt.scatter(X, Y) # График рассеяния - plt.plot(X, a * X**2 + b * X + c, color='red') # Квадратичная функция без шума - plt.title("Квадратичная зависимость с шумом") - plt.show() diff --git a/app/zadachi/zadacha2.py b/app/zadachi/zadacha2.py deleted file mode 100644 index 20fb055..0000000 --- a/app/zadachi/zadacha2.py +++ /dev/null @@ -1,35 +0,0 @@ -import numpy as np - -class zadacha2: - def calculate_coefficients(self, X, Y): - """ - Функция для расчета коэффициентов b0 и b1 по методу наименьших квадратов (МНК) - :param X: массив значений независимой переменной - :param Y: массив значений зависимой переменной - :return: коэффициенты b0 и b1 - """ - n = len(X) - - # Вычисляем средние значения X и Y - mean_x = np.mean(X) - mean_y = np.mean(Y) - - # Вычисляем коэффициент b1 - numerator = np.sum((X - mean_x) * (Y - mean_y)) - denominator = np.sum((X - mean_x) ** 2) - b1 = numerator / denominator - - # Вычисляем коэффициент b0 - b0 = mean_y - b1 * mean_x - - return b0, b1 - - def predict(self, X, b0, b1): - """ - Функция для предсказания значений Y на основе коэффициентов b0 и b1 - :param X: массив значений независимой переменной - :param b0: коэффициент b0 (свободный член) - :param b1: коэффициент b1 (коэффициент при X) - :return: предсказанные значения Y - """ - return b0 + b1 * X diff --git a/app/zadachi/zadacha3.py b/app/zadachi/zadacha3.py deleted file mode 100644 index 1c9e37a..0000000 --- a/app/zadachi/zadacha3.py +++ /dev/null @@ -1,54 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - -class zadacha3: - def __init__(self, X, Y, b0, b1): - self.X = X - self.Y = Y - self.b0 = b0 - self.b1 = b1 - - def calculate_sse(self): - """ - Функция для расчета суммы квадратов ошибок (SSE) - :return: SSE - """ - Y_pred = self.b0 + self.b1 * self.X - sse = np.sum((self.Y - Y_pred) ** 2) - return sse - - def calculate_r_squared(self): - """ - Функция для расчета коэффициента детерминации R^2 - :return: R^2 - """ - y_mean = np.mean(self.Y) - ss_total = np.sum((self.Y - y_mean) ** 2) - ss_residual = self.calculate_sse() - r_squared = 1 - (ss_residual / ss_total) - return r_squared - - def plot_regression(self): - """ - Функция для построения графика регрессии и отображения метрик SSE и R^2 - """ - Y_pred = self.b0 + self.b1 * self.X - - plt.figure(figsize=(10, 6)) - - # График рассеяния и регрессионная прямая - plt.subplot(2, 1, 1) - plt.scatter(self.X, self.Y, label='Данные') - plt.plot(self.X, Y_pred, color='red', label='Регрессия') - plt.title("Линейная регрессия") - plt.xlabel("X") - plt.ylabel("Y") - plt.legend() - - # Расчет метрик и отображение на графике - sse = self.calculate_sse() - r_squared = self.calculate_r_squared() - plt.text(0.05, 0.95, f"SSE: {sse:.2f}\nR^2: {r_squared:.2f}", - transform=plt.gca().transAxes, verticalalignment='top') - - plt.show() diff --git a/app/zadachi/zadacha4.py b/app/zadachi/zadacha4.py deleted file mode 100644 index 10728dd..0000000 --- a/app/zadachi/zadacha4.py +++ /dev/null @@ -1,62 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from app.zadachi.zadacha2 import zadacha2 -from app.zadachi.zadacha1 import zadacha1 - -class zadacha4: - def __init__(self): - self.z1 = zadacha1() - self.z2 = zadacha2() - - def analyze_residuals(self): - """ - Выполняет визуальный анализ остатков по линейной модели. - Строит графики 2*2: скаттерограмма, график исходной зависимости, - распределение остатков и гистограмму остатков. - """ - # Генерируем данные - X, Y = self.z1.create_lin_array() - - # Рассчитываем коэффициенты методом наименьших квадратов - b0, b1 = self.z2.calculate_coefficients(X, Y) - - # Предсказанные значения - Y_pred = self.z2.predict(X, b0, b1) - - # Вычисляем остатки - residuals = Y - Y_pred - - # Создаем графики 2*2 - fig, axs = plt.subplots(2, 2, figsize=(12, 8)) - - # Первый график: скаттерограмма с линией МНК - axs[0, 0].scatter(X, Y, label='Данные с шумом', alpha=0.6) - axs[0, 0].plot(X, Y_pred, color='red', label='Прямая по МНК') - axs[0, 0].set_title('Скаттерограмма и прямая по МНК') - axs[0, 0].legend() - - # Второй график: исходная зависимость и линия МНК - axs[0, 1].scatter(X, Y, label='Данные', alpha=0.6) - axs[0, 1].plot(X, self.z1.create_lin_array(b0, b1, len(X))[1], color='green', label='Истинная линия') - axs[0, 1].plot(X, Y_pred, color='red', linestyle='--', label='Прямая по МНК') - axs[0, 1].set_title('Истинная линия и прямая по МНК') - axs[0, 1].legend() - - # Третий график: распределение остатков - axs[1, 0].scatter(X, residuals, color='purple', alpha=0.6) - axs[1, 0].axhline(y=0, color='red', linestyle='--') - axs[1, 0].set_title('Распределение остатков') - - # Четвертый график: гистограмма остатков - axs[1, 1].hist(residuals, bins=30, color='orange', edgecolor='black', alpha=0.7) - axs[1, 1].set_title('Гистограмма остатков') - - # Выравниваем графики и отображаем - plt.tight_layout() - plt.show() - - # Отдельное окно для гистограммы - plt.figure() - plt.hist(residuals, bins=30, color='orange', edgecolor='black', alpha=0.7) - plt.title('Гистограмма остатков (отдельно)') - plt.show() \ No newline at end of file diff --git a/app/zadachi/zadacha5.py b/app/zadachi/zadacha5.py deleted file mode 100644 index 00c1a8f..0000000 --- a/app/zadachi/zadacha5.py +++ /dev/null @@ -1,79 +0,0 @@ -import numpy as np -from app.zadachi.zadacha2 import zadacha2 -from app.zadachi.zadacha1 import zadacha1 - -class zadacha5: - def __init__(self): - self.z1 = zadacha1() - self.z2 = zadacha2() - - def calculate_spearman_rank_test(self): - """ - Выполняет тест ранговой корреляции Спирмена для проверки взаимосвязи между X и Y. - :return: коэффициент корреляции Спирмена и p-value - """ - # Генерируем данные - X, Y = self.z1.create_lin_array() - - # Вычисляем ранги - X_ranks = np.argsort(np.argsort(X)) - Y_ranks = np.argsort(np.argsort(Y)) - - # Вычисляем разницу рангов - diff_ranks = X_ranks - Y_ranks - - # Числитель формулы Спирмена - numerator = 6 * np.sum(diff_ranks**2) - - # Количество наблюдений - n = len(X) - - # Коэффициент корреляции Спирмена - spearman_coef = 1 - numerator / (n * (n**2 - 1)) - - # Оценка значимости результата (p-value) не рассчитывается вручную здесь, но коэффициент корреляции интерпретируется. - print(f'Коэффициент корреляции Спирмена: {spearman_coef:.3f}') - - # Интерпретация результата - if np.abs(spearman_coef) > 0.5: - print("Существует значимая взаимосвязь между X и Y.") - else: - print("Взаимосвязь между X и Y слабая или отсутствует.") - - def calculate_gq_test(self): - """ - Выполняет тест Голдфелда-Квандта для проверки гомоскедастичности. - :return: значение статистики F и результат теста - """ - # Генерируем данные - X, Y = self.z1.create_lin_array() - - # Рассчитываем коэффициенты регрессии - b0, b1 = self.z2.calculate_coefficients(X, Y) - - # Вычисляем остатки - residuals = Y - self.z2.predict(X, b0, b1) - - # Сортируем данные по X для выполнения теста - sorted_indices = np.argsort(X) - residuals_sorted = residuals[sorted_indices] - - # Разбиваем данные на две части - split_index = len(residuals_sorted) // 2 - residuals_low = residuals_sorted[:split_index] - residuals_high = residuals_sorted[split_index:] - - # Вычисляем средние квадраты остатков - var_low = np.var(residuals_low, ddof=1) - var_high = np.var(residuals_high, ddof=1) - - # Тестовая статистика F - f_stat = var_high / var_low - - print(f'Статистика F: {f_stat:.3f}') - - # Интерпретация результата - if f_stat > 2 or f_stat < 0.5: - print("Остатки не гомоскедастичны. Условие линейной регрессии нарушено.") - else: - print("Остатки гомоскедастичны. Условие линейной регрессии выполнено.") diff --git a/main.py b/main.py index a088d68..8f11364 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,142 @@ -from app.utils.config import debug -from app.menu_load import menu +import matplotlib.pyplot as plt +import matplotlib +import numpy as np +matplotlib.use('TkAgg') -if __name__ == "__main__": - if debug == True: - print("Running in debug mode...") - from test_module.debug_main import debug_main_start - debug_main_start() - else: - mn = menu() - mn.main_menu() +# 1. Функция для генерации линейной зависимости +def F(b0, b1, N): + # Генерация случайных данных для X + x = np.random.uniform(0, 10, size=N) + x.sort() # Упорядочиваем массив для наглядности + err = np.random.normal(0, 2, N) # Добавляем случайный шум + Y = b0 + b1 * x + err # Вычисляем Y с линейной зависимостью и шумом + # Построение графика рассеяния и идеальной линейной функции + plt.scatter(x, Y, facecolors='none', edgecolor='blue', s=40) + plt.plot(x, b0 + b1 * x, color='red') + plt.title("Линейная зависимость с шумом") + plt.xlabel('X') + plt.ylabel('Data Y') + plt.show() + return Y, x # Возвращаем массивы X и Y + +# 2. Функция для генерации квадратичной зависимости +def F2(a, b, c, N): + xp = np.random.uniform(0, 10, size=N) + xp.sort() + err = np.random.normal(0, 5, N) # Шум для квадратичной зависимости + Yp = a * xp ** 2 + b * xp + c + err + # Построение графика рассеяния и идеальной квадратичной функции + plt.scatter(xp, Yp, facecolors='none', edgecolor='blue', s=40) + plt.plot(xp, a * xp ** 2 + b * xp + c, color='red') + plt.title("Квадратичная зависимость с шумом") + plt.xlabel('X') + plt.ylabel('Data Yp') + plt.show() + return Yp, xp + +# Генерация данных +Y, x = F(6, 9, 500) # Линейная зависимость +Yp, xp = F2(3, 1, -2, 500) # Квадратичная зависимость + +# 3. Метод наименьших квадратов (МНК) для расчета коэффициентов линейной регрессии +def MHk(Y, x): + # Вычисление коэффициента наклона (b1) + b1 = sum((x - np.mean(x)) * (Y - np.mean(Y))) / sum((x - np.mean(x)) ** 2) + # Вычисление свободного члена (b0) + b0 = np.mean(Y) - b1 * np.mean(x) + return b1, b0 + +b1, b0 = MHk(Y, x) # Коэффициенты для линейной зависимости +print(f"Линейные коэффициенты: b1={b1:.2f}, b0={b0:.2f}") + +# Аналогичная функция для квадратичных данных +def MHkp(Yp, xp): + b1p = sum((xp - np.mean(xp)) * (Yp - np.mean(Yp))) / sum((xp - np.mean(xp)) ** 2) + b0p = np.mean(Yp) - b1p * np.mean(xp) + return b1p, b0p + +b1p, b0p = MHkp(Yp, xp) +print(f"Коэффициенты квадратичной регрессии: b1={b1p:.2f}, b0={b0p:.2f}") + +# 4. Построение регрессионных линий +Y_l = b0 + b1 * x # Линейная регрессия +Y_s = b0p + b1p * xp # Квадратичная регрессия + +# 5. Вычисление SSE и коэффициента детерминации (R²) +SSE = sum((Y - (b0 + b1 * x)) ** 2) # Ошибка для линейной регрессии +SST = sum((Y - np.mean(Y)) ** 2) # Общая дисперсия данных +R2_lin = 1 - (SSE / SST) # Коэффициент детерминации для линейной +print(f"SSE линейной: {SSE:.2f}, R^2 линейной функции: {R2_lin:.3f}") + +SSEp = sum((Yp - Y_s) ** 2) # Ошибка для квадратичной регрессии +SSTp = sum((Yp - np.mean(Yp)) ** 2) +R2_quad = 1 - (SSEp / SSTp) # Коэффициент детерминации для квадратичной +print(f"SSE квадратичной: {SSEp:.2f}, R^2 квадратичной функции: {R2_quad:.3f}") + +# 6. Построение графиков +fig, axs = plt.subplots(2, 2, figsize=(14, 10)) + +# Линейная регрессия +axs[0, 0].scatter(x, Y, facecolors='none', edgecolor='blue', s=40) +axs[0, 0].plot(x, Y_l, color='red', label=f'Линейная регрессия, R^2 = {R2_lin:.3f}') +axs[0, 0].set_xlabel('X') +axs[0, 0].set_ylabel('Y') +axs[0, 0].set_title('Линейная регрессия') +axs[0, 0].legend() + +# Квадратичная регрессия +axs[0, 1].scatter(xp, Yp, facecolors='none', edgecolor='blue', s=40) +axs[0, 1].plot(xp, Y_s, color='red', label=f'Квадратичная регрессия, R^2 = {R2_quad:.3f}') +axs[0, 1].set_xlabel('X') +axs[0, 1].set_ylabel('Yp') +axs[0, 1].set_title('Квадратичная регрессия') +axs[0, 1].legend() + +# Остатки для линейной регрессии +residuals_lin = Y - (b0 + b1 * x) +axs[1, 0].scatter(x, residuals_lin, color='green') +axs[1, 0].axhline(y=0, color='black', linestyle='--') +axs[1, 0].set_xlabel('X') +axs[1, 0].set_ylabel('Остатки') +axs[1, 0].set_title('Остатки для линейной регрессии') + +# Остатки для квадратичной регрессии +residuals_quad = Yp - Y_s +axs[1, 1].scatter(xp, residuals_quad, color='green') +axs[1, 1].axhline(y=0, color='black', linestyle='--') +axs[1, 1].set_xlabel('X') +axs[1, 1].set_ylabel('Остатки') +axs[1, 1].set_title('Остатки для квадратичной регрессии') + +plt.show() + +# 7. Гистограммы остатков +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6)) +ax1.hist(residuals_lin, bins=20, color='blue', alpha=0.7, edgecolor='black') +ax1.set_xlabel('Остатки') +ax1.set_ylabel('Количество') +ax1.set_title('Гистограмма остатков для линейной регрессии') + +ax2.hist(residuals_quad, bins=20, color='blue', alpha=0.7, edgecolor='black') +ax2.set_xlabel('Остатки') +ax2.set_ylabel('Количество') +ax2.set_title('Гистограмма остатков для квадратичной регрессии') + +plt.show() + +# 8. Тест Голдфельда-Квандта +def goldfeld_quandt_test(e): + n = 500 + k = 166 + e1 = e[:k] + e3 = e[(n-k):] + s1 = np.sum(e1**2) + s3 = np.sum(e3**2) + f = s3 / s1 + return f + +f_lin = goldfeld_quandt_test(residuals_lin) +f_sqr = goldfeld_quandt_test(residuals_quad) + +print(f"Статистика F для линейной функции: {f_lin:.3f}") +print(f"Статистика F для квадратичной функции: {f_sqr:.3f}")