Spring 2026 Midterm 1: The Impact of Skategate on Figure Skating

Version 1.0.0

All of the header information is important. Please read it..

Topics number of exercises: This problem builds on your knowledge of building and manipulating data structures, math as code, string parsing and cleansing. It has 9 exercises numbered 0 to 8. There are 17 available points. However to earn 100% the threshold is 12 points. (Therefore once you hit 12 points you can stop. There is no extra credit for exceeding this threshold.)

Exercise ordering: Each exercise builds logically on previous exercises but you may solve them in any order. That is if you can't solve an exercise you can still move on and try the next one. Use this to your advantage as the exercises are not necessarily ordered in terms of difficulty. Higher point values generally indicate more difficult exercises.

Demo cells: Code cells starting with the comment ### Run Me!!! load results from prior exercises applied to the entire data set and use those to build demo inputs. These must be run for subsequent demos to work properly but they do not affect the test cells. The data loaded in these cells may be rather large (at least in terms of human readability). You are free to print or otherwise use Python to explore them but we may not print them in the starter code.

Debugging your code: Right before each exercise test cell there is a block of text explaining the variables available to you for debugging. You may use these to test your code and can print/display them as needed (careful when printing large objects you may want to print the head or chunks of rows at a time).

Exercise point breakdown:

  • Exercise 0 - : 2 point(s)

  • Exercise 1 - : 2 point(s)

  • Exercise 2 - : 2 point(s)

  • Exercise 3 - : 1 point(s)

  • Exercise 4 - : 1 point(s)

  • Exercise 5 - : 1 point(s)

  • Exercise 6 - : 3 point(s)

  • Exercise 7 - : 3 point(s)

  • Exercise 8 - : 2 point(s)

Final reminders:

  • Submit after every exercise
  • Review the generated grade report after you submit to see what errors were returned
  • Stay calm, skip problems as needed and take short breaks at your leisure
In [1]:
### Global imports
import dill
from cse6040_devkit import plugins, utils
from cse6040_devkit.training_wheels import run_with_timeout, suppress_stdout
import tracemalloc
from time import time
import re 
import pandas as pd

Task Overview

As the 2026 Winter Olympics conclude, let us investigate the scandalous history of one of the sports: figure skating. We will determine how the changes to its scoring system have impacted athlete performance in the quest for judging fairness. The following exercises will:

  • Cleanse and standardize skater name data
  • Determine winners under the old scoring system
  • Understand how the "Skategate" scandal happened
  • Compute scores under the new, complex scoring system
  • Evaluate whether judges' scores accurately reflect the technical difficulty of skating programs under the new system

This exam also includes two debugging exercises:

The function names of these exercises begin with DEBUG_. The provided solutions to these exercises contain errors. You must edit the provided solution code to pass the test cell and autograder.

Exercise 0: (2 points)

cleanse_skater_names

Your task: define cleanse_skater_names as follows:

To begin, let's clean up our list of skater names, so they are standardized for future exercises. For each name in skaters, extract and cleanse the last name.

Input: skaters: A list of skater names. For example, the same skater with first name Kevin and last name Van Der Perren will be in one of the three following formats:

  • Kevin VAN DER PERREN
  • VAN DER PERREN Kevin
  • Van Der Perren, Kevin

You would return Van Der Perren as your result.

Return: cleansed_skaters: A list of the last names found in skaters with the first letter of any words found in the last name capitalized.

Requirements:

  • Return the names in your result in the same order as the input
  • Last names and first names may contain multiple words. Your solution must handle such cases, returning all words within the last name

Hint: Regular Expressions are not required to solve this exercise!

In [2]:
### Solution - Exercise 0  
def cleanse_skater_names(skaters: list) -> list:
    ### BEGIN SOLUTION
    cleaned_names = []
    for name in skaters:
        if ',' in name:
            last_name, _ = name.split(',')
        else:
            words = name.split()
            last_name = ' '.join([word for word in words if word.isupper()])
        cleaned_names.append(last_name.title())

    return cleaned_names
    ### END SOLUTION

### Demo function call
cleanse_skater_names_demo = ['Evan LYSACEK', 'PETERS Evan', 'Hanyu, Yuzuru',
                             'Kevin VAN DER PERREN', 'Ingrid Charlotte WOLTER', 'PLUSHENKO Evgeni']
demo_result_cleanse_skater_names = cleanse_skater_names(cleanse_skater_names_demo)
print(demo_result_cleanse_skater_names)
['Lysacek', 'Peters', 'Hanyu', 'Van Der Perren', 'Wolter', 'Plushenko']

The demo should display this printed output.

['Lysacek', 'Peters', 'Hanyu', 'Van Der Perren', 'Wolter', 'Plushenko']


The cell below will test your solution for cleanse_skater_names (exercise 0). The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. Any key:value pair in original_input_vars should also exist in input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [3]:
### Test Cell - Exercise 0  


from cse6040_devkit.tester_fw.testers import Tester
from yaml import safe_load
from time import time

tracemalloc.start()
mem_start, peak_start = tracemalloc.get_traced_memory()
print(f"initial memory usage: {mem_start/1024/1024:.2f} MB")

# Load testing utility
with open('resource/asnlib/publicdata/execute_tests', 'rb') as f:
    executor = dill.load(f)

@run_with_timeout(error_threshold=200.0, warning_threshold=100.0)
@suppress_stdout
def execute_tests(**kwargs):
    return executor(**kwargs)


# Execute test
start_time = time()
passed, test_case_vars, e = execute_tests(func=cleanse_skater_names,
              ex_name='cleanse_skater_names',
              key=b'XXLjq9aUPdzwNjsrPPGTZkd4dNWqkqvfmeLkcL7ZQmg=', 
              n_iter=201)
# Assign test case vars for debugging
input_vars, original_input_vars, returned_output_vars, true_output_vars = test_case_vars
duration = time() - start_time
print(f"Test duration: {duration:.2f} seconds")
current_memory, peak_memory = tracemalloc.get_traced_memory()
print(f"memory after test: {current_memory/1024/1024:.2f} MB")
print(f"memory peak during test: {peak_memory/1024/1024:.2f} MB")
tracemalloc.stop()
if e: raise e
assert passed, 'The solution to cleanse_skater_names did not pass the test.'

###
### AUTOGRADER TEST - DO NOT REMOVE
###

print('Passed! Please submit.')
initial memory usage: 0.00 MB
Test duration: 0.17 seconds
memory after test: 3.09 MB
memory peak during test: 4.43 MB
Passed! Please submit.

Exercise 1: (2 points)

compute_ranking

Your task: define compute_ranking as follows:

Before 2004, judges ranked each skater on a scale from 0.0 to 6.0 in 0.1 increments across two areas: technical merit and presentation.

