Fairness Week 1: University admissions¶
How to use this notebook
Simply read the text and follow the instructions.
This notebook contains code cells, which can be modified and must be executed to see the result of their content.
To execute a cell, select it and click on the play button (▶) in the tool bar, or type Shift + Enter or Ctr + Enter.
As the variables contained in a cell are stored in memory, the order of execution of the cells is important!
Notebook by Maxime Lelièvre, Cécile Hardebolle and the Responsible software team (2025).
Exercises adapted from "College Admission Algorithm" by Natalie Garrett and Casey Fiesler, used under license CC BY 4.0.
Source: https://www.internetruleslab.com/ethicsbased-computer-science-assignments
Reference: Fiesler, C., Friske, M., Garrett, N., Muzny, F., Smith, J. J., & Zietz, J. (2021). Integrating Ethics into Introductory Programming Classes. Proceedings of the 52nd ACM Technical Symposium on Computer Science Education, 1027–1033. https://doi.org/10.1145/3408877.3432510
Except where otherwise noted, the content of this notebook is licensed under a Creative Commons Attribution International License (CC BY 4.0 International).

Introduction¶
How and why can algorithms lead to unfaire outcomes? How and why can algorithms be biased? A growing body of research shows that algorithmic bias affects many areas of life in today's societies and has substantial, far-reaching impact on access to opportunities, decision-making, and everyday interactions.
In this exercise session, we will take the example of university admissions to introduce how bias can occur in the design and development of software and apply some tools and techniques to mitigate bias in algorithms and data.
Learning goals
What will be covered:
- Part 1: An algorithm for university admissions
- Part 2: Admission data from Berkeley
By the end of the session you will be able to:
- ✅ identify different sources of bias leading to unfairness in software
- ✅ apply graphical and numerical analysis to identify unfairness in software
- ✅ explain how bias can hide in data
- ✅ describe one criterion of algorithmic fairness
Part 1: An algorithm for university admissions¶
With the number of applicants increasing, more and more universities are relying on algorithms to assist admissions, typically for tasks like sorting applications or flagging applications for human review.
These algorithms often rely on quantitative data such as grade averages or scores on national exams. Sometimes, other types of data are collected from prospective students, for instance some colleges in the U.S. track data about prospective student engagement - e.g., whether they open emails, visit the college website, engage on social media, etc - to represent their "demonstrated interest".
Scenario¶
You’ve just been hired as a junior software engineer at Lakeside Polytechnic, a forward-looking institution in the U.S. that specializes in technology, engineering, and applied sciences, preparing students for innovation-driven careers. The admissions office is overwhelmed by the number of applications and has asked you to design an algorithm that can help them prioritize which applicants should be admitted.
As a responsible engineer, you want to make sure that the algorithm you design is equitable and avoids disadvantaging applicants on unfair grounds.
Part 1.1: Implementing the algorithm¶
Instructions
Run the cell below to import the necessary libraries.
# import libraries
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from res.utils import *
from res.tests import *
The dataset¶
Based on a recent survey of college admissions officers, the admission office has selected the prospective student data that should be used in the algorithm, associated with respective importance weights. Your task is to create a program that provides a recommendation for which prospective students are likely to be the best candidates for admission based on this data.
Prospective student data is organized in the dataset/admission_algorithms_dataset.csv file, where each row represents one student.
Here is a description of the available information for each student: | attributes | value range | description |----------|----------|--------| | SAT score | [0,1600] | standardized test taken in the U.S. to measure students' abilities before they go to college | | GPA | [0,5] | sum of all the course grades throughout the high school career divided by the total number of credits | | Interest | [0,10] | from very low to very high interest: representation of student interest by tracking whether they open emails, visit the college website, engage on social media | | High School Quality | [0,10] | from very low-quality to very high-quality high school | | Semester 1,2,3,4 | grade over 100 | average grade for semester 1,2,3,4 |
Instructions
Run the cell below to load the dataset and see its first rows.
df = load_dataset()
df.head()
| Student | SAT | GPA | Interest | High School Quality | Semester 1 | Semester 2 | Semester 3 | Semester 4 | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | Abbess Horror | 1300.0 | 3.61 | 10.0 | 7.0 | 95.0 | 86.0 | 91.0 | 94.0 |
| 1 | Adele Hawthorne | 1400.0 | 3.67 | 0.0 | 9.0 | 97.0 | 83.0 | 85.0 | 86.0 |
| 2 | Adelicia von Krupp | 900.0 | 4.00 | 5.0 | 2.0 | 88.0 | 92.0 | 83.0 | 72.0 |
| 3 | Adriana Soria | 1360.0 | 3.19 | 0.0 | 9.0 | 90.0 | 85.0 | 97.0 | 87.0 |
| 4 | Adriana Princess | 1180.0 | 2.93 | 2.0 | 7.0 | 93.0 | 81.0 | 90.0 | 87.0 |
Problem 1: Data normalization¶
You may have noticed that the different attributes we have for students come in very different scales. This is an issue when designing an algorithm because the attributes with the largest numerical ranges can dominate the results even if in reality their importance is low. In our case, the SAT score, which is in [0, 1600] would dominate over the GPA, which is in [0, 5].
Therefore you need to normalize the data before using it, i.e. bring all the variables onto a similar scale, ensuring consistency for comparison and analysis.
Instructions
Normalize both SAT and GPA so that all the data (except the semesters' grades) are on a 0 to 10 scale.
To do this, divide the SAT score by 160, and multiply the GPA by 2.
You must add two columns to the original dataframe: SAT_norm and GPA_norm.
def normalization(df):
""" normalizes the columns SAT and GPA of the dataframe
Arguments:
- df: original dataframe
Returns:
- dataframe with 2 new columns with the SAT and GPA normalized: SAT_norm and GPA_norm
"""
### YOUR CODE HERE ###
df['SAT_norm'] = df['SAT']/160 # SOLUTION
df['GPA_norm'] = 2*df['GPA'] # SOLUTION
######################
return df
df = normalization(df)
df.head()
| Student | SAT | GPA | Interest | High School Quality | Semester 1 | Semester 2 | Semester 3 | Semester 4 | SAT_norm | GPA_norm | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Abbess Horror | 1300.0 | 3.61 | 10.0 | 7.0 | 95.0 | 86.0 | 91.0 | 94.0 | 8.125 | 7.22 |
| 1 | Adele Hawthorne | 1400.0 | 3.67 | 0.0 | 9.0 | 97.0 | 83.0 | 85.0 | 86.0 | 8.750 | 7.34 |
| 2 | Adelicia von Krupp | 900.0 | 4.00 | 5.0 | 2.0 | 88.0 | 92.0 | 83.0 | 72.0 | 5.625 | 8.00 |
| 3 | Adriana Soria | 1360.0 | 3.19 | 0.0 | 9.0 | 90.0 | 85.0 | 97.0 | 87.0 | 8.500 | 6.38 |
| 4 | Adriana Princess | 1180.0 | 2.93 | 2.0 | 7.0 | 93.0 | 81.0 | 90.0 | 87.0 | 7.375 | 5.86 |
Test your code with the cell below!
test(normalization)
✅ You passed the test!
Problem 2: Prospective student score¶
Your idea for this selection algorithm is to compute an aggregate score that takes into account the variables recommended by the admissions office, then to set a threshold and select only students who have a score above the threshold. You will therefore follow two steps:
- Compute the aggregate score for all students.
- Select students who have a score above the threshold.
Instructions
You want to write a function that calculates an overall score for each student as a weighted sum. Each attribute will be multiplied by a coefficient that reflects its importance in the overall score, all weights adding up to 1:
- SAT: 0.3
- GPA: 0.4
- Interest: 0.1
- High School Quality: 0.2
These coefficients are provided in the coeff variable below. You can access each coefficient with array indexing, e.g. coeff[1] will give you the coefficient for the GPA.
The function calculate_student_score should:
- take a dataframe as a parameter and calculate an overall score (over 10) for each student based on the provided coefficients,
- add the
Student scoreas a new column to the dataframe and return the dataframe.
The values within this column should be rounded to two decimal places (see functionround).
coeff = [0.3,0.4,0.1,0.2] # [SAT coeff, GPA coeff, Interest coeff, High school quality coeff]
def calculate_student_score(df, coeff):
""" computes the student score of each student and adds them as a new column to the dataframe
Arguments:
- df: dataframe with the SAT and GPA normalized
- coeff: coefficients to compute the student score ([SAT, GPA, Interest, High School Quality])
Returns:
- dataframe with the new column 'Student score'
"""
### YOUR CODE HERE ###
# BEGIN SOLUTION
df['Student score'] = coeff[0]*df['SAT_norm'] + coeff[1]*df['GPA_norm'] + coeff[2]*df['Interest'] + coeff[3]*df['High School Quality']
df['Student score'] = df['Student score'].round(2)
# END SOLUTION
######################
return df
df = calculate_student_score(df, coeff)
df.head()
| Student | SAT | GPA | Interest | High School Quality | Semester 1 | Semester 2 | Semester 3 | Semester 4 | SAT_norm | GPA_norm | Student score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Abbess Horror | 1300.0 | 3.61 | 10.0 | 7.0 | 95.0 | 86.0 | 91.0 | 94.0 | 8.125 | 7.22 | 7.73 |
| 1 | Adele Hawthorne | 1400.0 | 3.67 | 0.0 | 9.0 | 97.0 | 83.0 | 85.0 | 86.0 | 8.750 | 7.34 | 7.36 |
| 2 | Adelicia von Krupp | 900.0 | 4.00 | 5.0 | 2.0 | 88.0 | 92.0 | 83.0 | 72.0 | 5.625 | 8.00 | 5.79 |
| 3 | Adriana Soria | 1360.0 | 3.19 | 0.0 | 9.0 | 90.0 | 85.0 | 97.0 | 87.0 | 8.500 | 6.38 | 6.90 |
| 4 | Adriana Princess | 1180.0 | 2.93 | 2.0 | 7.0 | 93.0 | 81.0 | 90.0 | 87.0 | 7.375 | 5.86 | 6.16 |
Test your code with the cell below!
test_result(calculate_student_score, df)
✅ You passed the test !
Instructions
Now your goal is to select students who have a score above the threshold.
Write a function apply_selection_base that:
- adds a column
selected_baseto the dataframe with True if the student has aStudent scoreof 6 or higher and False otherwise.
Note: You have learned about boolean filtering with Pandas Dataframes in the Tutorial and Safety 1 notebooks. We provide a short tutorial here again if you need a refresher: tutorial_booleans.ipynb (~3 min).
def apply_selection_base(df):
""" applies a basic selection based on the student score and adds a column to the dataframe
Arguments:
- df: dataframe with the SAT and GPA normalized and the Student score
Returns:
- df: dataframe with one added column 'selected_base'
"""
### YOUR CODE HERE ###
# BEGIN SOLUTION
df['selected_base'] = df['Student score'] >= 6
# END SOLUTION
######################
return df
df = apply_selection_base(df)
df.head()
| Student | SAT | GPA | Interest | High School Quality | Semester 1 | Semester 2 | Semester 3 | Semester 4 | SAT_norm | GPA_norm | Student score | selected_base | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Abbess Horror | 1300.0 | 3.61 | 10.0 | 7.0 | 95.0 | 86.0 | 91.0 | 94.0 | 8.125 | 7.22 | 7.73 | True |
| 1 | Adele Hawthorne | 1400.0 | 3.67 | 0.0 | 9.0 | 97.0 | 83.0 | 85.0 | 86.0 | 8.750 | 7.34 | 7.36 | True |
| 2 | Adelicia von Krupp | 900.0 | 4.00 | 5.0 | 2.0 | 88.0 | 92.0 | 83.0 | 72.0 | 5.625 | 8.00 | 5.79 | False |
| 3 | Adriana Soria | 1360.0 | 3.19 | 0.0 | 9.0 | 90.0 | 85.0 | 97.0 | 87.0 | 8.500 | 6.38 | 6.90 | True |
| 4 | Adriana Princess | 1180.0 | 2.93 | 2.0 | 7.0 | 93.0 | 81.0 | 90.0 | 87.0 | 7.375 | 5.86 | 6.16 | True |
Test your code with the cell below!
test_result(apply_selection_base, df)
✅ You passed the test !
Problem 3: Looking for outliers¶
Since you want your algorithm to be equitable, you consider ways in which it might systematically miss certain kinds of edge cases. For example, a student who has a 0 for demonstrated interest because they do not use social media or do not have access to a computer at home. Or a student who has a very high GPA but a SAT score that is low enough to bring their score down, because they were sick on the day of the test.
In the following, your goal is to improve the inclusiveness of your selection process by taking into account these different edge cases into your selection algorithm. For this you will need to use boolean logic with Pandas Dataframes.
You will again go through two steps:
- Identify the outliers
- Take into account the outlier information in your selection function
Instructions
Write a function is_outlier that:
- checks for certain kinds of outliers such as:
- a demonstrated interest score of 0.
- a normalized GPA that is more than 2 points higher than the normalized SAT score.
- adds a column
is_outlierto the dataframe with True if one of the two conditions above is true, and False otherwise.
def is_outlier(df):
""" checks for certain kinds of outliers and adds a column to the dataframe accordingly
Arguments:
- df: dataframe with all the added columns so far
Returns:
- df: dataframe with the added column `is_outlier`
"""
### YOUR CODE HERE ###
# BEGIN SOLUTION
df['is_outlier'] = (df['Interest'] == 0) | (df['GPA_norm'] > df['SAT_norm'] + 2)
# END SOLUTION
######################
return df
df = is_outlier(df)
df.head()
| Student | SAT | GPA | Interest | High School Quality | Semester 1 | Semester 2 | Semester 3 | Semester 4 | SAT_norm | GPA_norm | Student score | selected_base | is_outlier | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Abbess Horror | 1300.0 | 3.61 | 10.0 | 7.0 | 95.0 | 86.0 | 91.0 | 94.0 | 8.125 | 7.22 | 7.73 | True | False |
| 1 | Adele Hawthorne | 1400.0 | 3.67 | 0.0 | 9.0 | 97.0 | 83.0 | 85.0 | 86.0 | 8.750 | 7.34 | 7.36 | True | True |
| 2 | Adelicia von Krupp | 900.0 | 4.00 | 5.0 | 2.0 | 88.0 | 92.0 | 83.0 | 72.0 | 5.625 | 8.00 | 5.79 | False | True |
| 3 | Adriana Soria | 1360.0 | 3.19 | 0.0 | 9.0 | 90.0 | 85.0 | 97.0 | 87.0 | 8.500 | 6.38 | 6.90 | True | True |
| 4 | Adriana Princess | 1180.0 | 2.93 | 2.0 | 7.0 | 93.0 | 81.0 | 90.0 | 87.0 | 7.375 | 5.86 | 6.16 | True | False |
Test your code with the cell below!
test(is_outlier)
✅ You passed the test!
Now that you know which students are outliers, you can take that information into account in your selection function.
Instructions
Write a function apply_selection_improved that:
- checks the two following conditions:
- the student has a
Student scoreof 6 or higher. - the student is an outlier and has a
Student scoreof 5 or higher.
- the student has a
- adds a column
selected_improvedto the dataframe with True if one of the two conditions above is true, and False otherwise.
def apply_selection_improved(df):
""" applies an improved selection while adding a column to the dataframe
Arguments:
- df: dataframe with all the added columns so far
Returns:
- df: dataframe with the added column 'selected_improved'
"""
### YOUR CODE HERE ###
# BEGIN SOLUTION
df['selected_improved'] = (df['Student score'] >= 6) | ((df['is_outlier'] == True) & (df['Student score'] >= 5))
# END SOLUTION
######################
return df
df = apply_selection_improved(df)
df.head()
| Student | SAT | GPA | Interest | High School Quality | Semester 1 | Semester 2 | Semester 3 | Semester 4 | SAT_norm | GPA_norm | Student score | selected_base | is_outlier | selected_improved | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Abbess Horror | 1300.0 | 3.61 | 10.0 | 7.0 | 95.0 | 86.0 | 91.0 | 94.0 | 8.125 | 7.22 | 7.73 | True | False | True |
| 1 | Adele Hawthorne | 1400.0 | 3.67 | 0.0 | 9.0 | 97.0 | 83.0 | 85.0 | 86.0 | 8.750 | 7.34 | 7.36 | True | True | True |
| 2 | Adelicia von Krupp | 900.0 | 4.00 | 5.0 | 2.0 | 88.0 | 92.0 | 83.0 | 72.0 | 5.625 | 8.00 | 5.79 | False | True | True |
| 3 | Adriana Soria | 1360.0 | 3.19 | 0.0 | 9.0 | 90.0 | 85.0 | 97.0 | 87.0 | 8.500 | 6.38 | 6.90 | True | True | True |
| 4 | Adriana Princess | 1180.0 | 2.93 | 2.0 | 7.0 | 93.0 | 81.0 | 90.0 | 87.0 | 7.375 | 5.86 | 6.16 | True | False | True |
Test your code with the cell below!
test_result(apply_selection_improved, df)
✅ You passed the test !
Part 1.2: Analyzing the fairness of the algorithm¶
You are conscious that the admission algorithm you are designing will have a very important impact on the life of students, by deciding whether they get admitted or not to Lakeside Polytechnic. In the previous section, you have paid close attention to making your algorithm equitable by taking into account specific situations that could unfairly penalize some students. But you want to make sure that your algorithm does not indirectly discriminate students on unfair grounds.
Question 1: How fair is our algorithm?¶
In this section, you are going to investigate whether the two different selection methods you implemented are fair:
- Selection base: considering only the student score
- Selection improved: considering the student score and GPA-SAT outliers
Instructions
The cell below will show you the number (left) or percentage (right) of students who pass the selection i.e. who are allowed to enter the university, depending on the quality of their high school. To simplify the analysis the values of the attribute High School Quality (HSQ) are segmented into 3 levels: high, mid, low.
Run the cell below to see the resulting distributions you obtain with your two selection methods: base (bottom) or improved (top).
# segmenting the High School Quality attribute by grouping: 0-2 as low, 3-7 as mid and 8-10 as high
df['High School Quality'].replace({0: "low", 1: "low", 2: "low", 3: "mid", 4: "mid", 5: "mid", 6: "mid", 7: "mid", 8: "high", 9: "high", 10: "high"}, inplace=True)
# plotting the number and percentage of students passing the selection, by High School Quality segment
selection_summary, selection_summary_perct = update_lists_passing_students(df)
plot_selection(selection_summary, selection_summary_perct)
Reflection time!
Do the results look fair according to you?
- Yes
- No
Why? On which criteria do you base your assessment?
Feedback - Click on the "..." below only once you have really tried to answer the question!
On the right-side graph, we can clearly see that a student coming from a lower-quality high school has almost half as many chances to be accepted than a student from a higher-quality high school.
It is also interesting to note that students coming from a higher-quality high school have a 100% chance to be accepted.
Students from lower-quality high schools get slightly more chances with the improved selection whereas students from mid-quality high schools get slightly less chances.
If we define fairness in this context as having equal acceptance rates among groups with different high school quality, then our selection here cannot be said to be fair.
This notion of fairness is called demographic parity: an algorithm is deemed fair if it gives the same rate of positive outcomes to people independently from their demographic group.
Now in the context of university admissions, different people may have differing opinions in terms of what would be 'fair'.
Not only is fairness contextual, there are also different ways to define and measure it, which we will review in the videos.
Did you have a different opinion on the fairness of your selection method?
Question 2: Where does the unfairness come from?¶
You identify that the students with higher-quality high schools have way more chances to be accepted to the university, though the selection gets slightly better with the adjustments made with the second selection.
You decide to explore the dataset in more detail to investigate the possible sources of unfairness with the help of graphical representations and summary statistics.
A. Graphical analysis¶
We provide you with the function choice_visualization that:
- takes as input the type of visualization (boxplot or histogram) and
- outputs the visualization.
Instructions
Choose the type you prefer (boxplot or histogram) and run the cell below!
# BEGIN SOLUTION NO PROMPT
choice_visualization(visu_type = 'boxplot')
# END SOLUTION
""" # BEGIN PROMPT
choice_visualization(visu_type = '...')
"""; # END PROMPT
Reflection time!
Which conclusion can you make based on the plots?
- Students from higher-quality high schools have better GPAs
- Students from higher-quality high schools have better SATs
- Students from higher-quality high schools have greater Interest score
- None of the above
Feedback - Click on the "..." below only once you have really tried to answer the question!
None of the above. The boxplots clearly show that there are almost no differences in the means, medians and ranges of the Interest, GPA and SAT variables between the low, mid or high High School Quality groups.
The histograms may be a bit harder to read but indicate only small variations in the shape of the distribution of the variables between the groups. The main visible difference on these histograms is that the mid group has more students than the other two (the orange line is higher that the other two overall).
B. Summary statistics¶
In addition to the visual inspection, you also check with statistics.
Instructions
Run the cell below to have a quick numerical analysis of the attributes.
# computing the mean, median and standard deviation of SAT, GPA and Interest for the 3 High School Quality groups
analysis = df.groupby(['High School Quality'], sort=False).agg(
{'SAT_norm':['mean', 'median','std'],
'GPA_norm':['mean', 'median','std'],
'Interest':['mean', 'median','std']}).round(2).reindex(['high', 'mid', 'low'])
# adding borders to the resulting analysis table
analysis.style.format(precision = 2).set_table_styles([{'selector': 'th, td', 'props': [('border-style','solid'),('border-width','1px'),('border-color','lightgray')]}])
| SAT_norm | GPA_norm | Interest | |||||||
|---|---|---|---|---|---|---|---|---|---|
| mean | median | std | mean | median | std | mean | median | std | |
| High School Quality | |||||||||
| high | 7.95 | 7.88 | 0.81 | 6.83 | 6.81 | 0.64 | 4.92 | 5.00 | 3.52 |
| mid | 7.83 | 7.88 | 0.92 | 6.87 | 6.80 | 0.93 | 5.09 | 5.00 | 3.27 |
| low | 7.95 | 7.88 | 0.98 | 7.06 | 6.96 | 1.11 | 5.12 | 5.00 | 3.57 |
Reflection time!
- What conclusion can you make based on the table above?
- Students from higher-quality high schools have better GPAs.
- Students from higher-quality high schools have better SATs.
- Students from higher-quality high schools have greater Interest score.
- None of the above.
- Did your conclusion change from the graphical analysis?
- Yes
- No
Feedback - Click on the "..." below only once you have really tried to answer the question!
Overall, there are very few differences in mean, median and standard deviation between the groups for the different attributes:
- Students from the low group have SAT scores that are comparable to that of the high group, and since this is the results of a standardized test, it seems to indicate that the High School Quality does not make a difference in training students for this test.
- For the GPA, students from the low group have higher scores than the other two groups, which could, maybe, indicate a tendency to grade inflation in these schools.
- Students from the low group actually seem to demonstrate more interest than the other two groups.
The information provided by the summary statistics seem to confirm the visual inspection and shows no real differences in Interest, GPA and SAT between the low, mid and high High School Quality groups.
In other words, no bias is visible on these attributes.
So where does the unfairness of your selection algorithm come from?
Question 3: Improving the fairness of the selection process¶
To recap, you have seen so far that:
- the selections you implemented are not fair for students coming from lower-quality high schools.
- the distribution of the different attributes in the dataset are not significantly different across different high school qualities (high, mid, low).
While you have paid close attention to not use attributes such as gender or ethnicity that could lead to discrimination in your algorithm - these are called sensitive attributes - you start to suspect that one of the attributes you use could actually encode hidden bias. This type of attributes is called a proxy attribute: while not being directly sensitive, they encode information related to other sensitive attributes with a substantial degree of accuracy. For example, a zip code is very often a proxy for socioeconomic status or ethnicity.
In this section, you are going to further analyze how the selection algorithm affects students depending on their high school group and identify how you could improve the algorithm.
A. Manual exploration¶
You want to determine whether a different weighting of the attributes in the computation of the selection score could lead to more balanced outcomes.
The cell below allows you to quickly see graphically how a change in the weights of the coefficients change the outcomes per High School Quality.
Instructions
Modify the values of the coefficients and then execute the cell to see if it improves the fairness of the algorithm.
⚠️ The sum of the coefficients must always remain equal to 1.
def change_weights_student_score(df, coeff):
""" changes the coefficients of the student score and updates the selection accordingly
"""
#selection base
df = apply_selection_base(calculate_student_score(normalization(df), coeff).round(2))
# selection improved
df = apply_selection_improved(is_outlier(df))
df['High School Quality'].replace({0: "low", 1: "low", 2: "low", 3: "mid", 4: "mid", 5: "mid", 6: "mid", 7: "mid", 8: "high", 9: "high", 10: "high"}, inplace=True)
selection_summary, selection_summary_perct = update_lists_passing_students(df)
plot_selection(selection_summary, selection_summary_perct)
return selection_summary, selection_summary_perct
df_copy = load_dataset()
##### YOUR CODE HERE #########
# coeff: coefficients to compute the student score ([SAT, GPA, Interest, HSQ])
coeff = [1.0, 0.0, 0.0, 0.0] # SOLUTION
##############################
if np.abs(np.sum(coeff) - 1) < 1e-6:
selection_summary, selection_summary_perct = change_weights_student_score(df_copy, coeff)
else:
raise Exception("The sum of the coefficients must be equal to 1")
Reflection time!
What do you observe from your exploration when modifying the coefficients?
Feedback - Click on the "..." below only once you have really tried to answer the question!
This manual observation should show you that the choice of the coefficients largely influences the fairness of the selections, which means that the unfairness you observe is actually constructed into your Student Score through the choice of the coefficients.
Here is a figure which shows the distribution of the Student Score for different combinations of coefficients:
As this figure illustrates, the coefficient for the High School Quality, in particular, plays a substantial role in the (un)fairness of our selection process.
This raises the question of the legitimacy of using such a criteria for selecting students, which is quite a complex question requiring a clear policy decision from the university given the potential implications.
If you had not analyzed the fairness of the algorithm, this could have been implemented and applied through our algorithm to thousands of students without question.
Another element to note from this exploration is that, while adjusting the coefficients of the Student Score may lead to a fairer selection, it also leads to accepting approximately 80% of applicants, which may not be very realistic as universities generally have limited number of seats, particularly top-tier universities with a high volume of applications.
Why does this matter?
In many countries, the structure of the school system can contribute to systemic bias in access to university. Differences in public school funding, availability of advanced courses, and access to extracurricular opportunities often favor students from wealthier areas, giving them a head start in meeting selective university criteria. As a result, students’ chances of admission are influenced not only by their abilities but also by the resources and support available in their schools and communities.
- In the USA, "school districts rely heavily on local property tax revenue, which means where one lives - particularly in which district - in no small part determines how well one’s neighborhood’s schools are funded. The mutually dependent relationship between economic and racial/ethnic segregation simultaneously depresses revenue and increases costs in racially isolated districts, creating a self-sustaining cycle of unequal opportunity and unequal outcomes."
- In Switzerland, the composition of secondary schools mirrors the social and ethnic inequalities in the neighborhoods. According to Schmutz (2024), "circumstances beyond students' control account for 21% of the variation in achievement at the national level."
- According to a 2022 worldwide study by the OECD that measures 15-year-old students’ skills in reading, mathematics, and science to compare educational systems across countries (PISA: Programme for International Student Assessment), "About 15% of the variation in mathematics performance on average across OECD countries can be attributed to students’ economic, social and cultural background."
In this context, the admission process of universities can play an important role in increasing (or decreasing) the fairness of access to higher education.
B. Optimization of the coefficients¶
You would like to find the optimal values for the coefficients to optimize the selection's fairness.
Since the initial passing criteria is a Student score above 6, you think that if each group of High School Quality had an average Student score of 6, the selection algorithm could become fair.
This is an optimization problem where the goal is to find the optimal weights $w_{SAT}, w_{GPA}, w_{Interest}, w_{HSQ}$ so that the average Student score is equal to 6 for every level of High School Quality.
By "forcing" every level of High School Quality to have an average Student Score of 6 and using it as a criterion for applicant selection, this approach would address the issue of admitting around 80% of applicants mentioned before (it can be compared to the Gaussian rule applied by some university professors when exams are excessively challenging, although in this case, it operates in the opposite direction).
This optimization approach can improve fairness because the SAT, GPA, and Interest attributes have similar distributions across various High School Quality. If the quality of the high school had had an impact on the SAT of its students (for example students from high quality high schools have higher SAT in average), then "forcing" the Student Score to have a mean of 6 for each level of high school quality would disadvantage some students as they might have a higher Student Score originally.
We provide you below with the code to solve this problem, you don't need to understand it, but we provide some details below for your information.
Note
To solve the problem we:
- build a matrix containing the mean values for the different attributes (SAT, GPA, Interest, High School Quality) for each level of High School Quality (from 0 to 10)
- build a target vector with the mean value that we want (6)
- start with a random guess for the coefficients then adjust them to minimize the norm of the difference between the actual value and the target value
$$ A w \approx b \equiv \begin{bmatrix} 7.99 & 6.7 & 5.24 & 0 \\ 7.94 & 7.26 & 5.26 & 1 \\ 7.9 & 7.27 & 4.82 & 2 \\ 7.71 & 7.02 & 5 & 3 \\ 7.74 & 6.94 & 5.42 & 4 \\ 7.97 & 6.65 & 5.06 & 5 \\ 7.87 & 7 & 4.77 & 6 \\ 7.93 & 6.71 & 5.06 & 7 \\ 8.22 & 7.12 & 6.77 & 8 \\ 7.75 & 6.62 & 4.55 & 9 \\ 7.99 & 6.86 & 4.33 & 10 \\ \end{bmatrix} \begin{bmatrix} w_{SAT} \\ w_{GPA} \\ w_{Interest} \\ w_{HSQ} \end{bmatrix} \approx \begin{bmatrix} 6 \\ 6 \\ 6 \\ 6 \\ 6 \\ 6 \\ 6 \\ 6 \\ 6 \\ 6 \\ 6 \end{bmatrix} $$
Note that there are multiple approaches to tackle this problem, and we've opted for a minimization method.
Instructions
Execute the cell below.
Analyze the values you obtain for the different coefficients.
from scipy.optimize import minimize
# Define the matrices and vectors
df_initial = load_dataset()
df_initial= normalization(df_initial)
df_mean = df_initial.groupby('High School Quality').apply(lambda x: pd.Series({"mean_SAT": round(x['SAT_norm'].mean(),2),
"mean_GPA": round(x['GPA_norm'].mean(),2),
"mean_Interest": round(x['Interest'].mean(),2),
"mean_HSQ": round(x['High School Quality'].median(),2),}))
A = df_mean.values
b = np.ones((10, 1)) * 6 # Target vector filled with 6
# Define the objective function to minimize
def objective_function(X):
return np.linalg.norm(np.dot(A, X) - b) # Minimize the norm of the difference
initial_guess = np.random.rand(4) # Initial guess for X
result = minimize(objective_function, initial_guess)
print(f"Optimal Solution [SAT, GPA, Interest, HSQ]: {result.x}")
selection_summary, selection_summary_perct = change_weights_student_score(df_copy, result.x)
Optimal Solution [SAT, GPA, Interest, HSQ]: [ 0.67544379 0.1658548 -0.09444297 -0.00176872]
Reflection time!
- What does a negative value mean for the weight associated to High School Quality?
- Students from higher-quality high schools will be advantaged by the selection, which raises some ethical questions.
- Students from higher-quality high schools will be disadvantaged by the selection, which is fair for everyone.
- Students from lower-quality high schools will be advantaged by the selection, which is fair for everyone.
- Students from lower-quality high schools will be disadvantaged by the selection, which raises some ethical questions.
- None of the above seems correct.
- What do you conclude from the optimization solution?
- Compared to the other attributes (GPA, Interest, High School Quality), the SAT has a small impact on the student score.
- If we had to choose only two attributes, choosing the Interest and High School Quality would be the best choice to make the selection fair.
- Taking into account more attributes, in addition to SAT, GPA, Interest and High School Quality, could make the selection more fair.
- To make the selection fair, the optimization solution suggests penalizing the students from higher-quality high schools.
- None of the above.
Feedback - Click on the "..." below only once you have really tried to answer the question!
- None of the above seems correct.
- To make the selection fair, the optimization solution suggests penalizing the students from higher-level high schools.
The optimization results in small negative coefficients for the attributes Interest and High School Quality, which means that if 2 students have the same SAT and GPA, the one showing higher interest or coming from a higher-quality high school will have a slightly smaller Student score and thus a slightly smaller chance to be selected. This might be considered an unfair disadvantage, however you have to keep in mind that these coefficients are very very small.
Actually, the coefficients resulting from the optimization seem to indicate that more weight should be given to the SAT score and, to a lesser extent to the GPA score, to ensure an equal representation of students from all three levels of High School Quality.
One thing to keep in mind though, is that when you have a limited number of spaces to attribute, increasing the proportion of admissions for students in the low High School Quality group necessarily means decreasing the proportion of admissions in the other groups, which might also be considered unfair. Trying to solve this issue with a selection algorithm only means that the root causes and social factors that play a role in access to higher education are not actually addressed.
Conclusion of Part 1¶
The goal of this first exercise was to make you realize that:
- Unfairness can result from extremely simple algorithms (here a weighted sum!), so it is not because a piece of software is simple that it is automatically fair (and a more complex algorithm is not necessarily fairer!).
- The choice of the data on which an algorithm works plays an important role in shaping the fairness of the outcomes as proxy attributes (in our case High School Quality) can encode hidden biases - you will learn more about this concept in the videos. It is essential to evaluate the fairness of algorithms to be able to detect such hidden biases.
- Making an algorithm perfectly fair is usually impossible, a trade-off often needs to be found between different design choices which lead to different fairness issues (for instance students from higher-level high schools being penalized to obtain a fairer selection overall).
- Algorithms are not a magic bullet, if they result in unfair outcomes it often reflects an underlying societal issue that will not be solved with technical means only. In our case, reconsidering the criteria that are used to select students for admission would require a collaboration with the admission office and inputs from experts in social sciences would be highly valuable to better understand the mechanisms at play.
Part 2: Admission data from Berkeley¶
In Part 1, we explored how the way we design algorithms can affect fairness.
Now, in Part 2, we'll take a closer look at another factor that can influence fairness: the data we use. We'll see how the data can have a big impact on whether our outcomes are fair or not.
Scenario¶
The University of California, Berkeley (UC Berkeley) is challenged by rising numbers of applicants: in the Fall of 2022, they have selected 4,000 students from a pool of over 40,000 applicants. They have heard of your great work for the Lakeside Polytechnic and they contact you to create an algorithm that can sort through a large number of candidates for their upcoming admissions.
The dataset¶
They provide you with historical data containing all the applicants to its graduate programs of one specific year. Your job is to use this historical data to design a sorting algorithm for their current admission process.
The dataset is made up of 12,763 rows and 3 columns as described below:
| Attribute | Values | Description |
|---|---|---|
| Major | A, B, C, D, E, F, or Other | An anonymized major code, equivalent to sections at EPFL. The specific majors are unknown except that A–F are the six majors with the most applicants in Fall 1973. |
| Gender | M or F | Applicant self-reported gender. |
| Admission | Rejected or Accepted | Admission decision. |
⚠️ Disclaimer: The categories used for Gender in this dataset are out of date. We chose not to modify it since this is a public dataset.
Instructions
Run the cell below to load the dataset and see its first rows!
berkeley = load_berkeley_dataset()
berkeley.head()
| Major | Gender | Admission | |
|---|---|---|---|
| 0 | C | F | Rejected |
| 1 | B | M | Accepted |
| 2 | Other | F | Accepted |
| 3 | Other | M | Accepted |
| 4 | Other | M | Rejected |
Before diving into the algorithm creation and based on the lessons learned from your previous mission at Lakeside Polytechnic, you begin by analyzing the historical data to explore possible patterns among the accepted candidates and criteria that might have influenced their admission. You want in particular to look into potential imbalances in the past admission process, which could lead to unfairness in your algorithm.
You will split the analysis in two parts:
- Fairness of representation: Reviewing the candidates who applied and were accepted allows you to assess whether there is an imbalance in the representation of the data. This can provide an initial indication of potential biases in historical data.
- Fairness of outcomes: Looking specifically at the relations between the attributes (Major, Gender) and the outcome variable (Admission) can help you find if certain groups were accepted or rejected more often, and in which circumstances.
Part 2.1: Fairness of representation¶
For this analysis, you choose a graphical approach to explore the distribution of the different attributes in the dataset.
Here are two possible options:
- As seen in the Introduction notebook, you can use the function value_counts from Pandas and then plot the result with
.plot(kind=...) - The library Seaborn provides a function countplot that does both tasks (counting and plotting) at the same time. It take as arguments a dataframe and the name of a column to plot.
Instructions
Plot the distribution of the attributes Major, Gender and Admission in three separate plots.
# Plot 1: Countplot with Major variable
### YOUR CODE HERE ###
sns.countplot(data=berkeley, x='Major') # SOLUTION
###
plt.title('Major Distribution');
# Plot 2: Countplot with Gender variable
### YOUR CODE HERE ###
sns.countplot(x='Gender', data=berkeley) # SOLUTION
###
plt.title('Gender Distribution');
# Plot 3: Countplot with Admission variable
### YOUR CODE HERE ###
sns.countplot(x='Admission', data=berkeley) # SOLUTION
###
plt.title('Admission Distribution');
Reflection time!
What can you say about the distribution of the gender among the applicants? Propose an hypothesis for why it is like this.
Feedback - Click on the "..." below only once you have really tried to answer the question!
We can see that the proportion of men among the candidates is higher than that of women.
There is a large range of factors that can influence the gender distribution in university applications:
- Economic factors: tuition costs, financial aid, and perceptions of long-term economic benefits can shape application rates differently for men and women.
- Cultural and social norms: expectations about gender roles may encourage or discourage certain groups from pursuing higher education.
- Educational preparation: differences in school performance, subject choice, and access to advanced courses affect who feels ready to apply.
- Representation in fields of study: perceptions of belonging in certain disciplines (e.g., STEM, education, healthcare) affect who applies and where.
- Institutional policies: scholarships, outreach programs, and recruitment strategies can shift the balance of applicants by gender.
Did you identify other hypotheses?
One thing that we have not mentioned yet is that the dataset dates back to 1973. In the U.S., this was a period where the gender difference in college applications drastically changed: after an over-representation of men, the gender gap caught up and then reversed to an over-representation of women in applicants. Today, more women apply and graduate from university in the U.S.
See this reference for more information: Goldin, C., Katz, L. F., & Kuziemko, I. (2006). The Homecoming of American College Women: The Reversal of the College Gender Gap. Journal of Economic Perspectives, 20(4), 133–156.
This trend is also present in Switzerland, although it seems less marked.
Part 2.2.: Fairness of outcomes¶
You now want to analyze how the outcome (Admission) relates to different factors. You know that designing an admissions system while taking these aspects into account is critical because it could create serious issues if not correctly handled. By studying past data widely and within specific departments, your goal is to detect any unfairness or bias in past admissions. This investigation aims to make sure that everyone, no matter their background, gets a fair chance during the admission process, making it more equal for all applicants.
A. Outcomes at the university level¶
You look first at the outcomes of the admission process at the level of the whole university.
For this you continue to use the countplot function of Seaborn, in which the parameter hue allows you to group the counts by a separate column.
Instructions
Run the cell below to look at the gender distribution between the students who got accepted and rejected.
sns.countplot(data = berkeley, x = 'Admission', hue = 'Gender')
plt.title("Distribution of admissions at Berkeley")
plt.show()
Now you analyze the data numerically, explicitly looking at the proportions.
For this you use the crosstab function from Pandas, which allows you to easily compute a frequency table for two attributes.
Instructions
Run the cell below to get a summary table.
# First get the frequency table showing raw numbers
print("Raw applicant numbers:")
display(pd.crosstab(berkeley['Gender'], berkeley['Admission']))
# Computing the percentages by gender (i.e. proportions by row)
print("Percentages by gender (%):")
display(pd.crosstab(berkeley['Gender'], berkeley['Admission'], normalize="index").round(4) * 100)
Raw applicant numbers:
| Admission | Accepted | Rejected |
|---|---|---|
| Gender | ||
| F | 1494 | 2827 |
| M | 3738 | 4704 |
Percentages by gender (%):
| Admission | Accepted | Rejected |
|---|---|---|
| Gender | ||
| F | 34.58 | 65.42 |
| M | 44.28 | 55.72 |
Reflection time!
Based on the plot and table above, which gender might be treated unfairly in the past admission process?
- Male
- Female
- None of them, the selection seems to be fair.
Feedback - Click on the "..." below only once you have really tried to answer the question!
- Female
Both the graph and the table show that female applicants are less likely to be accepted compared to male applicants (difference of 10%), which raises fairness issues for the university.
As a careful computer scientist, you want to check your conclusion by taking a closer look at each major's details.
B. Outcomes per major¶
You're aware of the dataset's imbalance, due to more men than women applying. You thus decide to look at the proportions by gender for the different majors.
Instructions
Run the cell below to look at the proportions of male to female applicants by admission status (Accepted or Rejected) and gender (Male or Female) in each major.
plot_admissions_proportions()
Reflection time!
By comparing the number of majors where female applicants are disadvantaged with the number of majors where male applicants are disadvantaged, which option is true according to you?
- Male applicants are the most affected by unfairness and the difference is large.
- Male applicants are the most affected by unfairness but the difference is not large.
- Female applicants are the most affected by unfairness and the difference is large.
- Female applicants are the most affected by unfairness but the difference is not large.
- None of the above.
Feedback - Click on the "..." below only once you have really tried to answer the question!
- Male applicants are the most affected by unfairness but the difference is not large.
By looking at the plot of proportions, you can see that there are 4 majors out of 7 where male applicants are disadvantaged. From there, you can conclude that male applicants seem to be most frequently treated unfairly when you look at the admission results per major, which contradicts the first result when you viewed them globally. Nevertheless, you can see that the differences are pretty small for each major indicating that the unfairness is not significant (Note: The term "significant" is being used informally here. In a rigorous statistical analysis, determining the significance of results involves conducting specific statistical tests and examining the associated p-values).
Conclusion
At the university level, you concluded that female applicants were disadvantaged in the admission process as they had a 34% of chance of being accepted whereas male applicants had 44%.
However, at the major level, you concluded that male applicants were disadvantaged in the admission process as they were more likely to be rejected in the majority of majors (4 out of 7).
Actually, the Berkeley dataset is a classic example of Simpson's paradox.
In short, Simpson's paradox occurs when a trend appears in different groups of data but reverses when these groups are combined. It arises due to confounding variables that distort the overall perception of relationships within the data, leading to a misleading or contradictory conclusion. Essentially, what holds true for individual groups might not hold when these groups are merged.
This phenomenon can make the assessment of fairness very complex.
Indeed, in your analysis, you may have first assumed that, if there is a bias in the proportion of female applicants admitted, it is because of a link between gender and decision to admit. But there is actually a link between gender and department to which admission is sought. The tendency of men and women to seek entry to different departments is marked, which alters the overall analysis.
Part 2.3: Improving the fairness of the selection process¶
Now that you have analyzed the results of prior admissions, you wonder what you could do to improve the situation.
Reflection time!
Based on those results, propose a solution that could improve the fairness of the admission process at Berkeley. Give one technical solution (can be outside of the scope of this notebook) and one non-technical solution.
Note:
- By technical, we mean a solution that involves the application of specific algorithms, methodologies, or technologies to address the problem.
- By non-technical, we mean a solution that focuses on human-centric approaches, policies, or strategies that do not rely heavily on advanced technology or algorithms.
Feedback - Click on the "..." below only once you have really tried to answer the question!
Technical solution:
If you define fairness as having equal chances of acceptance across majors, as in the Demographic parity metric, you could aim for an equal percentage of admissions for men and women in each major. This could result in more male students than female students in certain majors due to the higher number of male applicants overall.Non-technical solution:
These admissions are the result of decisions made by people, e.g. in the administration offices of the different majors. Interviewing these persons to understand the process from their point of view, the challenges faced by each specific major in terms of the profiles of their applicants as well as their constraints (e.g. limits in terms of space), would be very important to improve the situation. Since the goal of an admission algorithm would be to help those in charge of this process, it is fundamental to take their inputs into account.
In addition, exploring the reasons behind the lower number of female applicants overall would also be essential to improve the gender balance at the university.
Conclusion of Part 2¶
The goal of this first exercise was to make you realize that:
- Bias might be present in data collected in the past. Biases in data can stem from various sources such as historical imbalances, societal prejudices, or even inadvertent errors in data collection. As we will see next week, employing machine learning models trained on such biased data can perpetuate or exacerbate existing unfairness.
- Analyzing fairness can be complex, highlighting the complexity of identifying its root causes due to the multitude of factors involved. Working with experts in human and social sciences who have a deeper understanding of the complex social phenomena at play can prove invaluable.
Synthesis¶
It is now time to step back and reflect on what you have discovered in this notebook!
Final reflection time!
Summarize what you have learned from this notebook:
- Name two sources of bias in software
- Explain the concept of proxy attribute
- Explain what demographic parity is and what it represents
- Describe Simpson's paradox and why it can be a challenge when analyzing bias in data
Feedback - Click on the "..." below only once you have really tried to answer the question!
- The notebook illustrates two sources of bias in software: algorithms and data.
- A proxy attribute is an attribute that should not lead to ethical issues such as discrimination in apparence but carries hidden biases by correlating with sensitive attributes such as ethnicity, gender or socioeconomic status.
- Demographic parity is a fairness criterion that requires an algorithm’s positive outcome (e.g., admissions, hiring) to be distributed equally across different demographic groups.
- Simpson's paradox is a statistical phenomenon where a trend appears in several groups of data but reverses or disappears when the groups are combined. It creates a challenge when looking for bias in data because aggregated patterns may hide or even contradict subgroup trends, making it difficult to detect where unfairness truly occurs.
Congratulations! You have finished this notebook!