This will walk you through how to analyze a specific police department using data from the FBI Uniform Crime Reporting program and The Washington Post’s Fatal Force project [github]. This was part of the story: As fatal police shootings increase, more go unreported
Before you proceed, please sign up for an API key on data.gov.
Here’s the documentation for the different FBI crime data endpoints.
fbi_key <- "XXXX"
state_abb <- "CO"
# Loading the appropriate libraries
library(tidyverse)
library(lubridate)
library(jsonlite)
library(DT)
library(knitr)
# Importing the json data based off the API
state_df <- fromJSON(paste0("https://api.usa.gov/crime/fbi/sapi/api/agencies/byStateAbbr/", str_to_upper(state_abb), "?API_KEY=", fbi_key))
state_df <- state_df$results
# dropping a few columns for display purposes
state_df %>%
select(-state_name, -nibrs, -nibrs_start_date, -region_desc, -division_name) %>%
datatable()
Above, we have all the agency identification numbers from the state we specified.
Save the spreadsheet for easier reference, if you’d like.
write_csv(state_df, paste0(state_abb, "_oricodes.csv"), na="")
You should be able to narrow down the law enforcement agency of your
choice through the agency_name
column.
Here’s how.
# saving the name of an agency we want to look up
department_search <- "denver police"
# filtering out the oricode from the imported state data based on the agency name
ori_only <- state_df %>%
filter(str_detect(str_to_lower(agency_name), str_to_lower(department_search))) %>%
pull(ori)
ori_only
## [1] "CODPD0000"
Now we know that Denver Police’s ori code is CODPD0000.
You’ll need 3 arguments:
start <- 2015
end <- 2021
emp_url <- paste0("https://api.usa.gov/crime/fbi/sapi/api/police-employment/agencies/", ori_only, "/", start, "/", end, "?API_KEY=", fbi_key)
emp_list <- fromJSON(emp_url)
emp_list <- emp_list$results
# dropping a few columns for display purposes
emp_list_df <- emp_list %>%
select(ori, agency_name_edit, year=data_year, population, agency_name_edit, female_officer_ct, male_officer_ct) %>%
# calculating total officer count by adding female and male officer counts
mutate(officer_count=female_officer_ct+male_officer_ct) %>%
mutate(year=as.numeric(year))
emp_list_df %>%
datatable()
Officer counts are necessary to calculate fatal police shootings per officer.
FBI’s agency-level police-involved shootings statistics
The FBI’s API does not give the level of detail necessary to isolate at an agency level officer-involved justifiable homicides from civilian-involved ones.
Going down this path of analysis will require pulling the full Expanded Homicide data set from the FBI’s Crime Data Explorer site. Alternatively, Jacob Kaplan’s Concatenated UCR files from 1976-2020 are also available. A summarized version from the Post of homicide totals and officer shootings by agency by year can be found here.
Before it recently connected department names to federal ori codes, The Post used internal id numbers to identify law enforcement agencies.
Joining the FBI data with the Post data requires the following crosswalk file.
fatal_force_ag <- read_csv("https://github.com/washingtonpost/data-police-shootings/raw/master/v2/fatal-police-shootings-agencies.csv")
# showing the first five rows
fatal_force_ag %>%
slice(1:5) %>%
datatable()
This table shows the total shootings in the Post’s Fatal Force data by police department starting in 2015 until today.
Grab the id
for the police agency we’re interested in
based on the oricode identified above. Using the oricode as a way to
filter is better than the agency name because there may be inconsistent
spellings between the Fatal Force and FBI data sets.
#Using string detect instead of == because there are some agencies we grouped multiple oricodes for simplicity, ie. state police
fatal_force_id <- fatal_force_ag %>%
filter(str_detect(ori_only, oricodes)) %>%
pull(id)
fatal_force_id
## [1] 106
Now that we have the department id, we can download the Fatal Force data and filter it to that specific agency.
Some shootings involve more than agency, so we need to transform the data a little to split them up individually.
Note: id
from the department data is
agency_ids
in the shootings data
fatal_force_df <- read_csv("https://github.com/washingtonpost/data-police-shootings/raw/master/v2/fatal-police-shootings-data.csv")
# Splitting out the incidents with multiple departments so they stand alone and can then be filtered successfully
fatal_force_df <- separate_rows(fatal_force_df, agency_ids, convert = TRUE)
# Now we can filter without worry of leaving any out
fatal_force_df <- fatal_force_df %>%
filter(agency_ids==fatal_force_id)
# Showing a selection of the columns (not all)
fatal_force_df %>%
select(id, agency_ids, date, flee_status, armed_with, city, state, name, age, gender, race ) %>%
kable()
id | agency_ids | date | flee_status | armed_with | city | state | name | age | gender | race |
---|---|---|---|---|---|---|---|---|---|---|
20 | 106 | 2015-01-26 | not | vehicle | Denver | CO | Jessie Hernandez | 17 | female | H |
628 | 106 | 2015-07-12 | foot | knife | Denver | CO | Paul Castaway | 35 | male | N |
1029 | 106 | 2015-11-22 | not | gun | Denver | CO | Miguel Angel Martinez | 28 | male | H |
1049 | 106 | 2015-12-02 | car | gun | Denver | CO | Phillip Munoz | 35 | male | H |
1164 | 106 | 2016-01-11 | not | gun | Denver | CO | Ramone Lonergan | 32 | male | W |
1301 | 106 | 2016-02-22 | other | gun | Denver | CO | Gerardino Cayetano Gonzalez Jr. | 33 | male | H |
1456 | 106 | 2016-04-12 | car | unarmed | Denver | CO | Dion Daman | 40 | male | W |
1821 | 106 | 2016-08-27 | not | knife | Denver | CO | Terry Salazar | 49 | male | NA |
1841 | 106 | 2016-08-31 | foot | gun | Denver | CO | Michael Ferguson | 21 | male | W |
2031 | 106 | 2016-11-08 | other | gun | Denver | CO | Juan Ramos | 23 | male | H |
2979 | 106 | 2017-09-22 | not | undetermined | Denver | CO | NA | NA | male | W |
3130 | 106 | 2017-11-10 | foot | gun | Denver | CO | John Bazemore III | 25 | male | B |
3387 | 106 | 2018-02-06 | not | knife | Denver | CO | Alex Duran | 29 | male | H |
3411 | 106 | 2018-02-13 | not | gun;knife | Denver | CO | Peter Le | 52 | male | A |
3520 | 106 | 2018-03-19 | car | unarmed | Denver | CO | Steven Nguyen | 27 | male | A |
3644 | 106 | 2018-04-25 | car | gun | Denver | CO | Charles Boeh | 36 | male | W |
3771 | 106 | 2018-06-13 | foot | gun | Denver | CO | NA | NA | male | NA |
4548 | 106 | 2019-02-25 | not | gun | Denver | CO | David Litton | 40 | male | W |
4823 | 106 | 2019-07-01 | not | gun | Denver | CO | Christopher Barela | 22 | male | H |
4856 | 106 | 2019-07-04 | foot | gun | Lakewood | CO | Leonel Mendez | 34 | male | H |
4966 | 106 | 2019-08-15 | foot | gun | Denver | CO | Jamie Fernandez | 31 | female | H |
5031 | 106 | 2019-08-31 | not | gun | Denver | CO | Juan Carlos Macias | 37 | male | H |
5214 | 106 | 2019-10-21 | car | gun | Denver | CO | Adam Martinez | 36 | male | H |
5403 | 106 | 2020-01-01 | not | knife | Denver | CO | Gerardo Antonio Conchas-Bustas | 20 | male | H |
5355 | 106 | 2020-01-06 | car | gun | Aurora | CO | Nico Descheenie | 25 | male | W |
5425 | 106 | 2020-01-25 | car | gun | Aurora | CO | D’ovion Semaj Perkins | 19 | male | B |
5776 | 106 | 2020-05-01 | other | gun | Denver | CO | William Debose | 21 | male | B |
5938 | 106 | 2020-06-22 | not | gun | Aurora | CO | David Guillen | 22 | male | H |
6161 | 106 | 2020-09-09 | not | gun | Denver | CO | Antonio Black Bear | 41 | male | N |
6160 | 106 | 2020-09-12 | other | gun | Denver | CO | Christopher Escobedo | NA | male | H |
6172 | 106 | 2020-09-15 | not | gun | Denver | CO | NA | NA | male | W |
6472 | 106 | 2020-12-28 | foot | gun | Denver | CO | Larry Hamm | 47 | male | B |
6855 | 106 | 2021-05-14 | car | gun | Denver | CO | NA | NA | male | NA |
6870 | 106 | 2021-05-19 | not | knife | Denver | CO | Raul Rosas-Zarsosa | 52 | male | NA |
6901 | 106 | 2021-05-30 | not | gun | Denver | CO | Shannon Wright | 29 | male | B |
7611 | 106 | 2021-06-04 | not | gun | Denver | CO | Colton Frederic Wagner | 31 | male | W |
6945 | 106 | 2021-06-13 | not | gun | Denver | CO | Duane Manzanares Jr. | 30 | male | H |
8244 | 106 | 2022-07-13 | car | gun | Denver | CO | NA | NA | male | NA |
8253 | 106 | 2022-07-15 | not | knife | Denver | CO | Chaz Gallegos | 33 | male | W |
8466 | 106 | 2022-09-29 | foot | gun | Broomfield | CO | NA | NA | male | NA |
These are all the police shootings tracked by the Post for the specific department.
Now, we count up the shootings by year and join it with the FBI data
# creating a new year column based on the year
fatal_force_df_annual <- fatal_force_df %>%
mutate(year=year(date)) %>%
count(year, name="fatal_force")
kable(fatal_force_df_annual)
year | fatal_force |
---|---|
2015 | 4 |
2016 | 6 |
2017 | 2 |
2018 | 5 |
2019 | 6 |
2020 | 9 |
2021 | 5 |
2022 | 3 |
# join with the FBI data
fatal_force_df_joined <- fatal_force_df_annual %>%
full_join(emp_list_df) %>%
mutate(per1ko=fatal_force/officer_count*1000) %>%
filter(year!=2022) # taking out 2022 data because 2022 FBI data doesn't exist yet
## Joining, by = "year"
kable(fatal_force_df_joined)
year | fatal_force | ori | agency_name_edit | population | female_officer_ct | male_officer_ct | officer_count | per1ko |
---|---|---|---|---|---|---|---|---|
2015 | 4 | CODPD0000 | DENVER | 682418 | 164 | 1304 | 1468 | 2.724796 |
2016 | 6 | CODPD0000 | DENVER | 699259 | 176 | 1307 | 1483 | 4.045853 |
2017 | 2 | CODPD0000 | DENVER | 706616 | 191 | 1288 | 1479 | 1.352265 |
2018 | 5 | CODPD0000 | DENVER | 720745 | 208 | 1309 | 1517 | 3.295979 |
2019 | 6 | CODPD0000 | DENVER | 728941 | 217 | 1334 | 1551 | 3.868472 |
2020 | 9 | CODPD0000 | DENVER | 737709 | 226 | 1326 | 1552 | 5.798969 |
2021 | 5 | CODPD0000 | DENVER | 740209 | 214 | 1233 | 1447 | 3.455425 |
It’s useful to have a single number, which is why we average the annual rate. This minimizes the intensity of outlier years but for comparison purposes, we also limit the rates of the smallest 5% of agencies in officer count in our interactive. Here’s how to get the rate.
average_annual <- sum(fatal_force_df_joined$per1ko, na.rm = T)/length(fatal_force_df_joined)
print(paste0(str_to_title(department_search), " average shootings per 1,000 officers: ", round(average_annual, 2)))
## [1] "Denver Police average shootings per 1,000 officers: 2.73"