Let's replicate the old scoring system in this exercise: we will take in a dictionary containing the scores each skater received by the panel of judges for their technical merit and presentation.

Then, for each skater we will sum the two scores received by each judge and rank the skaters from highest to lowest by judge. In the subsequent exercise we will use the result of this exercise to determine the winner.

Input: scores: A dictionary containing the skater names as keys with their values being a list of list of scores received by the judges for technical merit and presentation.

In the example below, Plushenko received a technical merit score of 5.7 and a presentation score of 5.2 from the first judge (J0). He received a technical merit score of 5.8 and a presentation score of 5.3 from the second judge (J1).

scores = {
    'Plushenko': [[5.7, 5.8, 6.0],[5.2, 5.3, 6.0]],
    'Goebel': [[5.6, 5.0, 5.6],[5.2, 5.4, 5.4]],
    'Honda': [[5.7, 5.2, 5.6],[5.1, 5.4, 5.4]],
    'Yagudin': [[5.9, 5.9, 5.9],[5.3, 5.4, 5.5]]
    }

Return: ranking_by_judge: A dictionary containing the judge as the key (J followed by the index position of the judge) with their values being a list of tuples containing the skater name and total score received by the judge. You must order the list of tuples in descending order by the total score, with ties broken by the skater's name in ascending alphabetical order. For the demo input, we would return:

ranking_by_judge = {
    'J0': [('Yagudin', 11.2),('Plushenko', 10.9),('Goebel', 10.8),('Honda', 10.8)],
    'J1': [('Yagudin', 11.3),('Plushenko', 11.1),('Honda', 10.6),('Goebel', 10.4)],
    'J2': [('Plushenko', 12.0),('Yagudin', 11.4),('Goebel', 11.0),('Honda', 11.0)]
    }

Requirements:

  1. Sum the scores received by a judge for technical merit and presentation for each skater. Round the result to two decimal places
  2. Create a new dictionary with keys for each judge of the format J followed by the index position of the judge in the input list scores. For example, the judge in the 0th index position should be named J0
  3. The values in your new dictionary should be a list of tuples containing the skater name and summed score, where each list is sorted in descending order by score, with ties broken in ascending order by skater name

Note: There could be any number of judges and any number of skaters. Your solution must dynamically handle this. You must infer the number of judges from the length of the lists provided in scores.

In [4]:
### Solution - Exercise 1  
def compute_ranking(scores: dict) -> dict:
    ### BEGIN SOLUTION
    judge_dict = {}
    skaters_list = list(scores.keys())
    num_of_judges = len(scores[skaters_list[0]][0])
    
    for j in range(num_of_judges):
        j_scores = []
        for skater in scores:
            j_scores.append((skater, round(scores[skater][0][j] + scores[skater][1][j], 2)))
        j_scores.sort(key=lambda x: (-x[1], x[0]))
        judge_dict['J'+str(j)] = j_scores
        
    return judge_dict
    ### END SOLUTION

### Demo function call
compute_ranking_demo = {
    'Plushenko': [[5.7, 5.8, 6.0],[5.2, 5.3, 6.0]],
    'Goebel': [[5.6, 5.0, 5.6],[5.2, 5.4, 5.4]],
    'Honda': [[5.7, 5.2, 5.6],[5.1, 5.4, 5.4]],
    'Yagudin': [[5.9, 5.9, 5.9],[5.3, 5.4, 5.5]]
}
demo_result_compute_ranking = compute_ranking(compute_ranking_demo)
print(demo_result_compute_ranking)
{'J0': [('Yagudin', 11.2), ('Plushenko', 10.9), ('Goebel', 10.8), ('Honda', 10.8)], 'J1': [('Yagudin', 11.3), ('Plushenko', 11.1), ('Honda', 10.6), ('Goebel', 10.4)], 'J2': [('Plushenko', 12.0), ('Yagudin', 11.4), ('Goebel', 11.0), ('Honda', 11.0)]}

The demo should display this printed output.

{'J0': [('Yagudin', 11.2), ('Plushenko', 10.9), ('Goebel', 10.8), ('Honda', 10.8)], 'J1': [('Yagudin', 11.3), ('Plushenko', 11.1), ('Honda', 10.6), ('Goebel', 10.4)], 'J2': [('Plushenko', 12.0), ('Yagudin', 11.4), ('Goebel', 11.0), ('Honda', 11.0)]}


The cell below will test your solution for compute_ranking (exercise 1). The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. Any key:value pair in original_input_vars should also exist in input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [5]:
### Test Cell - Exercise 1  


from cse6040_devkit.tester_fw.testers import Tester
from yaml import safe_load
from time import time

tracemalloc.start()
mem_start, peak_start = tracemalloc.get_traced_memory()
print(f"initial memory usage: {mem_start/1024/1024:.2f} MB")

# Load testing utility
with open('resource/asnlib/publicdata/execute_tests', 'rb') as f:
    executor = dill.load(f)

@run_with_timeout(error_threshold=200.0, warning_threshold=100.0)
@suppress_stdout
def execute_tests(**kwargs):
    return executor(**kwargs)


# Execute test
start_time = time()
passed, test_case_vars, e = execute_tests(func=compute_ranking,
              ex_name='compute_ranking',
              key=b'XXLjq9aUPdzwNjsrPPGTZkd4dNWqkqvfmeLkcL7ZQmg=', 
              n_iter=202)
# Assign test case vars for debugging
input_vars, original_input_vars, returned_output_vars, true_output_vars = test_case_vars
duration = time() - start_time
print(f"Test duration: {duration:.2f} seconds")
current_memory, peak_memory = tracemalloc.get_traced_memory()
print(f"memory after test: {current_memory/1024/1024:.2f} MB")
print(f"memory peak during test: {peak_memory/1024/1024:.2f} MB")
tracemalloc.stop()
if e: raise e
assert passed, 'The solution to compute_ranking did not pass the test.'

###
### AUTOGRADER TEST - DO NOT REMOVE
###

print('Passed! Please submit.')
initial memory usage: 0.00 MB
Test duration: 0.50 seconds
memory after test: 0.15 MB
memory peak during test: 2.56 MB
Passed! Please submit.

Exercise 2: (2 points)

determine_winner

Your task: define determine_winner as follows:

Now let's understand how they determined the winners under the old scoring system. They used an ordinal ranking system, in which the final result is not determined by summing or averaging the scores, but by looking at the ranking (1st, 2nd, 3rd) assigned by each judge to each skater. The skater with the majority of the ranking wins. Check out an example below:

ranking_by_judge = {
    'J0': [('Yagudin', 11.2),('Plushenko', 10.9),('Goebel', 10.8),('Honda', 10.8)],
    'J1': [('Yagudin', 11.3),('Plushenko', 11.1),('Honda', 10.6),('Goebel', 10.4)],
    'J2': [('Plushenko', 12.0),('Yagudin', 11.4),('Goebel', 11.0),('Honda', 11.0)]
    }

