Pick any two date ranges and compare incident counts by crime category. The defaults are the last 30 days of data against the same 30 days one year earlier — matching the calendar dates, not just "the month before," is the honest comparison.
What's counted: incidents whose Date Occurred falls inside each chosen range (both endpoints included), bucketed by the crime categories used across this site. The arrest rate is arrests linked to incidents in the range ÷ incidents in the range.
Watch out for: the log's coverage runs from January 2015 to a few days before today; ranges outside that window are clamped. Small counts make big percentages — a jump from 2 to 5 incidents is +150% and usually not a story. See the cherry-picking lesson.
library(tidyverse)
library(lubridate)
activity <- read_csv("https://raw.githubusercontent.com/dwillis/umpd-logs/master/data/all-police-activity.csv")
activity |>
mutate(d = as_date(`Date Occurred`),
period = case_when(
d >= ymd("2026-08-01") & d <= ymd("2026-08-30") ~ "A",
d >= ymd("2025-08-01") & d <= ymd("2025-08-30") ~ "B")) |>
filter(!is.na(period)) |>
count(period, `Crime Type`) |>
pivot_wider(names_from = period, values_from = n, values_fill = 0)