Исходный размер 960x1280

Эффективность ваших тренировок

PROTECT STATUS: not protected

Введение.

Спорт и здоровый образ жизни — не просто тренды, а важная часть современной культуры заботы о себе. Однако зачастую люди следуют шаблонным рекомендациям, не учитывая индивидуальные особенности: пол, уровень подготовки, состав тела или даже привычки, такие как потребление воды. И поскольку я сама не так давно начала ходить в спортивный зал и работать над своим телом, эта тема мне особенно интересна.

Данные я искала на платформе Kaggle и решила остановиться на датасете «Gym Members Exercise Dataset», содержащий подробную информацию о 973 тренировках — от физиологических показателей (ЧСС, калории, % жира) до поведенческих (тип тренировки, частота, длительность, гидратация).

В этом проекте я решила исследовать, какие факторы действительно влияют на эффективность тренировки. Цель — найти закономерности, которые помогут сделать тренировки умнее, безопаснее и результативнее.

Поскольку меня заинтересовал этот проект, я решила подробнее углубиться в способы визуализации и анализа данных и дополнительно изучила курс на stepik по библиотеке matplotlib «Визуализация с Matplotlib для анализа данных» и документацию по библиотекам matplotlib, seaborn, numpy, pandas.

Ссылка на копию ноутбука с кодом: https://colab.research.google.com/drive/1L3N8pEg2tLE09fTxX6ZHAkJKmge-NpQo

Коррелограмма.

После первичного осмотра данных и минимальной обработки, я решила исследовать зависимости между признаками в датасете. Поскольку подавляющее большинство признаков — численные, естественным шагом стало вычисление коэффициента корреляции Пирсона, он показывает силу линейной связи между двумя переменными.

Чтобы визуализировать все попарные корреляции сразу, я построила тепловую карту —коррелограмму. Чтобы избежать дублирования и улучшить читаемость, отображена только нижняя треугольная часть матрицы: диагональ (где каждый признак коррелирует сам с собой, r=1) и зеркальные значения исключены как избыточные.

data_corr = data[num_cols].corr ().round (2) mask = np.triu (np.ones_like (data_corr, dtype=bool)) # Приводим к нижнетреугольной матрице

plt.figure (figsize=(10, 8)) heatmap = sns.heatmap ( data_corr, mask=mask, annot=True, fmt=».2f», cmap='coolwarm', square=True, linewidths=0.6, linecolor='#E0E0E0') heatmap.set_facecolor ('#FAFAFA') heatmap.tick_params (axis='x', rotation=45, colors='#333333', labelsize=9) heatmap.tick_params (axis='y', rotation=0, colors='#333333', labelsize=9)

plt.title ('Коррелограмма', fontsize=14, pad=20, fontweight='bold', color='#222222') plt.tight_layout () plt.show ()

Исходный размер 890x790

Анализ зависимостей признаков с высокой корреляцией.

Важно помнить, что высокая корреляция не гарантирует наличия реальной линейной зависимости, а также не учитывает возможные выбросы, нелинейные тренды или смещения в распределении. Поэтому я не ограничилась числами — для всех пар признаков с коэффициентом корреляции выше 0.5 я построила диаграммы рассеяния (scatter plots).

Можем заметить очень высокую положительную корреляцию между временем тренировки и количеством сожженных калорий на ней. при прочих равных условиях, чем дольше длится физическая активность, тем больше энергии тратит организм.

Такую же отчетливую зависимость мы можем наблюдать между весом и индексом массы тела человека (BMI), потому что BMI вычисляется как вес, деленный на квадрат роста. Поскольку рост в выборке варьируется меньше, чем вес, основной вклад в изменение BMI вносит именно масса тела.

Отрицательная корреляция между количеством сожженых калорий и процентом жира человека тоже согласуется с ожиданиями: регулярные тренировки способствуют снижению жировой массы, особенно в сочетании с контролем питания. Однако важно помнить: корреляция не означает причинно-следственную связь — возможно, люди с уже низким % жира просто чаще и интенсивнее тренируются.

Другие графики также достаточно интерпретируемы: зависимость процента жира и количества выпиваемой воды в день — «спортивные» люди с низким процентом жира следят чаще придерживаются рекомендаций по гидратации и потребляют больше воды, нежели обычные зеваки, зависимость продолжительности тренировки и уровня спортивной подготовки — более опытные спортсмены лучше выносят продолжительные регулярные занятия, чем начинающие, их выносливость и восстановительный потенциал выше.

