/
exenty
/
UniProject_KorobkoSV
Обзор
Документация
Войти
/
exenty
/
UniProject_KorobkoSV
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
FirstPart
698 строк
24 KB
exenty
create: FirstPart
01 июн 2026, 14:42
Верифицирован
01 июн 2026, 14:42
83414fe
Код
Авторство
О чём код?
import matplotlib import matplotlib.pyplot as plt import matplotlib.colors as mcolors import numpy as np import seaborn as sns import os import pandas as pd import glob from collections import Counter from matplotlib.widgets import RadioButtons from scipy import stats """ # 1.2.1. # Графики Matplotlib plt.rcParams.update({ 'font.size': 9, 'axes.titlesize': 10, 'axes.labelsize': 9, 'xtick.labelsize': 8, 'ytick.labelsize': 8, 'figure.titlesize': 12 }) scoring = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') cols_to_plot = ['lgID', 'pos', 'GP', 'G', 'A', 'Pts', 'PIM'] fig, axes = plt.subplots(2, 4, figsize=(16, 9)) axes = axes.ravel() for i, col in enumerate(cols_to_plot): if col not in scoring.columns: axes[i].axis('off') continue if col in ['lgID', 'pos']: value_counts = scoring[col].dropna().value_counts().sort_index() axes[i].bar(value_counts.index, value_counts.values, edgecolor='black', color='steelblue', alpha=0.85) axes[i].set_title(f'{col}') axes[i].set_xlabel('Категория') axes[i].set_ylabel('Частота') axes[i].grid(axis='y', alpha=0.3, linestyle='--') axes[i].tick_params(axis='x', rotation=45, labelsize=7) elif col == 'GP': axes[i].hist(scoring[col].dropna(), bins=range(1, int(scoring[col].max()) + 2), edgecolor='black', color='skyblue', alpha=0.85) axes[i].set_title(f'{col}') axes[i].set_xlabel('Значение') axes[i].set_ylabel('Частота') axes[i].grid(axis='y', alpha=0.3, linestyle='--') axes[i].tick_params(axis='x', labelsize=7) else: axes[i].hist(scoring[col].dropna(), bins=20, edgecolor='black', color='lightgreen', alpha=0.85) axes[i].set_title(f'{col}') axes[i].set_xlabel('Значение') axes[i].set_ylabel('Частота') axes[i].grid(axis='y', alpha=0.3, linestyle='--') axes[i].tick_params(axis='x', labelsize=7) axes[len(cols_to_plot)].axis('off') plt.suptitle('Распределение признаков', fontsize=13, fontweight='bold', y=0.995) plt.tight_layout() plt.show() """ """ # 1.2.2. # Графики Seaborn df = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') main_pos = ['D', 'L', 'R', 'C', 'G'] df_main = df[df['pos'].isin(main_pos)].copy() sns.set_theme(style='whitegrid', palette='deep', font_scale=1.1) fig, axes = plt.subplots(2, 2, figsize=(14, 11)) fig.suptitle('Визуализация пар признаков — ScoringSC', fontsize=15, fontweight='bold', y=1.01) ax1 = axes[0, 0] sns.regplot(data=df_main, x='G', y='A', ax=ax1, scatter_kws={'alpha': 0.5, 'color': '#4C72B0'}, line_kws={'color': '#C44E52', 'linewidth': 2}) ax1.set_title('Голы (G) и Передачи (A)') ax1.set_xlabel('Голы (G)') ax1.set_ylabel('Передачи (A)') ax2 = axes[0, 1] sns.scatterplot(data=df_main, x='GP', y='Pts', hue='pos', palette='tab10', ax=ax2, alpha=0.7, s=80) ax2.set_title('Игры (GP) и Очки (Pts)') ax2.set_xlabel('Сыгранные игры (GP)') ax2.set_ylabel('Очки (Pts)') ax2.legend(title='Позиция', loc='upper left', fontsize=9) ax3 = axes[1, 0] sns.boxplot(data=df_main, x='pos', y='PIM', hue='pos', palette='Set2', ax=ax3, legend=False) ax3.set_title('Штрафные минуты (PIM) по позициям') ax3.set_xlabel('Позиция') ax3.set_ylabel('Штрафные минуты (PIM)') ax4 = axes[1, 1] sns.violinplot(data=df_main, x='pos', y='Pts', hue='pos', palette='muted', inner='quartile', ax=ax4, density_norm='width', legend=False) ax4.set_title('Распределение Очков (Pts) по позициям') ax4.set_xlabel('Позиция') ax4.set_ylabel('Очки (Pts)') plt.tight_layout() plt.show() """ """ # 1.2.3. # Графики pyplot df = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') df = df.dropna(subset=['GP', 'G', 'Pts', 'PIM']) fig, ax = plt.subplots(figsize=(9, 6)) plt.subplots_adjust(left=0.3) current_feature = 'G' scatter = ax.scatter(df['GP'], df[current_feature], alpha=0.6, c='crimson', edgecolors='none', s=40) ax.set_xlim(0, df['GP'].max() + 0.5) ax.set_xlabel('Сыгранные матчи (GP)', fontsize=11) ax.set_ylabel(f'Показатель: {current_feature}', fontsize=11) ax.set_title('Анализ хоккейной статистики', fontsize=13, fontweight='bold') ax.grid(True, linestyle='--', alpha=0.5) # 3. Создание панели для интерактивных кнопок (RadioButtons) ax_buttons = plt.axes([0.03, 0.4, 0.2, 0.2], facecolor='lightgrey') radio = RadioButtons(ax_buttons, ('Голы (G)', 'Очки (Pts)', 'Штраф (PIM)')) feature_mapping = { 'Голы (G)': 'G', 'Очки (Pts)': 'Pts', 'Штраф (PIM)': 'PIM' } color_mapping = { 'G': 'red', 'Pts': 'orange', 'PIM': 'purple' } def change_feature(label): feature = feature_mapping[label] scatter.set_offsets(np.c_[df['GP'], df[feature]]) scatter.set_color(color_mapping[feature]) ax.set_ylabel(f'Показатель: {feature}', fontsize=11) ax.set_ylim(df[feature].min() - 0.5, df[feature].max() + 1) ax.set_xlim(0, df['GP'].max() + 0.5) fig.canvas.draw_idle() radio.on_clicked(change_feature) plt.show() """ """ # 1.2.5. # Тепловые карты df = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') fig, axes = plt.subplots(1, 2, figsize=(16, 7)) fig.suptitle('Тепловые карты', fontsize=15, fontweight='bold', y=1.02) ax1 = axes[0] num_cols = ['GP', 'G', 'A', 'Pts', 'PIM', 'year'] labels_ru = {'GP': 'Игры', 'G': 'Голы', 'A': 'Передачи', 'Pts': 'Очки', 'PIM': 'Штраф. мин.', 'year': 'Год'} corr = df[num_cols].corr() corr.index = [labels_ru[c] for c in corr.index] corr.columns = [labels_ru[c] for c in corr.columns] im1 = ax1.imshow(corr.values, cmap='RdYlGn', vmin=-1, vmax=1, aspect='auto') ax1.set_xticks(range(len(corr.columns))) ax1.set_yticks(range(len(corr.index))) ax1.set_xticklabels(corr.columns, rotation=35, ha='right', fontsize=10) ax1.set_yticklabels(corr.index, fontsize=10) for i in range(len(corr)): for j in range(len(corr.columns)): val = corr.values[i, j] color = 'black' if abs(val) < 0.7 else 'white' ax1.text(j, i, f'{val:.2f}', ha='center', va='center', fontsize=10, fontweight='bold', color=color) plt.colorbar(im1, ax=ax1, fraction=0.046, pad=0.04, label='Коэффициент корреляции') ax1.set_title('Корреляционная матрица\nчисловых признаков', fontsize=12, fontweight='bold', pad=12) ax1.set_facecolor('#f5f5f5') ax2 = axes[1] main_pos = ['C', 'L', 'R', 'D', 'F', 'G'] df2 = df[df['pos'].isin(main_pos)] pivot = df2.pivot_table(values='G', index='pos', columns='year', aggfunc='mean', fill_value=0) im2 = ax2.imshow(pivot.values, cmap='YlOrRd', aspect='auto', interpolation='nearest') ax2.set_xticks(range(len(pivot.columns))) ax2.set_yticks(range(len(pivot.index))) ax2.set_xticklabels(pivot.columns, rotation=45, ha='right', fontsize=9) ax2.set_yticklabels(pivot.index, fontsize=10) for i in range(pivot.shape[0]): for j in range(pivot.shape[1]): val = pivot.values[i, j] color = 'black' if val < pivot.values.max() * 0.6 else 'white' if val > 0: ax2.text(j, i, f'{val:.1f}', ha='center', va='center', fontsize=7.5, color=color) else: ax2.text(j, i, '—', ha='center', va='center', fontsize=7.5, color='#aaaaaa') plt.colorbar(im2, ax=ax2, fraction=0.046, pad=0.04, label='Среднее кол-во голов') ax2.set_title('Средние голы: позиция × год', fontsize=12, fontweight='bold', pad=12) ax2.set_xlabel('Год', fontsize=10) ax2.set_ylabel('Позиция', fontsize=10) plt.tight_layout() plt.show() """ """ # Scatterplot с линией регрессии: голы очки plt.figure(figsize=(8, 6)) sns.regplot( data=scoring_sc.dropna(subset=['G', 'Pts']), x='G', y='PTS', scatter_kws={'alpha': 0.3, 's': 20, 'color': 'green'}, line_kws={'color': 'red', 'linewidth': 1.5}, ci=95 # доверительный интервал 95% ) plt.title('Рисунок 3. Зависимость очков (PTS) от заброшенных шайб (G)', fontsize=11, fontweight='bold', pad=15) plt.xlabel('Заброшенные шайбы (G)', fontsize=10) plt.ylabel('Очки (PTS)', fontsize=10) plt.grid(alpha=0.3, linestyle='--') plt.tight_layout() plt.savefig('fig3_regplot_G_PTS.png', dpi=300, bbox_inches='tight') plt.show() """ """ # Scatterplot с регрессией: передачи очки plt.figure(figsize=(8, 6)) sns.regplot( data=scoring_sc.dropna(subset=['A', 'PTS']), x='A', y='PTS', scatter_kws={'alpha': 0.3, 's': 20, 'color': 'darkblue'}, line_kws={'color': 'orange', 'linewidth': 1.5}, ci=95 ) plt.title('Рисунок 4. Зависимость очков (PTS) от голевых передач (A)', fontsize=11, fontweight='bold', pad=15) plt.xlabel('Голевые передачи (A)', fontsize=10) plt.ylabel('Очки (PTS)', fontsize=10) plt.grid(alpha=0.3, linestyle='--') plt.tight_layout() plt.savefig('fig4_regplot_A_PTS.png', dpi=300, bbox_inches='tight') plt.show() """ """ # Условная фильтрация семплов # 1.2.8. df = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') filter_single = df[df['GP'] > 4] filter_and = df[(df['pos'] == 'C') & (df['A'] > 0) & (df['G'] == 0)] filter_complex = df[((df['G'] > 0) | (df['A'] > 0)) & (df['lgID'].isin(['NHL', 'PCHA']))] print("Результаты фильтрации") print(f" По одному признаку (GP > 4): найдено {len(filter_single)} игроков.") print(f" По логическому 'И' (Центры с передачами, но без голов): найдено {len(filter_and)} игроков.") print(f" Комплексный фильтр (Игроки с очками из NHL/PCHA): найдено {len(filter_complex)} игроков.") print("\n Пример отфильтрованных данных:") print(filter_and[['playerID', 'year', 'pos', 'GP', 'G', 'A']].head()) """ # Добавление шума # 1.2.9. df = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') np.random.seed(42) gp_std = df['G'].std() noise_g = np.random.normal(0, 0.15 * gp_std, size=len(df)) df['G_noise'] = (df['G'] + noise_g).clip(lower=0).round(2) noise_pim = np.random.uniform(0.85, 1.15, size=len(df)) df['PIM_noise'] = (df['PIM'] * noise_pim).round(2) ORIG = 'blue' NOISY = 'red' TEXT = 'black' TITLE = 'black' GRID = 'white' BG = 'white' AX_BG = 'lightgrey' fig, axes = plt.subplots(1, 2, figsize=(14, 5), facecolor=BG) fig.patch.set_facecolor(BG) for ax, orig_col, noise_col, label in [ (axes[0], df['G'], df['G_noise'], 'G (Заброшенные шайбы)'), (axes[1], df['PIM'], df['PIM_noise'], 'PIM (Штрафные минуты)'), ]: ax.set_facecolor(AX_BG) bins = np.linspace( min(orig_col.min(), noise_col.min()), max(orig_col.max(), noise_col.max()), 20 ) ax.hist(orig_col, bins=bins, color=ORIG, alpha=0.7, label='Оригинал', edgecolor=BG) ax.hist(noise_col, bins=bins, color=NOISY, alpha=0.7, label='С шумом', edgecolor=BG) ax.set_title(label, color=TITLE, fontsize=12, fontweight='bold', pad=10) ax.set_xlabel('Значение', color=TEXT, fontsize=10) ax.set_ylabel('Частота', color=TEXT, fontsize=10) ax.tick_params(colors=TEXT, labelsize=9) for sp in ax.spines.values(): sp.set_color(GRID) ax.grid(axis='y', color=GRID, linewidth=0.5, alpha=0.7) ax.legend(facecolor='#1e2433', edgecolor=GRID, labelcolor=TEXT, fontsize=10) fig.suptitle('Добавление шума к признакам датасета ', color=TITLE, fontsize=14, fontweight='bold', y=1.02) plt.tight_layout() plt.show() """ # 1.2.10. # Преобразование числовых даннных в категоральные df = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') print("\nПозиции (pos) - агрегация редких значений") print("До преобразования:") print(df['pos'].value_counts().to_string()) rare_pos = df['pos'].value_counts() rare_pos = rare_pos[rare_pos < 5] print(f"\nРедкие позиции: {rare_pos.index.tolist()}") def map_pos(p): if p in ['L', 'R', 'C', 'F', 'R/L', 'R/C', 'L/C']: return 'Forward' elif p in ['D', 'R/D', 'D/R']: return 'Defense' elif p == 'G': return 'Goalie' return 'Other' df['pos_cat'] = df['pos'].apply(map_pos) print("\nПосле агрегации (pos_cat):") print(df['pos_cat'].value_counts().to_string()) """ """ # Гистограмма распределения по позициям sns.set_style("whitegrid") plt.rcParams['font.size'] = 10 scoring_sc = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') plt.figure(figsize=(8, 5)) pos_counts = scoring_sc['pos'].value_counts() bars = plt.bar(pos_counts.index, pos_counts.values, color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FECA57'], edgecolor='black', alpha=0.9) plt.title('Рисунок 9., fontsize=12, fontweight='bold', pad=15) plt.xlabel('Позиция', fontsize=10) plt.ylabel('Количество записей', fontsize=10) plt.grid(axis='y', alpha=0.3, linestyle='--') # Добавление подписей значений на столбцы for bar in bars: height = bar.get_height() plt.text(bar.get_x() + bar.get_width()/2., height, f'{int(height)}', ha='center', va='bottom', fontsize=9) plt.tight_layout() plt.show() """ """ # Круговая диаграмма для лиг scoring_sc = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') sns.set_style("whitegrid") plt.rcParams['font.size'] = 10 plt.figure(figsize=(8, 6)) lg_counts = scoring_sc['lgID'].value_counts().sort_index() bars = plt.bar( lg_counts.index, lg_counts.values, color=['#2E86AB', '#A23B72'], edgecolor='black', alpha=0.9, width=0.6 ) plt.title('Рисунок 10.', fontsize=12, fontweight='bold', pad=15) plt.xlabel('Лига', fontsize=10) plt.ylabel('Количество записей', fontsize=10) plt.grid(axis='y', alpha=0.3, linestyle='--') for bar in bars: height = bar.get_height() plt.text(bar.get_x() + bar.get_width() / 2., height, f'{int(height):,}', ha='center', va='bottom', fontsize=10, fontweight='bold' ) total = lg_counts.sum() for bar in bars: height = bar.get_height() pct = (height / total) * 100 plt.text( bar.get_x() + bar.get_width() / 2., height / 2, f'{pct:.1f}%', ha='center', va='center', fontsize=9, color='white', fontweight='bold' ) plt.tight_layout() plt.show() """ """ # 1.3.4 scoring_sc = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') team_counts = scoring_sc['tmID'].value_counts() rare_teams = team_counts[team_counts < 10].index print(f"Редкие команды (<100 записей): {len(rare_teams)} из {len(team_counts)}") print(f"Примеры редких: {list(rare_teams[:5])}") # Анализ позиций pos_counts = scoring_sc['pos'].value_counts() print(f"\nРаспределение по позициям:\n{pos_counts}") """ """ # 1.3.5 scoring_sc = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') features = ['Pts', 'G', 'A', 'PIM'] # 2. Заполнение пропусков перед нормализацией df_num = scoring_sc[features].fillna(0) # 3. Ручная Min-Max нормализация средствами Pandas df_scaled = pd.DataFrame() for col in features: col_min = df_num[col].min() col_max = df_num[col].max() # Защита от деления на ноль (если все значения в столбце одинаковы) if col_max == col_min: df_scaled[col] = 0.0 else: df_scaled[col] = (df_num[col] - col_min) / (col_max - col_min) scoring_sc['player_score'] = ( df_scaled['Pts'] * 0.5 + df_scaled['G'] * 0.5 + df_scaled['A'] * 0.3 + (1 - df_scaled['PIM']) * 0.2 ) scoring_sc['player_tier'] = pd.qcut( scoring_sc['player_score'], q=4, labels=['Bronze', 'Silver', 'Gold', 'Platinum'], duplicates='drop' ) # 6. Вывод распределения новой категории print("Распределение по уровню игрока (player_tier):") print(scoring_sc['player_tier'].value_counts().sort_index()) plt.figure(figsize=(8, 5)) tier_counts = scoring_sc['player_tier'].value_counts().sort_index() colors = ['#CD7F32', '#C0C0C0', '#FFD700', '#E5E4E2'] bars = plt.bar(tier_counts.index, tier_counts.values, color=colors, edgecolor='black', alpha=0.9) plt.title('Распределение игроков по уровню (player_tier)', fontsize=12, fontweight='bold', pad=15) plt.xlabel('Уровень игрока', fontsize=10) plt.ylabel('Количество записей', fontsize=10) plt.grid(axis='y', alpha=0.3, linestyle='--') for bar in bars: height = bar.get_height() plt.text(bar.get_x() + bar.get_width()/2., height, f'{int(height)}', ha='center', va='bottom', fontsize=9) plt.tight_layout() plt.show() """ """ # 1.4.2. # Гипотеза 1 df = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') print("Игроки WCHL vs NHL — среднее число очков (Pts)") # 2. Фильтрация: только WCHL и NHL wchl = df[df['lgID'] == 'WCHL'].copy() nhl = df[df['lgID'] == 'NHL'].copy() print(f"\nВсего записей WCHL: {len(wchl)}") print(f"Всего записей NHL : {len(nhl)}") # 3. Описательная статистика по GP print("\nРаспределение GP") print(f"WCHL: min={wchl['GP'].min()}, max={wchl['GP'].max()}, " f"mean={wchl['GP'].mean():.1f}, median={wchl['GP'].median()}") print(f"NHL : min={nhl['GP'].min()}, max={nhl['GP'].max()}, " f"mean={nhl['GP'].mean():.1f}, median={nhl['GP'].median()}") wchl_filtered = wchl[wchl['GP'] > 0].copy() nhl_filtered = nhl[nhl['GP'] > 0].copy() wchl_filtered['pts_per_game'] = wchl_filtered['Pts'] / wchl_filtered['GP'] nhl_filtered['pts_per_game'] = nhl_filtered['Pts'] / nhl_filtered['GP'] print("\nPts/GP (очков за игру)") print(f"WCHL: mean={wchl_filtered['pts_per_game'].mean():.4f}, " f"std={wchl_filtered['pts_per_game'].std():.4f}, n={len(wchl_filtered)}") print(f"NHL : mean={nhl_filtered['pts_per_game'].mean():.4f}, " f"std={nhl_filtered['pts_per_game'].std():.4f}, n={len(nhl_filtered)}") # тест Уэлча t_stat, p_value = stats.ttest_ind( wchl_filtered['pts_per_game'], nhl_filtered['pts_per_game'], alternative='less', equal_var=False ) print(f"\nТест А — Pts/GP тест Уэлча (WCHL < NHL)") print(f" статистика = {t_stat:.4f}") print(f" p-value = {p_value:.4f}") alpha = 0.05 if p_value < alpha: print(f"При α={alpha}: WCHL < NHL — WCHL значимо меньше NHL") else: print(f"При α={alpha}: НЕТ оснований отвергнуть WCHL < NHL") # 5. Подход Б — матчинг по GP (точное совпадение) common_gp = set(wchl['GP'].unique()) & set(nhl['GP'].unique()) print(f"\nматчинг по GP") wchl_matched = wchl[wchl['GP'].isin(common_gp)] nhl_matched = nhl[nhl['GP'].isin(common_gp)] print(f"Записей после матчинга — WCHL: {len(wchl_matched)}, NHL: {len(nhl_matched)}") wchl_pts = wchl_matched['Pts'].values nhl_pts = nhl_matched['Pts'].values print(f"\nСреднее Pts — WCHL: {wchl_pts.mean():.4f}, NHL: {nhl_pts.mean():.4f}") t2, p2 = stats.ttest_ind(wchl_pts, nhl_pts, alternative='less', equal_var=False) print(f"\nматчинг по GP t-тест Уэлча") print(f" статистика = {t2:.4f}") print(f" p-value = {p2:.4f}") if p2 < alpha: print(f"При α={alpha}: WCHL значимо меньше NHL") else: print(f"При α={alpha}: НЕТ оснований отвергнуть WCHL < NHL") # 7. Итоговый вывод print("Итог") print(f" Среднее Pts/GP в WCHL : {wchl_filtered['pts_per_game'].mean():.4f}") print(f" Среднее Pts/GP в NHL : {nhl_filtered['pts_per_game'].mean():.4f}") raznica = nhl_filtered['pts_per_game'].mean() - wchl_filtered['pts_per_game'].mean() print(f" Разница (NHL − WCHL) : {raznica:.4f} очков за игру") """ """ # Гипотеза 2 df = pd.read_csv('C:/Users/0/Desktop/Курсовая/archiveTAB/ScoringSC.csv') print("Игроки до 1920 vs после 1920 по очкам (Pts)") before_1920 = df[df['year'] < 1920]['Pts'] after_1920 = df[df['year'] >= 1920]['Pts'] print(f"\nГруппа до 1920 года:") print(f" Кол-во записей : {len(before_1920)}") print(f" Среднее Pts : {before_1920.mean():.3f}") print(f" Медиана Pts : {before_1920.median():.3f}") print(f" Std : {before_1920.std():.3f}") print(f" Min / Max : {before_1920.min()} / {before_1920.max()}") print(f"\nГруппа 1920 года (включая 1920):") print(f" Кол-во записей : {len(after_1920)}") print(f" Среднее Pts : {after_1920.mean():.3f}") print(f" Медиана Pts : {after_1920.median():.3f}") print(f" Std : {after_1920.std():.3f}") print(f" Min / Max : {after_1920.min()} / {after_1920.max()}") t_stat, p_value = stats.ttest_ind(before_1920, after_1920, equal_var=False) print(f"\nКритерий Уэлча") print(f" t-статистика : {t_stat:.4f}") print(f" p-значение : {p_value:.6f}") print(f" Уровень знач.: α = 0.05") print(f"\nВывод") if before_1920.mean() < after_1920.mean(): direction = "Меньше" else: direction = "Больше или равно" print(f"Среднее Pts до 1920: {before_1920.mean():.3f}") print(f"Среднее Pts от 1920: {after_1920.mean():.3f}") print(f"Среднее количество очков у игроков до 1920 {direction}, чем у игроков после 1920.") if p_value < 0.05: print(f"p = {p_value:.6f} < 0.05: разница статистически значимая.") if before_1920.mean() < after_1920.mean(): print("Гипотеза подтверждается.") else: print("Гипотеза опровергается (разница значима, но направление обратное).") else: print(f"p = {p_value:.6f} ≥ 0.05: разница статистически не значима.") print("Гипотеза не подтверждается на уровне значимости 0.05.") """