Table of Contents

  1. Officers in Schools: The number of officers in schools rose sharply in the 90s and then held steady until 2014.
  2. Weapons Officers Carry Between 2006 and 2010, schools that employed officers saw an 8-point rise in tasers and an 8-point drop in firearms.
  3. COPS Funding Most federal grants to hire SROs came in the 90s and early 2000s.

This is a Jupyter notebook. It uses the R programming language.

First, some boilerplate R:

# install.packages('ggplot2')
## install.packages('prettyR') # for lemas13
#
# Haven 0.2.0 can't read a compressed SAS file. Install GitHub version by
# running this:
#
#     library(devtools); devtools::install_github('hadley/haven')
library(haven)     # to read SPSS and SAS files
library(ggplot2)   # to build charts
library(foreign)   # for xport file

# Keep file size small-ish, so GitHub renders it
options(
    jupyter.plot_mimetypes='image/png',
    repr.plot.width  = 6,
    repr.plot.height = 4
)

Input

SSOCS: School Survey on Crime and Safety

The National Center for Education Statistics (NCES) sends surveys to school pricipals from time to time. Download the SPSS data files from their data_products page. Their 2010 download page is separate.

Unzip all the .sav files into data/. Then we'll load them:

ssocs00 <- read_sav('data/SSOCS/SSOCS1.sav')
ssocs04 <- read_sav('data/SSOCS/ssocs_puf.sav')
ssocs06 <- read_sav('data/SSOCS/pu_ssocs06_spss.sav')
ssocs08 <- read_sav('data/SSOCS/pu_ssocs08_spss.sav')
ssocs10 <- read_sav('data/SSOCS/pu_ssocs10_spss.sav')

FRSS_SSD: Fast Response Survey System, School Safety and Discipline

To make up for a gap in the SSOCS responses, we consider one other NCES survey from 2014. It has a couple of questions that are identical to the SSOCS ones, and the methodology is very similar, so we can make comparisons.

The full disclaimer, from http://nces.ed.gov/programs/digest/d15/tables/dt15_233.70.asp:

Data for 2013-14 were collected using the Fast Response Survey System, while data for earlier years were collected using the School Survey on Crime and Safety (SSOCS). The 2013-14 survey was designed to allow comparisons with SSOCS data. However, respondents to the 2013-14 survey could choose either to complete the survey on paper (and mail it back) or to complete the survey online, whereas respondents to SSOCS did not have the option of completing the survey online. The 2013-14 survey also relied on a smaller sample. The smaller sample size and change in survey administration may have impacted 2013-14 results.

Then there's an old survey not in data form. It has a percentage of schools with school resource officers.

frss_ssd14 <- read_sas('data/FRSS/frss106puf.sas7bdat')

frss_vdpups97 <- list(
    year=1997,
    n=77752,
    percent_ft_officer=6,
    percent_pt_officer=4,
    percent_officer=10
)
# Now add lots of variables to the data frame, to make summary "ssocs" data frame

# YEAR
ssocs00$year <- 2000L
ssocs04$year <- 2004L
ssocs06$year <- 2006L
ssocs08$year <- 2008L
ssocs10$year <- 2010L
frss_ssd14$year <- 2014L

# WEIGHT
# Some schools "count" for more than others. Here's the weight of each school:
ssocs00$weight <- ssocs00$FWT
ssocs04$weight <- ssocs04$FINALWGT
ssocs06$weight <- ssocs06$FINALWGT
ssocs08$weight <- ssocs08$FINALWGT
ssocs10$weight <- ssocs10$FINALWGT
frss_ssd14$weight <- frss_ssd14$AWT

# HAS_OFFICER
# During the 1999-2000 school year, at what times did your school regularly
# use paid law enforcement or security services at school? (Circle one response on each line.)
# ... At any time during school hours
# 1=yes
# 2=no
ssocs00$has_officer <- !is.na(ssocs00$Q8A) & ssocs00$Q8A == 1
# During the 2003-2004 school year, did you have any sworn law enforcement officers,
# security guards, or security personnel present at your school on a regular basis?
# 1=yes
ssocs04$has_officer <- ssocs04$Q7 == 1
# 2008: changed to clarify "at least once a week"
ssocs06$has_officer <- ssocs06$C0220 == 1
ssocs08$has_officer <- ssocs08$C0220 == 1
# Beware: In 2010 the questionnaire changed. 2004-2008 had columns for part-time vs
# full-time; in 2010 there's a single column.
ssocs10$has_officer <- ssocs10$C0220 == 1
frss_ssd14$has_officer <- frss_ssd14$Q4 == 1