If we received this ranking_by_judge, Yagudin would be the winner, as he received 2 out of the 3 first place votes.

Input: ranking_by_judge: A dictionary containing the judge name as the key with their values being a list of tuples containing the skater name and total score received by the judge ranked from highest to lowest. For each judge, the skater listed in the 0th index position in the list received the first place vote.

Return: winner: The name of the skater who achieved the majority of the first place votes. In the event a majority is not achieved, return the string 'No Clear Winner'

Requirements:

  1. Determine if any skater received a majority of first place votes. A majority is defined as over half of the first place votes
  2. For each judge, the skater listed in the 0th index position in the list received the first place vote
  3. If a majority was achieved, return that skater's name as your result
  4. If a majority was not achieved, return the string 'No Clear Winner'. For example, if there are 6 judges, but the skater with the highest number of first place votes only received 3 first place votes, you should return 'No Clear Winner' as a majority was not achieved
In [6]:
### Solution - Exercise 2  
def determine_winner(ranking_by_judge: dict) -> str:
    ### BEGIN SOLUTION
    first_place_votes = []
    for k, v in ranking_by_judge.items():
        first_place_votes.append(v[0][0])
    
    from collections import Counter
    
    counts = Counter(first_place_votes)
    counts_sorted = counts.most_common()
    
    if counts_sorted[0][1] > len(first_place_votes)/2:
        return counts_sorted[0][0]
    else:
        return 'No Clear Winner'
    ### END SOLUTION
    
### Demo function call
determine_winner_demo = {
'J0': [('Yagudin', 11.2),('Plushenko', 10.9),('Goebel', 10.8),('Honda', 10.8)],
'J1': [('Yagudin', 11.3),('Plushenko', 11.1),('Honda', 10.6),('Goebel', 10.4)],
'J2': [('Plushenko', 12.0),('Yagudin', 11.4),('Goebel', 11.0),('Honda', 11.0)]
}
demo_result_determine_winner = determine_winner(determine_winner_demo)
print(demo_result_determine_winner)
Yagudin

The demo should display this printed output.

Yagudin


The cell below will test your solution for determine_winner (exercise 2). The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. Any key:value pair in original_input_vars should also exist in input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [7]:
### Test Cell - Exercise 2  


from cse6040_devkit.tester_fw.testers import Tester
from yaml import safe_load
from time import time

tracemalloc.start()
mem_start, peak_start = tracemalloc.get_traced_memory()
print(f"initial memory usage: {mem_start/1024/1024:.2f} MB")

# Load testing utility
with open('resource/asnlib/publicdata/execute_tests', 'rb') as f:
    executor = dill.load(f)

@run_with_timeout(error_threshold=200.0, warning_threshold=100.0)
@suppress_stdout
def execute_tests(**kwargs):
    return executor(**kwargs)


# Execute test
start_time = time()
passed, test_case_vars, e = execute_tests(func=determine_winner,
              ex_name='determine_winner',
              key=b'XXLjq9aUPdzwNjsrPPGTZkd4dNWqkqvfmeLkcL7ZQmg=', 
              n_iter=202)
# Assign test case vars for debugging
input_vars, original_input_vars, returned_output_vars, true_output_vars = test_case_vars
duration = time() - start_time
print(f"Test duration: {duration:.2f} seconds")
current_memory, peak_memory = tracemalloc.get_traced_memory()
print(f"memory after test: {current_memory/1024/1024:.2f} MB")
print(f"memory peak during test: {peak_memory/1024/1024:.2f} MB")
tracemalloc.stop()
if e: raise e
assert passed, 'The solution to determine_winner did not pass the test.'

###
### AUTOGRADER TEST - DO NOT REMOVE
###

print('Passed! Please submit.')
initial memory usage: 0.00 MB
Test duration: 0.10 seconds
memory after test: 0.11 MB
memory peak during test: 1.89 MB
Passed! Please submit.

Exercise 3: (1 points)

DEBUG_calc_avg_score

Your task: define DEBUG_calc_avg_score as follows:

Note: This is a debugging exercise!

Let us now compare the ordinal ranking system detailed in the prior exercise to a simplistic scoring system in which we compute the average score for a skater from the judge's scores. You will notice that if you compare the demo result in this exercise to the demo result in the prior exercise, Plushenko would have won if an average score system like the one in this exercise was used!

Input: scores: A dictionary containing the skater names as keys with their values being a list of list of scores received by the judges for technical merit and presentation.

In the example below, Plushenko received a technical merit score of 5.7 and a presentation score of 5.2 from the first judge. He received a technical merit score of 5.8 and a presentation score of 5.3 from the second judge.

scores = {
    'Plushenko': [[5.7, 5.8, 6.0],[5.2, 5.3, 6.0]],
    'Goebel': [[5.6, 5.0, 5.6],[5.2, 5.4, 5.4]],
    'Honda': [[5.7, 5.2, 5.6],[5.3, 5.1, 5.7]],
    'Adams': [[5.7, 5.2, 5.6],[5.3, 5.1, 5.7]],
    'Yagudin': [[5.9, 5.9, 5.9],[5.3, 5.4, 5.5]]
}

Return: avg_score_by_skater: A list of tuples, with each tuple containing the name of the skater followed by their average techinal score + their average presentation score. You must order your result in descending order by the total score, with ties broken by the skater's name in ascending alphabetical order. Round the total average score to two decimal places.

Requirements:

  1. This is a debugging exercise! You are free to delete the provided code and write your own solution if you wish though.
  2. One or more issues exist in the solution provided below. You must identify them and correct them to pass the test cell and autograder
In [8]:
### Solution - Exercise 3  
def DEBUG_calc_avg_score(scores: dict) -> list:
    ### BEGIN SOLUTION
    summed_scores = []
    for key, value in scores.items():
        technical = sum(value[0]) / len(value[0])
        presentation = sum(value[1]) / len(value[1])
        total_score = round(technical + presentation, 2)
        summed_scores.append((key, total_score))
        
    return sorted(summed_scores, key=lambda x: (-x[1], x[0]))
    ### END SOLUTION

### Demo function call
calc_avg_score_demo = {
'Plushenko': [[5.7, 5.8, 6.0],[5.2, 5.3, 6.0]],
'Goebel': [[5.6, 5.0, 5.6],[5.2, 5.4, 5.4]],
'Honda': [[5.7, 5.2, 5.6],[5.3, 5.1, 5.7]],
'Adams': [[5.7, 5.2, 5.6],[5.3, 5.1, 5.7]],
'Yagudin': [[5.9, 5.9, 5.9],[5.3, 5.4, 5.5]]
}
demo_result_calc_avg_score = DEBUG_calc_avg_score(calc_avg_score_demo)
print(demo_result_calc_avg_score)
[('Plushenko', 11.33), ('Yagudin', 11.3), ('Adams', 10.87), ('Honda', 10.87), ('Goebel', 10.73)]

