This analysis examines preventable death rates across Texas largest county jails (Bexar, Dalls, Harris, Travis) from 2015-2025
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.2.0 ✔ readr 2.1.6
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.2 ✔ tibble 3.3.1
## ✔ lubridate 1.9.5 ✔ tidyr 1.3.2
## ✔ purrr 1.2.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readxl)
library(psych)
##
## Attaching package: 'psych'
##
## The following objects are masked from 'package:ggplot2':
##
## %+%, alpha
countyjail_data <- read_excel(
"~/Desktop/capstone bexar county jail/excel work sheets and r code/TXJail_DeathsCounties2015_2025 Capstone analysis.xlsx",
sheet = "All_Data")
head(countyjail_data)
## # A tibble: 6 × 35
## record_id agency_county death_year death_date preventable_death
## <chr> <chr> <dbl> <chr> <dbl>
## 1 23-1091-CJ BEXAR 2023 2023-08-15 0
## 2 23-1219-CJ TRAVIS 2023 2023-09-13 0
## 3 23-1079-CJ BEXAR 2023 2023-08-06 0
## 4 23-1103-CJ DALLAS 2023 2023-08-07 0
## 5 23-1783-CJ BEXAR 2023 2023-08-27 0
## 6 23-1593-CJ BEXAR 2023 2023-09-29 0
## # ℹ 30 more variables: age_at_time_of_death <dbl>, age_group <chr>, sex <chr>,
## # race <chr>, sex_male <dbl>, race_white <dbl>, race_black <dbl>,
## # race_hispanic <dbl>, age_26_35 <dbl>, age_36_45 <dbl>, age_46_55 <dbl>,
## # manner_of_death <chr>, specific_type_of_custody_facility <chr>,
## # housing_type <chr>, days_from_custody_to_death <dbl>,
## # timing_category <chr>, exhibit_any_mental_health_problems <chr>,
## # make_suicidal_statements <chr>, housing_single_cell <dbl>, …
summary(countyjail_data)
## record_id agency_county death_year death_date
## Length:390 Length:390 Min. :2015 Length:390
## Class :character Class :character 1st Qu.:2018 Class :character
## Mode :character Mode :character Median :2021 Mode :character
## Mean :2020
## 3rd Qu.:2023
## Max. :2025
## preventable_death age_at_time_of_death age_group sex
## Min. :0.0000 Min. :18.00 Length:390 Length:390
## 1st Qu.:0.0000 1st Qu.:34.25 Class :character Class :character
## Median :0.0000 Median :47.00 Mode :character Mode :character
## Mean :0.1641 Mean :46.92
## 3rd Qu.:0.0000 3rd Qu.:58.00
## Max. :1.0000 Max. :89.00
## race sex_male race_white race_black
## Length:390 Min. :0.0000 Min. :0.0000 Min. :0.0000
## Class :character 1st Qu.:1.0000 1st Qu.:0.0000 1st Qu.:0.0000
## Mode :character Median :1.0000 Median :0.0000 Median :0.0000
## Mean :0.8872 Mean :0.3385 Mean :0.3974
## 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.0000
## Max. :1.0000 Max. :1.0000 Max. :1.0000
## race_hispanic age_26_35 age_36_45 age_46_55
## Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000
## 1st Qu.:0.0000 1st Qu.:0.0000 1st Qu.:0.0000 1st Qu.:0.0000
## Median :0.0000 Median :0.0000 Median :0.0000 Median :0.0000
## Mean :0.2179 Mean :0.1974 Mean :0.1846 Mean :0.2436
## 3rd Qu.:0.0000 3rd Qu.:0.0000 3rd Qu.:0.0000 3rd Qu.:0.0000
## Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000
## manner_of_death specific_type_of_custody_facility housing_type
## Length:390 Length:390 Length:390
## Class :character Class :character Class :character
## Mode :character Mode :character Mode :character
##
##
##
## days_from_custody_to_death timing_category
## Min. : 0.0 Length:390
## 1st Qu.: 5.0 Class :character
## Median : 36.0 Mode :character
## Mean : 120.3
## 3rd Qu.: 155.8
## Max. :1277.0
## exhibit_any_mental_health_problems make_suicidal_statements
## Length:390 Length:390
## Class :character Class :character
## Mode :character Mode :character
##
##
##
## housing_single_cell housing_multiple mh_unknown mh_yes
## Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000
## 1st Qu.:0.0000 1st Qu.:0.0000 1st Qu.:0.0000 1st Qu.:0.0000
## Median :0.0000 Median :0.0000 Median :0.0000 Median :0.0000
## Mean :0.2538 Mean :0.3821 Mean :0.4333 Mean :0.1795
## 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:0.0000
## Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000
## suicidal_yes county_dallas county_harris county_travis
## Min. :0.00000 Min. :0.0000 Min. :0.0000 Min. :0.0000
## 1st Qu.:0.00000 1st Qu.:0.0000 1st Qu.:0.0000 1st Qu.:0.0000
## Median :0.00000 Median :0.0000 Median :0.0000 Median :0.0000
## Mean :0.07179 Mean :0.1974 Mean :0.3872 Mean :0.1103
## 3rd Qu.:0.00000 3rd Qu.:0.0000 3rd Qu.:1.0000 3rd Qu.:0.0000
## Max. :1.00000 Max. :1.0000 Max. :1.0000 Max. :1.0000
## death_from_pre_existing_medical_condition type_of_offense
## Length:390 Length:390
## Class :character Class :character
## Mode :character Mode :character
##
##
##
## offense_1 were_the_charges
## Length:390 Length:390
## Class :character Class :character
## Mode :character Mode :character
##
##
##
#Convert variables to factors
# Convert categorical variables to factors
countyjail_data <- countyjail_data %>%
mutate(
# Main categorical variables
agency_county = factor(agency_county),
sex = factor(sex),
race = factor(race),
age_group = factor(age_group, levels = c("18-25", "26-35", "36-45", "46-55", "56-65", "66+")),
manner_of_death = factor(manner_of_death),
housing_type = factor(housing_type),
timing_category = factor(timing_category),
exhibit_any_mental_health_problems = factor(exhibit_any_mental_health_problems),
make_suicidal_statements = factor(make_suicidal_statements),
death_from_pre_existing_medical_condition = factor(death_from_pre_existing_medical_condition),
type_of_offense = factor(type_of_offense),
were_the_charges = factor(were_the_charges),
# Convert death_date to actual date format
death_date = as.Date(death_date)
)
# Verify the changes
str(countyjail_data)
## tibble [390 × 35] (S3: tbl_df/tbl/data.frame)
## $ record_id : chr [1:390] "23-1091-CJ" "23-1219-CJ" "23-1079-CJ" "23-1103-CJ" ...
## $ agency_county : Factor w/ 4 levels "BEXAR","DALLAS",..: 1 4 1 2 1 1 1 1 1 2 ...
## $ death_year : num [1:390] 2023 2023 2023 2023 2023 ...
## $ death_date : Date[1:390], format: "2023-08-15" "2023-09-13" ...
## $ preventable_death : num [1:390] 0 0 0 0 0 0 1 0 1 0 ...
## $ age_at_time_of_death : num [1:390] 67 21 54 66 37 33 31 44 22 82 ...
## $ age_group : Factor w/ 6 levels "18-25","26-35",..: 6 1 4 6 3 2 2 3 1 6 ...
## $ sex : Factor w/ 2 levels "FEMALE","MALE": 2 2 2 2 2 2 2 2 2 2 ...
## $ race : Factor w/ 4 levels "BLACK","HISPANIC",..: 2 1 4 1 2 4 3 2 1 4 ...
## $ sex_male : num [1:390] 1 1 1 1 1 1 1 1 1 1 ...
## $ race_white : num [1:390] 0 0 1 0 0 1 0 0 0 1 ...
## $ race_black : num [1:390] 0 1 0 1 0 0 0 0 1 0 ...
## $ race_hispanic : num [1:390] 1 0 0 0 1 0 0 1 0 0 ...
## $ age_26_35 : num [1:390] 0 0 0 0 0 1 1 0 0 0 ...
## $ age_36_45 : num [1:390] 0 0 0 0 1 0 0 1 0 0 ...
## $ age_46_55 : num [1:390] 0 0 1 0 0 0 0 0 0 0 ...
## $ manner_of_death : Factor w/ 6 levels "ACCIDENTAL","ALCOHOL/DRUG INTOXICATION",..: 4 5 4 4 3 4 6 3 6 4 ...
## $ specific_type_of_custody_facility : chr [1:390] "HOSPITAL/INFIRMARY" "HOSPITAL/INFIRMARY" "HOSPITAL/INFIRMARY" "HOSPITAL/INFIRMARY" ...
## $ housing_type : Factor w/ 7 levels "Day Room/Recreation",..: 4 4 4 4 6 5 7 4 4 4 ...
## $ days_from_custody_to_death : num [1:390] 274 10 29 47 0 4 6 24 1 261 ...
## $ timing_category : Factor w/ 6 levels "<24 hours",">90 days",..: 2 6 4 4 1 5 5 4 3 2 ...
## $ exhibit_any_mental_health_problems : Factor w/ 3 levels "NO","UNKNOWN",..: 1 3 1 2 2 2 1 2 2 2 ...
## $ make_suicidal_statements : Factor w/ 3 levels "NO","UNKNOWN",..: 1 3 1 2 1 1 1 2 2 2 ...
## $ housing_single_cell : num [1:390] 0 0 0 0 0 0 1 0 0 0 ...
## $ housing_multiple : num [1:390] 0 0 0 0 0 1 0 0 0 0 ...
## $ mh_unknown : num [1:390] 0 0 0 1 1 1 0 1 1 1 ...
## $ mh_yes : num [1:390] 0 1 0 0 0 0 0 0 0 0 ...
## $ suicidal_yes : num [1:390] 0 1 0 0 0 0 0 0 0 0 ...
## $ county_dallas : num [1:390] 0 0 0 1 0 0 0 0 0 1 ...
## $ county_harris : num [1:390] 0 0 0 0 0 0 0 0 0 0 ...
## $ county_travis : num [1:390] 0 1 0 0 0 0 0 0 0 0 ...
## $ death_from_pre_existing_medical_condition: Factor w/ 4 levels "DEVELOPED CONDITION AFTER ADMISSION",..: 4 3 4 4 3 3 2 2 2 3 ...
## $ type_of_offense : Factor w/ 12 levels "ALCOHOL / DRUG OFFENSE",..: 10 5 8 10 1 10 12 10 10 10 ...
## $ offense_1 : chr [1:390] "CONTINUOUS SEX ABUSE" "BURGLARY OF VEHICLE" "CRUELTY-NONLIESTOCK" "ASSAULT CAUSES BODILY INJURY FAMILY VIOLENCE" ...
## $ were_the_charges : Factor w/ 4 levels "CONVICTED","FILED",..: 2 2 2 4 3 2 2 2 2 2 ...
# Verify the changes
str(countyjail_data)
## tibble [390 × 35] (S3: tbl_df/tbl/data.frame)
## $ record_id : chr [1:390] "23-1091-CJ" "23-1219-CJ" "23-1079-CJ" "23-1103-CJ" ...
## $ agency_county : Factor w/ 4 levels "BEXAR","DALLAS",..: 1 4 1 2 1 1 1 1 1 2 ...
## $ death_year : num [1:390] 2023 2023 2023 2023 2023 ...
## $ death_date : Date[1:390], format: "2023-08-15" "2023-09-13" ...
## $ preventable_death : num [1:390] 0 0 0 0 0 0 1 0 1 0 ...
## $ age_at_time_of_death : num [1:390] 67 21 54 66 37 33 31 44 22 82 ...
## $ age_group : Factor w/ 6 levels "18-25","26-35",..: 6 1 4 6 3 2 2 3 1 6 ...
## $ sex : Factor w/ 2 levels "FEMALE","MALE": 2 2 2 2 2 2 2 2 2 2 ...
## $ race : Factor w/ 4 levels "BLACK","HISPANIC",..: 2 1 4 1 2 4 3 2 1 4 ...
## $ sex_male : num [1:390] 1 1 1 1 1 1 1 1 1 1 ...
## $ race_white : num [1:390] 0 0 1 0 0 1 0 0 0 1 ...
## $ race_black : num [1:390] 0 1 0 1 0 0 0 0 1 0 ...
## $ race_hispanic : num [1:390] 1 0 0 0 1 0 0 1 0 0 ...
## $ age_26_35 : num [1:390] 0 0 0 0 0 1 1 0 0 0 ...
## $ age_36_45 : num [1:390] 0 0 0 0 1 0 0 1 0 0 ...
## $ age_46_55 : num [1:390] 0 0 1 0 0 0 0 0 0 0 ...
## $ manner_of_death : Factor w/ 6 levels "ACCIDENTAL","ALCOHOL/DRUG INTOXICATION",..: 4 5 4 4 3 4 6 3 6 4 ...
## $ specific_type_of_custody_facility : chr [1:390] "HOSPITAL/INFIRMARY" "HOSPITAL/INFIRMARY" "HOSPITAL/INFIRMARY" "HOSPITAL/INFIRMARY" ...
## $ housing_type : Factor w/ 7 levels "Day Room/Recreation",..: 4 4 4 4 6 5 7 4 4 4 ...
## $ days_from_custody_to_death : num [1:390] 274 10 29 47 0 4 6 24 1 261 ...
## $ timing_category : Factor w/ 6 levels "<24 hours",">90 days",..: 2 6 4 4 1 5 5 4 3 2 ...
## $ exhibit_any_mental_health_problems : Factor w/ 3 levels "NO","UNKNOWN",..: 1 3 1 2 2 2 1 2 2 2 ...
## $ make_suicidal_statements : Factor w/ 3 levels "NO","UNKNOWN",..: 1 3 1 2 1 1 1 2 2 2 ...
## $ housing_single_cell : num [1:390] 0 0 0 0 0 0 1 0 0 0 ...
## $ housing_multiple : num [1:390] 0 0 0 0 0 1 0 0 0 0 ...
## $ mh_unknown : num [1:390] 0 0 0 1 1 1 0 1 1 1 ...
## $ mh_yes : num [1:390] 0 1 0 0 0 0 0 0 0 0 ...
## $ suicidal_yes : num [1:390] 0 1 0 0 0 0 0 0 0 0 ...
## $ county_dallas : num [1:390] 0 0 0 1 0 0 0 0 0 1 ...
## $ county_harris : num [1:390] 0 0 0 0 0 0 0 0 0 0 ...
## $ county_travis : num [1:390] 0 1 0 0 0 0 0 0 0 0 ...
## $ death_from_pre_existing_medical_condition: Factor w/ 4 levels "DEVELOPED CONDITION AFTER ADMISSION",..: 4 3 4 4 3 3 2 2 2 3 ...
## $ type_of_offense : Factor w/ 12 levels "ALCOHOL / DRUG OFFENSE",..: 10 5 8 10 1 10 12 10 10 10 ...
## $ offense_1 : chr [1:390] "CONTINUOUS SEX ABUSE" "BURGLARY OF VEHICLE" "CRUELTY-NONLIESTOCK" "ASSAULT CAUSES BODILY INJURY FAMILY VIOLENCE" ...
## $ were_the_charges : Factor w/ 4 levels "CONVICTED","FILED",..: 2 2 2 4 3 2 2 2 2 2 ...
# Load packages if not already loaded
library(tidyverse)
library(psych)
##Descriptive Statistics
# Overall sample size
nrow(countyjail_data)
## [1] 390
# Preventable death frequency and percentage
table(countyjail_data$preventable_death)
##
## 0 1
## 326 64
prop.table(table(countyjail_data$preventable_death)) * 100
##
## 0 1
## 83.58974 16.41026
# Age statistics
summary(countyjail_data$age_at_time_of_death)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 18.00 34.25 47.00 46.92 58.00 89.00
sd(countyjail_data$age_at_time_of_death, na.rm = TRUE)
## [1] 14.88874
# Demographics
table(countyjail_data$sex)
##
## FEMALE MALE
## 44 346
prop.table(table(countyjail_data$sex)) * 100
##
## FEMALE MALE
## 11.28205 88.71795
table(countyjail_data$race)
##
## BLACK HISPANIC OTHER WHITE
## 155 85 18 132
prop.table(table(countyjail_data$race)) * 100
##
## BLACK HISPANIC OTHER WHITE
## 39.743590 21.794872 4.615385 33.846154
# Mental health problems
table(countyjail_data$exhibit_any_mental_health_problems)
##
## NO UNKNOWN YES
## 86 169 70
prop.table(table(countyjail_data$exhibit_any_mental_health_problems)) * 100
##
## NO UNKNOWN YES
## 26.46154 52.00000 21.53846
# Suicidal statements
table(countyjail_data$make_suicidal_statements)
##
## NO UNKNOWN YES
## 134 163 28
prop.table(table(countyjail_data$make_suicidal_statements)) * 100
##
## NO UNKNOWN YES
## 41.230769 50.153846 8.615385
# Days in custody
summary(countyjail_data$days_from_custody_to_death)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0 5.0 36.0 120.3 155.8 1277.0
sd(countyjail_data$days_from_custody_to_death, na.rm = TRUE)
## [1] 198.9063
##Four County Comparison # Preventable death rate by county
county_summary <- countyjail_data %>%
group_by(agency_county) %>%
summarise(
total_deaths = n(),
preventable_deaths = sum(preventable_death == 1),
preventable_rate = mean(preventable_death) * 100,
.groups = 'drop'
) %>%
arrange(desc(preventable_rate))
print(county_summary)
## # A tibble: 4 × 4
## agency_county total_deaths preventable_deaths preventable_rate
## <fct> <int> <int> <dbl>
## 1 TRAVIS 43 12 27.9
## 2 BEXAR 119 33 27.7
## 3 HARRIS 151 13 8.61
## 4 DALLAS 77 6 7.79
#Four County Comparison
four_county_summary <- countyjail_data %>%
filter(agency_county %in% c("BEXAR", "DALLAS", "HARRIS", "TRAVIS")) %>%
group_by(agency_county) %>%
summarise(
total_deaths = n(),
preventable_deaths = sum(preventable_death == 1),
preventable_rate = mean(preventable_death) * 100,
.groups = 'drop'
)
print(four_county_summary)
## # A tibble: 4 × 4
## agency_county total_deaths preventable_deaths preventable_rate
## <fct> <int> <int> <dbl>
## 1 BEXAR 119 33 27.7
## 2 DALLAS 77 6 7.79
## 3 HARRIS 151 13 8.61
## 4 TRAVIS 43 12 27.9
# Comprehensive county comparison
county_characteristics <- countyjail_data %>%
filter(agency_county %in% c("BEXAR", "DALLAS", "HARRIS", "TRAVIS")) %>%
group_by(agency_county) %>%
summarise(
n = n(),
preventable_rate = mean(preventable_death) * 100,
# Demographics
avg_age = mean(age_at_time_of_death, na.rm = TRUE),
male_pct = mean(sex_male) * 100,
white_pct = mean(race_white) * 100,
black_pct = mean(race_black) * 100,
hispanic_pct = mean(race_hispanic) * 100,
# Mental health
mh_yes_pct = mean(mh_yes) * 100,
mh_unknown_pct = mean(mh_unknown) * 100,
suicidal_pct = mean(suicidal_yes) * 100,
# Custody characteristics
avg_days_in_custody = mean(days_from_custody_to_death, na.rm = TRUE),
single_cell_pct = mean(housing_single_cell) * 100,
.groups = 'drop'
)
print(county_characteristics)
## # A tibble: 4 × 13
## agency_county n preventable_rate avg_age male_pct white_pct black_pct
## <fct> <int> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 BEXAR 119 27.7 45.7 85.7 36.1 22.7
## 2 DALLAS 77 7.79 49.5 90.9 31.2 46.8
## 3 HARRIS 151 8.61 48.2 91.4 30.5 54.3
## 4 TRAVIS 43 27.9 41.5 83.7 44.2 23.3
## # ℹ 6 more variables: hispanic_pct <dbl>, mh_yes_pct <dbl>,
## # mh_unknown_pct <dbl>, suicidal_pct <dbl>, avg_days_in_custody <dbl>,
## # single_cell_pct <dbl>
# Compare characteristics by preventable death status
preventable_comparison <- countyjail_data %>%
group_by(preventable_death) %>%
summarise(
n = n(),
avg_age = mean(age_at_time_of_death, na.rm = TRUE),
male_pct = mean(sex_male) * 100,
white_pct = mean(race_white) * 100,
black_pct = mean(race_black) * 100,
hispanic_pct = mean(race_hispanic) * 100,
mh_yes_pct = mean(mh_yes) * 100,
suicidal_pct = mean(suicidal_yes) * 100,
avg_days_custody = mean(days_from_custody_to_death, na.rm = TRUE),
.groups = 'drop'
)
print(preventable_comparison)
## # A tibble: 2 × 10
## preventable_death n avg_age male_pct white_pct black_pct hispanic_pct
## <dbl> <int> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0 326 48.9 89.3 30.7 43.9 20.2
## 2 1 64 36.6 85.9 50 18.8 29.7
## # ℹ 3 more variables: mh_yes_pct <dbl>, suicidal_pct <dbl>,
## # avg_days_custody <dbl>
# Table 1: Sample Characteristics
table1 <- countyjail_data %>%
summarise(
`Total Deaths` = n(),
`Preventable Deaths (n)` = sum(preventable_death == 1),
`Preventable Deaths (%)` = mean(preventable_death) * 100,
`Mean Age (SD)` = paste0(round(mean(age_at_time_of_death), 1), " (",
round(sd(age_at_time_of_death), 1), ")"),
`Male (%)` = mean(sex_male) * 100,
`Mental Health Issues (%)` = mean(mh_yes) * 100,
`Mean Days in Custody (SD)` = paste0(round(mean(days_from_custody_to_death), 1), " (",
round(sd(days_from_custody_to_death), 1), ")")
)
print(table1)
## # A tibble: 1 × 7
## `Total Deaths` `Preventable Deaths (n)` Preventable Deaths (…¹ `Mean Age (SD)`
## <int> <int> <dbl> <chr>
## 1 390 64 16.4 46.9 (14.9)
## # ℹ abbreviated name: ¹`Preventable Deaths (%)`
## # ℹ 3 more variables: `Male (%)` <dbl>, `Mental Health Issues (%)` <dbl>,
## # `Mean Days in Custody (SD)` <chr>
##Visualizations
write.csv(county_summary, "county_preventable_rates.csv", row.names = FALSE)
write.csv(county_characteristics, "county_characteristics.csv", row.names = FALSE)
# Bar chart: Preventable death rates by county
ggplot(four_county_summary, aes(x = reorder(agency_county, -preventable_rate),
y = preventable_rate)) +
geom_bar(stat = "identity", fill = "steelblue") +
geom_text(aes(label = paste0(round(preventable_rate, 1), "%")),
vjust = -0.5, size = 4) +
labs(title = "Preventable Death Rates in Texas County Jails (2015-2025)",
subtitle = "Bexar, Dallas, Harris, and Travis Counties",
x = "County",
y = "Preventable Death Rate (%)") +
theme_minimal() +
theme(text = element_text(size = 12))
# Age distribution by preventable death
ggplot(countyjail_data, aes(x = factor(preventable_death),
y = age_at_time_of_death)) +
geom_boxplot(fill = "lightblue") +
labs(title = "Age Distribution by Preventable Death Status",
x = "Preventable Death (0 = No, 1 = Yes)",
y = "Age at Death") +
theme_minimal()
#Bivarte Analysis : Unadjusted Associations ##Age and Preventable Death (t-test)
cat("T-test: Age by Preventable Death\n")
## T-test: Age by Preventable Death
t_test_age <- t.test(age_at_time_of_death ~ preventable_death,
data = countyjail_data)
print(t_test_age)
##
## Welch Two Sample t-test
##
## data: age_at_time_of_death by preventable_death
## t = 8.0366, df = 120.46, p-value = 7.136e-13
## alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
## 95 percent confidence interval:
## 9.266044 15.323872
## sample estimates:
## mean in group 0 mean in group 1
## 48.93558 36.64062
# Mean ages
age_summary <- countyjail_data %>%
group_by(preventable_death) %>%
summarise(
n = n(),
mean_age = mean(age_at_time_of_death, na.rm = TRUE),
sd_age = sd(age_at_time_of_death, na.rm = TRUE)
)
knitr::kable(age_summary, digits = 2,
caption = "Age by Preventable Death Status")
| preventable_death | n | mean_age | sd_age |
|---|---|---|---|
| 0 | 326 | 48.94 | 14.82 |
| 1 | 64 | 36.64 | 10.33 |
### Days in Custody and Preventable Death (t-test)
cat("\n\nT-test: Days in Custody by Preventable Death\n")
##
##
## T-test: Days in Custody by Preventable Death
t_test_days <- t.test(days_from_custody_to_death ~ preventable_death,
data = countyjail_data)
print(t_test_days)
##
## Welch Two Sample t-test
##
## data: days_from_custody_to_death by preventable_death
## t = 0.68259, df = 80.006, p-value = 0.4968
## alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
## 95 percent confidence interval:
## -40.96204 83.73179
## sample estimates:
## mean in group 0 mean in group 1
## 123.8067 102.4219
### Mental Health and Preventable Death (Chi-square)
cat("\n\nChi-square: Mental Health by Preventable Death\n")
##
##
## Chi-square: Mental Health by Preventable Death
mh_table <- table(countyjail_data$exhibit_any_mental_health_problems,
countyjail_data$preventable_death)
print(mh_table)
##
## 0 1
## NO 67 19
## UNKNOWN 150 19
## YES 57 13
chisq_mh <- chisq.test(mh_table)
print(chisq_mh)
##
## Pearson's Chi-squared test
##
## data: mh_table
## X-squared = 5.631, df = 2, p-value = 0.05987
# Percentages
prop.table(mh_table, 1) * 100
##
## 0 1
## NO 77.90698 22.09302
## UNKNOWN 88.75740 11.24260
## YES 81.42857 18.57143
### County and Preventable Death (Chi-square)
cat("\n\nChi-square: County by Preventable Death\n")
##
##
## Chi-square: County by Preventable Death
county_table <- table(countyjail_data$agency_county,
countyjail_data$preventable_death)
chisq_county <- chisq.test(county_table)
print(chisq_county)
##
## Pearson's Chi-squared test
##
## data: county_table
## X-squared = 26.13, df = 3, p-value = 8.96e-06
### Race and Preventable Death (Chi-square)
cat("\n\nChi-square: Race by Preventable Death\n")
##
##
## Chi-square: Race by Preventable Death
race_table <- table(countyjail_data$race,
countyjail_data$preventable_death)
chisq_race <- chisq.test(race_table)
## Warning in chisq.test(race_table): Chi-squared approximation may be incorrect
print(chisq_race)
##
## Pearson's Chi-squared test
##
## data: race_table
## X-squared = 18.128, df = 3, p-value = 0.0004139
library(tidyverse)
library(readxl)
library(psych)
library(knitr)
##ANOVA : County Differences
##Age across counties
anova_age <- aov(age_at_time_of_death ~ agency_county,
data = countyjail_data %>%
filter(agency_county %in% c("BEXAR", "DALLAS", "HARRIS", "TRAVIS")))
summary(anova_age)
## Df Sum Sq Mean Sq F value Pr(>F)
## agency_county 3 2193 731.0 3.358 0.0189 *
## Residuals 386 84038 217.7
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Post-hoc test
TukeyHSD(anova_age)
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = age_at_time_of_death ~ agency_county, data = countyjail_data %>% filter(agency_county %in% c("BEXAR", "DALLAS", "HARRIS", "TRAVIS")))
##
## $agency_county
## diff lwr upr p adj
## DALLAS-BEXAR 3.825057 -1.743174 9.39328810 0.2882031
## HARRIS-BEXAR 2.496856 -2.170039 7.16374999 0.5123687
## TRAVIS-BEXAR -4.167090 -10.941287 2.60710711 0.3871057
## HARRIS-DALLAS -1.328202 -6.659606 4.00320277 0.9180113
## TRAVIS-DALLAS -7.992147 -15.240162 -0.74413263 0.0240764
## TRAVIS-HARRIS -6.663946 -13.244860 -0.08303202 0.0458887
### Days in custody across counties
anova_days <- aov(days_from_custody_to_death ~ agency_county,
data = countyjail_data %>%
filter(agency_county %in% c("BEXAR", "DALLAS", "HARRIS", "TRAVIS")))
summary(anova_days)
## Df Sum Sq Mean Sq F value Pr(>F)
## agency_county 3 109969 36656 0.926 0.428
## Residuals 386 15280323 39586
##Logistic Regression Models
#Filter to 4 main counties
four_counties <- countyjail_data %>%
filter(agency_county %in% c("BEXAR", "DALLAS", "HARRIS", "TRAVIS"))
### Model 1: Demographic factors only
model1 <- glm(preventable_death ~ age_at_time_of_death + sex_male +
race_black + race_hispanic,
data = four_counties,
family = binomial)
cat("Model 1: Demographics Only\n")
## Model 1: Demographics Only
summary(model1)
##
## Call:
## glm(formula = preventable_death ~ age_at_time_of_death + sex_male +
## race_black + race_hispanic, family = binomial, data = four_counties)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 2.21355 0.67731 3.268 0.00108 **
## age_at_time_of_death -0.07489 0.01256 -5.961 2.51e-09 ***
## sex_male -0.15009 0.44079 -0.341 0.73347
## race_black -1.59295 0.39056 -4.079 4.53e-05 ***
## race_hispanic -0.22262 0.35661 -0.624 0.53245
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 348.20 on 389 degrees of freedom
## Residual deviance: 286.98 on 385 degrees of freedom
## AIC: 296.98
##
## Number of Fisher Scoring iterations: 5
# Odds ratios
model1_or <- data.frame(
Variable = names(coef(model1)),
Odds_Ratio = exp(coef(model1)),
CI_Lower = exp(confint(model1))[,1],
CI_Upper = exp(confint(model1))[,2],
P_Value = summary(model1)$coefficients[,4]
)
## Waiting for profiling to be done...
## Waiting for profiling to be done...
kable(model1_or, digits = 3, caption = "Model 1: Odds Ratios (Demographics)")
| Variable | Odds_Ratio | CI_Lower | CI_Upper | P_Value | |
|---|---|---|---|---|---|
| (Intercept) | (Intercept) | 9.148 | 2.456 | 35.526 | 0.001 |
| age_at_time_of_death | age_at_time_of_death | 0.928 | 0.904 | 0.950 | 0.000 |
| sex_male | sex_male | 0.861 | 0.374 | 2.140 | 0.733 |
| race_black | race_black | 0.203 | 0.091 | 0.425 | 0.000 |
| race_hispanic | race_hispanic | 0.800 | 0.392 | 1.595 | 0.532 |
### Model 2: Add mental health and custody factors
model2 <- glm(preventable_death ~ age_at_time_of_death + sex_male +
race_black + race_hispanic + mh_yes + suicidal_yes +
days_from_custody_to_death,
data = four_counties,
family = binomial)
cat("\n\nModel 2: Demographics + Mental Health + Custody Time\n")
##
##
## Model 2: Demographics + Mental Health + Custody Time
summary(model2)
##
## Call:
## glm(formula = preventable_death ~ age_at_time_of_death + sex_male +
## race_black + race_hispanic + mh_yes + suicidal_yes + days_from_custody_to_death,
## family = binomial, data = four_counties)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 2.335e+00 6.960e-01 3.355 0.000793 ***
## age_at_time_of_death -7.801e-02 1.288e-02 -6.058 1.38e-09 ***
## sex_male -2.530e-01 4.485e-01 -0.564 0.572647
## race_black -1.661e+00 4.011e-01 -4.142 3.44e-05 ***
## race_hispanic -2.050e-01 3.627e-01 -0.565 0.571903
## mh_yes -2.854e-01 4.698e-01 -0.607 0.543523
## suicidal_yes 1.648e+00 6.054e-01 2.722 0.006493 **
## days_from_custody_to_death 7.565e-05 8.169e-04 0.093 0.926215
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 348.20 on 389 degrees of freedom
## Residual deviance: 278.95 on 382 degrees of freedom
## AIC: 294.95
##
## Number of Fisher Scoring iterations: 5
model2_or <- data.frame(
Variable = names(coef(model2)),
Odds_Ratio = exp(coef(model2)),
CI_Lower = exp(confint(model2))[,1],
CI_Upper = exp(confint(model2))[,2],
P_Value = summary(model2)$coefficients[,4]
)
## Waiting for profiling to be done...
## Waiting for profiling to be done...
kable(model2_or, digits = 3, caption = "Model 2: Odds Ratios (+ Mental Health)")
| Variable | Odds_Ratio | CI_Lower | CI_Upper | P_Value | |
|---|---|---|---|---|---|
| (Intercept) | (Intercept) | 10.330 | 2.683 | 41.805 | 0.001 |
| age_at_time_of_death | age_at_time_of_death | 0.925 | 0.901 | 0.948 | 0.000 |
| sex_male | sex_male | 0.776 | 0.332 | 1.954 | 0.573 |
| race_black | race_black | 0.190 | 0.083 | 0.405 | 0.000 |
| race_hispanic | race_hispanic | 0.815 | 0.394 | 1.644 | 0.572 |
| mh_yes | mh_yes | 0.752 | 0.282 | 1.811 | 0.544 |
| suicidal_yes | suicidal_yes | 5.195 | 1.592 | 17.464 | 0.006 |
| days_from_custody_to_death | days_from_custody_to_death | 1.000 | 0.998 | 1.002 | 0.926 |
### Model 3: Add county (main model)
model3 <- glm(preventable_death ~ age_at_time_of_death + sex_male +
race_black + race_hispanic + mh_yes + suicidal_yes +
days_from_custody_to_death + agency_county,
data = four_counties,
family = binomial)
cat("\n\nModel 3: Full Model with County\n")
##
##
## Model 3: Full Model with County
summary(model3)
##
## Call:
## glm(formula = preventable_death ~ age_at_time_of_death + sex_male +
## race_black + race_hispanic + mh_yes + suicidal_yes + days_from_custody_to_death +
## agency_county, family = binomial, data = four_counties)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 2.7576094 0.7389734 3.732 0.000190 ***
## age_at_time_of_death -0.0761146 0.0131995 -5.766 8.09e-09 ***
## sex_male -0.1598480 0.4669244 -0.342 0.732093
## race_black -1.4740555 0.4152225 -3.550 0.000385 ***
## race_hispanic -0.4468179 0.3855439 -1.159 0.246485
## mh_yes -0.0766539 0.5012327 -0.153 0.878453
## suicidal_yes 1.7043545 0.6184724 2.756 0.005856 **
## days_from_custody_to_death 0.0003156 0.0007943 0.397 0.691178
## agency_countyDALLAS -1.5638428 0.5500578 -2.843 0.004468 **
## agency_countyHARRIS -1.1498989 0.4120464 -2.791 0.005259 **
## agency_countyTRAVIS -0.4172569 0.4828710 -0.864 0.387524
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 348.20 on 389 degrees of freedom
## Residual deviance: 265.31 on 379 degrees of freedom
## AIC: 287.31
##
## Number of Fisher Scoring iterations: 6
model3_or <- data.frame(
Variable = names(coef(model3)),
Odds_Ratio = exp(coef(model3)),
CI_Lower = exp(confint(model3))[,1],
CI_Upper = exp(confint(model3))[,2],
P_Value = summary(model3)$coefficients[,4]
)
## Waiting for profiling to be done...
## Waiting for profiling to be done...
kable(model3_or, digits = 3, caption = "Model 3: Full Model with County Effects")
| Variable | Odds_Ratio | CI_Lower | CI_Upper | P_Value | |
|---|---|---|---|---|---|
| (Intercept) | (Intercept) | 15.762 | 3.811 | 70.349 | 0.000 |
| age_at_time_of_death | age_at_time_of_death | 0.927 | 0.902 | 0.950 | 0.000 |
| sex_male | sex_male | 0.852 | 0.352 | 2.226 | 0.732 |
| race_black | race_black | 0.229 | 0.098 | 0.503 | 0.000 |
| race_hispanic | race_hispanic | 0.640 | 0.296 | 1.349 | 0.246 |
| mh_yes | mh_yes | 0.926 | 0.330 | 2.391 | 0.878 |
| suicidal_yes | suicidal_yes | 5.498 | 1.651 | 19.064 | 0.006 |
| days_from_custody_to_death | days_from_custody_to_death | 1.000 | 0.999 | 1.002 | 0.691 |
| agency_countyDALLAS | agency_countyDALLAS | 0.209 | 0.065 | 0.577 | 0.004 |
| agency_countyHARRIS | agency_countyHARRIS | 0.317 | 0.138 | 0.700 | 0.005 |
| agency_countyTRAVIS | agency_countyTRAVIS | 0.659 | 0.248 | 1.662 | 0.388 |
# Model fit statistics
cat("\n\nModel Fit Statistics:\n")
##
##
## Model Fit Statistics:
cat("Model 1 AIC:", AIC(model1), "\n")
## Model 1 AIC: 296.981
cat("Model 2 AIC:", AIC(model2), "\n")
## Model 2 AIC: 294.9452
cat("Model 3 AIC:", AIC(model3), "\n")
## Model 3 AIC: 287.3051
# Pseudo R-squared
cat("\nMcFadden's Pseudo R-squared:\n")
##
## McFadden's Pseudo R-squared:
cat("Model 1:", 1 - (model1$deviance / model1$null.deviance), "\n")
## Model 1: 0.1758164
cat("Model 2:", 1 - (model2$deviance / model2$null.deviance), "\n")
## Model 2: 0.1988944
cat("Model 3:", 1 - (model3$deviance / model3$null.deviance), "\n")
## Model 3: 0.2380675
#Interaction Analysis: Does Mental Health Effect Vary by County?
model_interaction <- glm(preventable_death ~ age_at_time_of_death + sex_male +
race_black + race_hispanic +
mh_yes * agency_county, # Interaction term
data = four_counties,
family = binomial)
summary(model_interaction)
##
## Call:
## glm(formula = preventable_death ~ age_at_time_of_death + sex_male +
## race_black + race_hispanic + mh_yes * agency_county, family = binomial,
## data = four_counties)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 2.72855 0.73696 3.702 0.000214 ***
## age_at_time_of_death -0.07855 0.01349 -5.821 5.86e-09 ***
## sex_male 0.15723 0.46992 0.335 0.737940
## race_black -1.43054 0.41633 -3.436 0.000590 ***
## race_hispanic -0.47787 0.38640 -1.237 0.216188
## mh_yes -0.84308 1.19269 -0.707 0.479646
## agency_countyDALLAS -1.06785 0.55840 -1.912 0.055833 .
## agency_countyHARRIS -1.70431 0.47499 -3.588 0.000333 ***
## agency_countyTRAVIS -0.34601 0.56269 -0.615 0.538609
## mh_yes:agency_countyDALLAS -0.28190 1.67930 -0.168 0.866686
## mh_yes:agency_countyHARRIS 3.36686 1.39265 2.418 0.015623 *
## mh_yes:agency_countyTRAVIS 0.95917 1.40777 0.681 0.495656
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 348.20 on 389 degrees of freedom
## Residual deviance: 261.59 on 378 degrees of freedom
## AIC: 285.59
##
## Number of Fisher Scoring iterations: 6
interaction_or <- data.frame(
Variable = names(coef(model_interaction)),
Odds_Ratio = exp(coef(model_interaction)),
CI_Lower = exp(confint(model_interaction))[,1],
CI_Upper = exp(confint(model_interaction))[,2],
P_Value = summary(model_interaction)$coefficients[,4]
)
## Waiting for profiling to be done...
## Waiting for profiling to be done...
kable(interaction_or, digits = 3,
caption = "Interaction Model: Mental Health × County")
| Variable | Odds_Ratio | CI_Lower | CI_Upper | P_Value | |
|---|---|---|---|---|---|
| (Intercept) | (Intercept) | 15.311 | 3.707 | 67.908 | 0.000 |
| age_at_time_of_death | age_at_time_of_death | 0.924 | 0.899 | 0.948 | 0.000 |
| sex_male | sex_male | 1.170 | 0.481 | 3.076 | 0.738 |
| race_black | race_black | 0.239 | 0.102 | 0.527 | 0.001 |
| race_hispanic | race_hispanic | 0.620 | 0.286 | 1.310 | 0.216 |
| mh_yes | mh_yes | 0.430 | 0.020 | 3.235 | 0.480 |
| agency_countyDALLAS | agency_countyDALLAS | 0.344 | 0.104 | 0.962 | 0.056 |
| agency_countyHARRIS | agency_countyHARRIS | 0.182 | 0.068 | 0.445 | 0.000 |
| agency_countyTRAVIS | agency_countyTRAVIS | 0.708 | 0.219 | 2.050 | 0.539 |
| mh_yes:agency_countyDALLAS | mh_yes:agency_countyDALLAS | 0.754 | 0.021 | 27.896 | 0.867 |
| mh_yes:agency_countyHARRIS | mh_yes:agency_countyHARRIS | 28.987 | 2.401 | 794.779 | 0.016 |
| mh_yes:agency_countyTRAVIS | mh_yes:agency_countyTRAVIS | 2.610 | 0.206 | 72.421 | 0.496 |
#Additional Visulizations
###1. Age Distribution by Preventable Death
ggplot(countyjail_data, aes(x = factor(preventable_death),
y = age_at_time_of_death,
fill = factor(preventable_death))) +
geom_boxplot() +
scale_fill_manual(values = c("lightblue", "coral")) +
labs(title = "Age Distribution by Preventable Death Status",
x = "Preventable Death (0 = No, 1 = Yes)",
y = "Age at Death",
fill = "Preventable") +
theme_minimal() +
theme(legend.position = "none")
### 2. Mental Health by County and Preventable Death
mh_county_plot <- four_counties %>%
group_by(agency_county, preventable_death) %>%
summarise(mh_pct = mean(mh_yes) * 100, .groups = 'drop')
ggplot(mh_county_plot, aes(x = agency_county, y = mh_pct,
fill = factor(preventable_death))) +
geom_bar(stat = "identity", position = "dodge") +
scale_fill_manual(values = c("steelblue", "coral"),
labels = c("Non-Preventable", "Preventable")) +
labs(title = "Mental Health Documentation by County and Preventable Death",
x = "County",
y = "% with Mental Health Issues",
fill = "Death Type") +
theme_minimal()
### 3. Days in Custody by County
ggplot(four_counties, aes(x = agency_county,
y = days_from_custody_to_death,
fill = agency_county)) +
geom_boxplot() +
labs(title = "Days from Custody to Death by County",
x = "County",
y = "Days in Custody") +
theme_minimal() +
theme(legend.position = "none")
### 4. Preventable Death Rate by Mental Health Status
mh_prev_plot <- countyjail_data %>%
group_by(exhibit_any_mental_health_problems) %>%
summarise(
n = n(),
preventable_rate = mean(preventable_death) * 100,
.groups = 'drop'
) %>%
filter(exhibit_any_mental_health_problems != "UNKNOWN")
ggplot(mh_prev_plot, aes(x = exhibit_any_mental_health_problems,
y = preventable_rate,
fill = exhibit_any_mental_health_problems)) +
geom_bar(stat = "identity") +
geom_text(aes(label = paste0(round(preventable_rate, 1), "%")),
vjust = -0.5, size = 5) +
labs(title = "Preventable Death Rate by Mental Health Status",
x = "Mental Health Problems Documented",
y = "Preventable Death Rate (%)") +
theme_minimal() +
theme(legend.position = "none")
### 5. Race Distribution by County
race_county <- four_counties %>%
count(agency_county, race) %>%
group_by(agency_county) %>%
mutate(pct = n / sum(n) * 100)
ggplot(race_county, aes(x = agency_county, y = pct, fill = race)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Racial Composition of Deaths by County",
x = "County",
y = "Percentage",
fill = "Race") +
theme_minimal()
### 6. Timing of Death by County
timing_county <- four_counties %>%
count(agency_county, timing_category) %>%
group_by(agency_county) %>%
mutate(pct = n / sum(n) * 100)
ggplot(timing_county, aes(x = agency_county, y = pct, fill = timing_category)) +
geom_bar(stat = "identity") +
labs(title = "Timing of Death by County",
x = "County",
y = "Percentage",
fill = "Time in Custody") +
theme_minimal()
#Conclusion The Evidence Is Clear: Preventable Deaths Are Preventable This analysis of 390 custodial deaths across Texas’s four largest county jail systems between 2015 and 2025 reveals a troubling truth. The likelihood of experiencing a preventable death in custody depends more on which county jail you’re in than on individual risk factors. The data demonstrates a 3.6-fold variation in preventable death rates from Travis County’s 27.9% to Dallas County’s 7.8%, that cannot be attributed to demographics, facility size, or chance. Travis and Bexar counties each classify more than 1 in 4 deaths as preventable, while Dallas and Harris counties prevent the vast majority of deaths that could be avoided, with rates below 9%.
What the Numbers Mean
These are not abstract statistics. Behind Travis County’s 27.9% rate are 12 individuals who died preventable deaths. Behind Bexar County’s 27.7% rate are 33 people, including my brother Francisco Bazan and 32 others whose lives could have been saved with proper institutional safeguards. Together, Travis and Bexar counties account for 45 of the 64 preventable deaths (70%) in this four- county sample, despite representing only 42% of total deaths. If these two counties had achieved Dallas County’s preventable death rate, an estimated 32 lives would have been saved over this decade.
Institutional Factors Trump Individual Risk
The near-identical preventable death rates in Travis (27.9%) and Bexar (27.7%), two counties with different facility sizes and populations point to shared institutional deficiencies rather than individual level factors. The similarity suggests systemic problems in:
Medical screening and assessment protocols Mental health crisis intervention Staff training and emergency response Monitoring of at-risk individuals Continuity of care for chronic conditions
Statistical analysis confirms these institutional failures. Bivariate analyses reveal that younger age (p < 0.001), race (p < 0.001), and suicidal statements are significantly associated with preventable death. Individuals who died preventable deaths were, on average, 12.3 years younger (36.6 vs. 48.9 years, p < 0.001) than those who died non-preventable deaths. Those who made suicidal statements had dramatically elevated risk. However, multivariate logistic regression controlling for age, sex, race, mental health status, suicidal ideation, and time in custody demonstrates that county-level differences remain statistically significant (p < 0.01). Even after accounting for who is in custody and their documented risk factors:
Dallas County inmates have 79% lower odds of preventable death compared to Bexar County (OR = 0.21, 95% CI = 0.07-0.58, p = 0.004) Harris County inmates have 68% lower odds of preventable death compared to Bexar County (OR = 0.32, 95% CI = 0.14-0.70, p = 0.005) Travis County shows no statistical difference from Bexar County (OR = 0.66, 95% CI = 0.25-1.66, p = 0.388), confirming their shared institutional failures
This confirms that institutional practices, not just the characteristics of people in custody—drive mortality outcomes. Travis and Bexar’s identical preventable death rates persist even after controlling for every measurable individual risk factor, proving these are institutional, not individual, failures.
Critical Finding on Suicidal Ideation: Individuals who made suicidal statements had 5.5 times the odds of experiencing a preventable death (OR = 5.50, 95% CI = 1.65-19.06, p = 0.006), even after controlling for all other factors. This suggests that suicidal statements are a major red flag that is not being adequately addressed in jail mental health protocols, particularly in Travis and Bexar counties. Mental Health Systems Matter: Interaction analysis reveals that mental health effects vary dramatically by county. In Harris County, documented mental health problems interact with institutional practices in ways that significantly reduce preventable deaths (p = 0.016). This suggests Harris County has implemented mental health screening and intervention protocols that other counties lack protocols that should be immediately investigated and mandated statewide. Conversely, the consistently low rates in Dallas (7.8%) and Harris (8.6%) demonstrate that proper systems prevent deaths. These counties prove that current conditions in Travis and Bexar are not inevitable, they are institutional failures
Conversely, the consistently low rates in Dallas (7.8%) and Harris (8.6%) demonstrate that proper systems prevent deaths. These counties prove that current conditions in Travis and Bexar are not inevitable, they are institutional failures.
Size Does Not Determine Outcomes
Harris County’s success is particularly instructive. Despite having 151 deaths, the largest sample and nearly 4 times Travis County’ s volume, Harris maintained the second-lowest preventable death rate at 8.6%. This definitively proves that facility size and death volume do not predetermine outcomes. Large jail systems can and do prevent deaths when proper protocols are in place.
The Urgent Need for Reform
These findings demand immediate action on multiple fronts:
The state must mandate:
Standardized medical screening at intake. Mental health assessment and crisis intervention protocos. Regular monitoring of medically and mentally vulnerable individuals. Evidence-based staff training on recognizing and responding to medical emergencies. Transparent reporting and independent investigation of all custodial deaths.
An urgent priority must be identifying what Dallas and Harris counties are doing differently. Their practices whether superior medical staffing, better training protocols, enhanced monitoring systems, or more robust mental health services—should be documented, validated, and required statewide.
The regression analysis points to specific areas for investigation. Harris County’s success appears particularly linked to how they handle mental health cases statistical modeling shows their mental health protocols significantly reduce preventable deaths in ways not seen in other counties. Dallas County’s success spans multiple factors, achieving lower preventable death rates across all risk categories. Both counties must be managing suicidal ideation more effectively, given that suicidal statements increase preventable death risk by 450% in the overall sample. What screening tools do they use? How quickly do they respond to suicidal statements? What monitoring protocols prevent crises from becoming fatalities? These specific practices must be identified, documented, and required statewide immediately.
Counties with elevated preventable death rates require:
Immediate external audits of death prevention protocols. Enhanced state oversight and intervention. Mandatory corrective action plans. Public transparency in custodial death investigations. Consequences for systemic failures to protect people in custody.
Preventing deaths requires investment. Counties need adequate resources for:
Medical and mental health staffing. Staff training and continuing education. Facility improvements for monitoring and care. Technology and systems for tracking at-risk individuals.
However, resource constraints cannot excuse the current disparities. Harris County manages 151 deaths with an 8.6% preventable rate, demonstrating that effective prevention is achievable even at scale.
Every Preventable Death Is a Policy Failure
The fundamental finding of this research is that preventable deaths in Texas county jails are exactly that, preventable. The existence of counties with 7.8% preventable death rates proves that the 27.9% rate in other counties represents institutional choices, not inevitable outcomes.
Each of the 64 preventable deaths in this sample represents:
A person who entered custody alive. A death that could have been avoided. A family forever changed by loss. A failure of the system to fulfill its duty of care.
For my brother, Francisco Bazan and the 32 other individuals who died preventable deaths in Bexar County, and the 12 in Travis County these numbers represent profound, irreversible loss that proper institutional practices could have prevented.
The Path Forward
The evidence presented here provides a clear roadmap for reform:
Immediately investigate what practices Dallas and Harris counties employ that Travis and Bexar do not. Mandate statewide adoption of evidence-based protocols for medical screening, mental health assessment, and emergency response. Require external oversight of counties with elevated preventable death rates. Ensure transparency through public reporting of custodial deaths and investigation findings. Invest resources in medical and mental health staffing sufficient to prevent deaths. Hold counties accountable for failing to implement life-saving protocols.
Final Thoughts
This analysis began with a simple question: Why do preventable death rates vary so dramatically across Texas county jails? The answer is equally simple but profoundly troubling, some counties have systems that prevent deaths, and others do not. The 3.6-fold difference between Travis and Dallas counties is not a statistical curiosity, it is evidence of preventable institutional failure that has cost dozens of lives over the past decade. The good news is that we know prevention is possible because some counties are already doing it. The tragedy is that this knowledge has not yet translated into statewide action. The moral imperative is clear, Texas must ensure that all county jails adopt the practices that save lives. Every person in custody deserves the same standard of care, the same protection from preventable harm, and the same chance of leaving custody alive. Until Texas achieves that standard, the families of those who died preventable deaths and the communities they came from will continue to bear the cost of institutional failure. The question is no longer whether preventable deaths can be avoided. The data proves they can. The question is whether Texas will act on this evidence to ensure they are.
This analysis is dedicated to my brother, Francisco Bazan and the 63 other individuals whose preventable deaths in Texas county jails between 2015 and 2025. We demand accountability, reform, and a commitment that their lives and deaths will drive meaningful change.