# N_FULL_TIME_OFFICERS
ssocs00$n_full_time_officers <- NA
# In 2004-2006, the number of officers is "topcoded": if an agency has more than 10
# officers, the survey says it had 11.
ssocs04$n_full_time_officers <- ifelse(ssocs04$Q9A1 > 0, ssocs04$Q9A1, 0) +
                                ifelse(ssocs04$Q9B1 > 0, ssocs04$Q9B1, 0) +
                                ifelse(ssocs04$Q9C1 > 0, ssocs04$Q9C1, 0)
ssocs06$n_full_time_officers <- ifelse(strtoi(ssocs06$C0232_R, 10) > 0, strtoi(ssocs06$C0232_R, 10), 0) +
                                ifelse(strtoi(ssocs06$C0236_R, 10) > 0, strtoi(ssocs06$C0236_R, 10), 0) +
                                ifelse(strtoi(ssocs06$C0240_R, 10) > 0, strtoi(ssocs06$C0240_R, 10), 0)
ssocs08$n_full_time_officers <- ifelse(ssocs08$SEC_FT > 0, ssocs08$SEC_FT, 0)
ssocs10$n_full_time_officers <- ifelse(ssocs10$SEC_FT10 > 0, ssocs10$SEC_FT10, 0)
frss_ssd14$n_full_time_officers <- ifelse(frss_ssd14$T_Q5FT > 0, frss_ssd14$T_Q5FT, 0)

# N_PART_TIME_OFFICERS
ssocs00$n_part_time_officers <- NA
ssocs04$n_part_time_officers <- ifelse(ssocs04$Q9A2 > 0, ssocs04$Q9A2, 0) +
                                ifelse(ssocs04$Q9B2 > 0, ssocs04$Q9B2, 0) +
                                ifelse(ssocs04$Q9C2 > 0, ssocs04$Q9C2, 0)
ssocs06$n_part_time_officers <- ifelse(strtoi(ssocs06$C0234_R, 10) > 0, strtoi(ssocs06$C0234_R, 10), 0) +
                                ifelse(strtoi(ssocs06$C0238_R, 10) > 0, strtoi(ssocs06$C0238_R, 10), 0) +
                                ifelse(strtoi(ssocs06$C0242_R, 10) > 0, strtoi(ssocs06$C0242_R, 10), 0)
ssocs08$n_part_time_officers <- ifelse(ssocs08$SEC_PT > 0, ssocs08$SEC_PT, 0)
ssocs10$n_part_time_officers <- ifelse(ssocs10$SEC_PT10 > 0, ssocs10$SEC_PT10, 0)
frss_ssd14$n_part_time_officers <- ifelse(frss_ssd14$T_Q5PT > 0, frss_ssd14$T_Q5PT, 0)

# N_WITH_TASER, N_WITH_SPRAY, N_WITH_FIREARM
#
# Different officers carry different weapons. This is a survey of _schools_,
# not officers, so all we can know is whether the _school_ has officers with
# certain weapons.
#
# If a school has one officer and it has officers with taser, we can conclude
# that the school has one officer who has a taser. But if a school has three
# officers and a taser, we don't know how many tasers there are.
ssocs00$has_taser <- NA
ssocs04$has_taser <- NA
ssocs06$has_taser <- ssocs06$C0246 == 1
ssocs08$has_taser <- ssocs08$C0246 == 1
ssocs10$has_taser <- ssocs10$C0246 == 1
frss_ssd14$has_taser <- NA

ssocs00$has_spray <- NA
ssocs04$has_spray <- NA
ssocs06$has_spray <- ssocs06$C0248 == 1
ssocs08$has_spray <- ssocs08$C0248 == 1
ssocs10$has_spray <- ssocs10$C0248 == 1
frss_ssd14$has_spray <- NA

ssocs00$has_firearm <- NA
ssocs04$has_firearm <- NA
ssocs06$has_firearm <- ssocs06$C0250 == 1
ssocs08$has_firearm <- ssocs08$C0250 == 1
ssocs10$has_firearm <- ssocs10$C0250 == 1
frss_ssd14$has_firearm <- NA

# "SSOCS": the sum of all surveys
ssocs_columns <- c(
    'year',
    'weight',
    'has_officer',
    'n_full_time_officers',
    'n_part_time_officers',
    'has_taser',
    'has_spray',
    'has_firearm'
)
ssocs <- rbind(
    ssocs00[ssocs_columns],
    ssocs04[ssocs_columns],
    ssocs06[ssocs_columns],
    ssocs08[ssocs_columns],
    ssocs10[ssocs_columns],
    frss_ssd14[ssocs_columns]
)
ssocs$n <- 1

for (col in c(
    'n_full_time_officers',
    'n_part_time_officers'
)) {
    ssocs[is.na(ssocs[col]),col] <- 0
}

