1 Introduction

This document represents exploratory data analysis of ICE’s Fiscal FY21 Year End Detention Report. I downloaded the data on 15 April 2021, so the report should represent the first 6 months of FY21 (see below). Analysis was performed in R Studio, written in Rmarkdown, and all code is supplied in the Appendix.

1.1 Data

The FY21 Year End Report was downloaded from the ICE Detention Management webpage on 15 Apr 2021. FY21 ends on 30 September 2021, so the data in the table represent the first half of FY21. The data are likely to change over the course of the last half of FY21.

For mapping purposes, facility coordinates were based on facility address and established using Geocodio. The coordinates were appended to the original table in Excel. The geocoded file is available in the data directory of the project GitHub repository.

The map below plots all 142 ICE detention facilities listed in the FY21 year end spreadsheet. Note there are five facilities located outside of the continental US. These are from West to East:

  • Honolulu Federal Detention Center, HI
  • Hiland Mountain Correctional Center, AK
  • Guaynabo MDC PR
  • Department Of Corrections GU
  • Saipan Department Of Corrections MP

2 Summary Statistics

The table below reports summary statistics of the numeric values provided in the ICE FY21 Year End Detention Report Table. Of the 142 facilities reported the average total detained is 109 persons (SD = 138.54, Median = 54.23). On 15 April 2021, the day the data were downloaded, ICE reported there were 15,344 persons in detention. An average detained population of 109 x 142 facilities results in 15478 which is very similar to the total detained population reported by ICE.

Mean Std.Dev Min Median Max
Female Crim 3.07 6.76 0.00 0.04 51.50
Female Non-Crim 9.17 28.14 0.00 0.09 188.14
FY21 ALOS 66.04 67.93 0.75 53.45 378.75
Guaranteed Minimum 687.80 493.90 2.00 580.00 2400.00
ICE Threat Level 1 31.71 45.49 0.00 14.17 275.59
ICE Threat Level 2 12.06 16.05 0.00 6.29 98.26
ICE Threat Level 3 11.49 15.73 0.00 5.66 93.87
Level A 45.82 84.22 0.00 8.86 506.74
Level B 16.76 26.19 0.00 6.56 155.75
Level C 23.81 33.46 0.00 10.14 183.41
Level D 23.04 34.34 0.00 9.87 181.38
Male Crim 52.25 71.06 0.00 26.36 465.87
Male Non-Crim 44.93 76.76 0.00 11.71 513.02
Mandatory 74.75 95.60 0.00 35.15 526.37
No ICE Threat Level 54.17 87.14 0.00 14.22 512.59
Percent No Threat 39.10 27.54 0.00 31.96 100.00
Second to Last Inspection Date 43514.23 703.61 39241.00 43695.50 44265.00
Total Detained 109.43 138.54 0.02 54.23 733.71
Total Females Detained 12.25 31.34 0.00 0.24 194.94
Total Males Detained 97.18 129.33 0.00 47.59 733.61

The table below lists ranked by the total detained population and the map following plots all facilities illustrating the total detained population by the size of the point. Once can see that the facilities with the largest detained populations are located along the southern border with large concentrations in southern California, West Texas, South Texas, Louisiana, South Georgia, and South Florida. Of the facilities with the most number of detained persons, 9 of the 10 are private (either CDF or IGSA). In fact, 18 of the 20 facilities with the largest total detained population are private.

3 Facility Types

The FY21 table lists 6 kinds of facilities. The table below shows the number and percent total of of each facility type. The following bar chart graphically represents the breakdown by facility type. IGSA’s constitute more than half of the facilities.

The facet map below shows the location of facilities by type. One can see that CDF, IGSA, “Other,” and SPC facilities are concentrated along the southern border. There is a large concentration of USMS IGA’s located in the Northeast, but there is also a sizeable concentration of these facilities along the southern border, just like the other facility types.

4 Guaranteed Minimums

