library(ggplot2)
library(plotly)
library(kableExtra)
library(tidyverse)
library(forcats)
library(here)
library(readxl)

make_url <- function(x) {
    paste(sprintf('<a href="%1$s">Link</a>', x),
          collapse=",")
}

df <- read_excel("1507IndProsecutorCleaned.xlsx") %>%
  mutate(State = sub(" -.*", "", State)) %>%
  rename(location = `Prosecutor Office Location`,
         position = `Prosecutor Position`,
         first = `Local PA first`,
         jurisdiction = `Jurisdiction`,
         scope = `Scope of actions prosecuted`,
         same = `Same office does investigation?`,
         input = `Non-LE input of any kind`,
         access = `Public access to results`)

Introduction

We searched the National Conference of State Legislatures (NCSL) online database, using the keyword “prosecut”, for 2020 and 2021.

All bills that addressed prosecution of law enforcement officers for offenses of any type were included in our summary spreadsheet.

We coded information on: year, status, location of prosecution office, prosecutor position, whether local PA gets first shot, jurisdiction of the prosecutor, conditions of jurisdiction, scope of offenses, whether the same office also does the investigation, input by non-LE at any stage, and whether the public has access to the results.

This report breaks down the bills by the attributes above, and provides a table of all bills at the end, with a link to each bill, and a summary of the key elements.

We use the abbreviation “PA” throughout to refer to the local (County or District) Prosecuting Attorney.


Three bills stand out:

  • Minnesota s334 (pending) This bill sets up a completely independent permanent prosecutor, selected by a 3 member board that includes an impacted family member. The Governor selects the board, but does not play a role in selecting the prosecutor.

  • Michigan S668 (pending) Requires the local PA to report these cases to the AG and declare a COI, the AG then decides how to proceed.

  • California S710 (pending) This bill requires hierarchical recusals of the local PA, and state AG if they have received any monetary benefits, including campaign contributions, from an organization or association solely representing law enforcement. If both the PA and the AG have conflicts, a special prosecutor is appointed.


Basic Stats

Number by state and year

sjPlot::tab_xtab(var.row = df$State, 
                 var.col = df$Year, 
                 title = "Bills by State and Year",
                 show.summary = F,
                 emph.total = T)
Bills by State and Year
State Year Total
2020 2021
California 1 2 3
Georgia 1 1 2
Illinois 0 1 1
Iowa 1 1 2
Maryland 0 1 1
Michigan 0 1 1
Minnesota 4 2 6
New Mexico 0 1 1
New York 1 5 6
Ohio 1 0 1
Oregon 1 0 1
Tennessee 0 1 1
Texas 0 2 2
Virginia 1 0 1
Total 11 18 29

Bill status

df %>%
  group_by(Status) %>%
  count() %>%
  ggplot(aes(x=reorder(Status, n, desc), y=n)) +
  geom_bar(stat="identity", fill="blue", alpha = 0.7) +
  
    labs(title = "Bill Status (as of 11/2021)",
       x = "Status",
       y = "Count") 

Office location and position

Location

df %>%
  group_by(location) %>%
  count() %>%
  ggplot(aes(x=reorder(location, n, desc), y=n)) +
  geom_bar(stat="identity", fill="blue", alpha = 0.7) +
  
    labs(title = "Location of Prosecutor Office",
       x = "Office location",
       y = "Count") +
  
  theme(axis.text.x = element_text(angle = 45, hjust = 0.95))

Position

df %>%
  group_by(position) %>%
  count() %>%
  ggplot(aes(x=reorder(position, n, desc), y=n)) +
  geom_bar(stat="identity", fill="blue", alpha = 0.7) +
  
  labs(title = "Prosecutor Position",
       x = "Prosecutor",
       y = "Count") +
  
  theme(axis.text.x = element_text(angle = 45, hjust = 0.95))

Location x Position

sjPlot::tab_xtab(var.row = df$location, 
                 var.col = df$position, 
                 title = "Office location and position",
                 show.summary = F,
                 emph.total = T)
