Skip to Content
Course content

209: Fairness Metrics for Predictive Models in R

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're managing a hiring process for a tech company. You've decided to be "objective," so you build a tool that screens resumes. On paper, the tool is brilliant—it has 90% accuracy. But after a month, you notice something unsettling: the tool is recommending 80% of male candidates for interviews, but only 20% of female candidates, even though the pool of applicants was split 50/50. Now you're in a bind. Is the tool reflecting a real difference in skill, or is it just echoing the biases of the historical data it was trained on?

This is the core of fairness metrics. In R, we don't just care if the model is "right" on average; we care about how the errors are distributed across different groups. Let's map that hiring analogy to the technical concepts we'll use in the code:

  • The Protected Attribute: In our analogy, this is gender. In your code, this is the column you want to ensure doesn't unfairly influence the outcome (like race, age, or zip code).
  • Demographic Parity: This is the "80% vs 20%" observation. It asks: "Is the probability of a positive outcome the same for all groups, regardless of their actual qualifications?"
  • Equalized Odds: This is more nuanced. It asks: "Of the people who are actually qualified, does the model identify them at the same rate regardless of their group?" If the model misses qualified women more often than qualified men, you've failed this metric.

The Illusion of Overall Accuracy

I've seen a lot of junior devs make the mistake of reporting a high AUC or Accuracy score and calling it a day. The problem is that high overall accuracy can hide systemic failure for a minority group. If 95% of your users are from Group A and 5% are from Group B, your model could be 0% accurate for Group B and you'd still have a "95% accurate" model. That's not a success; it's a liability.

To handle this in R, I highly recommend the fairmodels package. It integrates beautifully with caret and tidymodels, allowing you to wrap your existing model and audit it without rewriting your entire pipeline.

Auditing a Loan Approval Model

Let's look at a concrete example. Suppose we have a model predicting whether a loan should be approved. We want to make sure the model isn't discriminating based on a protected attribute, like age_group.

library(fairmodels)
library(caret)

# Let's assume 'loan_model' is already trained and 'test_data' contains our holdout set
# We'll use 'age_group' as our protected attribute

# 1. Generate predictions
predictions <- predict(loan_model, test_data)
actuals <- test_data$loan_status

# 2. Wrap the model in a fairmodels object
# This is where the magic happens. We tell it which column is the protected one.
fmodel <- fairmodels(loan_model, 
                     data = test_data, 
                     protected = test_data$age_group, 
                     outcome = actuals)

# 3. View the fairness metrics
print(fmodel)

When you run this, you'll see a set of ratios. In fairness auditing, a ratio of 1.0 is perfect parity. If you see a "Statistical Parity" ratio of 0.6, it means the protected group is receiving the positive outcome at only 60% of the rate of the privileged group. That's a red flag you can't ignore.

Visualizing the Gap

Numbers are fine, but stakeholders usually need a visual to actually "feel" the bias. The plot_fairness function in R is your best friend here. It allows you to see exactly where the disparity lies—whether it's in the false positive rate (giving loans to people who can't pay them back) or the false negative rate (denying loans to people who are actually creditworthy).

# This produces a comprehensive plot of various fairness metrics
plot_fairness(fmodel)

If you see that the False Negative Rate is significantly higher for the "Young" age group, your model is essentially "stricter" with young people than it is with older people, even when their financial profiles are identical. That's the kind of insight that saves a company from a lawsuit or a PR disaster.

Choosing Your Definition of Fairness

Here is the hard truth: you often cannot satisfy every fairness metric simultaneously. There is a mathematical trade-off between Demographic Parity and Predictive Parity. If you force the outcomes to be equal (Demographic Parity) but the underlying ground-truth distributions are different, you'll inevitably sacrifice some accuracy or introduce other types of unfairness.

As an engineer, your job isn't to find a "perfect" number, but to surface these trade-offs to the product owners. You provide the metrics; they provide the policy decision on which type of fairness is most critical for the specific business case.




📋 Practical Task

Auditing Gender Bias in a Mortgage Approval Pipeline

You have been handed a pre-trained mortgage approval model and a test dataset. Your task is to determine if the model exhibits gender bias and identify specifically which metric (Statistical Parity or Equal Opportunity) is being violated.

Setup:

# Load necessary libraries
library(fairmodels)
library(caret)

# Create a dummy dataset for the exercise
set.seed(42)
n <- 1000
test_data <- data.frame(
  gender = sample(c("Male", "Female"), n, replace = TRUE),
  income = rnorm(n, 50000, 15000),
  credit_score = rnorm(n, 700, 50)
)
# Simulate a biased ground truth
test_data$approved <- ifelse(test_data$gender == "Male" & test_data$credit_score > 680, 1,
                      ifelse(test_data$gender == "Female" & test_data$credit_score > 720, 1, 0))

# Create a simple model that mirrors this bias
loan_model <- glm(approved ~ gender + income + credit_score, data = test_data, family = binomial)

Your Requirements:

  • Use the fairmodels package to wrap the loan_model.
  • Identify gender as the protected attribute.
  • Calculate the fairness metrics and print them to the console.
  • Generate a fairness plot to visualize the disparity.
  • Based on the output, write a short comment in your code explaining whether the model satisfies Statistical Parity (equal outcome rates) or Equal Opportunity (equal true positive rates).
Rating
0 0

There are no comments for now.

to be the first to leave a comment.