The demo should display this printed output.

[('Plushenko', 11.33), ('Yagudin', 11.3), ('Adams', 10.87), ('Honda', 10.87), ('Goebel', 10.73)]


The cell below will test your solution for DEBUG_calc_avg_score (exercise 3). The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. Any key:value pair in original_input_vars should also exist in input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [9]:
### Test Cell - Exercise 3  


from cse6040_devkit.tester_fw.testers import Tester
from yaml import safe_load
from time import time

tracemalloc.start()
mem_start, peak_start = tracemalloc.get_traced_memory()
print(f"initial memory usage: {mem_start/1024/1024:.2f} MB")

# Load testing utility
with open('resource/asnlib/publicdata/execute_tests', 'rb') as f:
    executor = dill.load(f)

@run_with_timeout(error_threshold=200.0, warning_threshold=100.0)
@suppress_stdout
def execute_tests(**kwargs):
    return executor(**kwargs)


# Execute test
start_time = time()
passed, test_case_vars, e = execute_tests(func=DEBUG_calc_avg_score,
              ex_name='DEBUG_calc_avg_score',
              key=b'XXLjq9aUPdzwNjsrPPGTZkd4dNWqkqvfmeLkcL7ZQmg=', 
              n_iter=201)
# Assign test case vars for debugging
input_vars, original_input_vars, returned_output_vars, true_output_vars = test_case_vars
duration = time() - start_time
print(f"Test duration: {duration:.2f} seconds")
current_memory, peak_memory = tracemalloc.get_traced_memory()
print(f"memory after test: {current_memory/1024/1024:.2f} MB")
print(f"memory peak during test: {peak_memory/1024/1024:.2f} MB")
tracemalloc.stop()
if e: raise e
assert passed, 'The solution to DEBUG_calc_avg_score did not pass the test.'

###
### AUTOGRADER TEST - DO NOT REMOVE
###

print('Passed! Please submit.')
initial memory usage: 0.00 MB
Test duration: 0.17 seconds
memory after test: 0.04 MB
memory peak during test: 1.79 MB
Passed! Please submit.

What was Skategate?

The 2002 Winter Olympics "Skategate" scandal involved a judging scandal in pairs figure skating where Russian skaters were awarded gold over Canada, despite a stronger performance by the Canadians.

The Russian team received first place votes from five of the nine judges, giving them the gold medal under the ordinal ranking majority-based system. It was later determined that the French judge was pressured by her federation to vote for the Russian pair in exchange for votes for the French ice dancing team.

The scandal led to the abandonment of the subjective "6.0" judging system we have been working with so far, and the creation of a new, technical, transparent, and infinitely more complex ISU Judging System (IJS). The remainder of the exercises will focus on the new scoring system.

Exercise 4: (1 points)

IJS__FREE

Example: we have defined IJS__FREE as follows:

This is a free exercise!

Please run the test cell below to collect your FREE point!

The next three exercises focus on calculating the components that form the total element score under the new scoring system. The total element score considers the technical elements executed in the skater's program. In particular, how difficult each is and how well the judges feel they are executed. Check out a sample scoring sheet below - we are going to work with the highlighted items.

In [10]:
### Solution - Exercise 4  
def IJS__FREE(score_sheet: str):
    print("See IJS sample score sheet below:")

### Demo function call
score_sheet="./resource/asnlib/publicdata/sample_score_sheet.JPG"
IJS__FREE(score_sheet)
See IJS sample score sheet below:

Sample Score Sheet Under IJS



The test cell below will always pass. Please submit to collect your free points for IJS__FREE (exercise 4).

In [11]:
### Test Cell - Exercise 4  


print('Passed! Please submit.')
Passed! Please submit.

Exercise 5: (1 points)

get_element_scores

Your task: define get_element_scores as follows:

Under the new ISU Judging System (IJS), any element that could be executed by skaters is assigned a base score. Skaters can then know the technical value of what their skating program is worth before they even skate. Let's construct a simple dictionary that we can use in future exercises to compute skaters' base scores under the new system.

Input: elements: A dictionary containing the possible elements of a figure skating program, divided into jumps vs. spins and step sequences. Each element contains its abbreviated code name and its potential point value(s). Some elements have degrees of difficulties (ranging from LB to L4) and can face penalties if not executed correctly.

Return: base_value_dict: A consolidated dictionary containing the code names of the jumps, spins, and step sequences found in elements as the keys. The value for each key should be a list with the first element containing the point value and the second element of the list containing either 'j' for a jump or 's' for a spin or sequence.

Requirements:

  • Iterate over the elements within the dictionary elements finding the code for each. For example, a Double Toe Loop has code name 2T
  • For jumps:
    • Extract the SOV point value
    • Second element in your returned list should be the string 'j'
  • For spins and step sequences:
    • Extract the L4 point value if available. Else, extract the LB value
    • Second element in your returned list should be the string 's'
  • All point values should be of type float
In [12]:
### Solution - Exercise 5  
def get_element_scores(elements: dict) -> dict:
    ### BEGIN SOLUTION
    from collections import defaultdict
    base_values = defaultdict(lambda: defaultdict(list))
    for key_outer, value_outer in elements.items():
        for key_inner, value_inner in value_outer.items():
            if key_outer == 'jumps':
                element_code = value_inner['code']
                element_score = value_inner['SOV']
                base_values[element_code] = [float(element_score), 'j'] 
            if key_outer == 'spins and step seq':
                element_code = value_inner['code']
                if 'L4' in value_inner.keys():
                    element_score = value_inner['L4']
                else:
                    element_score = value_inner['LB']
                base_values[element_code] = [float(element_score), 's']
    return dict(base_values)
    ### END SOLUTION

### Demo function call
get_element_scores_demo = {'jumps':
          {'Double Toe Loop': {'code':'2T', 'SOV': '1.3', 'single penalty':'1.04'},
           'Double Salchow': {'code':'2S', 'SOV': '1.3', 'single penalty':'1.04'},
           'Double Loop': {'code':'2Lo', 'SOV': '1.7', 'single penalty':'1.36'},
           'Double Flip': {'code':'2F', 'SOV': '1.8', 'single penalty':'1.44', 'double_penalty':'1.08'},
           'Double Lutz': {'code':'2Lz', 'SOV': '2.1', 'single penalty':'1.68', 'double_penalty':'1.26'},
           'Double Axel': {'code':'2A', 'SOV': '3.3', 'single penalty':'2.64'},
           'Triple Toe Loop': {'code':'3T', 'SOV': '4.2', 'single penalty':'3.36'}
           },
        'spins and step seq':
         {'Upright Spin':  {'code':'USp', 'LB': '1.0', 'L1':'1.2', 'L2':'1.5', 'L3':'1.9', 'L4':'2.4'},
          'Layback Spin':  {'code':'LSp', 'LB': '1.2', 'L1':'1.5', 'L2':'1.9', 'L3':'2.4', 'L4':'2.7'},
          'Camel Spin':  {'code':'CSp', 'LB': '1.1', 'L1':'1.4', 'L2':'1.8', 'L3':'2.3', 'L4':'2.6'},
          'Choreographic Sequence':  {'code':'ChSq', 'LB': '3.0'}
         }
        }