Of the 142 facilities listed in the FY21 ICE Detention Year End Report 35% (n = 50) have Guaranteed Minimums as part of their contracts. Based on data reported by ICE, there are a total of 34,390 guaranteed beds in FY21. The table below lists those facilities in descending order based on the Guaranteed Minimum number of beds stipulated in the contract.

Joining, by = "Type Detailed"
Joining, by = "Type Detailed"
`summarise()` has grouped output by 'Type Detailed'. You can override using the `.groups` argument.
  Mean Std.Dev Min Median Max N.Valid Pct.Valid
Type Detailed = CDF 696.20 409.53 120.00 640.00 1455.00 15.00 100.00
Type Detailed = IGSA 710.17 567.57 2.00 575.00 2400.00 30.00 100.00
Type Detailed = SPC 528.40 173.30 392.00 450.00 800.00 5.00 100.00

4.1 Estimated number of “Ghost Beds”

Selman and Leighton (2010, 114) explain that compensation for private prisons is fraught with guarantees, including the payment of a minimum number of inmates which over time became standard language in many contracts. Selman and Leighton refer to the payment of nonexistent inmate beds through guaranteed minimums as “ghost inmates.” Immigration detention is modeled on corrections and is effectively punishment imposed through a program of prevention through deterrence. However, individuals incarcerated by ICE are held in what is supposed to be non-punitive civil custody. Therefore, rather than referring to empty guaranteed beds that are paid for through contractual obligations as “ghost inmates” I refer to these in the immigration context as “ghost beds.”

Based on ICE data, for FY21 there are a total of 22,958 ghost beds. Assuming an average cost of $100 per day per bed, this comes out to $2,295,829 per day paid for empty detention beds.

5 Threat Level

6 Mandatory Detention

7 References

Selman, Donna, and Paul Leighton. 2010. Punishment for Sale: Private Prisons, Big Business, and the Incarceration Binge. Issues in Crime & Justice. Lanham, Md: Rowman & Littlefield Publishers.

8 Appendix A: Code

# Load libraries
library(sf)
library(ggplot2)
library(readxl)
library(tidyverse)
library(summarytools)
st_options(plain.ascii = FALSE,
           footnote = NA,
           subtitle.emphasis = FALSE,
           round.digits = 2)
library(knitr)
# knitr option that forces rounding
options(digits=3)
opts_chunk$set(results = 'asis',
               comment = NA,
               prompt = FALSE,
               cache = FALSE)
# knitr hook to put commas in the thousands place
# for inline numbers based on variables.
knit_hooks$set(inline = function(x) {
  prettyNum(x, big.mark=",")
})

# Turn off scientific notation
options(digits=5, scipen=15) 

# Load Datasets
facilities <- read_excel("data/facilities geocoded.xlsx")
states <- st_read("./map_data/cb_2018_us_state_500k.shp")

# Map of all facilities
ggplot()+
  geom_sf(data=states)+
  geom_point(data=facilities,
             aes(x=Longitude, y=Latitude), color = "Red", size = 1)+
    coord_sf(xlim = c(-165,150), ylim = c(10,72), expand = FALSE)+
  labs(
    title = "ICE Detention Facilities",
    caption = "Data source: https://www.ice.gov/detain/detention-management/"
    )

# Adding new columns
facilities <- facilities %>% 
  mutate(`Total Detained`=
           `Female Crim` +
           `Female Non-Crim` +
           `Male Crim` +
           `Male Non-Crim`) %>% 
  mutate(`Total Males Detained` =
           `Male Crim` +
           `Male Non-Crim`) %>% 
  mutate(`Total Females Detained` =
           `Female Crim` +
           `Female Non-Crim`) %>% 
  mutate(`Percent No Threat` =
            (`No ICE Threat Level` /
           `Total Detained`)*100) %>% 
  relocate(c(`Total Females Detained`,
             `Total Males Detained`,
             `Total Detained`,
             `Percent No Threat`), .after = `Guaranteed Minimum`)

