Created
November 12, 2023 20:42
-
-
Save barrysmyth/fa95aa0cd1aa210f3ceb929bb4a11604 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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