load("Data/cjtc_internal.rda")
cjtc <- cjtc_internal
The WA State process for investigating law enforcement officers for misconduct and possible decertification was revised in 2021 by Senate Bill 5051 (see the “SB 5051” tab above for details). The process is tracked internally by CJTC with a “certification database”, and this report presents basic descriptive statistics from that database (see the “On the data” tab above for details).
For some of the tables and plots we split the data into 3 periods by the date the case was received:
In other places, we combine the second two periods into “post-5051”.
A list of officers that have received multiple complaints or agency notices of misconduct can be found in the “Officers” section of this report. The list can be searched, sorted, filtered and downloaded in several different formats.
Navigating this report:
You can use the TOC on the left to view specific sections based on content.
Within sections, look for the tabs in the document to view all of the different tables and graphs. Tabs are typically just below the section headers, but some sections have additional nested tabs below.
This is a first draft, so there may be some errors in the precise numbers presented. Comments/suggestions/questions are welcomed and appreciated.
The text of SB5051 can be found here,
A simple description of the process can be found on the CJTC website here,
The current RCW that governs the process can be found online here.
The CJTC maintains a database to track publicly filed complaints, agency notifications of officer misconduct (“Form 1915”), and Division initiated investigations that require review and may lead to officer decertification. Note that decertification is, to date, a very rare outcome (you can verify this in the Outcomes section of this report).
Case processing involves 3 general steps: Intake (entered into the database, initial review), Assessment (may either be closed without investigation or forwarded to investigation) and Determination (may either decline to charge, or charge with a hearing and outcome). At any of these steps, an officer may voluntarily surrender their certification.
There are two versions of the database:
An internal version that includes all cases
A public version that excludes cases in the “Intake” process, and is available online at the CJTC website.
The format and case coverage of the two datasets is different, but they otherwise include almost the same information.
This report is based on the Internal Dataset
The internal dataset was obtained via PDR by Dom Campese of the WCPA, and contains all cases up through 9/24/2025.
One of the key differences between the internal and public dataset formats is how the individual records in the dataset are defined. In the public dataset, each record is a “case-person”, defined by a case number and a person’s name. In the internal dataset, one record is used per offense (a complaint or form 1915 can lead to multiple offenses being charged for the same case-person), with additional records used to track attorney- or union-represented officers, some complainants and witnesses, and a few other categories of persons. As a result there are many more records in the internal dataset than the public (5829 vs. 2200 as of 9/24/2025) .
For this report, we “de-duplicated” the internal dataset with the goal of obtaining a set of “case-person” records equivalent to the public dataset. This reduced the number of records in the internal dataset down from 5829 to 3931. The de-duplicated internal dataset still has more records than the public dataset because it includes cases that are in the Intake process.
tab <- cjtc %>%
group_by(Status = as.character(status3)) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number),
index = if_else(is.na(Status), -1, rank(Number)),
Status = replace_na(Status, "Missing")
) %>%
arrange(desc(index)) %>%
select(-index) %>%
bind_rows(data.frame(Status ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Case Status: all cases",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Status == "Missing"), italic=T) %>%
row_spec(which(tab$Status == "Total"), bold=T)
Status | Number | Percent |
---|---|---|
Closed | 2003 | 51.0% |
Open/Intake | 1739 | 44.2% |
Open/Active | 183 | 4.7% |
Missing | 6 | 0.2% |
Total | 3931 | 100.0% |
tab <- cjtc %>%
group_by(Status = as.character(status)) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number),
index = if_else(is.na(Status), -1, rank(Number)),
Status = replace_na(Status, "Missing")
) %>%
arrange(desc(index)) %>%
select(-index) %>%
bind_rows(data.frame(Status ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Case Status (original detail): all cases",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Status == "Missing"), italic=T) %>%
row_spec(which(tab$Status == "Total"), bold=T)
Status | Number | Percent |
---|---|---|
Closed | 2003 | 51.0% |
Intake Review | 1333 | 33.9% |
Intake | 406 | 10.3% |
Investigation | 90 | 2.3% |
Admin Review | 54 | 1.4% |
Hearing Coordinator | 13 | 0.3% |
AAG (Legal) | 9 | 0.2% |
Settlement | 8 | 0.2% |
Assistant Director Review | 5 | 0.1% |
Appeal - Post Discipline | 2 | 0.1% |
Discipline - Post Hearing | 1 | 0.0% |
Open - Pending Expiration | 1 | 0.0% |
Missing | 6 | 0.2% |
Total | 3931 | 100.0% |
tab <- cjtc %>%
group_by(Outcome = as.character(outcat)) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number),
index = if_else(is.na(Outcome), -1, rank(Number)),
Outcome = replace_na(Outcome, "Missing")
) %>%
arrange(desc(index)) %>%
select(-index) %>%
bind_rows(data.frame(Outcome ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Case Outcome: all cases",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Outcome == "Missing"), italic=T) %>%
row_spec(which(tab$Outcome == "Total"), bold=T)
Outcome | Number | Percent |
---|---|---|
Still Open | 1914 | 48.7% |
Admin Closure | 740 | 18.8% |
Duplicate | 492 | 12.5% |
Revoked/Denied Cert/Disciplined | 449 | 11.4% |
Declined/Dismissed/Settlement | 322 | 8.2% |
Other | 14 | 0.4% |
Total | 3931 | 100.0% |
tab <- cjtc %>%
mutate(case_determination = if_else(status3 != "Closed", "Still Open", case_determination)) %>%
group_by(Outcome = as.character(case_determination)) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number),
index = if_else(is.na(Outcome), -1, rank(Number)),
Outcome = replace_na(Outcome, "Missing")
) %>%
arrange(desc(index)) %>%
select(-index) %>%
bind_rows(data.frame(Outcome ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1)) #%>%
tab %>%
kbl(caption = "Case Outcome (original detail): all cases",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Outcome == "Missing"), italic=T) %>%
row_spec(which(tab$Outcome == "Total"), bold=T)
Outcome | Number | Percent |
---|---|---|
Still Open | 1922 | 48.9% |
Administrative Closure - Lack of Jurisdiction | 623 | 15.8% |
Administrative Closure - Duplicate Case | 492 | 12.5% |
Revocation - Default | 295 | 7.5% |
Decline | 293 | 7.5% |
Revocation - Surrendered Certification | 82 | 2.1% |
Revocation - Hearing | 52 | 1.3% |
Administrative Closure - Not Certified or Deceased | 48 | 1.2% |
Administrative Closure - Insufficient Information | 37 | 0.9% |
Charge(s) Dismissed | 21 | 0.5% |
Denied Certification | 17 | 0.4% |
Administrative Closure - Implausible | 16 | 0.4% |
Administrative Closure - Revoked | 15 | 0.4% |
Eligibility Reinstated | 8 | 0.2% |
Denied Reinstatement | 2 | 0.1% |
Administrative Closure - Lack of Jurisdiction; Administrative Closure - Not Certified or Deceased | 1 | 0.0% |
Revocation - Summary Judgment | 1 | 0.0% |
Missing | 6 | 0.2% |
Total | 3931 | 100.0% |
p <- ggplot(cjtc %>% filter(status3 == "Closed" & outcat != "Other"),
aes(x=period_r, group=desc(outcat),
fill=outcat)) +
geom_bar(alpha = 0.7, color="grey") +
scale_fill_brewer(palette = "Spectral") +
labs(title = "Closed case outcome by period received",
x = "Period",
y = "Number of closed cases",
fill = "Outcome")
ggplotly(p) %>% reverse_legend_labels()
xval = max(cjtc$yr_r)
yval = nrow(cjtc %>% filter(yr_r == max(cjtc$yr_r) &
period_r != "pre.5051" &
status3 == "Closed")) * 1.2
p <- ggplot(cjtc %>% filter(period_r != "pre.5051" & status3 == "Closed"),
aes(x=yr_r, group=desc(outcat),
fill=outcat)) +
geom_bar(alpha = 0.7, color="grey") +
scale_fill_brewer(palette = "Spectral") +
annotate("text", x=xval, y=yval, label= "Yr to date", size=4) +
labs(title = "Closed case outcomes by year received (post-5051)",
x = "Year received",
y = "Number of closed cases",
fill = "Outcome")
ggplotly(p) %>% reverse_legend_labels()
The “boxplots” below show the range of case processing times (in years) by period, separately for closed and still open cases. For closed cases, this is the difference between case date received and date closed. For open cases it is the difference between case date received and the current date.
ggplot(cjtc %>% filter(!is.na(status3)) %>%
mutate(status2 = if_else(status3=="Closed", "Closed", "Open")),
aes(x=period_r, y=case_length_yr, fill=period_r)) +
geom_boxplot(alpha = 0.5, varwidth = T) +
labs(title = "Case processing times by case status and period received",
x = "Period received",
y = "Time in years",
fill = "Period received") +
scale_y_continuous(breaks = seq(0,round(max(cjtc$case_length_yr)), 1)) +
facet_wrap("status2")
ggplot(cjtc %>% filter(period_r != "pre.5051" & status3 == "Closed"),
aes(x=period_r, y=case_length_yr, fill=period_r)) +
geom_boxplot(alpha = 0.5, varwidth = T) +
labs(title = "Closed case processing times by outcome and period received",
x = "Period received",
y = "Time to close in years",
fill = "Period received") +
facet_wrap("outcat")
rcd <- cjtc %>%
group_by(yr_mo = yr_mo_r) %>%
summarize(received = n())
clo <- cjtc %>%
group_by(yr_mo = yr_mo_c) %>%
summarize(closed = n())
index <- data.frame(
seqnum = 1:(12*24),
yr_mo = paste0(sort(rep(2002:2025, 12)), "-", rep(01:12, 24))) %>%
filter(seqnum < (max(seqnum)-2))
monthly_df <- left_join(index, rcd) %>%
left_join(clo) %>%
arrange(seqnum) %>%
mutate(across(received:closed, ~tidyr::replace_na(.x, 0)),
net_number = closed - received,
net_pct = 100*(closed/received -1),
cumulative_backlog = -cumsum(net_number))
p <- monthly_df %>%
select(seqnum, yr_mo, received, closed) %>%
pivot_longer(cols = c(received:closed),
names_to = "Measure",
values_to = "num") %>%
ggplot(aes(x=seqnum, y=num, group=Measure, color=Measure)) +
geom_point(alpha = 0.7, size = 1) +
geom_line(alpha = 0.5) +
labs(title = "Number of cases received and closed by month",
x = "Year",
y = "Number of cases") +
scale_x_continuous(
breaks = seq(1, nrow(monthly_df), 12),
label = as.character(2002:2025) # monthly_df$yr_mo[seq(1, nrow(monthly_df), 12)]
) +
theme(axis.text.x = element_text(size = 5))
ggplotly(p)
p <- monthly_df %>%
mutate(posneg = if_else(net_number >= 0, "positive", "negative")) %>%
ggplot(aes(x=seqnum, y=net_number, fill=posneg)) +
geom_bar(stat = "identity",
#fill = "blue",
alpha = 0.7) +
labs(title = "Net throughput: cases closed - cases received by month",
x = "Year",
y = "Net number of cases",
fill = "Net throughput") +
scale_x_continuous(
breaks = seq(1, nrow(monthly_df), 12),
label = as.character(2002:2025)
) +
theme(axis.text.x = element_text(size = 5))
ggplotly(p)
This shows the growth in the monthly number of open cases (open intake + open active) over time.
p <- monthly_df %>%
ggplot(aes(x=seqnum, y=cumulative_backlog)) +
geom_line(stat = "identity",
color = "grey") +
geom_point(stat = "identity",
color = "blue",
alpha = 0.7,
size = 1) +
labs(title = "Cumulative open caseload by month",
x = "Year",
y = "Number of open cases") +
scale_x_continuous(
breaks = seq(1, nrow(monthly_df), 12),
label = as.character(2002:2025)
) +
theme(axis.text.x = element_text(size = 5))
ggplotly(p)
# date_received < as.Date("2021-06-20") ~ "pre.5051",
# date_received > as.Date("2022-06-30") ~ "current",
p <- monthly_df %>% filter(yr_mo > "2021-6") %>%
ggplot(aes(x=seqnum, y=cumulative_backlog)) +
geom_line(stat = "identity",
color = "grey") +
geom_point(stat = "identity",
color = "blue",
alpha = 0.7,
size = 1) +
geom_vline(xintercept = monthly_df$seqnum[monthly_df$yr_mo == "2022-7"],
color = "darkgrey") +
annotate("text",
x = monthly_df$seqnum[monthly_df$yr_mo == "2022-1"],
y = max(monthly_df$cumulative_backlog),
label = "Interim period", size = 3) +
annotate("text",
x = monthly_df$seqnum[monthly_df$yr_mo == "2024-1"],
y = max(monthly_df$cumulative_backlog),
label = "Current period", size = 3) +
labs(title = "Cumulative open caseload by month - post 5051",
x = "Year",
y = "Number of open cases") +
scale_x_continuous(
breaks = seq(1, nrow(monthly_df), 12),
label = as.character(2002:2025)
) +
theme(axis.text.x = element_text(size = 5))
ggplotly(p)
tab <- cjtc %>%
group_by(Source = opened_reason) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number),
index = if_else(is.na(Source), -1, rank(Number)),
Source = replace_na(Source, "Missing")
) %>%
arrange(desc(index)) %>%
select(-index) %>%
bind_rows(data.frame(Source ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Case Source: all cases",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Source == "Missing"), italic=T) %>%
row_spec(which(tab$Source == "Total"), bold=T)
Source | Number | Percent |
---|---|---|
Complaint | 1488 | 37.9% |
CJ Form 1915 Reporting | 1427 | 36.3% |
NOS Misconduct | 957 | 24.3% |
Division Initiated | 49 | 1.2% |
Request for Reinstatement | 10 | 0.3% |
Total | 3931 | 100.0% |
tab <- cjtc %>%
filter(period_r != "pre.5051") %>%
group_by(Source = opened_reason) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number),
index = if_else(is.na(Source), -1, rank(Number)),
Source = replace_na(Source, "Missing")
) %>%
arrange(desc(index)) %>%
select(-index) %>%
bind_rows(data.frame(Source ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Case Source: post 5051",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Source == "Missing"), italic=T) %>%
row_spec(which(tab$Source == "Total"), bold=T)
Source | Number | Percent |
---|---|---|
Complaint | 1488 | 44.0% |
CJ Form 1915 Reporting | 1427 | 42.2% |
NOS Misconduct | 421 | 12.4% |
Division Initiated | 49 | 1.4% |
Total | 3385 | 100.0% |
Offenses are designated by the RCW that applies, and are coded by CJTC staff during the intake review process. Post-5051, offenses are classified into 2 categories: mandatory vs. discretionary decertification, if sustained. In addition, CJTC has proposed a priority ranking for each offense to help triage the existing caseload.
In the tables below each offense is counted separately. The number of records does not track the number of cases, because a single subject may have committed several alleged offenses, and because most cases in intake status have not yet been assigned an offense, so will also be missing in the Classification and Priority ranking tabs.
tab <- offense_list_internal %>%
group_by(Classification = offense_classification) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/nrow(offense_list_internal)) %>%
#arrange(desc(Number)) %>%
bind_rows(data.frame(Classification ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Classification of Offense",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Classification == "Not classified"), italic=T) %>%
row_spec(which(tab$Classification == "Total"), bold=T)
Classification | Number | Percent |
---|---|---|
Mandatory | 78 | 4.2% |
Discretionary | 637 | 34.1% |
Not classified | 1153 | 61.7% |
Total | 1868 | 100.0% |
The proposed priority ranking of an offense depends on the employment status of the offender. CJTC staff have proposed priority ranks for all of the RCW offense categories established by 5051, except for two: voluntary surrender of certification and separated for misconduct. These cases are defined by RCW and appear in the dataset, so we include them in the table:
43.101.105(k) “Has been suspended or discharged, has resigned or retired in lieu of discharge, or has separated from the agency after the alleged misconduct occurred, for any conduct listed in this section”
43.101.105(l) “Has voluntarily surrendered the person’s certification as a peace officer or corrections officer.”.
tab <- offense_list_internal %>%
mutate(
Offense_priority = factor(
case_when(
is.na(offense_priority_emp) ~ "Not ranked",
offense_priority_emp < 4 ~ as.character(offense_priority_emp),
offense_priority_emp == 4 ~ "Separated for misconduct",
offense_priority_emp == 5 ~ "Surrendered certification")),
Offense_priority = fct_relevel(Offense_priority, "Not ranked", after = Inf )
) %>%
group_by(Priority = Offense_priority) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/nrow(offense_list_internal)) %>%
#arrange(desc(Number)) %>%
bind_rows(data.frame(Priority ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Offense Priority: Employed",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Priority == "Total"), bold=T)
Priority | Number | Percent |
---|---|---|
1 | 156 | 8.4% |
2 | 365 | 19.5% |
Separated for misconduct | 109 | 5.8% |
Surrendered certification | 80 | 4.3% |
Not ranked | 1158 | 62.0% |
Total | 1868 | 100.0% |
tab <- offense_list_internal %>%
mutate(
Offense_priority = factor(
case_when(
is.na(offense_priority_sep) ~ "Not ranked",
offense_priority_sep < 4 ~ as.character(offense_priority_sep),
offense_priority_sep == 4 ~ "Separated for misconduct",
offense_priority_sep == 5 ~ "Surrendered certification")),
Offense_priority = fct_relevel(Offense_priority, "Not ranked", after = Inf )
) %>%
group_by(Priority = Offense_priority) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/nrow(offense_list_internal)) %>%
#arrange(desc(Number)) %>%
bind_rows(data.frame(Priority ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Offense Priority: Separated",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Priority == "Total"), bold=T)
Priority | Number | Percent |
---|---|---|
2 | 72 | 3.9% |
3 | 449 | 24.0% |
Separated for misconduct | 109 | 5.8% |
Surrendered certification | 80 | 4.3% |
Not ranked | 1158 | 62.0% |
Total | 1868 | 100.0% |
tab <- offense_list_internal %>%
group_by(RCW=offense_rcw, Description=offense_desc) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/nrow(offense_list_internal)) %>%
arrange(desc(Number)) %>%
bind_rows(data.frame(RCW ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Offense investigated",
align = "llrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$RCW == "Total"), bold=T)
RCW | Description | Number | Percent |
---|---|---|---|
RCW 43.101.105(3)(j)(iv) | Unethical or Unprofessional Conduct | 271 | 14.5% |
RCW 9A.76.175 | 155 | 8.3% | |
RCW 43.101.105(3)(k) | Separated for Misconduct | 109 | 5.8% |
RCW 43.101.010(8)(b) | 101 | 5.4% | |
RCW 43.101.105(3)(l) | Voluntary Surrender of Certification | 80 | 4.3% |
RCW 43.101.010(8) | 74 | 4.0% | |
RCW 43.101.105(1)(d) | 71 | 3.8% | |
RCW 43.101.010(9) | 63 | 3.4% | |
RCW 43.101.010(7)(b) | 58 | 3.1% | |
RCW 43.101.105(2)(d) | False or Misleading Statements | 56 | 3.0% |
RCW 9A.80.010 | Official Misconduct | 50 | 2.7% |
RCW 43.101.105(d) | 46 | 2.5% | |
RCW 43.101.105(3)(e) | Excessive Force | 43 | 2.3% |
RCW 43.101.105(4) | 38 | 2.0% | |
RCW 42.20.100 | Failure of Duty | 27 | 1.4% |
RCW 43.101.105(3)(h) | Discrimination | 27 | 1.4% |
FormerRCW 43.101.105(1)(d) (2011) | 24 | 1.3% | |
RCW 43.101.105(3)(j)(i) | Committed a Felony | 24 | 1.3% |
RCW 43.101.010(7) | 22 | 1.2% | |
RCW 9A.72.040 | 21 | 1.1% | |
RCW 43.101.010(7)(a) | 18 | 1.0% | |
RCW 9A.36.041 | 18 | 1.0% | |
RCW 43.101.105(1)(c) | 17 | 0.9% | |
RCW 43.101.105(3)(j)(iii) | Unsafe Practices Involving Firearms, Weapons, or Vehicles | 16 | 0.9% |
RCW 9.41.040(2)(a)(i) | 16 | 0.9% | |
RCW 42.20.040 | False Report | 15 | 0.8% |
RCW 43.101.105(3)(f) | Sexual Harassment | 15 | 0.8% |
RCW 43.101.105(c) | 15 | 0.8% | |
RCW 43.101.105(3)(j)(ii) | Violation of Rights | 14 | 0.7% |
RCW 43.101.105(1)(a) | 13 | 0.7% | |
RCW 43.101.010(8)(a)(i)(B) | 11 | 0.6% | |
RCW 43.101.010(8)(a)(iv) | 11 | 0.6% | |
RCW 43.101.105(2)(a)(i)(B) | Convicted of a Gross Misdemeanor or Domestic Violence Offense | 11 | 0.6% |
RCW 43.101.010(8)(b) (2008) | 9 | 0.5% | |
RCW 43.101.105(3)(g) | Use of Position for Personal Gain | 9 | 0.5% |
RCW 43.101.010(7)(c) | 8 | 0.4% | |
RCW 43.101.010(8)(a)(i) | 8 | 0.4% | |
RCW 43.101.010(b) | 8 | 0.4% | |
RCW 43.101.105(3)(b) | Administrative Error | 7 | 0.4% |
RCW 43.101.010(7)(a)(i) | 6 | 0.3% | |
RCW 9A.56.030(1)(a) | 6 | 0.3% | |
RCW 9A.56.050 | 6 | 0.3% | |
RCW 10.99.020(5) | 5 | 0.3% | |
RCW 43.101.010(7)(a)(ii) | 5 | 0.3% | |
RCW 43.101.010(8)(a)(ii) | 5 | 0.3% | |
RCW 43.101.105(2)(e) | Prohibited from Possessing Weapons | 5 | 0.3% |
RCW 43.101.105(3) | 5 | 0.3% | |
RCW 43.101.105(3)(c) | Falsified Information on Application | 5 | 0.3% |
RCW 43.101.105(3)(i) | Extremism | 5 | 0.3% |
RCW 43.101.115(4) | 5 | 0.3% | |
RCW 69.50.4013 | 5 | 0.3% | |
RCW 43.101.010(8)(a) and (b) (2008) | 4 | 0.2% | |
RCW 43.101.105(1)(b) | 4 | 0.2% | |
RCW 43.101.105(2)(a)(i)(A) | Convicted of a Felony Offense | 4 | 0.2% |
RCW 10.99.020 | 3 | 0.2% | |
RCW 43.101.010 (8)(a)(i)(B) | 3 | 0.2% | |
RCW 43.101.010(8)(a)(i)(A)(II) | 3 | 0.2% | |
RCW 43.101.010(8)(a)(ii) and (b) (2008) | 3 | 0.2% | |
RCW 43.101.010(8)(b) (2020) | 3 | 0.2% | |
RCW 43.101.010(8)(iii) | 3 | 0.2% | |
RCW 43.101.105(3)(d)(i) | Interfered with a Certification Investigation | 3 | 0.2% |
RCW 43.101.105(d) (2011) | 3 | 0.2% | |
RCW 69.50.403(1)(c) | 3 | 0.2% | |
RCW 9A.44.160 | 3 | 0.2% | |
RCW 9A.44.160(1)(b) | 3 | 0.2% | |
RCW 9A.46.110(1) | 3 | 0.2% | |
RCW 9A.52.110 | 3 | 0.2% | |
RCW 9A.60.020 | 3 | 0.2% | |
RCW 9A.72.020 | 3 | 0.2% | |
RCW 9A.76.020 | 3 | 0.2% | |
RCW 10.99.020(3) | 2 | 0.1% | |
RCW 43.101.010(4) | 2 | 0.1% | |
RCW 43.101.010(7)(iv) | 2 | 0.1% | |
RCW 43.101.010(8)(a) | 2 | 0.1% | |
RCW 43.101.010(8)(a) and (b) | 2 | 0.1% | |
RCW 43.101.095 | 2 | 0.1% | |
RCW 43.101.105(3)(a) | Failed to MeetRequirements for Certification | 2 | 0.1% |
RCW 43.101.155(1) | 2 | 0.1% | |
RCW 43.101.200 | 2 | 0.1% | |
RCW 69.41.030 | 2 | 0.1% | |
RCW 69.50.4013(1) | 2 | 0.1% | |
RCW 69.50.4014 | 2 | 0.1% | |
RCW 9.41.040 | 2 | 0.1% | |
RCW 9.73.030 | 2 | 0.1% | |
RCW 9A.28.020 | 2 | 0.1% | |
RCW 9A.36.031(1)(g) | 2 | 0.1% | |
RCW 9A.40.040 | 2 | 0.1% | |
RCW 9A.44.050 | 2 | 0.1% | |
RCW 9A.46.020(1)(a)(i) | 2 | 0.1% | |
RCW 9A.46.020(2)(b) | 2 | 0.1% | |
RCW 9A.52.030 | 2 | 0.1% | |
RCW 9A.52.100 | 2 | 0.1% | |
RCW 9A.52.120 | 2 | 0.1% | |
RCW 9A.56.020(1)(a) | 2 | 0.1% | |
RCW 9A.56.040 | 2 | 0.1% | |
RCW 9A.76.175 | False or Misleading Statements | 2 | 0.1% |
RCW 10.99.040 | 1 | 0.1% | |
RCW 26.50.010(7) | 1 | 0.1% | |
RCW 26.50.110(1) | 1 | 0.1% | |
RCW 42.20.050 | 1 | 0.1% | |
RCW 43.101.010 (8)(b) | 1 | 0.1% | |
RCW 43.101.010(6) | 1 | 0.1% | |
RCW 43.101.010(7)(a)(iii) | 1 | 0.1% | |
RCW 43.101.010(7)(a)(iv) | 1 | 0.1% | |
RCW 43.101.010(7)(b) and (8) | 1 | 0.1% | |
RCW 43.101.010(8)(a)(i)(A) | 1 | 0.1% | |
RCW 43.101.010(8)(a)(i)(A)(IV) | 1 | 0.1% | |
RCW 43.101.010(8)(a)(i)(B) (2008) | 1 | 0.1% | |
RCW 43.101.010(8)(a)(i)(B)(b) | 1 | 0.1% | |
RCW 43.101.010(8)(a)(i)(ii)(iv) | 1 | 0.1% | |
RCW 43.101.010(8)(a)(iii)(iv) | 1 | 0.1% | |
RCW 43.101.010(8)(b) and (9) | 1 | 0.1% | |
RCW 43.101.010(9) (2008) | 1 | 0.1% | |
RCW 43.101.105(1) | 1 | 0.1% | |
RCW 43.101.105(2)(a)(ii)(B) | 1 | 0.1% | |
RCW 43.101.105(2)(c)(i) | Terminated for Failure to Intervene in Use of Force | 1 | 0.1% |
RCW 43.101.105(3)(d)(ii) | Interfered with a Certification Investigation | 1 | 0.1% |
RCW 43.101.105(3)(d)(iii) | Interfered with a Certification Investigation | 1 | 0.1% |
RCW 43.101.157 | 1 | 0.1% | |
RCW 43.89.010 | 1 | 0.1% | |
RCW 46.52.010(1 | 1 | 0.1% | |
RCW 46.61.465 | 1 | 0.1% | |
RCW 46.61.500 | 1 | 0.1% | |
RCW 46.61.502 | 1 | 0.1% | |
RCW 46.61.520 | 1 | 0.1% | |
RCW 49.50.4013(1) | 1 | 0.1% | |
RCW 50.36.010 | 1 | 0.1% | |
RCW 69.50.101(g) | 1 | 0.1% | |
RCW 69.50.204(c)(22) | 1 | 0.1% | |
RCW 69.50.206(b)(4) | 1 | 0.1% | |
RCW 69.50.401 | 1 | 0.1% | |
RCW 69.50.401(1)(2)(c) | 1 | 0.1% | |
RCW 69.50.401(1)(a) | 1 | 0.1% | |
RCW 69.50.403(a) | 1 | 0.1% | |
RCW 9.35.020 | 1 | 0.1% | |
RCW 9.35.020(1) | 1 | 0.1% | |
RCW 9.35.020(3) | 1 | 0.1% | |
RCW 9.41.020(2)(a)(i) | 1 | 0.1% | |
RCW 9.68A.070(1) | 1 | 0.1% | |
RCW 9.68A.070(2)(a) | 1 | 0.1% | |
RCW 9.68A.090(1) | 1 | 0.1% | |
RCW 9.94A.535(2)(f) | 1 | 0.1% | |
RCW 9A.020(1)(b) | 1 | 0.1% | |
RCW 9A.04.040 | 1 | 0.1% | |
RCW 9A.04.110(23) | 1 | 0.1% | |
RCW 9A.08.020 | 1 | 0.1% | |
RCW 9A.28.040 | 1 | 0.1% | |
RCW 9A.28.040(3)(c) | 1 | 0.1% | |
RCW 9A.36.021 | 1 | 0.1% | |
RCW 9A.36.021(1) | 1 | 0.1% | |
RCW 9A.36.021(1)(a) | 1 | 0.1% | |
RCW 9A.36.021(1)(c) | 1 | 0.1% | |
RCW 9A.36.021(2)(a) | 1 | 0.1% | |
RCW 9A.36.021(c) | 1 | 0.1% | |
RCW 9A.36.031(f) | 1 | 0.1% | |
RCW 9A.36.041(1) | 1 | 0.1% | |
RCW 9A.36.050 | 1 | 0.1% | |
RCW 9A.40.010(1) | 1 | 0.1% | |
RCW 9A.44.010(1)(c) | 1 | 0.1% | |
RCW 9A.44.050(1)(B) | 1 | 0.1% | |
RCW 9A.44.050(1)(F) | 1 | 0.1% | |
RCW 9A.44.060 | 1 | 0.1% | |
RCW 9A.44.076 | 1 | 0.1% | |
RCW 9A.44.079 | 1 | 0.1% | |
RCW 9A.44.083 | 1 | 0.1% | |
RCW 9A.44.115 | 1 | 0.1% | |
RCW 9A.44.170(1)(b) | 1 | 0.1% | |
RCW 9A.46.110 | 1 | 0.1% | |
RCW 9A.46.110(5)(b)(ii) | 1 | 0.1% | |
RCW 9A.52.025(1) | 1 | 0.1% | |
RCW 9A.52.080 | 1 | 0.1% | |
RCW 9A.56.020 | 1 | 0.1% | |
RCW 9A.56.020(1) | 1 | 0.1% | |
RCW 9A.56.020(1)(b) | 1 | 0.1% | |
RCW 9A.56.130 | 1 | 0.1% | |
RCW 9A.56.160 | 1 | 0.1% | |
RCW 9A.56.170 | 1 | 0.1% | |
RCW 9A.56.300 | 1 | 0.1% | |
RCW 9A.64.020(1) | 1 | 0.1% | |
RCW 9A.72.030 | 1 | 0.1% | |
RCW 9A.72.110 | 1 | 0.1% | |
RCW 9A.72.150 | 1 | 0.1% | |
RCW 9A.76.070 | 1 | 0.1% | |
RCW 9A.82.050 | 1 | 0.1% | |
RCW 9A.83.020 | 1 | 0.1% | |
RCW 9A.83.020(1) | 1 | 0.1% | |
RCW 9A.83.020(4) | 1 | 0.1% | |
RCW 9A.88.080(1)(b) | 1 | 0.1% | |
RCW 9A.88.110 | 1 | 0.1% | |
RCW 9A.90.040 | 1 | 0.1% | |
Total | 1868 | 100.0% |
At the officer level, multiple offenses may be coded for each case (officers may also have multiple cases in the dataset, that is a different issue and is covered in the last section of the report)
tab <- cjtc %>%
group_by(Number = num_offenses) %>%
summarize(Persons = n()) %>%
mutate(Number = if_else(is.na(Number), "Not coded", as.character(Number)),
Percent = Persons/sum(Persons)) %>%
#arrange(desc(Persons)) %>%
bind_rows(data.frame(Number ="Total",
Persons = sum(.$Persons),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Number of Offenses",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Number == "Not coded"), italic=T) %>%
row_spec(which(tab$Number == "Total"), bold=T)
Number | Persons | Percent |
---|---|---|
1 | 245 | 6.2% |
2 | 137 | 3.5% |
3 | 186 | 4.7% |
4 | 102 | 2.6% |
5 | 34 | 0.9% |
6 | 18 | 0.5% |
7 | 10 | 0.3% |
8 | 2 | 0.1% |
9 | 1 | 0.0% |
10 | 1 | 0.0% |
Not coded | 3195 | 81.3% |
Total | 3931 | 100.0% |
Since subjects can commit multiple offenses, with multiple classifications, here we identify those with at least one offense that would require mandatory decertification if sustained
tab <- cjtc %>%
mutate(Classification = case_when(
num_mandatory_offenses > 0 ~ "At least one mandatory",
num_discretionary_offenses > 1 ~ "Multiple discretionary",
num_discretionary_offenses == 1 ~ "Single discretionary")
) %>%
group_by(Classification) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number),
index = if_else(is.na(Classification), -1, rank(Number)),
Classification = replace_na(Classification, "Not classified")
) %>%
arrange(desc(index)) %>%
select(-index) %>%
bind_rows(data.frame(Classification ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Case classification at the officer level: all cases",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Classification == "Not classified"), italic=T) %>%
row_spec(which(tab$Classification == "Total"), bold=T)
Classification | Number | Percent |
---|---|---|
Single discretionary | 143 | 3.6% |
Multiple discretionary | 141 | 3.6% |
At least one mandatory | 76 | 1.9% |
Not classified | 3571 | 90.8% |
Total | 3931 | 100.0% |
The documents for each case provide critical information. They are, however, often missing, even for closed cases.
tab <- cjtc %>%
mutate(Available = if_else(is.na(url), "No", "Yes")
) %>%
group_by(Available) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number)) %>%
arrange(desc(Number)) %>%
bind_rows(data.frame(Available ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>% kbl(caption = "Documents available?",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Available == "Missing"), italic=T) %>%
row_spec(which(tab$Available == "Total"), bold=T)
Available | Number | Percent |
---|---|---|
No | 2507 | 63.8% |
Yes | 1424 | 36.2% |
Total | 3931 | 100.0% |
tab <- cjtc %>%
filter(period_r != "pre.5051") %>%
mutate(Available = if_else(is.na(url), "No", "Yes")
) %>%
group_by(Available) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number)) %>%
arrange(desc(Number)) %>%
bind_rows(data.frame(Available ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>% kbl(caption = "Documents available for post 5051 cases",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Available == "Missing"), italic=T) %>%
row_spec(which(tab$Available == "Total"), bold=T)
Available | Number | Percent |
---|---|---|
No | 2501 | 73.9% |
Yes | 884 | 26.1% |
Total | 3385 | 100.0% |
These cases really should have documents available.
tab <- cjtc %>%
filter(period_r != "pre.5051" & status3 == "Closed" & outcat != "Duplicate") %>%
mutate(Available = if_else(is.na(url), "No", "Yes")
) %>%
group_by(Available) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number)) %>%
arrange(desc(Number)) %>%
bind_rows(data.frame(Available ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>% kbl(caption = "Documents available for post 5051 cases",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Available == "Missing"), italic=T) %>%
row_spec(which(tab$Available == "Total"), bold=T)
Available | Number | Percent |
---|---|---|
No | 487 | 50.1% |
Yes | 485 | 49.9% |
Total | 972 | 100.0% |
tab <- cjtc %>%
group_by(Type = organization_type) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number),
index = if_else(is.na(Type), -1, rank(Number)),
Type = replace_na(Type, "Missing")
) %>%
arrange(desc(index)) %>%
select(-index) %>%
bind_rows(data.frame(Type ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Employer agency type",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Type == "Missing"), italic=T) %>%
row_spec(which(tab$Type == "Total"), bold=T)
Type | Number | Percent |
---|---|---|
Law Enforcement Agency | 3462 | 88.1% |
WA State Agency | 234 | 6.0% |
Correctional Facility | 177 | 4.5% |
WA State Agency; Law Enforcement Agency | 9 | 0.2% |
Law Enforcement Agency; Correctional Facility | 5 | 0.1% |
Law Enforcement Agency; NA | 4 | 0.1% |
Correctional Facility; Law Enforcement Agency | 1 | 0.0% |
Law Enforcement Agency; WA State Agency | 1 | 0.0% |
Police Department | 1 | 0.0% |
Missing | 37 | 0.9% |
Total | 3931 | 100.0% |
tab <- cjtc %>%
filter(period_r != "pre.5051") %>%
group_by(Type = organization_type) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number),
index = if_else(is.na(Type), -1, rank(Number)),
Type = replace_na(Type, "Missing")
) %>%
arrange(desc(index)) %>%
select(-index) %>%
bind_rows(data.frame(Type ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Employer agency type",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Type == "Missing"), italic=T) %>%
row_spec(which(tab$Type == "Total"), bold=T)
Type | Number | Percent |
---|---|---|
Law Enforcement Agency | 2945 | 87.0% |
WA State Agency | 209 | 6.2% |
Correctional Facility | 174 | 5.1% |
WA State Agency; Law Enforcement Agency | 9 | 0.3% |
Law Enforcement Agency; Correctional Facility | 5 | 0.1% |
Law Enforcement Agency; NA | 4 | 0.1% |
Correctional Facility; Law Enforcement Agency | 1 | 0.0% |
Law Enforcement Agency; WA State Agency | 1 | 0.0% |
Police Department | 1 | 0.0% |
Missing | 36 | 1.1% |
Total | 3385 | 100.0% |
tbl <- cjtc %>%
filter(!is.na(agency)) %>%
mutate(employer = gsub("County Sheriff's Office", "Cnty SO", agency),
employer = gsub("Police Department", "PD", employer)
) %>%
group_by(employer) %>%
summarise(Number = n()) %>%
mutate(Percent = Number/sum(Number)) %>%
arrange(Number) %>%
filter(Number > 19)
ggplot(tbl,
aes(x = reorder(employer, Percent), y = Percent,
label = Number)) +
geom_text(aes(y = Percent), size = 3, nudge_y = .002) +
geom_bar(stat="identity", fill="blue", alpha=.5) +
labs(title = "Cases by Employer: Agencies with 20+ cases",
caption = "CJTC certification database",
x = "",
y = "Percent of Total") +
theme(axis.text.y = element_text(size=7)) +
scale_y_continuous(labels = scales::percent_format(acc=1)) +
coord_flip()
tbl2 <- cjtc %>%
filter(period_r != "pre.5051") %>%
filter(!is.na(agency)) %>%
mutate(employer = gsub("County Sheriff's Office", "Cnty SO", agency),
employer = gsub("Police Department", "PD", employer)
) %>%
group_by(employer) %>%
summarise(Number = n()) %>%
mutate(Percent = Number/sum(Number)) %>%
arrange(Number) %>%
filter(Number > 19)
ggplot(tbl2,
aes(x = reorder(employer, Percent), y = Percent,
label = Number)) +
geom_text(aes(y = Percent), size = 3, nudge_y = .002) +
geom_bar(stat="identity", fill="blue", alpha=.5) +
labs(title = "Cases by Employer post 5051: Agencies with 20+ cases",
caption = "CJTC certification database",
x = "",
y = "Percent of Total") +
theme(axis.text.y = element_text(size=7)) +
scale_y_continuous(labels = scales::percent_format(acc=1)) +
coord_flip()
tab <- cjtc %>%
group_by(Certification.status = certification_status) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number),
index = if_else(is.na(Certification.status), -1, rank(Number)),
Certification.status = replace_na(Certification.status, "Missing")
) %>%
arrange(desc(index)) %>%
select(-index) %>%
bind_rows(data.frame(Certification.status ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Certification status of subject: all cases",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Certification.status == "Missing"), italic=T) %>%
row_spec(which(tab$Certification.status == "Total"), bold=T)
Certification.status | Number | Percent |
---|---|---|
Certified | 2782 | 70.8% |
Not Certified | 182 | 4.6% |
Lapsed | 79 | 2.0% |
Expired | 29 | 0.7% |
Certified; NA | 4 | 0.1% |
Missing | 855 | 21.8% |
Total | 3931 | 100.0% |
tab <- cjtc %>%
group_by(Occupation = occupation_type) %>%
summarize(Number = n()) %>%
mutate(Percent = Number/sum(Number),
index = if_else(is.na(Occupation), -1, rank(Number)),
Occupation = replace_na(Occupation, "Missing")
) %>%
arrange(desc(index)) %>%
select(-index) %>%
bind_rows(data.frame(Occupation ="Total",
Number = sum(.$Number),
Percent = sum(.$Percent))) %>%
mutate(Percent = scales::percent(Percent, acc=.1))
tab %>%
kbl(caption = "Occupation type of subject: all cases",
align = "lrr") %>%
kable_styling(bootstrap_options = "striped") %>%
row_spec(which(tab$Occupation == "Missing"), italic=T) %>%
row_spec(which(tab$Occupation == "Total"), bold=T)
Occupation | Number | Percent |
---|---|---|
Peace Officer | 3101 | 78.9% |
Corrections Officer | 221 | 5.6% |
Recruit | 91 | 2.3% |
Non-Certified | 88 | 2.2% |
Peace Officer; NA | 4 | 0.1% |
Missing | 426 | 10.8% |
Total | 3931 | 100.0% |
Here we pull out and list by name officers with more than one case in the database – i.e., they have been the subject of multiple complaints and/or agency notifications of misconduct. Note that when the name is missing in the database, we are unable to identify these officers.
The information shown in the columns of the table for each officer is aggregated from all of their cases in the dataset; the list of distinct entries for each type of information is given, separated by “;”. The exception is the “outcomes” column – here if their certification was revoked for any of their cases, we list just the revocation.
The table is interactive: data can be searched, sorted, filtered and downloaded using one of the buttons or boxes shown at the top of the table.
cjtc_name <- cjtc %>%
filter(!is.na(first_name) & first_name != "Missing" &
!grepl("REDACT", first_name) & !grepl("Unknown", first_name) &
!is.na(last_name) & last_name != "Missing" &
!grepl("REDACT", last_name) & !grepl("Complainant", first_name)) %>%
mutate(employer = gsub("County Sheriff's Office", "CSO", agency),
employer = gsub("Police Department", "PD", employer),
employer = gsub("Department of Corrections", "DOC", employer),
source = case_when(
grepl("1915", opened_reason) ~ "1915 report",
grepl("Request", opened_reason) ~ "Reinstate rqst",
grepl("Division", opened_reason) ~ "Division",
TRUE ~ opened_reason),
name = paste(last_name, first_name, sep=", ")
) %>%
arrange(date_received) %>%
select(name, employer, source, date_received, status, case_determination, outcat) %>%
group_by(name) %>%
summarise(nrec = n(),
across(employer:outcat, ~paste(unique(.x), collapse="; ")), .groups = "drop") %>%
mutate(across(where(is.character), ~if_else(. == "NA", NA, .))) %>% # recover NAs
mutate(outcat = gsub("Revoked\\/", "", outcat),
final_outcome = if_else(grepl("Revo", case_determination), "Cert Revoked", outcat))
cjtc_name %>%
filter(nrec > 1) %>%
arrange(desc(nrec)) %>%
select(name, cases=nrec, sources=source, dates=date_received, employers=employer,
statuses=status, outcomes=final_outcome) %>%
DT::datatable(rownames = F,
caption = paste("Officers with more than one record in the database as of",
max(cjtc$date_received)),
filter = 'top',
escape = FALSE,
extensions = 'Buttons',
options =
list(dom = 'Bfrtip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print'))
)