# Creating a variable to use in the text
facilities %>% 
  select(c(-`Accuracy Score`, -Longitude, -Latitude, -Zip, -`Zip Geocoded`)) %>% 
  descr(stats = c("mean", "sd", "min", "med", "max"),
        transpose = TRUE,
        headings = FALSE) %>% 
  kable(digits = 2, scientific = FALSE)
# Facilities Ranked by Total Detained
facilities %>% 
  arrange(desc(`Total Detained`)) %>% 
  select(Name, State, `Type Detailed`, `Total Detained`) %>% 
  mutate(`Total Detained` = round(`Total Detained`,0))
  
# facilities by total detained
ggplot()+
  geom_sf(data=states)+
  geom_point(data=facilities,
             aes(x=Longitude, y=Latitude,
                 size = `Total Detained`))+
  scale_shape_discrete(solid = FALSE)+
  coord_sf(xlim = c(-125,-64), ylim = c(16.5, 49.5), expand = TRUE)+
  scale_x_continuous(breaks = c(-120, -100, -80))+
  labs(
    title = "ICE Detention Facility Showing Total Detained Population",
    caption = "Data source: https://www.ice.gov/detain/detention-management/"
    )

# convert character vector to factor vector
facilities$`Type Detailed` <- as.factor(facilities$`Type Detailed`)

# confirm the type is correct
class(facilities$`Type Detailed`)

# Facility count and percent total by facility type.
facility_types <- facilities %>% 
  count(`Type Detailed`) %>%
  mutate(`Percent Total` = prop.table(n)*100) %>% 
  mutate(`Percent Total` = round(`Percent Total`,2))
  
# Sort by percent total
facility_types %>% 
  arrange(`Percent Total`)

# Bar Chart of Facility Count by Type
ggplot(facilities, aes(y = fct_infreq(`Type Detailed`)))+
  geom_bar(stat = 'count')+
  ylab("Facility Type Detailed")+
  xlab("Count")+
  labs(
    title = "ICE Detention Center Facility Count by Facility Type",
    caption = "Data source: https://www.ice.gov/detain/detention-management/"
  )

# Facet map of Facilities by Type
ggplot()+
  geom_sf(data=states)+
  geom_point(data=facilities,
             aes(x=Longitude, y=Latitude,
                 color = `Type Detailed`), size = 2)+
  scale_shape_discrete(solid = FALSE)+
  coord_sf(xlim = c(-125,-64), ylim = c(16.5, 49.5), expand = TRUE)+
  scale_x_continuous(breaks = c(-120, -100, -80))+
  labs(
    title = "ICE Detention Facility Type",
    caption = "Data source: https://www.ice.gov/detain/detention-management/"
    )+
  facet_wrap(~`Type Detailed`, ncol =3)

# Variable of sum of guaranteed minimum beds, for use in text.
guaranteed_min <- sum(facilities$`Guaranteed Minimum`, na.rm = TRUE)

# Guaranteed Minimums by Facility
facilities_guaranteed_min <- 
facilities %>% 
  as_tibble() %>% 
  select(c(Name, `Type Detailed`, `Guaranteed Minimum`,
           `Total Detained`, `Mandatory`, 
           `Percent No Threat`)) %>% 
  filter(`Guaranteed Minimum` >0) %>% 
  arrange(desc(`Guaranteed Minimum`))
facilities_guaranteed_min %>% 
  select(-`Type Detailed`)

# Plot of Guaranteed Minimums
facilities_guaranteed_min %>% 
  ggplot()+
  geom_density(aes(x=`Guaranteed Minimum`, color=`Type Detailed`))

# Facility count and percent total by facility type.
facility_type_count <- 
facilities %>% 
  count(`Type Detailed`) %>%
  mutate(`Prop Total` = prop.table(n)) %>% 
  mutate(`Prop Total` = round(`Prop Total`, 2)) %>% 
  arrange(`Prop Total`)

# Count of Facilities by Type that have Guaranteed Minimums
facilities_guaranteed_count <- 
facilities %>% 
  filter(`Guaranteed Minimum` >0) %>% 
  count(`Type Detailed`) %>%
  rename(`n Guaranteed Min` = n)

