Skip to content

Instantly share code, notes, and snippets.

@barrysmyth
Created November 12, 2023 20:42
Show Gist options
  • Save barrysmyth/fa95aa0cd1aa210f3ceb929bb4a11604 to your computer and use it in GitHub Desktop.
Save barrysmyth/fa95aa0cd1aa210f3ceb929bb4a11604 to your computer and use it in GitHub Desktop.
def plot_session_pie(headline, header, chart, footer, session, fontsize=15, min_frac=0.05):
""" Plot a pie chart based on the minutes in each HR zone for the session.
Args:
headline - axis for the chart headline/title
header - axis for the chart header info (weekly mins, RE, % easy).
chart - axis for the main chart (horizontal bar).
footer - axis for the weekly kms and mean pace.
week - a dataframe with data for the week to be summarised.
fontsize - fontsize to be used for all text annotations.
min_frac - only plot wedges above this fraction.
"""
# -------------------------------------------------------------------------#
# Extract the data needed from the session dataframe.
# The title is based on the workout name, truncated to fit.
max_title_len = 18
title = session['workout_name']
title = title[:max_title_len]+'...' if len(title)>max_title_len else title
# Session distance and time
total_kms = session['distance']/1000
rel_kms = session['rel_distance']
total_mins = session['moving_time']/60
rel_mins = session['rel_moving_time']
# Calculate mean session pace and convert to mins:sec string.
mean_pace = convert_to_pace(total_mins/total_kms)
# Session relative effort/suffer score
suffer_score = session['suffer_score']
rel_suffer_score = session['rel_suffer_score']
# The distance in each HR zone
kms_in_zone = session.filter(regex='z\d_distance')/1000
mins_in_zone = session.filter(regex='z\d_moving_time')/60
fracs_in_zone = mins_in_zone/mins_in_zone.sum()
# The time elapsed since the previous session.
rest_hours = session['rest_hours']
# -------------------------------------------------------------------------#
# Creating the session pie chart.
# Remove all axis decoration.
headline.set_axis_off()
header.set_axis_off()
chart.set_axis_off()
footer.set_axis_off()
# A 6-step colourmap for the 6 HR zones.
use_cmap = mpl.colormaps['coolwarm']
use_colours = use_cmap(np.linspace(.25, 1, 6))
# The HR zone labels for the pie chart.
labels = ['Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'Z6']
labeldistance = 1.25 if len(fracs_in_zone[fracs_in_zone>=min_frac])>1 else 1.3
# The size of the pie chart is based on the relative session duration.
base_radius = 0.3
radius = (base_radius+(rel_mins*(1-base_radius)))**.5
# Draw the pie chart.
patches, labels = chart.pie(
mins_in_zone,
radius=radius,
# Adjust positioning of wedges to visually separate Z1/Z2, Z3/Z4, Z5/Z6.
explode=[0, 0, .075, .075, .15, .15],
# The wedge labels.
labels=labels, labeldistance=labeldistance, textprops=dict(fontsize=fontsize),
# The segments start at the 12 o'clock and proceed clockwise.
startangle=90, counterclock=False,
# The wedges colours.
colors=use_colours, wedgeprops=dict(ec='w', lw=1),
)
# Remove patches that are too small; avoids display problems.
for frac, patch, label in zip(fracs_in_zone, patches, labels):
if frac<min_frac:
patch.set_visible(False)
label.set_visible(False)
# Add the relative effort/suffer_score in the center of the pie and
# colour code it based on the relative suffer score.
chart.text(
0, 0, '{:.0f}'.format(suffer_score),
color='k' if 0.2<rel_suffer_score<0.75 else 'w', fontsize=fontsize,
ha='center', va='center',
bbox=dict(
boxstyle='circle',
facecolor=use_cmap(rel_suffer_score),
lw=.5, alpha=.75, pad=0.5
)
)
# -------------------------------------------------------------------------#
# Additional annotations.
# The session title is used as the headline.
headline.text(
np.mean(headline.get_xlim()), np.mean(headline.get_ylim()),
title,
fontsize=fontsize, fontweight='bold',
ha='center', va='center'
)
# Add session time and hours since previous session.
if rest_hours>0:
header.text(
np.mean(footer.get_xlim()), np.mean(footer.get_ylim()),
'{:.0f} mins ({:.0f} hrs rest)'.format(total_mins, rest_hours),
fontsize=fontsize, color='grey',
ha='center', va='center'
)
# Session distance and pace.
footer.text(
np.mean(footer.get_xlim()), np.mean(footer.get_xlim()),
'{:.1f} km @ {} mins/km'.format(total_kms, mean_pace),
fontsize=fontsize, color='grey',
ha='center', va='center'
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment