Skip to content
Snippets Groups Projects
Commit 872b44fb authored by Riku-Laine's avatar Riku-Laine
Browse files

Kuvien päivitys, synt pois tekstistä, analyyseissä joitain muutoksia

parent dc107546
No related branches found
No related tags found
No related merge requests found
No preview for this file type
No preview for this file type
This diff is collapsed.
This diff is collapsed.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
<h1>Table of Contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#Loading-the-Data" data-toc-modified-id="Loading-the-Data-1"><span class="toc-item-num">1&nbsp;&nbsp;</span>Loading the Data</a></span></li><li><span><a href="#Racial-Bias-in-Compas" data-toc-modified-id="Racial-Bias-in-Compas-2"><span class="toc-item-num">2&nbsp;&nbsp;</span>Racial Bias in Compas</a></span><ul class="toc-item"><li><span><a href="#Risk-of-Violent-Recidivism" data-toc-modified-id="Risk-of-Violent-Recidivism-2.1"><span class="toc-item-num">2.1&nbsp;&nbsp;</span>Risk of Violent Recidivism</a></span></li></ul></li><li><span><a href="#Predictive-Accuracy-of-COMPAS" data-toc-modified-id="Predictive-Accuracy-of-COMPAS-3"><span class="toc-item-num">3&nbsp;&nbsp;</span>Predictive Accuracy of COMPAS</a></span></li><li><span><a href="#Directions-of-the-Racial-Bias" data-toc-modified-id="Directions-of-the-Racial-Bias-4"><span class="toc-item-num">4&nbsp;&nbsp;</span>Directions of the Racial Bias</a></span></li><li><span><a href="#Risk-of-Violent-Recidivism" data-toc-modified-id="Risk-of-Violent-Recidivism-5"><span class="toc-item-num">5&nbsp;&nbsp;</span>Risk of Violent Recidivism</a></span></li><li><span><a href="#Gender-differences-in-Compas-scores" data-toc-modified-id="Gender-differences-in-Compas-scores-6"><span class="toc-item-num">6&nbsp;&nbsp;</span>Gender differences in Compas scores</a></span></li></ul></div>
%% Cell type:markdown id: tags:
# Compas Analysis # Compas Analysis
What follows are the calculations performed for ProPublica's analaysis of the COMPAS Recidivism Risk Scores. It might be helpful to open [the methodology](https://www.propublica.org/article/how-we-analyzed-the-compas-recidivism-algorithm/) in another tab to understand the following. What follows are the calculations performed for ProPublica's analaysis of the COMPAS Recidivism Risk Scores. It might be helpful to open [the methodology](https://www.propublica.org/article/how-we-analyzed-the-compas-recidivism-algorithm/) in another tab to understand the following.
## Loading the Data ## Loading the Data
We select fields for severity of charge, number of priors, demographics, age, sex, compas scores, and whether each person was accused of a crime within two years. We select fields for severity of charge, number of priors, demographics, age, sex, compas scores, and whether each person was accused of a crime within two years.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# filter dplyr warnings # filter dplyr warnings
%load_ext rpy2.ipython %load_ext rpy2.ipython
import warnings import warnings
warnings.filterwarnings('ignore') warnings.filterwarnings('ignore')
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
library(dplyr) library(dplyr)
library(ggplot2) library(ggplot2)
raw_data <- read.csv("./compas-scores-two-years.csv") raw_data <- read.csv("./compas-scores-two-years.csv")
nrow(raw_data) nrow(raw_data)
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
However not all of the rows are useable for the first round of analysis. However not all of the rows are useable for the first round of analysis.
There are a number of reasons remove rows because of missing data: There are a number of reasons remove rows because of missing data:
* If the charge date of a defendants Compas scored crime was not within 30 days from when the person was arrested, we assume that because of data quality reasons, that we do not have the right offense. * If the charge date of a defendants Compas scored crime was not within 30 days from when the person was arrested, we assume that because of data quality reasons, that we do not have the right offense.
* We coded the recidivist flag -- `is_recid` -- to be -1 if we could not find a compas case at all. * We coded the recidivist flag -- `is_recid` -- to be -1 if we could not find a compas case at all.
* In a similar vein, ordinary traffic offenses -- those with a `c_charge_degree` of 'O' -- will not result in Jail time are removed (only two of them). * In a similar vein, ordinary traffic offenses -- those with a `c_charge_degree` of 'O' -- will not result in Jail time are removed (only two of them).
* We filtered the underlying data from Broward county to include only those rows representing people who had either recidivated in two years, or had at least two years outside of a correctional facility. * We filtered the underlying data from Broward county to include only those rows representing people who had either recidivated in two years, or had at least two years outside of a correctional facility.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
df <- dplyr::select(raw_data, age, c_charge_degree, race, age_cat, score_text, sex, priors_count, df <- dplyr::select(raw_data, age, c_charge_degree, race, age_cat, score_text, sex, priors_count,
days_b_screening_arrest, decile_score, is_recid, two_year_recid, c_jail_in, c_jail_out) %>% days_b_screening_arrest, decile_score, is_recid, two_year_recid, c_jail_in, c_jail_out) %>%
filter(days_b_screening_arrest <= 30) %>% filter(days_b_screening_arrest <= 30) %>%
filter(days_b_screening_arrest >= -30) %>% filter(days_b_screening_arrest >= -30) %>%
filter(is_recid != -1) %>% filter(is_recid != -1) %>%
filter(c_charge_degree != "O") %>% filter(c_charge_degree != "O") %>%
filter(score_text != 'N/A') filter(score_text != 'N/A')
nrow(df) nrow(df)
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Higher COMPAS scores are slightly correlated with a longer length of stay. Higher COMPAS scores are slightly correlated with a longer length of stay.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
df$length_of_stay <- as.numeric(as.Date(df$c_jail_out) - as.Date(df$c_jail_in)) df$length_of_stay <- as.numeric(as.Date(df$c_jail_out) - as.Date(df$c_jail_in))
cor(df$length_of_stay, df$decile_score) cor(df$length_of_stay, df$decile_score)
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
After filtering we have the following demographic breakdown: After filtering we have the following demographic breakdown:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(df$age_cat) summary(df$age_cat)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(df$race) summary(df$race)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print("Black defendants: %.2f%%" % (3175 / 6172 * 100)) print("Black defendants: %.2f%%" % (3175 / 6172 * 100))
print("White defendants: %.2f%%" % (2103 / 6172 * 100)) print("White defendants: %.2f%%" % (2103 / 6172 * 100))
print("Hispanic defendants: %.2f%%" % (509 / 6172 * 100)) print("Hispanic defendants: %.2f%%" % (509 / 6172 * 100))
print("Asian defendants: %.2f%%" % (31 / 6172 * 100)) print("Asian defendants: %.2f%%" % (31 / 6172 * 100))
print("Native American defendants: %.2f%%" % (11 / 6172 * 100)) print("Native American defendants: %.2f%%" % (11 / 6172 * 100))
``` ```
%% Output %% Output
Black defendants: 51.44% Black defendants: 51.44%
White defendants: 34.07% White defendants: 34.07%
Hispanic defendants: 8.25% Hispanic defendants: 8.25%
Asian defendants: 0.50% Asian defendants: 0.50%
Native American defendants: 0.18% Native American defendants: 0.18%
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(df$score_text) summary(df$score_text)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
xtabs(~ sex + race, data=df) xtabs(~ sex + race, data=df)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(df$sex) summary(df$sex)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print("Men: %.2f%%" % (4997 / 6172 * 100)) print("Men: %.2f%%" % (4997 / 6172 * 100))
print("Women: %.2f%%" % (1175 / 6172 * 100)) print("Women: %.2f%%" % (1175 / 6172 * 100))
``` ```
%% Output %% Output
Men: 80.96% Men: 80.96%
Women: 19.04% Women: 19.04%
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
nrow(filter(df, two_year_recid == 1)) nrow(filter(df, two_year_recid == 1))
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
nrow(filter(df, two_year_recid == 1)) / nrow(df) * 100 nrow(filter(df, two_year_recid == 1)) / nrow(df) * 100
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Judges are often presented with two sets of scores from the Compas system -- one that classifies people into High, Medium and Low risk, and a corresponding decile score. There is a clear downward trend in the decile scores as those scores increase for white defendants. Judges are often presented with two sets of scores from the Compas system -- one that classifies people into High, Medium and Low risk, and a corresponding decile score. There is a clear downward trend in the decile scores as those scores increase for white defendants.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R -w 900 -h 363 -u px %%R -w 900 -h 363 -u px
library(grid) library(grid)
library(gridExtra) library(gridExtra)
pblack <- ggplot(data=filter(df, race =="African-American"), aes(ordered(decile_score))) + pblack <- ggplot(data=filter(df, race =="African-American"), aes(ordered(decile_score))) +
geom_bar() + xlab("Decile Score") + geom_bar() + xlab("Decile Score") +
ylim(0, 650) + ggtitle("Black Defendant's Decile Scores") ylim(0, 650) + ggtitle("Black Defendant's Decile Scores")
pwhite <- ggplot(data=filter(df, race =="Caucasian"), aes(ordered(decile_score))) + pwhite <- ggplot(data=filter(df, race =="Caucasian"), aes(ordered(decile_score))) +
geom_bar() + xlab("Decile Score") + geom_bar() + xlab("Decile Score") +
ylim(0, 650) + ggtitle("White Defendant's Decile Scores") ylim(0, 650) + ggtitle("White Defendant's Decile Scores")
grid.arrange(pblack, pwhite, ncol = 2) grid.arrange(pblack, pwhite, ncol = 2)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
xtabs(~ decile_score + race, data=df) xtabs(~ decile_score + race, data=df)
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Racial Bias in Compas ## Racial Bias in Compas
After filtering out bad rows, our first question is whether there is a significant difference in Compas scores between races. To do so we need to change some variables into factors, and run a logistic regression, comparing low scores to high scores. After filtering out bad rows, our first question is whether there is a significant difference in Compas scores between races. To do so we need to change some variables into factors, and run a logistic regression, comparing low scores to high scores.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
df <- mutate(df, crime_factor = factor(c_charge_degree)) %>% df <- mutate(df, crime_factor = factor(c_charge_degree)) %>%
mutate(age_factor = as.factor(age_cat)) %>% mutate(age_factor = as.factor(age_cat)) %>%
within(age_factor <- relevel(age_factor, ref = 1)) %>% within(age_factor <- relevel(age_factor, ref = 1)) %>%
mutate(race_factor = factor(race)) %>% mutate(race_factor = factor(race)) %>%
within(race_factor <- relevel(race_factor, ref = 3)) %>% within(race_factor <- relevel(race_factor, ref = 3)) %>%
mutate(gender_factor = factor(sex, labels= c("Female","Male"))) %>% mutate(gender_factor = factor(sex, labels= c("Female","Male"))) %>%
within(gender_factor <- relevel(gender_factor, ref = 2)) %>% within(gender_factor <- relevel(gender_factor, ref = 2)) %>%
mutate(score_factor = factor(score_text != "Low", labels = c("LowScore","HighScore"))) mutate(score_factor = factor(score_text != "Low", labels = c("LowScore","HighScore")))
model <- glm(score_factor ~ gender_factor + age_factor + race_factor + model <- glm(score_factor ~ gender_factor + age_factor + race_factor +
priors_count + crime_factor + two_year_recid, family="binomial", data=df) priors_count + crime_factor + two_year_recid, family="binomial", data=df)
summary(model) summary(model)
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Black defendants are 45% more likely than white defendants to receive a higher score correcting for the seriousness of their crime, previous arrests, and future criminal behavior. Black defendants are 45% more likely than white defendants to receive a higher score correcting for the seriousness of their crime, previous arrests, and future criminal behavior.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
control <- exp(-1.52554) / (1 + exp(-1.52554)) control <- exp(-1.52554) / (1 + exp(-1.52554))
exp(0.47721) / (1 - control + (control * exp(0.47721))) exp(0.47721) / (1 - control + (control * exp(0.47721)))
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Women are 19.4% more likely than men to get a higher score. Women are 19.4% more likely than men to get a higher score.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
exp(0.22127) / (1 - control + (control * exp(0.22127))) exp(0.22127) / (1 - control + (control * exp(0.22127)))
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Most surprisingly, people under 25 are 2.5 times as likely to get a higher score as middle aged defendants. Most surprisingly, people under 25 are 2.5 times as likely to get a higher score as middle aged defendants.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
exp(1.30839) / (1 - control + (control * exp(1.30839))) exp(1.30839) / (1 - control + (control * exp(1.30839)))
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Risk of Violent Recidivism ### Risk of Violent Recidivism
Compas also offers a score that aims to measure a persons risk of violent recidivism, which has a similar overall accuracy to the Recidivism score. As before, we can use a logistic regression to test for racial bias. Compas also offers a score that aims to measure a persons risk of violent recidivism, which has a similar overall accuracy to the Recidivism score. As before, we can use a logistic regression to test for racial bias.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
raw_data <- read.csv("./compas-scores-two-years-violent.csv") raw_data <- read.csv("./compas-scores-two-years-violent.csv")
nrow(raw_data) nrow(raw_data)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
df <- dplyr::select(raw_data, age, c_charge_degree, race, age_cat, v_score_text, sex, priors_count, df <- dplyr::select(raw_data, age, c_charge_degree, race, age_cat, v_score_text, sex, priors_count,
days_b_screening_arrest, v_decile_score, is_recid, two_year_recid) %>% days_b_screening_arrest, v_decile_score, is_recid, two_year_recid) %>%
filter(days_b_screening_arrest <= 30) %>% filter(days_b_screening_arrest <= 30) %>%
filter(days_b_screening_arrest >= -30) %>% filter(days_b_screening_arrest >= -30) %>%
filter(is_recid != -1) %>% filter(is_recid != -1) %>%
filter(c_charge_degree != "O") %>% filter(c_charge_degree != "O") %>%
filter(v_score_text != 'N/A') filter(v_score_text != 'N/A')
nrow(df) nrow(df)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(df$age_cat) summary(df$age_cat)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(df$race) summary(df$race)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(df$v_score_text) summary(df$v_score_text)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
nrow(filter(df, two_year_recid == 1)) / nrow(df) * 100 nrow(filter(df, two_year_recid == 1)) / nrow(df) * 100
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
nrow(filter(df, two_year_recid == 1)) nrow(filter(df, two_year_recid == 1))
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R -w 900 -h 363 -u px %%R -w 900 -h 363 -u px
library(grid) library(grid)
library(gridExtra) library(gridExtra)
pblack <- ggplot(data=filter(df, race =="African-American"), aes(ordered(v_decile_score))) + pblack <- ggplot(data=filter(df, race =="African-American"), aes(ordered(v_decile_score))) +
geom_bar() + xlab("Violent Decile Score") + geom_bar() + xlab("Violent Decile Score") +
ylim(0, 700) + ggtitle("Black Defendant's Violent Decile Scores") ylim(0, 700) + ggtitle("Black Defendant's Violent Decile Scores")
pwhite <- ggplot(data=filter(df, race =="Caucasian"), aes(ordered(v_decile_score))) + pwhite <- ggplot(data=filter(df, race =="Caucasian"), aes(ordered(v_decile_score))) +
geom_bar() + xlab("Violent Decile Score") + geom_bar() + xlab("Violent Decile Score") +
ylim(0, 700) + ggtitle("White Defendant's Violent Decile Scores") ylim(0, 700) + ggtitle("White Defendant's Violent Decile Scores")
grid.arrange(pblack, pwhite, ncol = 2) grid.arrange(pblack, pwhite, ncol = 2)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
df <- mutate(df, crime_factor = factor(c_charge_degree)) %>% df <- mutate(df, crime_factor = factor(c_charge_degree)) %>%
mutate(age_factor = as.factor(age_cat)) %>% mutate(age_factor = as.factor(age_cat)) %>%
within(age_factor <- relevel(age_factor, ref = 1)) %>% within(age_factor <- relevel(age_factor, ref = 1)) %>%
mutate(race_factor = factor(race, mutate(race_factor = factor(race,
labels = c("African-American", labels = c("African-American",
"Asian", "Asian",
"Caucasian", "Caucasian",
"Hispanic", "Hispanic",
"Native American", "Native American",
"Other"))) %>% "Other"))) %>%
within(race_factor <- relevel(race_factor, ref = 3)) %>% within(race_factor <- relevel(race_factor, ref = 3)) %>%
mutate(gender_factor = factor(sex, labels= c("Female","Male"))) %>% mutate(gender_factor = factor(sex, labels= c("Female","Male"))) %>%
within(gender_factor <- relevel(gender_factor, ref = 2)) %>% within(gender_factor <- relevel(gender_factor, ref = 2)) %>%
mutate(score_factor = factor(v_score_text != "Low", labels = c("LowScore","HighScore"))) mutate(score_factor = factor(v_score_text != "Low", labels = c("LowScore","HighScore")))
model <- glm(score_factor ~ gender_factor + age_factor + race_factor + model <- glm(score_factor ~ gender_factor + age_factor + race_factor +
priors_count + crime_factor + two_year_recid, family="binomial", data=df) priors_count + crime_factor + two_year_recid, family="binomial", data=df)
summary(model) summary(model)
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The violent score overpredicts recidivism for black defendants by 77.3% compared to white defendants. The violent score overpredicts recidivism for black defendants by 77.3% compared to white defendants.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
control <- exp(-2.24274) / (1 + exp(-2.24274)) control <- exp(-2.24274) / (1 + exp(-2.24274))
exp(0.65893) / (1 - control + (control * exp(0.65893))) exp(0.65893) / (1 - control + (control * exp(0.65893)))
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Defendands under 25 are 7.4 times as likely to get a higher score as middle aged defendants. Defendands under 25 are 7.4 times as likely to get a higher score as middle aged defendants.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
exp(3.14591) / (1 - control + (control * exp(3.14591))) exp(3.14591) / (1 - control + (control * exp(3.14591)))
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Predictive Accuracy of COMPAS ## Predictive Accuracy of COMPAS
In order to test whether Compas scores do an accurate job of deciding whether an offender is Low, Medium or High risk, we ran a Cox Proportional Hazards model. Northpointe, the company that created COMPAS and markets it to Law Enforcement, also ran a Cox model in their [validation study](http://cjb.sagepub.com/content/36/1/21.abstract). In order to test whether Compas scores do an accurate job of deciding whether an offender is Low, Medium or High risk, we ran a Cox Proportional Hazards model. Northpointe, the company that created COMPAS and markets it to Law Enforcement, also ran a Cox model in their [validation study](http://cjb.sagepub.com/content/36/1/21.abstract).
We used the counting model and removed people when they were incarcerated. Due to errors in the underlying jail data, we need to filter out 32 rows that have an end date more than the start date. Considering that there are 13,334 total rows in the data, such a small amount of errors will not affect the results. We used the counting model and removed people when they were incarcerated. Due to errors in the underlying jail data, we need to filter out 32 rows that have an end date more than the start date. Considering that there are 13,334 total rows in the data, such a small amount of errors will not affect the results.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
library(survival) library(survival)
library(ggfortify) library(ggfortify)
data <- filter(filter(read.csv("./cox-parsed.csv"), score_text != "N/A"), end > start) %>% data <- filter(filter(read.csv("./cox-parsed.csv"), score_text != "N/A"), end > start) %>%
mutate(race_factor = factor(race, mutate(race_factor = factor(race,
labels = c("African-American", labels = c("African-American",
"Asian", "Asian",
"Caucasian", "Caucasian",
"Hispanic", "Hispanic",
"Native American", "Native American",
"Other"))) %>% "Other"))) %>%
within(race_factor <- relevel(race_factor, ref = 3)) %>% within(race_factor <- relevel(race_factor, ref = 3)) %>%
mutate(score_factor = factor(score_text)) %>% mutate(score_factor = factor(score_text)) %>%
within(score_factor <- relevel(score_factor, ref=2)) within(score_factor <- relevel(score_factor, ref=2))
grp <- data[!duplicated(data$id),] grp <- data[!duplicated(data$id),]
nrow(grp) nrow(grp)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(grp$score_factor) summary(grp$score_factor)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(grp$race_factor) summary(grp$race_factor)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
f <- Surv(start, end, event, type="counting") ~ score_factor f <- Surv(start, end, event, type="counting") ~ score_factor
model <- coxph(f, data=data) model <- coxph(f, data=data)
summary(model) summary(model)
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
People placed in the High category are 3.5 times as likely to recidivate, and the COMPAS system's concordance 63.6%. This is lower than the accuracy quoted in the Northpoint study of 68%. People placed in the High category are 3.5 times as likely to recidivate, and the COMPAS system's concordance 63.6%. This is lower than the accuracy quoted in the Northpoint study of 68%.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
decile_f <- Surv(start, end, event, type="counting") ~ decile_score decile_f <- Surv(start, end, event, type="counting") ~ decile_score
dmodel <- coxph(decile_f, data=data) dmodel <- coxph(decile_f, data=data)
summary(dmodel) summary(dmodel)
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
COMPAS's decile scores are a bit more accurate at 66%. COMPAS's decile scores are a bit more accurate at 66%.
We can test if the algorithm is behaving differently across races by including a race interaction term in the cox model. We can test if the algorithm is behaving differently across races by including a race interaction term in the cox model.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
f2 <- Surv(start, end, event, type="counting") ~ race_factor + score_factor + race_factor * score_factor f2 <- Surv(start, end, event, type="counting") ~ race_factor + score_factor + race_factor * score_factor
model <- coxph(f2, data=data) model <- coxph(f2, data=data)
print(summary(model)) print(summary(model))
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The interaction term shows a similar disparity as the logistic regression above. The interaction term shows a similar disparity as the logistic regression above.
High risk white defendants are 3.61 more likely than low risk white defendants, while High risk black defendants are 2.99 more likely than low. High risk white defendants are 3.61 more likely than low risk white defendants, while High risk black defendants are 2.99 more likely than low.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import math import math
print("Black High Hazard: %.2f" % (math.exp(-0.18976 + 1.28350))) print("Black High Hazard: %.2f" % (math.exp(-0.18976 + 1.28350)))
print("White High Hazard: %.2f" % (math.exp(1.28350))) print("White High Hazard: %.2f" % (math.exp(1.28350)))
print("Black Medium Hazard: %.2f" % (math.exp(0.84286-0.17261))) print("Black Medium Hazard: %.2f" % (math.exp(0.84286-0.17261)))
print("White Medium Hazard: %.2f" % (math.exp(0.84286))) print("White Medium Hazard: %.2f" % (math.exp(0.84286)))
``` ```
%% Output %% Output
Black High Hazard: 2.99 Black High Hazard: 2.99
White High Hazard: 3.61 White High Hazard: 3.61
Black Medium Hazard: 1.95 Black Medium Hazard: 1.95
White Medium Hazard: 2.32 White Medium Hazard: 2.32
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R -w 900 -h 563 -u px %%R -w 900 -h 563 -u px
fit <- survfit(f, data=data) fit <- survfit(f, data=data)
plotty <- function(fit, title) { plotty <- function(fit, title) {
return(autoplot(fit, conf.int=T, censor=F) + ggtitle(title) + ylim(0,1)) return(autoplot(fit, conf.int=T, censor=F) + ggtitle(title) + ylim(0,1))
} }
plotty(fit, "Overall") plotty(fit, "Overall")
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Black defendants do recidivate at higher rates according to race specific Kaplan Meier plots. Black defendants do recidivate at higher rates according to race specific Kaplan Meier plots.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R -w 900 -h 363 -u px %%R -w 900 -h 363 -u px
white <- filter(data, race == "Caucasian") white <- filter(data, race == "Caucasian")
white_fit <- survfit(f, data=white) white_fit <- survfit(f, data=white)
black <- filter(data, race == "African-American") black <- filter(data, race == "African-American")
black_fit <- survfit(f, data=black) black_fit <- survfit(f, data=black)
grid.arrange(plotty(white_fit, "White defendants"), grid.arrange(plotty(white_fit, "White defendants"),
plotty(black_fit, "Black defendants"), ncol=2) plotty(black_fit, "Black defendants"), ncol=2)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(fit, times=c(730)) summary(fit, times=c(730))
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(black_fit, times=c(730)) summary(black_fit, times=c(730))
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(white_fit, times=c(730)) summary(white_fit, times=c(730))
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Race specific models have similar concordance values. Race specific models have similar concordance values.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(coxph(f, data=white)) summary(coxph(f, data=white))
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(coxph(f, data=black)) summary(coxph(f, data=black))
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Compas's violent recidivism score has a slightly higher overall concordance score of 65.1%. Compas's violent recidivism score has a slightly higher overall concordance score of 65.1%.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
violent_data <- filter(filter(read.csv("./cox-violent-parsed.csv"), score_text != "N/A"), end > start) %>% violent_data <- filter(filter(read.csv("./cox-violent-parsed.csv"), score_text != "N/A"), end > start) %>%
mutate(race_factor = factor(race, mutate(race_factor = factor(race,
labels = c("African-American", labels = c("African-American",
"Asian", "Asian",
"Caucasian", "Caucasian",
"Hispanic", "Hispanic",
"Native American", "Native American",
"Other"))) %>% "Other"))) %>%
within(race_factor <- relevel(race_factor, ref = 3)) %>% within(race_factor <- relevel(race_factor, ref = 3)) %>%
mutate(score_factor = factor(score_text)) %>% mutate(score_factor = factor(score_text)) %>%
within(score_factor <- relevel(score_factor, ref=2)) within(score_factor <- relevel(score_factor, ref=2))
vf <- Surv(start, end, event, type="counting") ~ score_factor vf <- Surv(start, end, event, type="counting") ~ score_factor
vmodel <- coxph(vf, data=violent_data) vmodel <- coxph(vf, data=violent_data)
vgrp <- violent_data[!duplicated(violent_data$id),] vgrp <- violent_data[!duplicated(violent_data$id),]
print(nrow(vgrp)) print(nrow(vgrp))
summary(vmodel) summary(vmodel)
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
In this case, there isn't a significant coefficient on African American's with High Scores. In this case, there isn't a significant coefficient on African American's with High Scores.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
vf2 <- Surv(start, end, event, type="counting") ~ race_factor + race_factor * score_factor vf2 <- Surv(start, end, event, type="counting") ~ race_factor + race_factor * score_factor
vmodel <- coxph(vf2, data=violent_data) vmodel <- coxph(vf2, data=violent_data)
summary(vmodel) summary(vmodel)
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(coxph(vf, data=filter(violent_data, race == "African-American"))) summary(coxph(vf, data=filter(violent_data, race == "African-American")))
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(coxph(vf, data=filter(violent_data, race == "Caucasian"))) summary(coxph(vf, data=filter(violent_data, race == "Caucasian")))
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R -w 900 -h 363 -u px %%R -w 900 -h 363 -u px
white <- filter(violent_data, race == "Caucasian") white <- filter(violent_data, race == "Caucasian")
white_fit <- survfit(vf, data=white) white_fit <- survfit(vf, data=white)
black <- filter(violent_data, race == "African-American") black <- filter(violent_data, race == "African-American")
black_fit <- survfit(vf, data=black) black_fit <- survfit(vf, data=black)
grid.arrange(plotty(white_fit, "White defendants"), grid.arrange(plotty(white_fit, "White defendants"),
plotty(black_fit, "Black defendants"), ncol=2) plotty(black_fit, "Black defendants"), ncol=2)
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Directions of the Racial Bias ## Directions of the Racial Bias
The above analysis shows that the Compas algorithm does overpredict African-American defendant's future recidivism, but we haven't yet explored the direction of the bias. We can discover fine differences in overprediction and underprediction by comparing Compas scores across racial lines. The above analysis shows that the Compas algorithm does overpredict African-American defendant's future recidivism, but we haven't yet explored the direction of the bias. We can discover fine differences in overprediction and underprediction by comparing Compas scores across racial lines.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
from truth_tables import PeekyReader, Person, table, is_race, count, vtable, hightable, vhightable from truth_tables import PeekyReader, Person, table, is_race, count, vtable, hightable, vhightable
from csv import DictReader from csv import DictReader
people = [] people = []
with open("./cox-parsed.csv") as f: with open("./cox-parsed.csv") as f:
reader = PeekyReader(DictReader(f)) reader = PeekyReader(DictReader(f))
try: try:
while True: while True:
p = Person(reader) p = Person(reader)
if p.valid: if p.valid:
people.append(p) people.append(p)
except StopIteration: except StopIteration:
pass pass
pop = list(filter(lambda i: ((i.recidivist == True and i.lifetime <= 730) or pop = list(filter(lambda i: ((i.recidivist == True and i.lifetime <= 730) or
i.lifetime > 730), list(filter(lambda x: x.score_valid, people)))) i.lifetime > 730), list(filter(lambda x: x.score_valid, people))))
recid = list(filter(lambda i: i.recidivist == True and i.lifetime <= 730, pop)) recid = list(filter(lambda i: i.recidivist == True and i.lifetime <= 730, pop))
rset = set(recid) rset = set(recid)
surv = [i for i in pop if i not in rset] surv = [i for i in pop if i not in rset]
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print("All defendants") print("All defendants")
table(list(recid), list(surv)) table(list(recid), list(surv))
``` ```
%% Output %% Output
All defendants All defendants
Low High Low High
Survived 2681 1282 0.55 Survived 2681 1282 0.55
Recidivated 1216 2035 0.45 Recidivated 1216 2035 0.45
Total: 7214.00 Total: 7214.00
False positive rate: 32.35 False positive rate: 32.35
False negative rate: 37.40 False negative rate: 37.40
Specificity: 0.68 Specificity: 0.68
Sensitivity: 0.63 Sensitivity: 0.63
Prevalence: 0.45 Prevalence: 0.45
PPV: 0.61 PPV: 0.61
NPV: 0.69 NPV: 0.69
LR+: 1.94 LR+: 1.94
LR-: 0.55 LR-: 0.55
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print("Total pop: %i" % (2681 + 1282 + 1216 + 2035)) print("Total pop: %i" % (2681 + 1282 + 1216 + 2035))
``` ```
%% Output %% Output
Total pop: 7214 Total pop: 7214
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import statistics import statistics
print("Average followup time %.2f (sd %.2f)" % (statistics.mean(map(lambda i: i.lifetime, pop)), print("Average followup time %.2f (sd %.2f)" % (statistics.mean(map(lambda i: i.lifetime, pop)),
statistics.stdev(map(lambda i: i.lifetime, pop)))) statistics.stdev(map(lambda i: i.lifetime, pop))))
print("Median followup time %i" % (statistics.median(map(lambda i: i.lifetime, pop)))) print("Median followup time %i" % (statistics.median(map(lambda i: i.lifetime, pop))))
``` ```
%% Output %% Output
Average followup time 622.87 (sd 392.19) Average followup time 622.87 (sd 392.19)
Median followup time 766 Median followup time 766
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Overall, the false positive rate is 32.35%. Overall, the false positive rate is 32.35%.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print("Black defendants") print("Black defendants")
is_afam = is_race("African-American") is_afam = is_race("African-American")
table(list(filter(is_afam, recid)), list(filter(is_afam, surv))) table(list(filter(is_afam, recid)), list(filter(is_afam, surv)))
``` ```
%% Output %% Output
Black defendants Black defendants
Low High Low High
Survived 990 805 0.49 Survived 990 805 0.49
Recidivated 532 1369 0.51 Recidivated 532 1369 0.51
Total: 3696.00 Total: 3696.00
False positive rate: 44.85 False positive rate: 44.85
False negative rate: 27.99 False negative rate: 27.99
Specificity: 0.55 Specificity: 0.55
Sensitivity: 0.72 Sensitivity: 0.72
Prevalence: 0.51 Prevalence: 0.51
PPV: 0.63 PPV: 0.63
NPV: 0.65 NPV: 0.65
LR+: 1.61 LR+: 1.61
LR-: 0.51 LR-: 0.51
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
That number is higher for African Americans at 44.85%. That number is higher for African Americans at 44.85%.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print("White defendants") print("White defendants")
is_white = is_race("Caucasian") is_white = is_race("Caucasian")
table(list(filter(is_white, recid)), list(filter(is_white, surv))) table(list(filter(is_white, recid)), list(filter(is_white, surv)))
``` ```
%% Output %% Output
White defendants White defendants
Low High Low High
Survived 1139 349 0.61 Survived 1139 349 0.61
Recidivated 461 505 0.39 Recidivated 461 505 0.39
Total: 2454.00 Total: 2454.00
False positive rate: 23.45 False positive rate: 23.45
False negative rate: 47.72 False negative rate: 47.72
Specificity: 0.77 Specificity: 0.77
Sensitivity: 0.52 Sensitivity: 0.52
Prevalence: 0.39 Prevalence: 0.39
PPV: 0.59 PPV: 0.59
NPV: 0.71 NPV: 0.71
LR+: 2.23 LR+: 2.23
LR-: 0.62 LR-: 0.62
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
And lower for whites at 23.45%. And lower for whites at 23.45%.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
44.85 / 23.45 44.85 / 23.45
``` ```
%% Output %% Output
1.9125799573560769 1.9125799573560769
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Which means under COMPAS black defendants are 91% more likely to get a higher score and not go on to commit more crimes than white defendants after two year. Which means under COMPAS black defendants are 91% more likely to get a higher score and not go on to commit more crimes than white defendants after two year.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
COMPAS scores misclassify white reoffenders as low risk at 70.4% more often than black reoffenders. COMPAS scores misclassify white reoffenders as low risk at 70.4% more often than black reoffenders.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
47.72 / 27.99 47.72 / 27.99
``` ```
%% Output %% Output
1.7048946052161487 1.7048946052161487
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
hightable(list(filter(is_white, recid)), list(filter(is_white, surv))) hightable(list(filter(is_white, recid)), list(filter(is_white, surv)))
``` ```
%% Output %% Output
Low High Low High
Survived 1407 81 0.61 Survived 1407 81 0.61
Recidivated 771 195 0.39 Recidivated 771 195 0.39
Total: 2454.00 Total: 2454.00
False positive rate: 5.44 False positive rate: 5.44
False negative rate: 79.81 False negative rate: 79.81
Specificity: 0.95 Specificity: 0.95
Sensitivity: 0.20 Sensitivity: 0.20
Prevalence: 0.39 Prevalence: 0.39
PPV: 0.71 PPV: 0.71
NPV: 0.65 NPV: 0.65
LR+: 3.71 LR+: 3.71
LR-: 0.84 LR-: 0.84
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
hightable(list(filter(is_afam, recid)), list(filter(is_afam, surv))) hightable(list(filter(is_afam, recid)), list(filter(is_afam, surv)))
``` ```
%% Output %% Output
Low High Low High
Survived 1511 284 0.49 Survived 1511 284 0.49
Recidivated 1160 741 0.51 Recidivated 1160 741 0.51
Total: 3696.00 Total: 3696.00
False positive rate: 15.82 False positive rate: 15.82
False negative rate: 61.02 False negative rate: 61.02
Specificity: 0.84 Specificity: 0.84
Sensitivity: 0.39 Sensitivity: 0.39
Prevalence: 0.51 Prevalence: 0.51
PPV: 0.72 PPV: 0.72
NPV: 0.57 NPV: 0.57
LR+: 2.46 LR+: 2.46
LR-: 0.72 LR-: 0.72
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Risk of Violent Recidivism ## Risk of Violent Recidivism
Compas also offers a score that aims to measure a persons risk of violent recidivism, which has a similar overall accuracy to the Recidivism score. Compas also offers a score that aims to measure a persons risk of violent recidivism, which has a similar overall accuracy to the Recidivism score.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
vpeople = [] vpeople = []
with open("./cox-violent-parsed.csv") as f: with open("./cox-violent-parsed.csv") as f:
reader = PeekyReader(DictReader(f)) reader = PeekyReader(DictReader(f))
try: try:
while True: while True:
p = Person(reader) p = Person(reader)
if p.valid: if p.valid:
vpeople.append(p) vpeople.append(p)
except StopIteration: except StopIteration:
pass pass
vpop = list(filter(lambda i: ((i.violent_recidivist == True and i.lifetime <= 730) or vpop = list(filter(lambda i: ((i.violent_recidivist == True and i.lifetime <= 730) or
i.lifetime > 730), list(filter(lambda x: x.vscore_valid, vpeople)))) i.lifetime > 730), list(filter(lambda x: x.vscore_valid, vpeople))))
vrecid = list(filter(lambda i: i.violent_recidivist == True and i.lifetime <= 730, vpeople)) vrecid = list(filter(lambda i: i.violent_recidivist == True and i.lifetime <= 730, vpeople))
vrset = set(vrecid) vrset = set(vrecid)
vsurv = [i for i in vpop if i not in vrset] vsurv = [i for i in vpop if i not in vrset]
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print("All defendants") print("All defendants")
vtable(list(vrecid), list(vsurv)) vtable(list(vrecid), list(vsurv))
``` ```
%% Output %% Output
All defendants All defendants
Low High Low High
Survived 4121 1597 0.89 Survived 4121 1597 0.89
Recidivated 347 389 0.11 Recidivated 347 389 0.11
Total: 6454.00 Total: 6454.00
False positive rate: 27.93 False positive rate: 27.93
False negative rate: 47.15 False negative rate: 47.15
Specificity: 0.72 Specificity: 0.72
Sensitivity: 0.53 Sensitivity: 0.53
Prevalence: 0.11 Prevalence: 0.11
PPV: 0.20 PPV: 0.20
NPV: 0.92 NPV: 0.92
LR+: 1.89 LR+: 1.89
LR-: 0.65 LR-: 0.65
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Even moreso for Black defendants. Even moreso for Black defendants.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print("Black defendants") print("Black defendants")
is_afam = is_race("African-American") is_afam = is_race("African-American")
vtable(list(filter(is_afam, vrecid)), list(filter(is_afam, vsurv))) vtable(list(filter(is_afam, vrecid)), list(filter(is_afam, vsurv)))
``` ```
%% Output %% Output
Black defendants Black defendants
Low High Low High
Survived 1692 1043 0.86 Survived 1692 1043 0.86
Recidivated 170 273 0.14 Recidivated 170 273 0.14
Total: 3178.00 Total: 3178.00
False positive rate: 38.14 False positive rate: 38.14
False negative rate: 38.37 False negative rate: 38.37
Specificity: 0.62 Specificity: 0.62
Sensitivity: 0.62 Sensitivity: 0.62
Prevalence: 0.14 Prevalence: 0.14
PPV: 0.21 PPV: 0.21
NPV: 0.91 NPV: 0.91
LR+: 1.62 LR+: 1.62
LR-: 0.62 LR-: 0.62
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print("White defendants") print("White defendants")
is_white = is_race("Caucasian") is_white = is_race("Caucasian")
vtable(list(filter(is_white, vrecid)), list(filter(is_white, vsurv))) vtable(list(filter(is_white, vrecid)), list(filter(is_white, vsurv)))
``` ```
%% Output %% Output
White defendants White defendants
Low High Low High
Survived 1679 380 0.91 Survived 1679 380 0.91
Recidivated 129 77 0.09 Recidivated 129 77 0.09
Total: 2265.00 Total: 2265.00
False positive rate: 18.46 False positive rate: 18.46
False negative rate: 62.62 False negative rate: 62.62
Specificity: 0.82 Specificity: 0.82
Sensitivity: 0.37 Sensitivity: 0.37
Prevalence: 0.09 Prevalence: 0.09
PPV: 0.17 PPV: 0.17
NPV: 0.93 NPV: 0.93
LR+: 2.03 LR+: 2.03
LR-: 0.77 LR-: 0.77
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Black defendants are twice as likely to be false positives for a Higher violent score than white defendants. Black defendants are twice as likely to be false positives for a Higher violent score than white defendants.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
38.14 / 18.46 38.14 / 18.46
``` ```
%% Output %% Output
2.066088840736728 2.066088840736728
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
White defendants are 63% more likely to get a lower score and commit another crime than Black defendants. White defendants are 63% more likely to get a lower score and commit another crime than Black defendants.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
62.62 / 38.37 62.62 / 38.37
``` ```
%% Output %% Output
1.63200416992442 1.63200416992442
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Gender differences in Compas scores ## Gender differences in Compas scores
In terms of underlying recidivism rates, we can look at gender specific Kaplan Meier estimates. There is a striking difference between women and men. In terms of underlying recidivism rates, we can look at gender specific Kaplan Meier estimates. There is a striking difference between women and men.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
female <- filter(data, sex == "Female") female <- filter(data, sex == "Female")
male <- filter(data, sex == "Male") male <- filter(data, sex == "Male")
male_fit <- survfit(f, data=male) male_fit <- survfit(f, data=male)
female_fit <- survfit(f, data=female) female_fit <- survfit(f, data=female)
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(male_fit, times=c(730)) summary(male_fit, times=c(730))
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R %%R
summary(female_fit, times=c(730)) summary(female_fit, times=c(730))
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
%%R -w 900 -h 363 -u px %%R -w 900 -h 363 -u px
grid.arrange(plotty(female_fit, "Female"), plotty(male_fit, "Male"),ncol=2) grid.arrange(plotty(female_fit, "Female"), plotty(male_fit, "Male"),ncol=2)
``` ```
%% Output %% Output
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
As these plots show, the Compas score treats a High risk women the same as a Medium risk man. As these plots show, the Compas score treats a High risk women the same as a Medium risk man.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
``` ```
......
This diff is collapsed.
analysis_and_scripts/tree.png

1.83 MiB

figures/valikoitumis_iso.jpg

107 KiB

figures/valikoitumisharha.png

11.3 KiB

<mxfile modified="2019-04-21T15:58:55.500Z" host="www.draw.io" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0" etag="NAxnU8qYrcP3EQhgaOI9" version="10.6.3" type="device"><diagram id="3qIiofentZYx9hMsOPwP" name="Page-1">7VpZc+MoEP41qpp9yJaErvjRcTKT2a05qrJXHlmLsRhLwoOQj/31CxJIQsfasWXF2fJLLBpoaPi+7gZi2LN4+4HCVfiJBCgygBlsDfveAMAyLZP/CMmukLieWwgWFAeyUSV4wv8g1VNKMxygVGvICIkYXunCOUkSNGeaDFJKNnqzbyTSR13BBWoJnuYwakv/xAELC+kt8Cv5I8KLUI1seZOiJoaqsbQkDWFANjWR/WDYM0oIK77i7QxFYvHUuhT93vfUlhOjKGGHdPjds25+QRb+uHx+frynn39zl3/cSC1rGGXSYAN4Edd3941wtXzWbCeXwvuREVVxk+YbNeUNLH+1rSr510L+5lr+VoKvxsw2pk7xl4mfO2+Z8qmjJf5eSBNDWFD0w6rfdyXhhuFKuxmiZImjQo9U3ewqbK+prCtYknSJ1lCsX8e80q6JMCVJs9IoXNNdE/dMuVwT2tGwq3OxBUoMtN0AlGRJgMTeWrx6E2KGnlZwLmo3nIpcFrI4ktVC03sY40iwcEbiVcYQFSDlfKVJqb4OJoUMRBna1kQSXB8QiRGjO95E1toK6JLp1q0sbyreAEfKwhpnPCmDkqqLUnWFZv4hAf0CcIMzgfuxhr1O0OIeBLAsIp3g2p0KrjeKGeABDTO2BVqYsdwOzDjnwoxzxcyFY8ZRGcTFYMbtwExjFVESTEU2wkvzCKYpnusLh7aY/cW/zZ9dWXqu1dwL001V2KlCwidfdPJdVX6uV1b98pLq2LsJKcnoHO1PFxikC8T2e14UaMlVe0trW9a1Y0pGUQQZXuspWdc2yhG+EpxzVUWmiR6ZWkgo7Ja96ilUU5HTUDRpKCoWpqUoR1Vp9vFA814daODCgOZcNtDcY4HW9HHmuEDze6NggNcHBUGPB8E8yDTi4KedjIEM4QQl/Rl4GZ3yEfWYdeokarH0BC3v9CPBCZp6w7aZqzOtn+pnhwEM70kEtKVuOBYenZnuSlJGyRLNSEQolyQkQWImOIoaIhjhRSL8kTjtcfmdiPWYH7GnsiLGQSCG6cwuqvzDlIbKSwLLe6UkFbgHJhxN9g+WcNyejZ4vTnb7FP2KUXQwxfUEdAzmn2zfW3Mh5kgu5JCtvFjP8p+epOl6BjnK9OQLr+ZZJoN4lhZoviQJTlkWF/5gKIIPQMo2I3fHMHKciH4N54OEc1snne13hHPTH5F06gJjaNY9yLhLruwbIBhe2TfIK4Ht72efZY/JPvtM7BM8COEaYsayOt6GznPfMBEto/t9rhx8BZPjR2cQ15aiHLrn4tzsmWs+0c/TPWwvZjrY+l8d1Gs5qNbzQpeDGjUntwZ9XwBH3fuOdO0rDdt77SuPKZdy7asSOAUZdUH00mtf4OrYc0BD0Zmvfa1BHxj8/wPS1NZeCtT8yTBQs4GO2dGh1nXTMO5b1khIcw5Fmn1RSHPdxjHZOxJprms3AupQb1m8WP37XNG8+idE++Ff</diagram></mxfile>
\ No newline at end of file
...@@ -140,4 +140,12 @@ ...@@ -140,4 +140,12 @@
year = "2016", year = "2016",
language={finnish}, language={finnish},
note = {viitattu 5.4.2019} note = {viitattu 5.4.2019}
}
@article{madras18,
title={Fairness Through Causal Awareness: Learning Latent-Variable Models for Biased Data},
author={Madras, David and Creager, Elliot and Pitassi, Toniann and Zemel, Richard},
journal={arXiv preprint arXiv:1809.02519},
year={2018},
language={finnish}
} }
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment