Car use in the City of Cambridge (source: American Community Survey, 2020)

Distribution of a random sample of 10,000 tickets from Dec. 2020 through Nov. 2021 (source: Cambridge Open Data API)

Distribution of all tickets issued from Dec. 2020 through Nov. 2021 by census block (source: Cambridge Open Data API)

Approx. distribution of ticket revenue generated from Dec. 2020 through Nov. 2021 by census block (source: Cambridge Open Data API)

The distribution of ticket fines across census blocks in Cambridge is highly unequal. (source: Cambridge Open Data API)

Between December 2020 to November 2021 period (the most recent yearlong period available in the data), the Gini coefficient of ticket fines across census blocks was 0.75. According to World Bank data, the country with the highest Gini coefficient in the world, South Africa, only reaches 0.63. This inequality is clearly illustrated by the Lorenz curve below.

## [1] 0.7513121

Alternatively, we can present the distribution of ticket fines across census blocks between Dec. 2020 and Nov. 2021 like this. (source: Cambridge Open Data API)

Here, as on the maps above, we can see that a disproportionately large share of Cambridge’s parking ticket revenue is collected from a small handful of outlier census blocks, most notably Block 1003 in Tract 3524, where 3,843 tickets were issued during the December 2020 to November 2021 period, yielding at least $116,645 in fines. Block 1003 contains a total of about five buildings.

The maps below show that this extreme inequality in ticket distribution is not a new phenomenon, though it does appear to have worsened over time.

Distribution of all tickets issued from Jul. 2019 through Nov. 2020 by census block (source: Cambridge Open Data API)

Approx. distribution of ticket revenue generated from Jul. 2019 through Nov. 2020 by census block (source: Cambridge Open Data API)

Census blocks where more of the population is African American are ticketed at far higher rates.

Compared to census blocks where only 10% of the population is African American, census blocks where 30% of the population is African American receive, on average, around 1.25 times as many tickets. Census blocks where 50% of the population is African American receive, on average, more than 1.5 times as many. A similar pattern holds for fines.

reg1nb <- glm.nb(tickets ~ pct_AA, data = AA_tickets); summary(reg1nb)
## 
## Call:
## glm.nb(formula = tickets ~ pct_AA, data = AA_tickets, init.theta = 0.5516688259, 
##     link = log)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -1.9521  -1.2017  -0.8190  -0.0326   3.5434  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  5.95552    0.06605  90.164   <2e-16 ***
## pct_AA       0.01109    0.00506   2.191   0.0285 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Negative Binomial(0.5517) family taken to be 1)
## 
##     Null deviance: 858.45  on 684  degrees of freedom
## Residual deviance: 853.59  on 683  degrees of freedom
## AIC: 9455.5
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  0.5517 
##           Std. Err.:  0.0249 
## 
##  2 x log-likelihood:  -9449.5270
odTest(reg1nb, alpha=.05)
## Likelihood ratio test of H0: Poisson, as restricted NB model:
## n.b., the distribution of the test-statistic under H0 is non-standard
## e.g., see help(odTest) for details/references
## 
## Critical value of test statistic at the alpha= 0.05 level: 2.7055 
## Chi-Square Test Statistic =  563057.3396 p-value = < 2.2e-16
IRR_1 <- exp(reg1nb[["coefficients"]][["pct_AA"]]); IRR_1
## [1] 1.011147
IRR_1.1 <- exp(reg1nb[["coefficients"]][["pct_AA"]]*30 - reg1nb[["coefficients"]][["pct_AA"]]*10); IRR_1.1
## [1] 1.248212
IRR_1.2 <- exp(reg1nb[["coefficients"]][["pct_AA"]]*50 - reg1nb[["coefficients"]][["pct_AA"]]*10); IRR_1.2
## [1] 1.558034
visreg(reg1nb, data=AA_tickets, xlab="Percent African American", ylab="ln(Tickets)", gg=TRUE, plot.type="gg")

