From d69951d2f35af1668b04704a5420a987b85807fe Mon Sep 17 00:00:00 2001 From: propcgamer20-png Date: Mon, 6 Jul 2026 09:13:39 +0530 Subject: [PATCH 1/7] Add explainer for predictive parity in fairness metrics Added an explainer on predictive parity, detailing its definition, formula, and implications in fairness metrics. Included real-world examples, detection code, and when to use predictive parity. --- explainers/predictive-parity.md | 102 ++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 explainers/predictive-parity.md diff --git a/explainers/predictive-parity.md b/explainers/predictive-parity.md new file mode 100644 index 0000000..c74ab4d --- /dev/null +++ b/explainers/predictive-parity.md @@ -0,0 +1,102 @@ +# Explainer: What Is Predictive Parity? + +*Metrics · Sufficiency-based fairness · COMPAS dual-reading · Chouldechova impossibility* + +## The one-sentence version + +A model satisfies **predictive parity** when a positive prediction means the same thing regardless of group membership — i.e., the Positive Predictive Value (PPV) is equal across groups. If a "high risk" flag turns into an actual bad outcome for 63% of one group and 63% of another, the flag has equal *meaning*, even if the two groups don't get flagged at the same rate. + +## The formula + +For a protected attribute with groups `a` and `b`: + +``` +PPV(group) = TP(group) / (TP(group) + FP(group)) + +Predictive Parity holds when: PPV(a) ≈ PPV(b) +``` + +This is a **sufficiency** metric — it conditions on the *prediction*, not the ground truth. That's what separates it from Equalized Odds (Explainer 04), which conditions on the ground truth and requires equal TPR/FPR instead. + +| Metric | Conditions on | Asks | +|---|---|---| +| Demographic Parity | nothing (prior) | Are positive predictions handed out at the same rate? | +| Equalized Odds | true label | Given the actual outcome, are error rates equal? | +| **Predictive Parity** | **predicted label** | **Given a positive prediction, is it equally trustworthy?** | + +## Real-world proof: the COMPAS dual-reading + +This is the metric at the center of the most famous fairness dispute in the field. In 2016, ProPublica's investigation of the COMPAS recidivism tool and Northpointe (its vendor) reached opposite conclusions from the *same dataset*, because they were checking different fairness criteria: + +- **ProPublica's reading (error-rate balance / Equalized Odds):** the false-positive rate for Black defendants was roughly double that of white defendants — meaning Black defendants who did *not* reoffend were far more likely to be wrongly flagged high-risk. +- **Northpointe's reading (predictive parity):** the PPV was close across race — a "high risk" label corresponded to actual reoffending at a similar rate for both groups, which Northpointe presented as evidence the tool wasn't biased. + +Both readings are internally consistent. Both are checking a real, legitimate fairness property. And **both cannot hold at once** unless base rates match — which is exactly the problem. + +## Why this conflicts with Equalized Odds + +Chouldechova (2017) proved the mechanism behind the COMPAS dispute directly: if a score satisfies predictive parity, but the underlying prevalence (base rate) of the outcome differs between groups, the false positive and false negative rates **cannot** be equal across those groups. It's not a bug in COMPAS — it's an algebraic identity. Kleinberg, Mullainathan, and Raghavan reached an equivalent conclusion in the same year via a separate impossibility result. + +In the Broward County COMPAS data, recidivism prevalence differs meaningfully between Black defendants and white defendants. Given that gap, no risk score can be simultaneously well-calibrated (predictive parity) *and* error-balanced (equalized odds) — one of the two has to give. This is the concrete case behind **Explainer 06 — Why Fairness Metrics Conflict**; read that explainer for the full impossibility-theorem writeup. + +## Detection code + +```python +import pandas as pd + +def predictive_parity_gap(df: pd.DataFrame, y_true: str, y_pred: str, group_col: str): + """ + Computes Positive Predictive Value (PPV) per group and the resulting + predictive-parity gap. A gap near 0 indicates predictive parity; a large + gap means a positive prediction is more trustworthy for one group than + another. + """ + ppv_by_group = {} + for group, sub in df.groupby(group_col): + predicted_positive = sub[sub[y_pred] == 1] + if len(predicted_positive) == 0: + ppv_by_group[group] = float("nan") + continue + true_positives = (predicted_positive[y_true] == 1).sum() + ppv_by_group[group] = true_positives / len(predicted_positive) + + groups = list(ppv_by_group.keys()) + gap = max(ppv_by_group.values()) - min(ppv_by_group.values()) + + print("PPV by group:") + for g, v in ppv_by_group.items(): + print(f" {g}: {v:.2%}") + print(f"Predictive Parity Gap: {gap:.2%}") + + return ppv_by_group, gap +``` + +Pair this with the base-rate check below — a small PPV gap next to a large base-rate gap is the signature Chouldechova identified: + +```python +def base_rate_gap(df: pd.DataFrame, y_true: str, group_col: str): + rates = df.groupby(group_col)[y_true].mean() + print("Base rate (true prevalence) by group:") + print(rates) + return rates.max() - rates.min() +``` + +## When predictive parity is (and isn't) the right check + +**Use it when:** the cost of the decision falls on whoever receives the positive label, and you care whether that label is equally reliable — e.g., a "high risk" score used to justify pretrial detention, a "high cost" flag used to deny a claim. + +**Don't rely on it alone when:** base rates differ across groups for reasons rooted in structural inequality rather than individual behavior (see Explainer 05 — Disparate Impact, and the base-rate discussion in the Healthcare Readmission audit). Satisfying predictive parity in that setting can still leave one group bearing a much higher error-rate burden, exactly as COMPAS did. + +## Related + +- Explainer 04 — Equalized Odds (the metric predictive parity trades off against) +- Explainer 05 — Disparate Impact / 80% Rule +- Explainer 06 — Why Fairness Metrics Conflict (the impossibility theorem this explainer is a worked instance of) +- Experiment 01 — COMPAS (the dataset and audit code referenced above) + +## References + +- Angwin, J. et al., "Machine Bias," ProPublica, 2016. +- Chouldechova, A., "Fair Prediction with Disparate Impact," *Big Data*, 2017. +- Kleinberg, J., Mullainathan, S., Raghavan, M., "Inherent Trade-Offs in the Fair Determination of Risk Scores," ITCS 2017. +- Dieterich, W., Mendoza, C., Brennan, T., "COMPAS Risk Scales: Demonstrating Accuracy Equity and Predictive Parity," Northpointe, 2016. From e70288bc85ad75dc99cc9e121a071ac7689daa2e Mon Sep 17 00:00:00 2001 From: propcgamer20-png Date: Mon, 6 Jul 2026 09:21:03 +0530 Subject: [PATCH 2/7] Revise predictive parity explainer for clarity and detail Updated the explainer on predictive parity to clarify definitions, metrics, and their implications in fairness assessments. Added sections on limitations, trade-offs, and related concepts. --- explainers/predictive-parity.md | 128 +++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 42 deletions(-) diff --git a/explainers/predictive-parity.md b/explainers/predictive-parity.md index c74ab4d..92558ae 100644 --- a/explainers/predictive-parity.md +++ b/explainers/predictive-parity.md @@ -1,55 +1,73 @@ -# Explainer: What Is Predictive Parity? +# What Is Predictive Parity? -*Metrics · Sufficiency-based fairness · COMPAS dual-reading · Chouldechova impossibility* +> *COMPAS's defenders and its critics ran fairness checks on the same defendants and reached opposite verdicts - because predictive parity and equalized odds are both legitimate, and when base rates differ, they mathematically cannot both hold.* -## The one-sentence version +## The One-Sentence Definition -A model satisfies **predictive parity** when a positive prediction means the same thing regardless of group membership — i.e., the Positive Predictive Value (PPV) is equal across groups. If a "high risk" flag turns into an actual bad outcome for 63% of one group and 63% of another, the flag has equal *meaning*, even if the two groups don't get flagged at the same rate. +**Predictive parity** is satisfied when the Positive Predictive Value, the share of positively-flagged cases that are actually positive, is equal across groups, so a "high risk" label carries the same real-world meaning no matter who receives it. -## The formula +## Why It Matters -For a protected attribute with groups `a` and `b`: +Algorithmic risk scores decide who gets held before trial, whose insurance claim gets flagged for review, and whose loan application gets extra scrutiny. Over 1M+ people are assessed by COMPAS-style recidivism tools every year, and 46 US states have used one in criminal sentencing. When a vendor says a "high risk" flag is trustworthy, predictive parity is usually the claim being made: given the flag, the predicted outcome holds up at the same rate for every group. -``` -PPV(group) = TP(group) / (TP(group) + FP(group)) +The non-obvious part is that "trustworthy for everyone" is not the same fairness property as "fair to everyone." A score can satisfy predictive parity perfectly and still send one group to jail on false pretenses far more often than another. Which group bears that cost depends on the false positive rate, a completely different metric, and the two cannot both be balanced unless the underlying base rates already match. -Predictive Parity holds when: PPV(a) ≈ PPV(b) -``` +## Predictive Parity vs Equalized Odds -This is a **sufficiency** metric — it conditions on the *prediction*, not the ground truth. That's what separates it from Equalized Odds (Explainer 04), which conditions on the ground truth and requires equal TPR/FPR instead. +Predictive parity is a **sufficiency** metric: it conditions on the *prediction* and asks whether that prediction means the same thing across groups. Equalized Odds (see [explainers/equalized-odds.md](equalized-odds.md)) is a **separation** metric: it conditions on the *true outcome* and asks whether error rates match. -| Metric | Conditions on | Asks | +| Metric | Conditions on | Question it asks | |---|---|---| | Demographic Parity | nothing (prior) | Are positive predictions handed out at the same rate? | | Equalized Odds | true label | Given the actual outcome, are error rates equal? | -| **Predictive Parity** | **predicted label** | **Given a positive prediction, is it equally trustworthy?** | +| Predictive Parity | predicted label | Given a positive prediction, is it equally trustworthy? | -## Real-world proof: the COMPAS dual-reading +``` +PPV(group) = TP(group) / (TP(group) + FP(group)) -This is the metric at the center of the most famous fairness dispute in the field. In 2016, ProPublica's investigation of the COMPAS recidivism tool and Northpointe (its vendor) reached opposite conclusions from the *same dataset*, because they were checking different fairness criteria: +Predictive Parity holds when: PPV(group a) ≈ PPV(group b) +``` -- **ProPublica's reading (error-rate balance / Equalized Odds):** the false-positive rate for Black defendants was roughly double that of white defendants — meaning Black defendants who did *not* reoffend were far more likely to be wrongly flagged high-risk. -- **Northpointe's reading (predictive parity):** the PPV was close across race — a "high risk" label corresponded to actual reoffending at a similar rate for both groups, which Northpointe presented as evidence the tool wasn't biased. +Chouldechova (2017) proved the mechanism algebraically: if predictive parity holds but the base rate of the outcome differs between two groups, the false positive rate and false negative rate cannot be equal across those same groups. It is not a modeling flaw. It is an identity that follows directly from the definitions once base rates diverge. -Both readings are internally consistent. Both are checking a real, legitimate fairness property. And **both cannot hold at once** unless base rates match — which is exactly the problem. +## Concrete Example: COMPAS - Audit 01 -## Why this conflicts with Equalized Odds +The [COMPAS](../COMPAS/) audit in this repo uses the same ProPublica recidivism dataset at the center of the 2016 "Machine Bias" investigation, where race is the protected attribute and the target is two-year re-offense. -Chouldechova (2017) proved the mechanism behind the COMPAS dispute directly: if a score satisfies predictive parity, but the underlying prevalence (base rate) of the outcome differs between groups, the false positive and false negative rates **cannot** be equal across those groups. It's not a bug in COMPAS — it's an algebraic identity. Kleinberg, Mullainathan, and Raghavan reached an equivalent conclusion in the same year via a separate impossibility result. +ProPublica and Northpointe (COMPAS's vendor) checked different fairness criteria on that dataset and both were internally correct. ProPublica's reading, an error-rate check, found the false positive rate for Black defendants was roughly double that of white defendants: Black defendants who did not reoffend were flagged high-risk far more often. Northpointe's reading, a predictive-parity check, found PPV was close across race: a "high risk" label corresponded to actual reoffending at a similar rate for both groups. Northpointe presented that second result as proof the tool was not biased. -In the Broward County COMPAS data, recidivism prevalence differs meaningfully between Black defendants and white defendants. Given that gap, no risk score can be simultaneously well-calibrated (predictive parity) *and* error-balanced (equalized odds) — one of the two has to give. This is the concrete case behind **Explainer 06 — Why Fairness Metrics Conflict**; read that explainer for the full impossibility-theorem writeup. +```python +# Approximating each side's check on a COMPAS-style dataframe +compas["flagged_high_risk"] = (compas["decile_score"] >= 7).astype(int) -## Detection code +ppv_by_race, gap = predictive_parity_gap( + compas, y_true="two_year_recid", y_pred="flagged_high_risk", group_col="race" +) +``` + +Both readings are correct and both cannot hold simultaneously once the two racial groups in the dataset have different recidivism base rates, which they do. Satisfying predictive parity here did nothing to close the false-positive gap ProPublica measured; the two metrics were trading off against each other the entire time, exactly as Chouldechova's proof predicts. + +## Detection Code + +Compute Positive Predictive Value per group, then pair it with a base-rate check, since a small PPV gap next to a large base-rate gap is the signature Chouldechova identified. ```python import pandas as pd + def predictive_parity_gap(df: pd.DataFrame, y_true: str, y_pred: str, group_col: str): """ Computes Positive Predictive Value (PPV) per group and the resulting - predictive-parity gap. A gap near 0 indicates predictive parity; a large - gap means a positive prediction is more trustworthy for one group than - another. + predictive-parity gap. + + Parameters: + df: dataframe containing predictions and outcomes + y_true: column name of the ground-truth binary label + y_pred: column name of the model's binary prediction + group_col: column name of the protected attribute + + Returns: + (dict of PPV by group, float gap between the max and min PPV) """ ppv_by_group = {} for group, sub in df.groupby(group_col): @@ -60,7 +78,6 @@ def predictive_parity_gap(df: pd.DataFrame, y_true: str, y_pred: str, group_col: true_positives = (predicted_positive[y_true] == 1).sum() ppv_by_group[group] = true_positives / len(predicted_positive) - groups = list(ppv_by_group.keys()) gap = max(ppv_by_group.values()) - min(ppv_by_group.values()) print("PPV by group:") @@ -69,34 +86,61 @@ def predictive_parity_gap(df: pd.DataFrame, y_true: str, y_pred: str, group_col: print(f"Predictive Parity Gap: {gap:.2%}") return ppv_by_group, gap -``` -Pair this with the base-rate check below — a small PPV gap next to a large base-rate gap is the signature Chouldechova identified: -```python def base_rate_gap(df: pd.DataFrame, y_true: str, group_col: str): + """ + Computes the true prevalence of the positive outcome per group. + A large gap here next to a small PPV gap indicates the Chouldechova + trade-off is active: predictive parity is being bought at the cost + of unequal error rates. + """ rates = df.groupby(group_col)[y_true].mean() print("Base rate (true prevalence) by group:") print(rates) return rates.max() - rates.min() + + +# Usage example +# predictive_parity_gap(compas_df, y_true="two_year_recid", y_pred="flagged_high_risk", group_col="race") +# base_rate_gap(compas_df, y_true="two_year_recid", group_col="race") ``` -## When predictive parity is (and isn't) the right check +## Limitations and Trade-offs + +### 1. It cannot be satisfied alongside Equalized Odds when base rates differ + +This is not a modeling choice, it is Chouldechova's impossibility result. If your domain has meaningfully different base rates across groups, you have to pick which metric to optimize for and be explicit about why. + +### 2. A close PPV does not mean the harm is evenly distributed + +As the COMPAS case shows, predictive parity can hold while one group absorbs a much larger false-positive rate. Reporting PPV alone, without the accompanying false-positive and false-negative rates, can make a system look fairer than the people affected by it actually experience. + +### 3. Small subgroups produce noisy PPV estimates + +PPV is computed only over the positively-predicted subset. If a group is small or rarely flagged positive, the denominator shrinks and the estimate becomes unstable. A large-looking gap in a small subgroup may be sampling noise rather than a real disparity, and a small gap can hide real disparity in a subgroup too small to measure reliably. + +### 4. It says nothing about where the threshold is set + +Predictive parity is a property of the decision threshold that produced the predictions, not a property of the underlying score. Moving the threshold up or down can shift PPV without changing whether the underlying risk score itself is fair, so a system can be re-tuned to pass a predictive-parity check without addressing the base-rate disparity driving it. + +## Related Concepts + +* [What is Equalized Odds?](equalized-odds.md) - the error-rate metric predictive parity trades off against once base rates diverge. +* [What is Demographic Parity?](demographic-parity.md) - the simpler, prior-only metric that ignores ground truth entirely, unlike predictive parity's reliance on it. +* [Why Fairness Metrics Conflict](fairness-metric-conflicts.md) - the full impossibility-theorem write-up that this explainer's COMPAS case is a worked instance of. +* [What is Disparate Impact?](disparate-impact.md) - a rate-based legal threshold that, like predictive parity, can be satisfied while a different fairness property is violated. -**Use it when:** the cost of the decision falls on whoever receives the positive label, and you care whether that label is equally reliable — e.g., a "high risk" score used to justify pretrial detention, a "high cost" flag used to deny a claim. +## Related Projects in This Repo -**Don't rely on it alone when:** base rates differ across groups for reasons rooted in structural inequality rather than individual behavior (see Explainer 05 — Disparate Impact, and the base-rate discussion in the Healthcare Readmission audit). Satisfying predictive parity in that setting can still leave one group bearing a much higher error-rate burden, exactly as COMPAS did. +* [`COMPAS/`](../COMPAS/) - the audit built on the same ProPublica dataset behind the real-world predictive-parity dispute described above. -## Related +## Further Reading -- Explainer 04 — Equalized Odds (the metric predictive parity trades off against) -- Explainer 05 — Disparate Impact / 80% Rule -- Explainer 06 — Why Fairness Metrics Conflict (the impossibility theorem this explainer is a worked instance of) -- Experiment 01 — COMPAS (the dataset and audit code referenced above) +* [Angwin, J. et al. (2016): Machine Bias](https://www.propublica.org/article/machine-bias-risk-assessments-in-criminal-sentencing) - the original ProPublica investigation that surfaced the COMPAS false-positive disparity. +* [Chouldechova, A. (2017): Fair Prediction with Disparate Impact](https://arxiv.org/abs/1610.07524) - the algebraic proof that predictive parity and error-rate balance conflict when base rates differ. +* Kleinberg, J., Mullainathan, S., Raghavan, M. (2017): Inherent Trade-Offs in the Fair Determination of Risk Scores, ITCS 2017 - an independent impossibility result reaching the same conclusion via a different route. -## References +--- -- Angwin, J. et al., "Machine Bias," ProPublica, 2016. -- Chouldechova, A., "Fair Prediction with Disparate Impact," *Big Data*, 2017. -- Kleinberg, J., Mullainathan, S., Raghavan, M., "Inherent Trade-Offs in the Fair Determination of Risk Scores," ITCS 2017. -- Dieterich, W., Mendoza, C., Brennan, T., "COMPAS Risk Scales: Demonstrating Accuracy Equity and Predictive Parity," Northpointe, 2016. +*Part of [The Fair Code Project](https://instagram.com/thefaircodeproject) - exposing and fixing algorithmic bias with real data and open code.* From b57cf9eed77bf8cf2ae0231c5359f8fa264e062a Mon Sep 17 00:00:00 2001 From: propcgamer20-png Date: Mon, 6 Jul 2026 09:21:31 +0530 Subject: [PATCH 3/7] Update README with confounding variable and predictive parity --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7cbf7b2..dad28f8 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,8 @@ Fair-Code/ │ ├── how-ai-detects-patterns.md │ ├── distribution-shift.md │ ├── ai-objectivity-myth.md -│ └── confounding-variable.md +│ ├── confounding-variable.md +│ └── predictive-parity.md │ ├── CHANGELOG.md ├── CITATION.cff @@ -567,6 +568,7 @@ features = [ | [What Is Distribution Shift?](explainers/distribution-shift.md) | Why a model that passes a bias audit at deployment can become biased again as the population it serves changes | | [The Biggest Myth About AI Objectivity](explainers/ai-objectivity-myth.md) | Why "it's just math" isn't a defense - models trained on biased history produce biased outcomes, and the math just makes them harder to challenge | | [What Is a Confounding Variable?](explainers/confounding-variable.md) | How a hidden third variable creates spurious correlations between a feature and an outcome - and why removing the protected attribute leaves the bias intact until the confounder is removed too | +| [What Is Predictive Parity?](explainers/predictive-parity.md) | Why the ProPublica vs Northpointe COMPAS dispute was really two correct fairness checks that cannot both hold when base rates differ | --- @@ -715,6 +717,7 @@ directly. Run the tests with `pytest tests/`. - [x] Explainer: What Is Distribution Shift - [x] Explainer: The Biggest Myth About AI Objectivity - [x] Explainer: What Is a Confounding Variable? +- [x] Explainer: What Is Predictive Parity? - [ ] Explainer: Why Accuracy Is Not Enough in Healthcare AI - [ ] Explainer: False Positives and False Negatives in Medical Risk Models - [ ] Facial recognition accuracy gaps (MIT Gender Shades methodology) @@ -771,4 +774,4 @@ Data. Code. Accountability. One post at a time. --- -*All datasets used in this project are publicly available. Fair Code is for educational and awareness purposes.* \ No newline at end of file +*All datasets used in this project are publicly available. Fair Code is for educational and awareness purposes.* From 10558bb9934c3fc62aba75906645d87ca3bd6af5 Mon Sep 17 00:00:00 2001 From: propcgamer20-png Date: Mon, 6 Jul 2026 09:22:06 +0530 Subject: [PATCH 4/7] Add entry for predictive parity in CONTRIBUTING.md --- CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 407c205..e3b796f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -273,6 +273,7 @@ Explainers live in `explainers/` and should make one fairness concept easy to un | `distribution-shift.md` | Why a model that passes a fairness audit can become biased again as the population it serves changes | | `ai-objectivity-myth.md` | Why "it's just math" isn't a defense - models trained on biased history reproduce that bias | | `confounding-variable.md` | How a hidden third variable creates spurious correlations that persist after protected-attribute removal - and how to detect and adjust for it | +| `predictive-parity.md` | Why an equally trustworthy positive prediction across groups can still hide an unequal false-positive burden | ### A good explainer should include @@ -412,4 +413,4 @@ Include in the PR description: --- -All datasets used in this project are publicly available. Fair Code is for educational and awareness purposes. \ No newline at end of file +All datasets used in this project are publicly available. Fair Code is for educational and awareness purposes. From f2206082d3a0483bdcf564e65ed415a4e7e19870 Mon Sep 17 00:00:00 2001 From: propcgamer20-png Date: Mon, 6 Jul 2026 09:22:38 +0530 Subject: [PATCH 5/7] Add Predictive Parity explainer and update documentation Added a new explainer on Predictive Parity with detailed comparisons and limitations, along with updates to the website and documentation. --- CHANGELOG.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99b4ac4..031450d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to Fair Code are documented here. --- +## [1.2.1] - 6 Jul 2026 +### Added +- Explainer: Predictive Parity - `predictive-parity.md` created (contributed by @propcgamer20-png), added to `index.html`, `README.md`, and `CONTRIBUTING.md` + - Full explainer covering predictive parity as a sufficiency metric: Positive Predictive Value equal across groups, and why that is a fundamentally different fairness check than error-rate parity + - Comparison table across Demographic Parity, Equalized Odds, and Predictive Parity by what each conditions on + - Real-world proof anchored to Audit 01 (COMPAS): the 2016 ProPublica vs Northpointe dispute, where ProPublica's error-rate reading found a roughly 2x false-positive gap for Black defendants while Northpointe's predictive-parity reading found PPV close across race, and Chouldechova's proof for why both readings can be correct at once + - Detection code: `predictive_parity_gap()` and `base_rate_gap()` - pandas group-wise PPV and base-rate computation, paired to surface the Chouldechova trade-off signature + - Four numbered limitations (impossibility with Equalized Odds under unequal base rates, uneven harm despite equal PPV, small-subgroup PPV noise, threshold-tuning blind spot), cross-links to equalized-odds, demographic-parity, disparate-impact, and fairness-metric-conflicts, and three further reading citations (Angwin et al. 2016, Chouldechova 2017, Kleinberg et al. 2017) + - Nav dropdown (desktop + mobile), ticker strips, and roadmap updated on website +### Changed +- `README.md`: `predictive-parity.md` added to explainers table, repository structure tree, and What's Next checklist +- `CONTRIBUTING.md`: `predictive-parity.md` added to existing explainers table + +--- + ## [1.2.0] - "Open Tools & Causal Foundations" - 30 Jun 2026 First release since **v1.1.0** (9 Jun 2026). The headline is the **Open Dataset Profiler** - Fair Code's first interactive, bring-your-own-data tool, turning the project from a showcase of six fixed audits into something visitors run on their own CSVs. This release also bundles the six explainers shipped between v1.1.0 and now, which deepen the causal and statistical foundations behind the audits. @@ -286,4 +301,4 @@ First release since **v1.1.0** (9 Jun 2026). The headline is the **Open Dataset - `fair.py`, `unfair.py`, dataset, proof images - Interactive website (`index.html`) deployed at fair-code-five.vercel.app - Light mode, mobile-responsive layout -- README with project overview, results table, and methodology \ No newline at end of file +- README with project overview, results table, and methodology From d6edb078ad162669fa15dc421323a3b7eeaa7184 Mon Sep 17 00:00:00 2001 From: propcgamer20-png Date: Mon, 6 Jul 2026 09:23:33 +0530 Subject: [PATCH 6/7] Add 'Predictive Parity' explainer data --- assets/explainers-data.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/assets/explainers-data.js b/assets/explainers-data.js index fba68b7..2bcf359 100644 --- a/assets/explainers-data.js +++ b/assets/explainers-data.js @@ -160,4 +160,11 @@ window.FAIR_CODE_EXPLAINERS = [ summary: 'Learn how a third variable that independently causes both a feature and an outcome creates spurious correlations that survive protected-attribute removal. See why COMPAS stayed biased after race was dropped - until the confounder was removed too.', tags: ['detection', 'data'], }, + { + slug: 'predictive-parity', + title: 'Predictive Parity', + subtitle: 'Equally trustworthy is not the same as equally fair', + summary: 'Learn why Positive Predictive Value equal across groups is a real fairness property, and why the 2016 ProPublica vs Northpointe COMPAS dispute shows it can hold while one group absorbs a much higher false-positive rate.', + tags: ['metrics', 'detection'], + }, ]; From 43aa95f5c80a99b0937477540929bee9676b1505 Mon Sep 17 00:00:00 2001 From: propcgamer20-png Date: Mon, 6 Jul 2026 09:28:43 +0530 Subject: [PATCH 7/7] Refactor roadmap items in index.html Removed duplicate roadmap item for Fairness Dashboard and updated status for other items. --- index.html | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/index.html b/index.html index 31ef958..e1e9b1d 100644 --- a/index.html +++ b/index.html @@ -2438,24 +2438,31 @@

The roadmap

-
+
-
Facial Recognition Gaps
-
MIT Gender Shades methodology · coming soon
+
Explainer: What Is Predictive Parity?
+
Sufficiency vs separation · PPV equality · COMPAS ProPublica vs Northpointe dispute · Chouldechova impossibility proof · base-rate trade-off
+
+
+
+
+
+
Fairness Dashboard
+
Interactive web app for live bias analysis
-
HMDA Mortgage Lending
-
Federal lending data · racial bias in loan approvals
+
Facial Recognition Gaps
+
MIT Gender Shades methodology · coming soon
-
Fairness Dashboard
-
Interactive web app for live bias analysis
+
HMDA Mortgage Lending
+
Federal lending data · racial bias in loan approvals
@@ -3035,4 +3042,4 @@

The roadmap

- \ No newline at end of file +