Особый интерес вызывает распределение на графике зависимости длительности тренировки от процента жира: точки словно формируют две обособленные группы. Чтобы понять, что стоит за этим паттерном, я разделила участников на три категории по уровню жировой массы — низкий, средний и высокий — и построила точечную диаграмму с расслоением (strip plot) для более детального сравнения.

data['Fat_Group'] = pd.cut (data['Fat_Percentage'], bins=[0, 15, 25, 100], labels=['Низкий', 'Норма', 'Высокий']) plt.figure (figsize=(10, 6)) ax = plt.gca () ax.set_facecolor ('#FAFAFA') plt.gcf ().set_facecolor ('white') ax.grid (axis='y', linestyle='--', linewidth=0.6, color='#CCCCCC', alpha=0.5)

sns.stripplot ( data=data, x='Fat_Group', y='Session_Duration', jitter=0.25, alpha=0.6, size=5, palette=['

609AC9', '

669DAD', '#567EB9'])

medians = data.groupby ('Fat_Group')['Session_Duration'].median () for i, (group, med) in enumerate (medians.items ()): plt.hlines (med, i — 0.3, i + 0.3, color='black', linestyle='--', linewidth=1.2)

plt.xlabel ('Группа по % жира') plt.ylabel ('Длительность тренировки (часы)') plt.title ('Длительность тренировки и процент жира', fontweight='bold', fontsize=16) sns.despine () plt.tight_layout () plt.show ()

Исходный размер 989x590

Люди с низким процентом жира в среднем демонстрируют более длительные тренировки, в то время как среди участников с высоким процентом жира наблюдается как группа с умеренной длительностью, так и заметное количество лиц с короткими сессиями (около 0.5 часа). Это может указывать на то, что регулярные и продолжительные тренировки ассоциированы с более низким уровнем жировой массы — либо как причина, либо как следствие более вовлечённого подхода к фитнесу.

Сжигают ли женщины и мужчины калории одинаково?

Мне стало интересно, действительно ли мужчины и женщины по-разному расходуют энергию при одинаковой нагрузке, и чтобы это понять я построила диаграмму рассеяния с линиями линейной регрессии, раздельно для каждого пола.

На графике по оси X отложена длительность тренировки, по оси Y — количество сожжённых калорий. Голубым цветом обозначены мужчины, а розовым — женщины. Линии регрессии позволяют оценить среднюю эффективность (наклон линии = калории в час) и сравнить её между группами.

gender_palette = { 'Male': '#609AC9', 'Female': '#B97FB9' } line_colors = { 'Male': '#406A8D', 'Female': '#7B4B7C' }

g = sns.lmplot (data=data, x='Session_Duration', y='Calories_Burned', hue='Gender', palette=gender_palette, height=6, aspect=1.2, scatter_kws={'alpha': 0.6, 's': 50, 'edgecolor': 'gray'}, line_kws={'linewidth': 3}, legend=False )

for i, gender in enumerate (['Male', 'Female']): g.ax.get_lines ()[i].set_color (line_colors[gender])

g.ax.set_facecolor ('#FAFAFA') g.ax.grid (True, linestyle='--', alpha=0.3) sns.despine () g.set_axis_labels ('Длительность тренировки (часы)', 'Сожжённые калории') g.fig.suptitle ('Сравнение эффективности тренировок по полу', fontsize=16, fontweight='bold')

handles = [ plt.Line2D ([0], [0], marker='o', color='w', markerfacecolor=gender_palette[gender], markersize=8, markeredgecolor='gray', linestyle='None', label='Мужчины' if gender == 'Male' else 'Женщины') for gender in ['Male', 'Female'] ] g.ax.legend ( handles=handles, title='Пол', loc='lower right', frameon=True, facecolor='#FAFAFA', framealpha=0.95 )

plt.tight_layout () plt.show ()

Исходный размер 709x592

Как мы видим, наклоны линий различаются — при одинаковом времени тренировки мужчины в среднем сжигают больше калорий, нежели женщины.

В целом это объясняется физиологическими особенностями: у мужчин, как правило, выше мышечная масса и ниже процент жира, а мышечная ткань — метаболически активна, даже в покое. Следовательно, при одинаковой нагрузке их организм тратит больше энергии.

Насколько % жира зависит от уровня спортивной подготовки для разных полов?

Чтобы понять, как уровень спортивной подготовки влияет на состав тела, я исследую распределение процента жира отдельно для мужчин и женщин на разных этапах фитнес-пути: от новичков (уровень 1) до опытных спортсменов (уровень 3).

Для визуализации я выбрала точечные диаграммы с наложением кривых плотности (strip plots с KDE), потому что они позволяют сравнивать формы распределений, а не только средние значения, кривая плотности (она же KDE) показывает, где сосредоточена основная масса данных (аналог гистограммы, но без зависимости от числа корзин). Это особенно важно, когда группы имеют разный размер — плотность нормирована на единицу площади, поэтому сравнение остаётся корректным.

Такой подход помогает увидеть не только средний сдвиг (% жира снижается с опытом), но и изменение разброса и формы распределения — например, становится ли состав тела более однородным у продвинутых атлетов.

levels = [1, 2, 3] gender_colors_fill = {'Male': '

609AC9', 'Female': '

B97FB9'} gender_colors_kde = {'Male': '

406A8D', 'Female': '

7B4B7C'}

fig, axes = plt.subplots (1, 3, figsize=(12, 5), sharex=True, sharey=True) fig.suptitle ('Распределение процента жира по уровню подготовки', fontsize=16, fontweight='bold')

for i, level in enumerate (levels): ax = axes[i] data_level = data[data['Experience_Level'] == level]

# Гистограмма в шкале плотности
for gender in ['Male', 'Female']:
    subset = data_level[data_level['Gender'] == gender]['Fat_Percentage']
    sns.histplot (subset, bins=15, stat='density', alpha=0.6, color=gender_colors_fill[gender], kde=False, ax=ax)

# 2. Добавляем кривые KDE
for gender in ['Male', 'Female']:
    subset = data_level[data_level['Gender'] == gender]['Fat_Percentage']
    sns.kdeplot (subset, color=gender_colors_kde[gender], linewidth=2.5, ax=ax)

ax.set_title (f’Уровень подготовки: {level}')
ax.set_facecolor ('#FAFAFA')
ax.grid (axis='y', linestyle='--', alpha=0.4)
sns.despine (ax=ax)
if i == 0:
    ax.set_ylabel ('Относительная частота')
else:
    ax.set_ylabel ('')

fig.text (0.5, 0.02, 'Процент жира (%)', ha='center', fontsize=12)

handles = [ plt.Rectangle ((0, 0), 1, 1, color=gender_colors_fill['Male'], alpha=0.6), plt.Rectangle ((0, 0), 1, 1, color=gender_colors_fill['Female'], alpha=0.6) ] fig.legend (handles, ['Мужчины', 'Женщины'], title='Пол', loc='lower left', bbox_to_anchor=(0.7, 0.02), ncol=2, shadow=True)

plt.tight_layout (rect=[0.02, 0.12, 0.98, 0.92]) plt.show ()

Исходный размер 1142x504

Любопытно, что между первым и вторым уровнями спортивной подготовки практически не наблюдается значимых различий в распределении процента жира. Существенное снижение жировой массы проявляется лишь на третьем (экспертном) уровне, что может указывать либо на нелинейный характер прогресса, либо на особенности критериев, использованных при классификации уровней в исходном датасете (к сожалению, методология формирования этих групп не раскрыта).

Как и предполагалось на основании физиологических различий, распределение процента жира у мужчин смещено влево по сравнению с женщинами. Это согласуется с биологическими нормами: у мужчин базовый уровень жировой массы ниже, поскольку значительная часть энергетических ресурсов направляется на поддержание мышечной ткани, тогда как у женщин жир играет важную роль в гормональной регуляции и репродуктивной функции.

Какой тип тренировок самый эффективный для сжигания жира?

Одна из главных целей фитнес-тренировок — снижение жировой массы. Чтобы оценить эффективность разных видов нагрузки, я рассчитала среднее количество калорий, сжигаемых за час (Calories_Burned / Session_Duration) для каждого типа тренировки и визуализировала результаты в виде горизонтальной столбчатой диаграммы (barh). Этот тип графика позволяет легко сравнивать значения между категориями и сразу видеть лидера по эффективности.

Попробуем еще поизучать различия в типах тренировок: для этого я построила четыре графика boxplot (так называемые «ящики с усами») по ключевым метрикам: сожжённые калории, средняя ЧСС (частота сердечных сокращений, интенсивность), длительность сессии, эффективность (ккал/час).

Boxplot’ы позволяют увидеть не только средние значения, но и полное распределение данных: разброс, наличие выбросов, асимметрию. Это особенно важно, когда средние могут вводить в заблуждение — например, из-за небольшого числа аномально длинных йога-сессий.

efficiency = data.groupby ('Workout_Type')['Calories_Per_Hour'].mean ().sort_values ()

color_map = { 'Cardio': '#91B7D6', 'Strength': '#609AC9', 'Yoga': '#567EB9', 'HIIT': '#669DAD' }

colors = [color_map[w] for w in efficiency.index]

plt.figure (figsize=(8, 4)) bars = plt.barh (efficiency.index, efficiency.values, color=colors)

for i, (workout, value) in enumerate (efficiency.items ()): plt.text (value + 10, i, f'{value:.0f} кал/ч', va='center', fontsize=11, color='#333333')

plt.xlabel ('Среднее количество калорий в час') plt.title ('Эффективность тренировок по сжиганию калорий', fontweight='bold', fontsize=16) sns.despine (left=True) plt.tight_layout () plt.show ()

Исходный размер 789x390

color_map = { 'Cardio': '#91B7D6', 'Strength': '#609AC9', 'Yoga': '#567EB9', 'HIIT': '#669DAD' } metrics = ['Water_Intake', 'Avg_BPM', 'Session_Duration', 'Calories_Per_Hour'] titles = [ 'Потребление воды (л/день)', 'Средняя ЧСС (уд/мин)', 'Длительность тренировки (ч)', 'Эффективность тренировок (кал/ч)' ]

fig, axes = plt.subplots (2, 2, figsize=(10, 7)) axes = axes.flatten () fig.suptitle ('Влияние типа тренировки на основные показатели', fontsize=16, fontweight='bold')

for i, (metric, title) in enumerate (zip (metrics, titles)): ax = axes[i] sns.boxplot (data=data, x='Workout_Type', y=metric, ax=ax, palette=color_map, width=0.6, linewidth=1.2) ax.set_title (title, fontsize=13, fontweight='bold', pad=12) ax.set_xlabel ('') ax.set_ylabel ('') ax.set_facecolor ('#FAFAFA') ax.grid (True, linestyle='--', linewidth=0.5, alpha=0.4)

fig.text (0.5, 0.02, 'Тип тренировки', ha='center', fontsize=12) fig.text (0.02, 0.5, 'Значение показателя', va='center', rotation='vertical', fontsize=12)

plt.tight_layout (rect=[0.03, 0.03, 1, 1]) plt.show ()

Исходный размер 985x696

Удивительно, но все типы тренировок — от йоги до HIIT — демонстрируют практически одинаковую эффективность, так еще и огромные значения около 720 калорий в час: это противоречит как здравому смыслу, так и физиологическим законам.

Такая аномальная однородность сильно наводит на мысль, что датасет синтетический или искусственно сбалансированный. К сожалению, очень вероятно, что значения типов тренировок были сгенерированы без учёта реальных метаболических различий между ними.

Как влияет потребление воды на эффективность тренировок?

Потребление воды — один из ключевых, но часто недооцениваемых аспектов фитнес-режима. Чтобы понять, как гидратация влияет не только на энергозатраты, но и на физиологические реакции организма, я проанализировала три аспекта: эффективность тренировки, интенсивность нагрузки и связь с жировой массой. Для каждой задачи выбран наиболее подходящий тип графика:

Первый график (scatter + LOESS) показывает нелинейную зависимость между объёмом воды и калориями в час. LOESS-сглаживание позволяет выявить оптимальную зону, не предполагая заранее форму связи (линейную, квадратичную и т. д.).

Второй график (pointplot с группами) использует агрегацию по категориям (Water_Group), чтобы сравнить среднюю ЧСС — ключевой индикатор интенсивности — между группами с разным уровнем гидратации. Pointplot с доверительными интервалами идеален для сравнения центральных тенденций в категориальных данных.

Третий график (множественные scatter-диаграммы по уровням подготовки) позволяет увидеть, как связь между водой и % жира меняется в зависимости от опыта. Разделение по Experience_Level и разбивка по полу даёт возможность выявить скрытые паттерны, которые теряются в агрегированных данных.

data['Water_Group'] = pd.cut (data['Water_Intake'], bins=[0, 1.5, 2.5, 4], labels=['<1.5 л', '1.5–2.5 л', '>2.5 л']) fig = plt.figure (figsize=(14, 8.5)) fig.suptitle ('Анализ влияния потребления воды на показатели здоровья и тренировок', fontsize=16, fontweight='bold', y=0.98)

1.

ax1 = plt.subplot (2, 2, 1) sns.regplot (data=data, x='Water_Intake', y='Calories_Per_Hour', lowess=True, scatter_kws={'alpha': 0.5, 's': 50, 'color': '

567EB9'}, line_kws={'color': '

496B9B', 'linewidth': 3}, ax=ax1) ax1.set_title ('Потребление воды и эффективность тренировки', fontsize=13, fontweight='bold') ax1.set_xlabel ('Вода (литры в день)', fontsize=11) ax1.set_ylabel ('Калории в час', fontsize=11) ax1.set_facecolor ('#FAFAFA') ax1.grid (True, linestyle='--', linewidth=0.6, alpha=0.4) sns.despine (ax=ax1)

2.

ax2 = plt.subplot (2, 2, 2) sns.pointplot (data=data, x='Water_Group', y='Avg_BPM', capsize=0.1, color='#567EB9', scale=0.8, ax=ax2) ax2.set_title ('Средняя ЧСС в зависимости от потребления воды', fontsize=13, fontweight='bold') ax2.set_xlabel ('Группа по водопотреблению', fontsize=11) ax2.set_ylabel ('Средняя ЧСС (уд/мин)', fontsize=11) ax2.set_facecolor ('#FAFAFA') ax2.grid (True, axis='y', linestyle='--', linewidth=0.6, alpha=0.4) sns.despine (ax=ax2)

3.

ax3 = plt.subplot (2, 3, 4) ax4 = plt.subplot (2, 3, 5) ax5 = plt.subplot (2, 3, 6) axes_bottom = [ax3, ax4, ax5] levels = [1, 2, 3]

for i, level in enumerate (levels): ax = axes_bottom[i] subset = data[data['Experience_Level'] == level] for gender in ['Male', 'Female']: group = subset[subset['Gender'] == gender] if not group.empty: ax.scatter ( group['Water_Intake'], group['Fat_Percentage'], color=gender_palette[gender], alpha=0.7, s=40, label='Мужчины' if gender == 'Male' else 'Женщины' ) ax.set_title (f’Уровень: {level}', fontsize=12) ax.set_xlabel ('Вода (литры)', fontsize=11) ax.set_ylabel ('% жира', fontsize=11) ax.set_facecolor ('#FAFAFA') ax.grid (True, linestyle='--', linewidth=0.5, alpha=0.4, color='#CCCCCC') sns.despine (ax=ax) fig.text (0.5, 0.475, 'Вода и жировая масса по уровню подготовки', fontsize=13, fontweight='bold', ha='center')

handles = [ plt.Line2D ([0], [0], marker='o', color='w', markerfacecolor=gender_palette['Male'], markersize=8, label='Мужчины', linestyle='None'), plt.Line2D ([0], [0], marker='o', color='w', markerfacecolor=gender_palette['Female'], markersize=8, label='Женщины', linestyle='None') ]

fig.legend (handles, ['Мужчины', 'Женщины'], title='Пол', loc='lower center', bbox_to_anchor=(0.8, 0.02), ncol=2, frameon=True, facecolor='

FAFAFA', edgecolor='

CCCCCC', shadow=True) ax2.set_ylabel ('') plt.tight_layout (rect=[0.02, 0.12, 0.98, 0.91]) plt.subplots_adjust (top=0.9, bottom=0.14, hspace=0.6) # Выделяет доп место для нижнего графика

plt.show ()

Исходный размер 1179x829

Анализ не выявил чёткой «точки насыщения» по эффективности: с ростом объёма потребляемой воды наблюдается устойчивый рост калорий, сжигаемых в час, что может указывать либо на прямую связь между гидратацией и производительностью, либо на то, что более мотивированные участники одновременно и лучше следят за водным балансом, и тренируются интенсивнее.

В то же время средняя частота сердечных сокращений (ЧСС) снижается с увеличением потребления воды, особенно в группе с самым высоким объёмом гидратации. Это может свидетельствовать о повышенной кардиальной эффективности: хорошо гидратированный организм справляется с нагрузкой при меньшей пульсовой нагрузке, что характерно для тренированных людей.

Третий график показывает, что мужчины в среднем потребляют больше воды, чем женщины — вероятно, из-за большего веса и объёма потоотделения. Что касается уровня спортивной подготовки, между первым и вторым уровнями различий в водопотреблении практически нет, однако на третьем (экспертном) уровне наблюдается высокая и одинаковая гидратация у всех участников, независимо от пола. Это говорит о том, что осознанное внимание к водному балансу становится частью фитнес-культуры лишь на продвинутых этапах тренировочного пути.

Вывод

Несмотря на признаки искусственности данных, этим маленьким исследованием я убедилась, что чем усерднее я буду заниматься спортом, тем большего добьюсь, хотя и далеко не сразу (у 1 и 2 групп по уровню спортивной подготовки особых различий не было), а также нужно не забывать поддерживать водный баланс. Кроме того, я узнала очень много новых графиков и способов визуализации, помогающих провести качественный анализ данных.

Эффективность ваших тренировок
Проект создан 17.01.2026
Мы используем файлы cookies для улучшения работы сайта и большего удобства его использования. Более подробную информац...
Показать больше