Module: AgeGrading
- Included in:
- Calcpace
- Defined in:
- lib/calcpace/age_grading.rb
Overview
Module for age-grading race performances with a versioned table
Age grading allows fairer comparison across ages by applying an age factor to the raw performance time.
Current scope:
-
Common road distances: 5K, 10K, half marathon, marathon
-
Sex: male/female
-
Age: 18+
-
Data file is versioned and replaceable (‘lib/calcpace/data/wma_2023_road.yml`)
Returned values include:
-
age grade percentage
-
age-graded time
-
open standard time for the selected distance/sex
-
performance category
rubocop:disable Metrics/ModuleLength
Constant Summary collapse
- DATA_PATH =
File.('data/wma_2023_road.yml', __dir__).freeze
- OPEN_STANDARDS_DATA_PATH =
File.('data/wma_2023_open_standards.yml', __dir__).freeze
- WMA_DATA =
YAML.safe_load_file(DATA_PATH, permitted_classes: [], aliases: false).freeze
- OPEN_STANDARDS_DATA =
YAML.safe_load_file(OPEN_STANDARDS_DATA_PATH, permitted_classes: [], aliases: false).freeze
- TABLE_VERSION =
OPEN_STANDARDS_DATA.fetch('meta').fetch('table_version').freeze
- AGE_GRADE_LABELS =
OPEN_STANDARDS_DATA.fetch('age_grade_classifications').map do |entry| { min: entry.fetch('min').to_f, label: entry.fetch('label') } end.freeze
- DISTANCE_TO_METERS =
{ 5.0 => '5000', 10.0 => '10000', 21.0975 => '21097', 42.195 => '42195' }.freeze
- RACE_TO_METERS =
{ '5k' => '5000', '10k' => '10000', 'half_marathon' => '21097', 'marathon' => '42195' }.freeze
- SUPPORTED_DISTANCES_KM =
DISTANCE_TO_METERS.keys.freeze
Instance Method Summary collapse
-
#age_grade(distance_km, time, age:, sex:) ⇒ Hash
Returns a full age-grading report for a race performance.
-
#age_grade_label(percent) ⇒ String
Returns a descriptive label for an age-grade percentage.
-
#age_grade_percent(distance_km, time, age:, sex:) ⇒ Float
Returns only the age-grade percentage.
Instance Method Details
#age_grade(distance_km, time, age:, sex:) ⇒ Hash
Returns a full age-grading report for a race performance
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/calcpace/age_grading.rb', line 58 def age_grade(distance_km, time, age:, sex:) distance_m = normalize_distance(distance_km) seconds = parse_time_seconds(time) age_value = normalize_age(age) sex_value = normalize_sex(sex) check_positive(seconds, 'Time') factor = interpolated_factor(sex_value, age_value, distance_m) age_graded_time = round_up_hundredth(seconds * factor) open_standard = open_standard_seconds(sex_value, distance_m) grade_percent = (open_standard / age_graded_time) * 100.0 rounded_percent = grade_percent.round(1) { age_grade_percent: rounded_percent, category: age_grade_label(rounded_percent), age_graded_time_seconds: age_graded_time, age_graded_time_clock: convert_to_clocktime(age_graded_time), open_standard_seconds: open_standard, open_standard_clock: convert_to_clocktime(open_standard), factor: factor.round(4), table_version: TABLE_VERSION } end |
#age_grade_label(percent) ⇒ String
Returns a descriptive label for an age-grade percentage
99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/calcpace/age_grading.rb', line 99 def age_grade_label(percent) percent_value = begin Float(percent) rescue ArgumentError, TypeError raise ArgumentError, 'Age-grade percent must be a numeric value greater than or equal to 0' end raise ArgumentError, 'Age-grade percent must be greater than or equal to 0' if percent_value.negative? AGE_GRADE_LABELS.find { |entry| percent_value >= entry[:min] }[:label] end |
#age_grade_percent(distance_km, time, age:, sex:) ⇒ Float
Returns only the age-grade percentage
91 92 93 |
# File 'lib/calcpace/age_grading.rb', line 91 def age_grade_percent(distance_km, time, age:, sex:) age_grade(distance_km, time, age: age, sex: sex)[:age_grade_percent] end |