ssocs$has_full_time_officer <- ssocs$n_full_time_officers > 0
ssocs$has_part_time_officer <- ssocs$n_part_time_officers > 0

for (col in names(ssocs)) {
    if (col != 'year' && col != 'weight') {
        ssocs[paste0(col, '_weighted')] <- ssocs[col] * ssocs$weight
    }
}

COPS Office

The Community Oriented Policing Services office supplied us with a summary of all the grants it provided.

This list is only the COPS office, and it's only amounts specifically spent on hiring school resource officers.

This is far from a list of SRO counts by year: COPS is a federal program, and law enforcement budgets are mostly supplied by local/state governments.

In the provided Word document, the grant amounts for 1999-2005 were all asterisked: "Additional funding may have been used to hire SROs under the COPS Office Universal Hiring Program, but was not specifically tracked."

cops_grants <- data.frame(
    year=seq(1994, 2015),
    amount=c(NA, NA, NA, NA, NA, 159937854, 159903439, 154229248, 158364762, 36613569, 50565923, 5040981, 0, 0, NA, NA, NA, 55109871, 4025463, 46450849, 22541206, 14901904),
    n_officers=c(NA, NA, NA, NA, NA, 1403, 1472, 1355, 1373, 311, 426, 41, 0, 0, NA, NA, NA, 238, 31, 370, 167, 128)
)

cops_grants

paste0(
    'Total $',
    round(sum(cops_grants[!is.na(cops_grants$amount),'amount']) / 1000000, 1),
    'M spent to hire ',
    sum(cops_grants[!is.na(cops_grants$n_officers),'n_officers']),
    ' officers'
)
yearamountn_officers
11994 NA NA
21995 NA NA
31996 NA NA
41997 NA NA
51998 NA NA
6 1999159937854 1403
7 2000159903439 1472
8 2001154229248 1355
9 2002158364762 1373
10 200336613569 311
11 200450565923 426
12 20055040981 41
132006 0 0
142007 0 0
152008 NA NA
162009 NA NA
172010 NA NA
18 201155109871 238
19 20124025463 31
20 201346450849 370
21 201422541206 167
22 201514901904 128
'Total $867.7M spent to hire 7315 officers'

Officers in schools

The numbers of school resource officers were given to us upon request by the Bureau of Justice Statistics for SSOCS 2003-2004, 2005-2006, 2007-2008, 2009-2010, from the private-use data files.

to_plot <- data.frame(
    year=c(
        1997,
        2000,
        2005,
        2006,
        2008,
        2010,
        2014
    ),
    percent_of_schools_with_sro=c(
        10, # 1997 FRSS -- includes all law enforcement officers
        31.34, # 2000 SSOCS -- includes all security guards, period -- a gross overcount
        100*26000/80500, # 2004 SSOCS private-use data file (numerator is # schools with SROs)
        100*26900/83200, # 2006 SSOCS private-use data file
        100*29400/83000, # 2008 SSOCS private-use data file
        100*25700/82800, # 2010 SSOCS private-use data file
        30 # 2014 FRSS, as cited in its report
    )
)

to_plot

ggplot(to_plot, aes(x=to_plot$year, y=to_plot$percent_of_schools_with_sro)) +
  expand_limits(y=0) +
  geom_line() +
  geom_label(aes(label=round(percent_of_schools_with_sro, 1))) +
  ggtitle('Percentage of schools with SRO')
yearpercent_of_schools_with_sro
11997 10
22000.00 31.34
32005.00000 32.29814
42006.00000 32.33173
52008.00000 35.42169
62010.00000 31.03865
72014 30
[output]

Weapons officers are holding

to_plot <- aggregate(
    list(
        n=ssocs$n_weighted,
        n_with_taser=ssocs$has_taser_weighted,
        n_with_spray=ssocs$has_spray_weighted,
        n_with_firearm=ssocs$has_firearm_weighted
    ),
    by=list(year=ssocs$year),
    FUN=sum
)
to_plot <- rbind(
    data.frame(year=to_plot$year, group='taser', percent=100 * to_plot$n_with_taser / to_plot$n),
    data.frame(year=to_plot$year, group='spray', percent=100 * to_plot$n_with_spray / to_plot$n),
    data.frame(year=to_plot$year, group='firearm', percent=100 * to_plot$n_with_firearm / to_plot$n)
)
to_plot <- to_plot[!is.na(to_plot$percent),]

ggplot(to_plot, aes(x=year, y=percent, group=group, colour=group)) +
  expand_limits(y=0) +
  geom_line() +
  geom_label(aes(label=round(to_plot$percent, 2))) +
  ggtitle('Percentage of schools with equipment')
[output]