demo_result_get_element_scores = get_element_scores(get_element_scores_demo)
print(demo_result_get_element_scores)
{'2T': [1.3, 'j'], '2S': [1.3, 'j'], '2Lo': [1.7, 'j'], '2F': [1.8, 'j'], '2Lz': [2.1, 'j'], '2A': [3.3, 'j'], '3T': [4.2, 'j'], 'USp': [2.4, 's'], 'LSp': [2.7, 's'], 'CSp': [2.6, 's'], 'ChSq': [3.0, 's']}

The demo should display this printed output.

{'2T': [1.3, 'j'], '2S': [1.3, 'j'], '2Lo': [1.7, 'j'], '2F': [1.8, 'j'], '2Lz': [2.1, 'j'], '2A': [3.3, 'j'], '3T': [4.2, 'j'], 'USp': [2.4, 's'], 'LSp': [2.7, 's'], 'CSp': [2.6, 's'], 'ChSq': [3.0, 's']}


The cell below will test your solution for get_element_scores (exercise 5). The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. Any key:value pair in original_input_vars should also exist in input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [13]:
### Test Cell - Exercise 5  


from cse6040_devkit.tester_fw.testers import Tester
from yaml import safe_load
from time import time

tracemalloc.start()
mem_start, peak_start = tracemalloc.get_traced_memory()
print(f"initial memory usage: {mem_start/1024/1024:.2f} MB")

# Load testing utility
with open('resource/asnlib/publicdata/execute_tests', 'rb') as f:
    executor = dill.load(f)

@run_with_timeout(error_threshold=200.0, warning_threshold=100.0)
@suppress_stdout
def execute_tests(**kwargs):
    return executor(**kwargs)


# Execute test
start_time = time()
passed, test_case_vars, e = execute_tests(func=get_element_scores,
              ex_name='get_element_scores',
              key=b'XXLjq9aUPdzwNjsrPPGTZkd4dNWqkqvfmeLkcL7ZQmg=', 
              n_iter=201)
# Assign test case vars for debugging
input_vars, original_input_vars, returned_output_vars, true_output_vars = test_case_vars
duration = time() - start_time
print(f"Test duration: {duration:.2f} seconds")
current_memory, peak_memory = tracemalloc.get_traced_memory()
print(f"memory after test: {current_memory/1024/1024:.2f} MB")
print(f"memory peak during test: {peak_memory/1024/1024:.2f} MB")
tracemalloc.stop()
if e: raise e
assert passed, 'The solution to get_element_scores did not pass the test.'

###
### AUTOGRADER TEST - DO NOT REMOVE
###

print('Passed! Please submit.')
initial memory usage: 0.00 MB
Test duration: 0.30 seconds
memory after test: 0.36 MB
memory peak during test: 3.81 MB
Passed! Please submit.

Exercise 6: (3 points)

DEBUG_compute_base_score

Your task: define DEBUG_compute_base_score as follows:

Note: This is a debugging exercise!

To ensure better fairness in scoring, the technical panel first computes the base score for each element in a skater's program. These base scores are the values for how much each jump, spin, and sequence are worth based on their degree of difficulty. Let's now compute a skater's base score for each element in their program.

Input:

  • exec_elements: A list of elements a skater's program is set to contain
  • base_value_dict: The result of the prior exercise. A dictionary containing the code names of the elements as the keys. The values are lists containing the base score associated with that element as the 0th element, and either j or s as the 1st element to designate whether the element is a jump or spin/step sequence.

Return: base_score_list: A list of the expected base scores for each element in the skater's program, each rounded to two decimal places

Requirements:

  1. Combinations are split by + signs. If present, you must sum up all elements within the combination
  2. The last three elements of the program containing a jump receive a 10% bonus. If the jump is contained within a combination, the entire combination should receive the 10% bonus and only count as 1 out of the 3 eligible bonuses (even if it contains multiple jumps within the combination)
  3. Your result should be a list with the same number of elements and ordering as the items found in exec_elements. Each item in your list should be a float value rounded to two decimal places
  4. This is a debugging exercise! One or more issues exist in the solution provided below. You must correct the solution in order to pass the test cell and autograder. You are allowed to delete the provided solution code and write your own solution if you wish to

Hint: The demo input matches the sample score sheet provided as an image in the free exercise. We suggest reviewing the Base Value column in the image. You will notice the elements marked with an X beside their base value received the 10% bonus (4T+3T, 3Lz+3T, 3F+1Eu+3S), as they were the last three elements of the program that contained a jump.

As an example, the base score for 3F+1Eu+3S was calculated as: (5.3 + 0.5 + 4.3) * 1.1 bonus = 11.11 base score

In [14]:
### Solution - Exercise 6  
def DEBUG_compute_base_score(exec_elements: list, base_value_dict: dict) -> list:
    ### BEGIN SOLUTION
    base_score_list = []
    element_list = exec_elements.copy()
    element_list.reverse()
    bonus_jump_count = 0
    
    for e in element_list:
        if '+' in e:
            # need to split and sum up
            combo_list = e.split('+')
            bonus_flag = False
            combo_sum = 0
            for c in combo_list:
                if base_value_dict[c][1] == 'j':
                    bonus_flag = True
                combo_sum += base_value_dict[c][0]
            if bonus_flag == True and bonus_jump_count < 3:
                combo_sum = combo_sum * 1.1
                bonus_jump_count += 1
            base_score_list.append(round(combo_sum,2))
        else:
            if base_value_dict[e][1] == 'j' and bonus_jump_count < 3:
                base_score_list.append(round(base_value_dict[e][0] * 1.1,2))
                bonus_jump_count += 1
            else:
                base_score_list.append(round(base_value_dict[e][0],2))
                
    base_score_list.reverse()            
    return base_score_list
    ### END SOLUTION