Office location and position
location position Total
Attorney General District Atty
General (local PA)
Hierarchy of options Multiple options Permanent
Independent
Prosecutor
Temporary Special
Prosecutor
AG decides 0 0 1 5 0 0 6
Attorney General’s
Office
15 0 0 0 1 1 17
Conditional on
conflict
0 0 1 0 0 0 1
Court appointed 0 0 0 0 0 1 1
District Atty
General (local PA)
0 1 0 0 0 0 1
Governor’s Office 0 0 0 0 1 0 1
Independent 0 0 0 0 1 0 1
Study committee 0 0 0 0 1 0 1
Total 15 1 2 5 4 2 29

Scope and conditions

Local PA first

df %>%
  filter(first != "n/a") %>%
  group_by(first) %>%
  count() %>%
  ggplot(aes(x=reorder(first, n, desc), y=n)) +
  geom_bar(stat="identity", fill="blue", alpha = 0.7) +
  
  labs(title = "Local PA has initial jurisdiction",
       x = "PA has initial jurisdiction",
       y = "Count") 

Jurisdiction type

df %>%
  group_by(jurisdiction) %>%
  count() %>%
  ggplot(aes(x=reorder(jurisdiction, n, desc), y=n)) +
  geom_bar(stat="identity", fill="blue", alpha = 0.7) +
  
  labs(title = "Type of Jurisdiction for Prosecutor",
       x = "Jurisdiction",
       y = "Count") +
  
  theme(axis.text.x = element_text(angle = 45, hjust = 0.95))

Jurisdiction Conditions

df %>%
  filter(!is.na(`Jurisdiction Conditions`)) %>%
  select(c(Jurisdiction=jurisdiction, `Jurisdiction Conditions`)) %>%
  arrange(Jurisdiction) %>%
  kable() %>%
  kable_styling(bootstrap_options = "striped")
Jurisdiction Jurisdiction Conditions
Joint AG leads, PA can be asked to collaborate
Joint AG leads, PA can be asked to collaborate
Joint AG leads, PA can be asked to collaborate
Joint AG leads, PA can be asked to collaborate
Joint Office of Special Investigation lead if: (1) lack of prosecutorial resources, (2) can’t effectively be conducted by local DA, (3) DA failed or refused, or (4) necessary to ensure public confidence
Sole Conflicts
Sole Conflicts
Sole AG if (1) lack of prosecutorial resources or (2) to ensure public confidence
Sole PA is disqualified

Scope

df %>%
  group_by(scope) %>%
  count() %>%
  ggplot(aes(x=reorder(scope, n, desc), y=n)) +
  geom_bar(stat="identity", fill="blue", alpha = 0.7) +
  
  labs(title = "Scope of actions prosecuted",
       x = "Offenses",
       y = "Count") +
  
  theme(axis.text.x = element_text(angle = 45, hjust = 0.95))

Investigation

df %>%
  group_by(same) %>%
  count() %>%
  ggplot(aes(x=reorder(same, n, desc), y=n)) +
  geom_bar(stat="identity", fill="blue", alpha = 0.7) +
  
  labs(title = "Same office for Investigation and Prosecution?",
       x = "Same office",
       y = "Count")

Public access and input

Non-LE input

df %>%
  group_by(input) %>%
  count() %>%
  ggplot(aes(x=reorder(input, n, desc), y=n)) +
  geom_bar(stat="identity", fill="blue", alpha = 0.7) +
  
  labs(title = "Non-LE input of any kind",
       x = "Input",
       y = "Count")

Public access to results

df %>%
  group_by(access) %>%
  count() %>%
  ggplot(aes(x=reorder(access, n, desc), y=n)) +
  geom_bar(stat="identity", fill="blue", alpha = 0.7) +
  
  labs(title = "Public access to results",
       x = "Access",
       y = "Count") +
  
  theme(axis.text.x = element_text(angle = 45, hjust = 0.95))

Bill Summaries

df %>%
  rowwise() %>%
  mutate(urlclick = make_url(URL)) %>%
  ungroup() %>%
  select(State, Year, Status, URL=urlclick, Summary) %>%
  arrange(State, Year) %>%
  DT::datatable(rownames = F,
                caption = "Bill Summaries and Links",
                filter = 'top',
                escape = FALSE)