reg2nb <- glm.nb(fines ~ pct_AA, data = AA_tickets); summary(reg2nb)
## 
## Call:
## glm.nb(formula = fines ~ pct_AA, data = AA_tickets, init.theta = 0.5522265998, 
##     link = log)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -1.9497  -1.2071  -0.8010  -0.0185   3.4706  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) 9.394835   0.065977 142.397   <2e-16 ***
## pct_AA      0.011843   0.005055   2.343   0.0191 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Negative Binomial(0.5522) family taken to be 1)
## 
##     Null deviance: 862.74  on 684  degrees of freedom
## Residual deviance: 856.93  on 683  degrees of freedom
## AIC: 14173
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  0.5522 
##           Std. Err.:  0.0249 
## 
##  2 x log-likelihood:  -14166.9580
odTest(reg2nb, alpha=.05)
## Likelihood ratio test of H0: Poisson, as restricted NB model:
## n.b., the distribution of the test-statistic under H0 is non-standard
## e.g., see help(odTest) for details/references
## 
## Critical value of test statistic at the alpha= 0.05 level: 2.7055 
## Chi-Square Test Statistic =  17819106.8791 p-value = < 2.2e-16
IRR_2 <- exp(reg2nb[["coefficients"]][["pct_AA"]]); IRR_2
## [1] 1.011914
IRR_2.1 <- exp(reg2nb[["coefficients"]][["pct_AA"]]*30 - reg2nb[["coefficients"]][["pct_AA"]]*10); IRR_2.1
## [1] 1.267267
IRR_2.2 <- exp(reg2nb[["coefficients"]][["pct_AA"]]*50 - reg2nb[["coefficients"]][["pct_AA"]]*10); IRR_2.2
## [1] 1.605965
visreg(reg2nb, data=AA_tickets, xlab="Percent African American", ylab="ln(Fines)", gg=TRUE, plot.type="gg")

Moreover, this seems to getting worse. With the sample restricted to tickets from between December 2020 and November 2021 (the most recent yearlong period available), we find that census blocks where 30% of the population is African American receive, on average, over 1.4 times as many tickets as census blocks where only 10% of the population is African American, and where 50% of the population is African American, they receive, on average, more than twice as many tickets. Once again, this pattern also holds for fines.

reg3nb <- glm.nb(tickets_21 ~ pct_AA, data = AA_tickets); summary(reg3nb)
## 
## Call:
## glm.nb(formula = tickets_21 ~ pct_AA, data = AA_tickets, init.theta = 0.4610055499, 
##     link = log)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.1172  -1.2281  -0.7575  -0.2027   3.6120  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) 4.965203   0.072311  68.665  < 2e-16 ***
## pct_AA      0.017635   0.005537   3.185  0.00145 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Negative Binomial(0.461) family taken to be 1)
## 
##     Null deviance: 877.46  on 684  degrees of freedom
## Residual deviance: 865.90  on 683  degrees of freedom
## AIC: 8011.5
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  0.4610 
##           Std. Err.:  0.0207 
## 
##  2 x log-likelihood:  -8005.5450
odTest(reg3nb, alpha=.05)
## Likelihood ratio test of H0: Poisson, as restricted NB model:
## n.b., the distribution of the test-statistic under H0 is non-standard
## e.g., see help(odTest) for details/references
## 
## Critical value of test statistic at the alpha= 0.05 level: 2.7055 
## Chi-Square Test Statistic =  255448.6953 p-value = < 2.2e-16
IRR_3 <- exp(reg3nb[["coefficients"]][["pct_AA"]]); IRR_3
## [1] 1.017792
IRR_3.1 <- exp(reg3nb[["coefficients"]][["pct_AA"]]*30 - reg3nb[["coefficients"]][["pct_AA"]]*10); IRR_3.1
## [1] 1.422909
IRR_3.2 <- exp(reg3nb[["coefficients"]][["pct_AA"]]*50 - reg3nb[["coefficients"]][["pct_AA"]]*10); IRR_3.2
## [1] 2.024669
visreg(reg3nb, data=AA_tickets, xlab="Percent African American", ylab="ln(Tickets)", gg=TRUE, plot.type="gg")