### Demo function call
base_value_dict_demo = {
    '1Eu': [0.5, 's'],
    '2T': [1.3, 'j'],
    '2S': [1.3, 'j'],
    '2Lo': [1.7, 'j'],
    '2F': [1.8, 'j'],
    '2Lz': [2.1, 'j'],
    '2A': [3.3, 'j'],
    '3T': [4.2, 'j'],
    '3S': [4.3, 'j'],
    '3Lo': [4.9, 'j'],
    '3F': [5.3, 'j'],
    '3Lz': [5.9, 'j'],
    '3A': [8.0, 'j'],
    '4T': [9.5, 'j'],
    '4S': [9.7, 'j'],
    '4Lo': [10.5, 'j'],
    '4F': [11.0, 'j'],
    '4Lz': [11.5, 'j'],
    '4A': [12.5, 'j'],
    'LSp': [2.7, 's'],
    'CSp': [2.6, 's'],
    'SSp': [2.5, 's'],
    'FLSp': [3.2, 's'],
    'FCSp': [3.2, 's'],
    'CCSp': [3.2, 's'],
    'CSSp': [3.0, 's'],
    'CoSp': [3.0, 's'],
    'CCoSp': [3.5, 's'],
    'StSq': [3.9, 's'],
    'ChSq': [3.0, 's']}
compute_base_score_demo = ['4Lz', '4F', '4T', '3A', 'CCSp', 'StSq', '4T+3T', '3Lz+3T', '3F+1Eu+3S', 'ChSq', 'CCoSp', 'CCoSp']
demo_result_compute_base_score = DEBUG_compute_base_score(compute_base_score_demo, base_value_dict_demo)
print(demo_result_compute_base_score)
[11.5, 11.0, 9.5, 8.0, 3.2, 3.9, 15.07, 11.11, 11.11, 3.0, 3.5, 3.5]

The demo should display this printed output.

[11.5, 11.0, 9.5, 8.0, 3.2, 3.9, 15.07, 11.11, 11.11, 3.0, 3.5, 3.5]


The cell below will test your solution for DEBUG_compute_base_score (exercise 6). The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. Any key:value pair in original_input_vars should also exist in input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [15]:
### Test Cell - Exercise 6  


from cse6040_devkit.tester_fw.testers import Tester
from yaml import safe_load
from time import time

tracemalloc.start()
mem_start, peak_start = tracemalloc.get_traced_memory()
print(f"initial memory usage: {mem_start/1024/1024:.2f} MB")

# Load testing utility
with open('resource/asnlib/publicdata/execute_tests', 'rb') as f:
    executor = dill.load(f)

@run_with_timeout(error_threshold=200.0, warning_threshold=100.0)
@suppress_stdout
def execute_tests(**kwargs):
    return executor(**kwargs)


# Execute test
start_time = time()
passed, test_case_vars, e = execute_tests(func=DEBUG_compute_base_score,
              ex_name='DEBUG_compute_base_score',
              key=b'XXLjq9aUPdzwNjsrPPGTZkd4dNWqkqvfmeLkcL7ZQmg=', 
              n_iter=201)
# Assign test case vars for debugging
input_vars, original_input_vars, returned_output_vars, true_output_vars = test_case_vars
duration = time() - start_time
print(f"Test duration: {duration:.2f} seconds")
current_memory, peak_memory = tracemalloc.get_traced_memory()
print(f"memory after test: {current_memory/1024/1024:.2f} MB")
print(f"memory peak during test: {peak_memory/1024/1024:.2f} MB")
tracemalloc.stop()
if e: raise e
assert passed, 'The solution to DEBUG_compute_base_score did not pass the test.'

###
### AUTOGRADER TEST - DO NOT REMOVE
###

print('Passed! Please submit.')
initial memory usage: 0.00 MB
Test duration: 0.25 seconds
memory after test: 0.05 MB
memory peak during test: 2.57 MB
Passed! Please submit.

Exercise 7: (3 points)

calc_goe_score

Your task: define calc_goe_score as follows:

The base value of an element defines how many points it is worth by the technical panel, but the Grade of Execution (GOE) evaluates how well an element is executed. Judges award GOEs from -5 to +5 in increments of 1. A well-executed element receives positive GOE, while a badly-executed element receives negative GOE.

The highest and lowest judges’ GOE scores are discarded while the remaining scores are used to calculate the overall GOE. The remaining GOE scores are scaled, in which 1 GOE translates to 10% of the element’s base value, except for elements containing the string ChSq. In those instances, the remaining raw GOE scores are simply divided in half.

The GOE scores across the judges are then averaged for that element. We repeat this process for all elements.

In jump combinations and sequences (elements joined by a + sign), the Grade of Execution (GOE) is based on the base value of the most valuable jump in the combination.

That's complex! Let's see a small example of this below:

judge_scores = {
    'J0': [1, 5],
    'J1': [1, 4],
    'J2': [2, 4],
    'J3': [3, 5]
    }

exec_elements = ['3Lz+1Eu+3S', 'ChSq2']

base_value_dict = {
    '1Eu': [1.0, 'j'],
    '3Lz': [5.90, 'j'],
    '3S': [2.0, 'j'],
    'ChSq2': [3.0, 's']
    }

For the first element 3Lz+1Eu+3S, we see that the highest base value in the combination is 3Lz with a value of 5.90. We multiply this value by 10% = .59. The raw GOE scores received by the judges are [1, 1, 2, 3]. We drop the lowest and highest, and then multiply each by the .59 to get [.59, 1.18]. We average these to get a GOE score of 0.89.

For the second element ChSq2, we notice it contains the string ChSq. Therefore, we simply drop the lowest and highest raw GOE scores from [5, 4, 4, 5] to get [4, 5]. Then we divide these scores by 2 and average them. This gives us a GOE score of 2.25.

Finally, we return a dictionary mapping each executed element to its GOE score.

goe_dict = {
    '3Lz+1Eu+3S': 0.89,
    'ChSq2': 2.25
    }

Input:

  • judge_scores: A dictionary containing the judge names as keys. The values are lists containing the grade of execution for each element in the skater's program. They are provided in the same order as encountered in exec_elements
  • exec_elements: A list of elements a skater's program is set to contain
  • base_value_dict: A dictionary containing the code names of the elements as the keys. The values are lists containing the base score associated with that element as the 0th element, and either j or s as the 1st element to designate whether the element is a jump or step/sequence.

Return: goe_dict: A dictionary mapping a skater's executed elements to the grade of execution score received. The grade of execution (GOE) score should be rounded to two decimal places.

Requirements:

  • Drop the lowest and highest judge scores for each element
  • If ChSq in element name:
    • GOE is the average of (remaining judge scores / 2)
  • If ChSq not in element name:
    • If combination exists (joined by + sign), find the element within the combination that has the highest base value in base_value_dict
    • If combination does not exist, find the base value for the element in base_value_dict
    • GOE is the average of (remaining judge scores * base value * 0.1)
  • Round GOE to two decimal places
  • Return a dictionary that contains the executed elements as the keys and the GOE scores as the values

