Skip to content

Commit

Permalink
Replace inner loops on classification data generation with vectorised…
Browse files Browse the repository at this point in the history
… operations

draft 1 replace inner loop on agb_outdoor classification data generation

draft 2 replace internal loops on agb 2023 outdoors

incomplete, old implementation still present

draft 3 complete replacement of inner loops

draft 4 vectorise handicap caluclation for field and indoor agb2023 classifications

draft 5 refactor distance calculations on agb_field to inside dedicate distance function
  • Loading branch information
TomHall2020 committed Dec 17, 2024
1 parent ef4bd0b commit dba0a1e
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 72 deletions.
39 changes: 19 additions & 20 deletions archeryutils/classifications/agb_field_classifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ def _make_agb_field_classification_dict() -> dict[str, GroupData]:
agb_classes_field = agb_classes_info_field["classes"]
agb_classes_field_long = agb_classes_info_field["classes_long"]


# Generate dict of classifications
# loop over all bowstyles, genders, ages
classification_dict = {}
Expand All @@ -90,7 +89,7 @@ def _make_agb_field_classification_dict() -> dict[str, GroupData]:

# Get max dists for category from json file data
# Use metres as corresponding yards >= metric
dists = _assign_dists(bowstyle["bowstyle"], age)
min_dists, max_distance = _assign_dists(bowstyle["bowstyle"], age)

# set step from datum based on age and gender steps required
delta_hc_age_gender = cls_funcs.get_age_gender_step(
Expand All @@ -100,27 +99,18 @@ def _make_agb_field_classification_dict() -> dict[str, GroupData]:
bowstyle["genderStep_field"],
)

classifications_count = len(agb_classes_field)

class_hc = np.empty(classifications_count)

min_dists = np.empty(classifications_count)
min_dists[0:6] = dists[0]
min_dists[6:9] = [max(dists[0] - 10 * i, 30) for i in range(1, 4)]

for i in range(classifications_count):
# Assign handicap for this classification
class_hc[i] = (
bowstyle["datum_field"]
+ delta_hc_age_gender
+ (i - 2) * bowstyle["classStep_field"]
)
# set handicap threshold values for all classifications in the category
class_hc = (
bowstyle["datum_field"]
+ delta_hc_age_gender
+ (np.arange(len(agb_classes_field)) - 2) * bowstyle["classStep_field"]
)

groupdata: GroupData = {
"classes": agb_classes_field,
"classes_long": agb_classes_field_long,
"class_HC": class_hc,
"max_distance": dists[1],
"max_distance": max_distance,
"min_dists": min_dists,
}

Expand Down Expand Up @@ -166,8 +156,17 @@ def _assign_dists(
# U15 All Blue, R/C Red, Others White
# U12 R/C/CL Red, All Blue, All White,
if bowstyle.lower().replace(" ", "") in ("compound", "recurve", "compoundlimited"):
return age["red"]
return age["blue"]
min_d, max_d = age["red"]
else:
min_d, max_d = age["blue"]

n_classes: int = 9 # [EMB, GMB, MB, B1, B2, B3, A1, A2, A3]

min_dists = np.empty(n_classes)
min_dists[0:6] = min_d
min_dists[6:9] = np.maximum(min_d - 10 * np.arange(1, 4), 30)

return min_dists, max_d


agb_field_classifications = _make_agb_field_classification_dict()
Expand Down
16 changes: 6 additions & 10 deletions archeryutils/classifications/agb_indoor_classifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,12 @@ def _make_agb_indoor_classification_dict() -> dict[str, GroupData]:
bowstyle["genderStep_in"],
)

classifications_count = len(agb_classes_in)

class_hc = np.empty(classifications_count)
for i in range(classifications_count):
# Assign handicap for this classification
class_hc[i] = (
bowstyle["datum_in"]
+ delta_hc_age_gender
+ (i - 1) * bowstyle["classStep_in"]
)
# set handicap threshold values for all classifications in the category
class_hc = (
bowstyle["datum_in"]
+ delta_hc_age_gender
+ (np.arange(len(agb_classes_in)) - 1) * bowstyle["classStep_in"]
)

