Running Metrics¶
Get best efforts (fastest times), best heart rate, elevation etc for a list of distances or durations within an activity file.
Use the WorkoutData class¶
Load the file into a WorkoutData instance:
import chironpy
from chironpy import WorkoutData
print('Loading example marathon data from FIT file...')
example1 = chironpy.examples(path="18360138543_ACTIVITY_Osaka_Marathon_2025.fit")
data1 = WorkoutData.from_file(example1)
print('\nWorkoutData from FIT file:')
print(data1.columns)
print(data1.head())
Outputs:
WorkoutData from FIT file:
Index(['latitude', 'longitude', 'distance', 'unknown_87', 'heartrate',
'cadence', 'temperature', 'fractional_cadence', 'unknown_135',
'unknown_136', 'record_sequence', 'unknown_90', 'session', 'lap',
'speed', 'elevation', 'time', 'is_moving', 'grade'],
dtype='object')
latitude longitude distance unknown_87 heartrate ... speed elevation time is_moving grade
datetime ...
2025-02-24 00:15:21+00:00 34.686225 135.520607 0.000 146.0 80.0 ... 1.7640 23.40 0 True 0.000000
2025-02-24 00:15:22+00:00 34.686248 135.520601 2.572 116.8 80.8 ... 1.8778 23.36 1 True -0.029455
2025-02-24 00:15:23+00:00 34.686270 135.520595 5.144 87.6 81.6 ... 1.9916 23.32 2 True -0.029933
2025-02-24 00:15:24+00:00 34.686293 135.520589 7.716 58.4 82.4 ... 2.1054 23.28 3 True -0.030411
2025-02-24 00:15:25+00:00 34.686316 135.520583 10.288 29.2 83.2 ... 2.2192 23.24 4 True -0.030890
[5 rows x 19 columns]
Compute fastest efforts, maximum average heart rate etc over a range of distances within the workout:
distances = [1000, 5000, 10000, 21100]
fastest1 = data1.fastest_distance_intervals(distances)
best_hr1 = data1.best_distance_intervals(distances, 'heartrate')
print(f'\n####\nBest efforts over {distances} meters for {example1}')
for i, best in enumerate(fastest1):
if best is None:
pace = None
else:
pace = best['value']/60 / distances[i] * 1000
print(str(distances[i] / 1000) + 'km', pace, best, "heartrate:", best_hr1[i])
Outputs:
####
Best efforts over [1000, 5000, 10000, 21100] meters for chironpy/examples/data/18360138543_ACTIVITY_Osaka_Marathon_2025.fit
1.0km 3.1666666666666665 {'value': 190, 'distance': 1000, 'speed': 5.2631578947368425, 'start_index': 5845, 'stop_index': 6035} heartrate: {'value': 194.14252336448598, 'start_index': 3896, 'stop_index': 4110}
5.0km 3.356666666666667 {'value': 1007, 'distance': 5000, 'speed': 4.965243296921549, 'start_index': 574, 'stop_index': 1581} heartrate: {'value': 192.1921606118547, 'start_index': 3540, 'stop_index': 4586}
10.0km 3.405 {'value': 2043, 'distance': 10000, 'speed': 4.894762604013706, 'start_index': 483, 'stop_index': 2526} heartrate: {'value': 191.01557259223767, 'start_index': 2562, 'stop_index': 4649}
21.1km 3.4368088467614535 {'value': 4351, 'distance': 21100, 'speed': 4.849459894277178, 'start_index': 156, 'stop_index': 4507} heartrate: {'value': 188.79730962152303, 'start_index': 2733, 'stop_index': 7119}
Or compute fastest efforts, max average heart rate etc over a range of durations:
durations = [30, 60, 120, 180, 300, 600, 1200, 1800, 3600]
fastest1 = data1.best_intervals(durations, 'speed')
best_hr1 = data1.best_intervals(durations, 'heartrate')
print(f'\n####\nBest efforts over {durations} seconds for {example1}')
for i, best in enumerate(fastest1):
if best is None:
pace = None
else:
pace = best['value']/60 / durations[i] * 1000
print(str(durations[i] / 60) + 'min', pace, best, "heartrate:", best_hr1[i])
best_20min_hr = data1.best_intervals(1200, 'heartrate')[0]
print(f'\n####\nBest 20min heartrate for {example1}: {best_20min_hr} bpm')
best_5k_hr = data1.fastest_distance_intervals(5000, 'heartrate')[0]
print(f'Best 5k heartrate for {example1}: {best_5k_hr} bpm')
Outputs:
####
Best efforts over [30, 60, 120, 180, 300, 600, 1200, 1800, 3600] seconds for chironpy/examples/data/18360138543_ACTIVITY_Osaka_Marathon_2025.fit
0.5min 2.948777777777778 {'value': 5.3078, 'start_index': 759, 'stop_index': 789} heartrate: {'value': 200.1, 'start_index': 4073, 'stop_index': 4103}
1.0min 1.4249706018518515 {'value': 5.1298941666666655, 'start_index': 745, 'stop_index': 805} heartrate: {'value': 199.125, 'start_index': 4041, 'stop_index': 4101}
2.0min 0.7000248842592592 {'value': 5.040179166666666, 'start_index': 500, 'stop_index': 620} heartrate: {'value': 196.74166666666667, 'start_index': 4018, 'stop_index': 4138}
3.0min 0.4646144547325102 {'value': 5.0178361111111105, 'start_index': 442, 'stop_index': 622} heartrate: {'value': 194.50277777777777, 'start_index': 3957, 'stop_index': 4137}
5.0min 0.27489444444444444 {'value': 4.9481, 'start_index': 262, 'stop_index': 562} heartrate: {'value': 193.435, 'start_index': 3849, 'stop_index': 4149}
10.0min 0.13661450462962962 {'value': 4.918122166666667, 'start_index': 230, 'stop_index': 830} heartrate: {'value': 192.84483333333333, 'start_index': 3540, 'stop_index': 4140}
20.0min 0.0670034201388889 {'value': 4.824246250000001, 'start_index': 4395, 'stop_index': 5595} heartrate: {'value': 192.175, 'start_index': 3373, 'stop_index': 4573}
30.0min 0.044466041666666664 {'value': 4.8023324999999994, 'start_index': 3966, 'stop_index': 5766} heartrate: {'value': 191.59044444444444, 'start_index': 2720, 'stop_index': 4520}
60.0min 0.021955931712962964 {'value': 4.742481250000001, 'start_index': 2865, 'stop_index': 6465} heartrate: {'value': 189.61708333333334, 'start_index': 2137, 'stop_index': 5737}
####
Best 20min heartrate for chironpy/examples/data/18360138543_ACTIVITY_Osaka_Marathon_2025.fit: {'value': 192.175, 'start_index': 3373, 'stop_index': 4573} bpm
Best 5k heartrate for chironpy/examples/data/18360138543_ACTIVITY_Osaka_Marathon_2025.fit: None bpm
Elevation gain:
print(f'Total elevation gain for {example1}: {data1.elevation_gain()} m')
Outputs:
Total elevation gain for chironpy/examples/data/18360138543_ACTIVITY_Osaka_Marathon_2025.fit: 286.0000000000019 m
Analyse workout file directly¶
import os
import chironpy
example = chironpy.examples(path="18360138543_ACTIVITY_Osaka_Marathon_2025.fit")
data = chironpy.read_file(example.path, resample=True, interpolate=True)
distances = [1000, 1500, 1608, 3000, 5000, 10000, 21100, 30000, 42200] # in metres
bests = chironpy.metrics.speed.multiple_fastest_distance_intervals(
data['distance'],
windows=distances
)
print(f'best efforts over {distances}m for {example}')
for i, best in enumerate(bests):
pace = best['value']/60 / distances[i] * 1000 # min/km
print(str(distances[i]/1000) + 'km', pace, best)
Outputs:
best efforts over [1000, 5000, 10000, 21100] for chironpy/examples/data/18360138543_ACTIVITY_Osaka_Marathon_2025.fit
1.0km 3.1666666666666665 {'value': 190, 'start_index': 5845, 'stop_index': 6035}
5.0km 3.356666666666667 {'value': 1007, 'start_index': 574, 'stop_index': 1581}
10.0km 3.405 {'value': 2043, 'start_index': 483, 'stop_index': 2526}
21.1km 3.4368088467614535 {'value': 4351, 'start_index': 156, 'stop_index': 4507}