Hint: The demo input contains the judges' scores for some of the executed elements in the sample score sheet image we provided in the free exercise. You can reference the GOE column and compare to your calculated demo output if needed.

In [16]:
### Solution - Exercise 7  
def calc_goe_score(judge_scores: dict, exec_elements: list, base_value_dict: dict) -> dict:
    ### BEGIN SOLUTION
    goe_dict = {}
    goe_list = [list_of_values for list_of_values in judge_scores.values()]

    d = {}

    for elem in goe_list:
        for i in range(len(elem)):
            if i in d:
                d[i].append(elem[i])
            else:
                d[i] = [elem[i]]    
    
    for key in d:
        d[key].sort() # Sort lists in place
        d[key] = d[key][1:-1] # remove first and last elements

        # can be ChSq1, ChSq2, etc
        if 'ChSq' in exec_elements[key]:
            goe_scores = [i/2 for i in d[key]]
            goe_dict[exec_elements[key]] = round(sum(goe_scores) / len(goe_scores), 2)
        else:
            if '+' not in exec_elements[key]:
                goe_scores = [i * base_value_dict[exec_elements[key]][0] * .1 for i in d[key]]
            else:
                # find out which element has the max score
                individual_elements = exec_elements[key].split('+')
                max_score = 0
                for i in individual_elements:
                    if base_value_dict[i][0] > max_score:
                        max_score = base_value_dict[i][0]

                goe_scores = [i * max_score * .1 for i in d[key]]
                
            goe_dict[exec_elements[key]] = round(sum(goe_scores) / len(goe_scores), 2)
    return goe_dict
    ### END SOLUTION

### Demo function call
judge_scores_demo = {
'J1': [4, 2, 3, 4, 2, 3, 4, 3, 0, 2],
'J2': [4, 1, 4, 4, 3, 5, 4, 4, 1, 4],
'J3': [4, 1, 4, 4, 5, 5, 4, 4, 1, 4],
'J4': [4, 3, 4, 4, 4, 4, 3, 4, 1, 4],
'J5': [4, 1, 4, 3, 4, 4, 3, 2, 0, 4],
'J6': [5, 3, 5, 5, 5, 4, 4, 4, 2, 5],
'J7': [4, 2, 3, 3, 4, 4, 4, 4, 0, 5],
'J8': [4, 2, 3, 4, 4, 5, 3, 3, 1, 5],
'J9': [5, 2, 3, 3, 3, 4, 3, 3, 0, 4]
}

base_value_dict_demo = {
 '1Eu': [0.5, 's'],
 '2T': [1.3, 'j'],
 '2S': [1.3, 'j'],
 '2Lo': [1.7, 'j'],
 '2F': [1.8, 'j'],
 '2Lz': [2.1, 'j'],
 '2A': [3.3, 'j'],
 '3T': [4.2, 'j'],
 '3S': [4.3, 'j'],
 '3Lo': [4.9, 'j'],
 '3F': [5.3, 'j'],
 '3Lz': [5.9, 'j'],
 '3A': [8.0, 'j'],
 '4T': [9.5, 'j'],
 '4S': [9.7, 'j'],
 '4Lo': [10.5, 'j'],
 '4F': [11.0, 'j'],
 '4Lz': [11.5, 'j'],
 '4A': [12.5, 'j'],
 'USp': [2.4, 's'],
 'LSp': [2.7, 's'],
 'CSp': [2.6, 's'],
 'SSp': [2.5, 's'],
 'FUSp': [2.9, 's'],
 'FLSp': [3.2, 's'],
 'FCSp': [3.2, 's'],
 'FSSp': [3.0, 's'],
 'CUSp': [2.9, 's'],
 'CLSP': [3.2, 's'],
 'CCSp': [3.2, 's'],
 'CSSp': [3.0, 's'],
 'CoSp': [3.0, 's'],
 'CCoSp': [3.5, 's'],
 'StSq': [3.9, 's'],
 'ChSq': [3.0, 's'],
 'ChSq1': [4.0, 's'],
 'ChSq2': [5.0, 's']
}

exec_elements_demo = ['4Lz', '4F', '4T', '3A', 'CCSp', 'StSq', '4T+3T', '3Lz+3T', '3F+1Eu+3S', 'ChSq1']
demo_result_calc_goe_score = calc_goe_score(judge_scores_demo, exec_elements_demo, base_value_dict_demo)
print(demo_result_calc_goe_score)
{'4Lz': 4.76, '4F': 2.04, '4T': 3.39, '3A': 2.97, 'CCSp': 1.23, 'StSq': 1.67, '4T+3T': 3.39, '3Lz+3T': 2.11, '3F+1Eu+3S': 0.3, 'ChSq1': 2.14}

The demo should display this printed output.

{'4Lz': 4.76, '4F': 2.04, '4T': 3.39, '3A': 2.97, 'CCSp': 1.23, 'StSq': 1.67, '4T+3T': 3.39, '3Lz+3T': 2.11, '3F+1Eu+3S': 0.3, 'ChSq1': 2.14}


The cell below will test your solution for calc_goe_score (exercise 7). The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. Any key:value pair in original_input_vars should also exist in input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [17]:
### Test Cell - Exercise 7  


from cse6040_devkit.tester_fw.testers import Tester
from yaml import safe_load
from time import time

tracemalloc.start()
mem_start, peak_start = tracemalloc.get_traced_memory()
print(f"initial memory usage: {mem_start/1024/1024:.2f} MB")

# Load testing utility
with open('resource/asnlib/publicdata/execute_tests', 'rb') as f:
    executor = dill.load(f)

@run_with_timeout(error_threshold=200.0, warning_threshold=100.0)
@suppress_stdout
def execute_tests(**kwargs):
    return executor(**kwargs)


# Execute test
start_time = time()
passed, test_case_vars, e = execute_tests(func=calc_goe_score,
              ex_name='calc_goe_score',
              key=b'XXLjq9aUPdzwNjsrPPGTZkd4dNWqkqvfmeLkcL7ZQmg=', 
              n_iter=201)
# Assign test case vars for debugging
input_vars, original_input_vars, returned_output_vars, true_output_vars = test_case_vars
duration = time() - start_time
print(f"Test duration: {duration:.2f} seconds")
current_memory, peak_memory = tracemalloc.get_traced_memory()
print(f"memory after test: {current_memory/1024/1024:.2f} MB")
print(f"memory peak during test: {peak_memory/1024/1024:.2f} MB")
tracemalloc.stop()
if e: raise e
assert passed, 'The solution to calc_goe_score did not pass the test.'

###
### AUTOGRADER TEST - DO NOT REMOVE
###

print('Passed! Please submit.')
initial memory usage: 0.00 MB
Test duration: 0.27 seconds
memory after test: 0.05 MB
memory peak during test: 1.76 MB
Passed! Please submit.