groupdata: GroupData = {
"classes": agb_classes_in,
Expand Down
64 changes: 22 additions & 42 deletions archeryutils/classifications/agb_outdoor_classifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,26 +97,18 @@ def _make_agb_outdoor_classification_dict() -> dict[str, GroupData]:
bowstyle["ageStep_out"],
bowstyle["genderStep_out"],
)
classifications_count = len(agb_classes_out)

class_hc = np.empty(classifications_count)
min_dists = np.empty(classifications_count)

for i in range(classifications_count):
# Assign handicap for this classification
class_hc[i] = (
bowstyle["datum_out"]
+ delta_hc_age_gender
+ (i - 2) * bowstyle["classStep_out"]
)

# Get minimum distance that must be shot for this classification
min_dists[i] = _assign_min_dist(
n_class=i,
gender=gender,
age_group=age["age_group"],
max_dists=max_dist,
)

# set handicap threshold values for all classifications in the category
class_hc = (
bowstyle["datum_out"]
+ delta_hc_age_gender
+ (np.arange(len(agb_classes_out)) - 2) * bowstyle["classStep_out"]
)

# get minimum distances to be shot for all classifications in the category
min_dists = _assign_min_dist(
gender=gender, age_group=age["age_group"], max_dists=max_dist
)

# Assign prestige rounds for the category
prestige_rounds = _assign_outdoor_prestige(
Expand All @@ -141,20 +133,17 @@ def _make_agb_outdoor_classification_dict() -> dict[str, GroupData]:


def _assign_min_dist(
n_class: int,
gender: str,
age_group: str,
max_dists: list[int],
) -> int:
) -> npt.NDArray[int]:
"""
Assign appropriate minimum distance required for a category and classification.
Appropriate for 2023 ArcheryGB age groups and classifications.
Parameters
----------
n_class : int
integer corresponding to classification [0=EMB, 8=A3]
gender : str
string defining gender
age_group : str,
Expand All @@ -164,8 +153,8 @@ def _assign_min_dist(
Returns
-------
min_dist : int
minimum distance [m] required for this classification
min_dists : array of int
minimum distance [m] required for this category
References
----------
Expand All @@ -181,35 +170,26 @@ def _assign_min_dist(
# List of maximum distances for use in assigning maximum distance [metres]
# Use metres because corresponding yards distances are >= metric ones
dists = [90, 70, 60, 50, 40, 30, 20, 15]

# Number of MB categories (distance restrictions superceded by prestige rounds.)
n_mb: int = 3
n_classes: int = 9 # [EMB, GMB, MB, B1, B2, B3, A1, A2, A3]

max_dist_index = dists.index(np.min(max_dists))

# B1 and above
if n_class <= n_mb:
# All MB and B1 require max distance for everyone:
return dists[max_dist_index]

# Below B1
# Age group trickery:
# U16 males and above step down for B2 and beyond
# U16 males and above step down for B2 and less
if gender.lower() in ("male") and age_group.lower().replace(" ", "") in (
"adult",
"50+",
"under21",
"under18",
"under16",
):
return dists[max_dist_index + (n_class - n_mb)]
idxs = np.array([0, 0, 0, 0, 1, 2, 3, 4, 5])

# All other categories require max dist for B1 and B2 then step down
try:
return dists[max_dist_index + (n_class - n_mb) - 1]
except IndexError:
# Distances stack at the bottom end as we can't go below 15m
return dists[-1]
else:
idxs = np.array([0, 0, 0, 0, 0, 1, 2, 3, 4])

return np.take(dists, idxs + max_dist_index, mode="clip")


def _assign_outdoor_prestige(
Expand Down

0 comments on commit dba0a1e

Please sign in to comment.