Original size 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 ()

Original size 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 ()

Original size 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 ()

Original size 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 ()

Original size 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 ()

Original size 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 ()

Original size 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 ()

Original size 1179x829

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

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

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

Вывод

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

Эффективность ваших тренировок
Project created at 17.01.2026
We use cookies to improve the operation of the website and to enhance its usability. More detailed information on the use of cookies can be fo...
Show more