Exercise 8: (2 points)

calculate_corr_coeff

Your task: define calculate_corr_coeff as follows:

We have seen in prior exercises how to calculate the base score of a skater's program based on the difficulty of the elements they plan to execute. We have also seen how the judges can assign a grade of execution score to each of these elements and how that impacts a skater's overall total segment score.

Let us now determine if skaters executing a more difficult program can expect to receive higher scores overall by computing the Pearson correlation coefficient (r) between base scores and total segment scores.

Input: results: A dictionary containing the skaters as the keys and the value as a tuple containing their base score as the first element, total segment score as the second, and any deductions as an optional third element.

Return: r: A float value representing Pearson's correlation coefficient rounded to 2 decimal places

Requirements:

  1. Extract the base scores found in results
  2. Extract the total segment scores found in results subtracting any deductions if present
  3. Return the Pearson correlation coefficient (r) between the two sets of data, rounded to 2 decimal places

Pearson Correlation Coefficient Formula: $$r = \frac{\sum (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum (x_i - \bar{x})^2 \sum (y_i - \bar{y})^2}}$$

where $\bar{x} \ \ \text{and} \ \ \bar{y}$ are the means of x and y

For the demo, this would be: $$r = \frac{3775.79}{\sqrt{1969.44 * 10890.73}} = 0.82$$

Note: We've rounded the components in the demo formula above for readability, but you should not round to two decimal places until the end.

In [18]:
### Solution - Exercise 8  
def calculate_corr_coeff(results: dict) -> float:
    ### BEGIN SOLUTION
    # step 0
    x = []
    y = []
    for key, value in results.items():
        x.append(value[0])
        if len(value) == 2:
            y.append(value[1])
        else:
            y.append(value[1] - value[2])

    # step 1
    x_bar = sum(x) / len(x)
    y_bar = sum(y) / len(y)
    n = len(x)

    numerator = 0
    denominator_1 = 0
    denominator_2 = 0
    for i in range(0, n):
        numerator += (x[i] - x_bar) * (y[i] - y_bar)
        denominator_1 += (x[i]-x_bar)**2
        denominator_2 += (y[i]-y_bar)**2

    denominator_1 = denominator_1**0.5
    denominator_2 = denominator_2**0.5
    denominator = denominator_1 * denominator_2

    corr_coeff = numerator / denominator

    return round(corr_coeff, 2)
    ### END SOLUTION

### Demo function call
calculate_corr_coeff_demo = {'CHEN': (94.34, 218.63),
       'KAGIYAMA': (88.35, 201.93),
       'HANYU': (89.93 , 190.06, 2.00),
       'GRASSL': (89.83 , 187.43),
       'UNO': (88.53, 188.10, 1.00),
       'BROWN': (67.15, 184.00),
       'CHA': (79.90, 183.87, 1.00),
       'JIN': (83.75, 179.45),
       'SEMENENKO': (84.37,  178.37),
       'MESSING': (70.34, 172.37),
       'KVITELASHVILI': (78.66, 170.64),
       'VASILJEVS': (75.57,  168.41, 1.00),
       'SIAO HIM FA': (78.56, 164.41, 1.00),
       'KONDRATIUK': (76.07, 163.71, 1.00),
       'AYMOZ': (69.00, 162.80, 1.00),
       'KERRY': (72.83, 160.01),
       'RIZZO': (68.51, 159.90, 1.00),
       'MOZALEV': (81.28, 158.28, 2.00),
       'LITVINTSEV': (77.78, 155.04),
       'MILYUKOV': (68.31, 143.73),
       'MAJOROV': (62.78, 142.24),
       'CARRILLO': (71.91, 139.44, 1.00),
       'BRITSCHGI': (64.45, 136.42),
       'SHMURATKO': (63.43, 128.65, 1.00)
       }
demo_result_calculate_corr_coeff = calculate_corr_coeff(calculate_corr_coeff_demo)
print(demo_result_calculate_corr_coeff)
0.82

The demo should display this printed output.

0.82


The cell below will test your solution for calculate_corr_coeff (exercise 8). The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. Any key:value pair in original_input_vars should also exist in input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [19]:
### Test Cell - Exercise 8  


from cse6040_devkit.tester_fw.testers import Tester
from yaml import safe_load
from time import time

tracemalloc.start()
mem_start, peak_start = tracemalloc.get_traced_memory()
print(f"initial memory usage: {mem_start/1024/1024:.2f} MB")

# Load testing utility
with open('resource/asnlib/publicdata/execute_tests', 'rb') as f:
    executor = dill.load(f)

@run_with_timeout(error_threshold=200.0, warning_threshold=100.0)
@suppress_stdout
def execute_tests(**kwargs):
    return executor(**kwargs)


# Execute test
start_time = time()
passed, test_case_vars, e = execute_tests(func=calculate_corr_coeff,
              ex_name='calculate_corr_coeff',
              key=b'XXLjq9aUPdzwNjsrPPGTZkd4dNWqkqvfmeLkcL7ZQmg=', 
              n_iter=201)
# Assign test case vars for debugging
input_vars, original_input_vars, returned_output_vars, true_output_vars = test_case_vars
duration = time() - start_time
print(f"Test duration: {duration:.2f} seconds")
current_memory, peak_memory = tracemalloc.get_traced_memory()
print(f"memory after test: {current_memory/1024/1024:.2f} MB")
print(f"memory peak during test: {peak_memory/1024/1024:.2f} MB")
tracemalloc.stop()
if e: raise e
assert passed, 'The solution to calculate_corr_coeff did not pass the test.'

###
### AUTOGRADER TEST - DO NOT REMOVE
###

print('Passed! Please submit.')
initial memory usage: 0.00 MB
Test duration: 0.12 seconds
memory after test: 0.04 MB
memory peak during test: 1.81 MB
Passed! Please submit.

FIN!

If you have made it this far, congratulations! Remember to submit the exam to ensure you receive all the points you have earned!

Epilogue:

Our comparison of the two scoring systems reveals that under the new criteria skaters who attempt high-difficulty elements consistently achieve higher overall scores. But does this correlation equate to fairness? The consensus remains divided. Critics argue the new system over-prioritizes technical difficulty, offering insufficient penalties for poorly executed jumps and spins. Meanwhile, skeptics maintain that the framework remains vulnerable to inherent bias and manipulation.

These vulnerabilities were thrust into the spotlight during the 2026 Winter Olympics following a controversy in the free dance event. A French judge was accused of inflating marks for their home country's pair while suppressing scores for the American duo. Remarkably, although five of the nine judges favored the Americans, the French judge’s scoring allowed for the French to secure the gold medal. Only time will tell whether this latest scandal will trigger another overhaul of the scoring system.