# Calculate Guaranteed Minimum by Type using Tidyverse R functions
facility_guaranteed_total_beds <- 
facilities %>% 
  filter(`Guaranteed Minimum`>0) %>% 
  group_by(`Type Detailed`) %>% 
  summarise(`Total Guaranteed Min` = sum(`Guaranteed Minimum`))

# Combine Tables
facility_type_count %>% 
  left_join(facilities_guaranteed_count) %>% 
  left_join(facility_guaranteed_total_beds) %>% 
  mutate(`Guaranteed Min %` = `n Guaranteed Min`/n*100) %>% 
  relocate(`Total Guaranteed Min`, .after = `Guaranteed Min %`)

# Remove temporary data
remove(facility_type_count, facilities_guaranteed_count, facility_guaranteed_total_beds)

# Summarizing Guaranteed Minimums
facilities %>% 
  group_by(`Type Detailed`) %>% 
  filter(`Guaranteed Minimum`>0) %>% 
  summarize(`Guaranteed Minimum`) %>% 
  descr(transpose = TRUE, stats = "common", headings = FALSE)

# Estimating Ghost Beds
ghost_beds <- facilities %>% 
  filter(`Guaranteed Minimum` >0) %>% 
  mutate(`Estimated Ghost Beds` = `Guaranteed Minimum` - `Total Detained`) %>% 
  arrange(desc(`Estimated Ghost Beds`))

# Mapping Ghost Beds
ghost_beds %>% 
    select(c(Name, `Estimated Ghost Beds`, `Guaranteed Minimum`, `Total Detained`, `Percent No Threat`))

# Sum of Ghost Beds
ghost_bed_sum <- sum(ghost_beds$`Estimated Ghost Beds`)

# Map of Ghost Beds
ggplot()+
  geom_sf(data=states)+
  geom_point(data=ghost_beds, aes(x=Longitude, y=Latitude, size=`Estimated Ghost Beds`))+
  coord_sf(xlim = c(-125,-64), ylim = c(16.5, 49.5), expand = FALSE)+
    labs(
    title = "ICE Detention Facilities",
    subtitle = "Showing Estimated Ghost Beds FY21",
    caption = "Data source: https://www.ice.gov/detain/detention-management/",
    size = "Estimated\nGhost Beds")

# % No Threat
no_threat <- 
  facilities %>% 
  arrange(desc(`Percent No Threat`))
no_threat %>% 
    select(c(Name, `Percent No Threat`, `Total Detained`))

# Histogram of No Threat
no_threat %>% 
ggplot()+
  geom_histogram(aes(`Percent No Threat`), bins = 20)

# Map of Estimated Percent No Threat
ggplot()+
  geom_sf(data=states)+
  geom_point(data=`no_threat`,
          aes(x=Longitude, y=Latitude,
              size=`Percent No Threat`, color=`Total Detained`))+
  coord_sf(xlim = c(-125,-64), ylim = c(16.5, 49.5), expand = FALSE)+
    labs(
    title = "ICE Detention Facilities",
    subtitle = "Showing Estimated Percent No ICE Threat",
    caption = "Data source: https://www.ice.gov/detain/detention-management/",
    color = "Total\nDetained",
    size = "Estimated\n% No Threat")

# Map of Manditory Detention and Guaranteed Minimums
ggplot()+
  geom_sf(data=states)+
  geom_point(data=facilities,
          aes(x=Longitude, y=Latitude,
              color = `Guaranteed Minimum`,
              size = `Mandatory`))+
  coord_sf(xlim = c(-125,-64), ylim = c(16.5, 49.5), expand = FALSE)+
  labs(
    title = "ICE Detention Facilities",
    subtitle = "Showing Mandatory Detention and Contractual Guaranteed Minimums",
    caption = "Data source: https://www.ice.gov/detain/detention-management",
    color ="Guaranteed\nMinimum",
    size = "Mandatory\nDetention")