reg4nb <- glm.nb(fines_21 ~ pct_AA, data = AA_tickets); summary(reg4nb)
## 
## Call:
## glm.nb(formula = fines_21 ~ pct_AA, data = AA_tickets, init.theta = 0.4612439289, 
##     link = log)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.1735  -1.2382  -0.7602  -0.2102   3.5144  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) 8.416164   0.072193 116.579  < 2e-16 ***
## pct_AA      0.017906   0.005531   3.237  0.00121 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Negative Binomial(0.4612) family taken to be 1)
## 
##     Null deviance: 893.16  on 684  degrees of freedom
## Residual deviance: 880.69  on 683  degrees of freedom
## AIC: 12723
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  0.4612 
##           Std. Err.:  0.0204 
## 
##  2 x log-likelihood:  -12716.7540
odTest(reg4nb, alpha=.05)
## Likelihood ratio test of H0: Poisson, as restricted NB model:
## n.b., the distribution of the test-statistic under H0 is non-standard
## e.g., see help(odTest) for details/references
## 
## Critical value of test statistic at the alpha= 0.05 level: 2.7055 
## Chi-Square Test Statistic =  8250423.6525 p-value = < 2.2e-16
IRR_4 <- exp(reg4nb[["coefficients"]][["pct_AA"]]); IRR_4
## [1] 1.018067
IRR_4.1 <- exp(reg4nb[["coefficients"]][["pct_AA"]]*30 - reg4nb[["coefficients"]][["pct_AA"]]*10); IRR_4.1
## [1] 1.430636
IRR_4.2 <- exp(reg4nb[["coefficients"]][["pct_AA"]]*50 - reg4nb[["coefficients"]][["pct_AA"]]*10); IRR_4.2
## [1] 2.04672
visreg(reg4nb, data=AA_tickets, xlab="Percent African American", ylab="ln(Fines)", gg=TRUE, plot.type="gg")

Tickets issued per day in Cambridge, Jan. 2014 - Nov. 2021 (source: Cambridge Open Data API)

How many hearing officers are employed by the Cambridge Department of Traffic, Parking & Transportation? One. (source: Cambridge Open Data API)

city_payroll <- read.socrata("https://data.cambridgema.gov/resource/ixg8-tyau.json")
city_payroll_tpt23 <- city_payroll %>% subset(fiscal_year == 2023 & department == "Traffic, Parking and Transportation")
hearing_officers <- city_payroll_tpt23 %>% subset(job_title == "Admin Hearings Officer/Tr")
head(hearing_officers)
##       fiscal_year       service                          department
## 11768        2023 Public Safety Traffic, Parking and Transportation
##                 division position_number                 job_title total_salary
## 11768 Parking Management        M140-001 Admin Hearings Officer/Tr        95375
nrow(hearing_officers)
## [1] 1

The City of Cambridge issues roughly 180,000 parking tickets per year (Cambridge has only 118,403 residents). Hearings are scheduled for 20-minute windows. The City’s one hearing officer can perform at most 5,625 of them in a year. That’s around 3% of the number of tickets issued annually.

nrow(tickets_a)
## [1] 181554
max_hearings_per_week <- (37.5*60)/20; print(max_hearings_per_week)
## [1] 112.5
max_hearings_per_year <- max_hearings_per_week*50; print(max_hearings_per_year)
## [1] 5625
max_percent_of_tickets_heard <- 100*(max_hearings_per_year/nrow(tickets_a)); print(max_percent_of_tickets_heard)
## [1] 3.098252

How many hearing officers could the City of Cambridge employ using only parking ticket revenue? 85.

city_revenue <- read.socrata("https://data.cambridgema.gov/api/odata/v4/ixyv-mje6")
city_revenue_parkingfines <- city_revenue %>% subset(fiscal_year >= 2020 & description == "Parking Fines")
city_revenue_parkingfines <- city_revenue_parkingfines %>% arrange(desc(fiscal_year))
head(city_revenue_parkingfines)
##   fiscal_year       service                     department_name
## 1        2023 Public Safety                              Police
## 2        2023 Public Safety Traffic, Parking and Transportation
## 3        2023     Education                           Education
## 4        2022 Public Safety                              Police
## 5        2022 Public Safety Traffic, Parking and Transportation
## 6        2022     Education                           Education
##           category   description  amount         fund
## 1 Fines & Forfeits Parking Fines 2603425 General Fund
## 2 Fines & Forfeits Parking Fines 5496575 Parking Fund
## 3 Fines & Forfeits Parking Fines  100000  School Fund
## 4 Fines & Forfeits Parking Fines 2603425 General Fund
## 5 Fines & Forfeits Parking Fines 5496575 Parking Fund
## 6 Fines & Forfeits Parking Fines  100000  School Fund
ticket_revenue <- city_revenue_parkingfines[1, 6] + city_revenue_parkingfines[2, 6] + city_revenue_parkingfines[3, 6]; print(ticket_revenue)
## [1] 8200000
hearing_officers$total_salary <- as.numeric(hearing_officers$total_salary)
max_hearing_officers <- ticket_revenue/hearing_officers[1, 7]; print(max_hearing_officers)
## [1] 85.97641