Project-1

Project-1

Author: Dhivyan Gopinath

Project Overview

This project explores the public arrests data of Baltimore city. I aim to investigate how criminal activity counts and demographics relate to arrests patterns.

Data Source

The data used in this study is sourced from BPD Arrests Data: Records of arrests made by the Baltimore Police Department.

Here is the link for the public data set:

BPD Arrests

Baltimore City

This data set represents the arrests made by the Baltimore Police Department. Data are updated weekly. Note: the “Info Updated” field reflects the current refresh date of the data.

Research Questions

I plan to explore two main questions:

  1. Is there a correlation between crime counts and the offenders’ age by district?

  2. Is there a correlation between crime counts and the offenders’ age by the type of crime?

Variable Definitions

To explore these relationships, I analyzed the following key variables: 1. District (Categorical): The 9 specific Baltimore Police Districts (e.g., Central, Northern, Southeast) where the incidents occurred. 2. Age (Quantitative): The age of individuals arrested within a specific district. 3. Charge Description - The charge that was made against the individual.

Data Processing & Methodology

I will be grouping the data based on the Charge description and the district to get my aggregate data which I will further explore to answer my questions.

Quarto

1. Is there a correlation between crime counts and the offenders’ age by district?

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.1     ✔ readr     2.2.0
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.3     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.2
✔ purrr     1.2.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)
library(ggplot2)
setwd("C:\\Dhivyan")
getwd()
[1] "C:/Dhivyan"
arrest_data <- readr::read_csv("BPD_Arrests.csv")
Rows: 399547 Columns: 20
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (12): IncidentNumber, Gender, Race, ArrestDateTime, ArrestLocation, Inci...
dbl  (8): X, Y, RowID, ArrestNumber, Age, Post, Latitude, Longitude

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
summ_age_dist <- arrest_data %>% 
  filter(!is.na(Age) & Age > 0) %>% # Excluding invalid age records for accuracy.
  group_by(District) %>% 
  summarise(avg_age = mean(Age)) %>% 
  filter(District != "")
summ_age_dist
# A tibble: 9 × 2
  District  avg_age
  <chr>       <dbl>
1 Central      33.3
2 Eastern      34.2
3 Northeast    31.3
4 Northern     33.9
5 Northwest    34.5
6 Southeast    32.7
7 Southern     32.2
8 Southwest    32.5
9 Western      34.0
summ_cnt_dist <- arrest_data %>% 
  filter(!is.na(Age) & Age > 0) %>% # Excluding invalid age records for accuracy.
  group_by(District) %>% 
  summarise(total = n()) %>% 
  filter(District != "")
summ_cnt_dist
# A tibble: 9 × 2
  District  total
  <chr>     <int>
1 Central   30503
2 Eastern   28796
3 Northeast 24212
4 Northern  13586
5 Northwest 22498
6 Southeast 24672
7 Southern  26430
8 Southwest 22656
9 Western   31162
analysis1 <- summ_cnt_dist %>% 
  inner_join(summ_age_dist, by = "District")
analysis1
# A tibble: 9 × 3
  District  total avg_age
  <chr>     <int>   <dbl>
1 Central   30503    33.3
2 Eastern   28796    34.2
3 Northeast 24212    31.3
4 Northern  13586    33.9
5 Northwest 22498    34.5
6 Southeast 24672    32.7
7 Southern  26430    32.2
8 Southwest 22656    32.5
9 Western   31162    34.0
ggplot(arrest_data, aes(x = factor(District), y = Age, fill = District)) + 
  geom_boxplot(outlier.color = "red", outlier.shape = 1, alpha = 0.7) +
  labs(x = "By district", y = "Crime Totals", 
       title = "Age by districts", 
       caption = "Source: https://data.baltimorecity.gov/") +
  theme_minimal() + theme(legend.position = "none") # Legend removed as x-axis labels identify districts.
Warning: Removed 133 rows containing non-finite outside the scale range
(`stat_boxplot()`).

ggplot(analysis1, aes(x = avg_age, y = total)) +
  geom_point(aes(color = District), size = 5) + 
  geom_smooth(method = "lm", color = "black", se = TRUE) + 
  geom_text(aes(label = District), vjust = -1.5, fontface = "bold") +
  theme_minimal() +
  labs(title = "Correlation: Avg Criminal Age vs. Crime totals", 
       subtitle = "Does a younger criminal demographic correlate with higher count?", caption = "https://data.baltimorecity.gov/", 
       x = "Average Age of Arrested Individuals", y = "Crimes total count") +
  theme(plot.title = element_text(hjust = 0.5, size = 16), plot.subtitle = element_text(hjust = 0.5), legend.position = "none")
`geom_smooth()` using formula = 'y ~ x'

balt_model1 <- lm(total ~ avg_age, data = analysis1)
summary(balt_model1)

Call:
lm(formula = total ~ avg_age, data = analysis1)

Residuals:
     Min       1Q   Median       3Q      Max 
-11385.7  -2265.1   -255.9   3811.5   6185.9 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)
(Intercept) 23737.56   62706.05   0.379    0.716
avg_age        36.43    1889.31   0.019    0.985

Residual standard error: 5700 on 7 degrees of freedom
Multiple R-squared:  5.311e-05, Adjusted R-squared:  -0.1428 
F-statistic: 0.0003718 on 1 and 7 DF,  p-value: 0.9852

2. Is there a correlation between crime counts and the offenders’ age by the type of crime?

summ_age_Charge <- arrest_data %>% 
  filter(!is.na(Age) & Age > 0) %>% # Excluding invalid age records for accuracy.
  group_by(ChargeDescription) %>% 
  summarise(avg_age = mean(Age)) %>% 
  filter(ChargeDescription != "");
summ_age_Charge
# A tibble: 13,836 × 2
   ChargeDescription              avg_age
   <chr>                            <dbl>
 1 !ST DEGREE ASSAUALT                 29
 2 !ST DEGREE ASSAULT                  32
 3 'ATTEMPT THEFT                      21
 4 (ATTEMPT) BURGLARY                  23
 5 (COM) ASSAULT                       29
 6 (COM)ASSAULT                        21
 7 (DRIVING, ATT.TO DRIVE) VEH. W      40
 8 (DRIVING, ATTEMPTING TO DRIVE       40
 9 (DRIVING, ATTEMPTING TO DRIVE)      34
10 0SC31736                            54
# ℹ 13,826 more rows
summ_cnt_Charge <- arrest_data %>% 
  filter(!is.na(Age) & Age > 0) %>% # Excluding invalid age records for accuracy.
  group_by(ChargeDescription) %>% 
  summarise(total = n()) %>% 
  filter(ChargeDescription != "");
summ_cnt_Charge
# A tibble: 13,836 × 2
   ChargeDescription              total
   <chr>                          <int>
 1 !ST DEGREE ASSAUALT                1
 2 !ST DEGREE ASSAULT                 3
 3 'ATTEMPT THEFT                     1
 4 (ATTEMPT) BURGLARY                 1
 5 (COM) ASSAULT                      1
 6 (COM)ASSAULT                       1
 7 (DRIVING, ATT.TO DRIVE) VEH. W     1
 8 (DRIVING, ATTEMPTING TO DRIVE      1
 9 (DRIVING, ATTEMPTING TO DRIVE)     3
10 0SC31736                           1
# ℹ 13,826 more rows
analysis2 <- summ_cnt_Charge %>% 
  inner_join(summ_age_Charge, by = "ChargeDescription");
analysis2
# A tibble: 13,836 × 3
   ChargeDescription              total avg_age
   <chr>                          <int>   <dbl>
 1 !ST DEGREE ASSAUALT                1      29
 2 !ST DEGREE ASSAULT                 3      32
 3 'ATTEMPT THEFT                     1      21
 4 (ATTEMPT) BURGLARY                 1      23
 5 (COM) ASSAULT                      1      29
 6 (COM)ASSAULT                       1      21
 7 (DRIVING, ATT.TO DRIVE) VEH. W     1      40
 8 (DRIVING, ATTEMPTING TO DRIVE      1      40
 9 (DRIVING, ATTEMPTING TO DRIVE)     3      34
10 0SC31736                           1      54
# ℹ 13,826 more rows
# Picking only the top 100 crimes
top_100 <- analysis2 %>%
  arrange(dplyr::desc(total)) %>% 
  slice_head(n = 100);

print(top_100)
# A tibble: 100 × 3
   ChargeDescription      total avg_age
   <chr>                  <int>   <dbl>
 1 FAILURE TO APPEAR      47917    34.6
 2 CDS VIOLATION          31230    33.8
 3 ASSAULT-SEC DEGREE     30868    33.7
 4 Unknown Charge         29875    32.9
 5 CDS                    23509    33.2
 6 VIOLATION OF PROBATION 10737    33.7
 7 COMMON ASSAULT         10336    33.6
 8 HGV                     7782    27.6
 9 ASSAULT-FIRST DEGREE    7722    33.1
10 ASSAULT                 5700    33.3
# ℹ 90 more rows
ggplot(top_100, aes(x = avg_age, y = total)) +
  geom_point(aes(color = ChargeDescription), size = 5) + 
  geom_smooth(method = "lm", color = "black", se = TRUE) + 
  
  
  theme_minimal() +
  labs(title = "Correlation: Avg Criminal Age vs. Crime totals", 
       subtitle = "Does a younger criminal demographic correlate with higher count?", caption = "https://data.baltimorecity.gov/", 
       x = "Average Age of Arrested Individuals", y = "Crimes total count") +
  theme(plot.title = element_text(hjust = 0.5, size = 16), plot.subtitle = element_text(hjust = 0.5), legend.position = "none")
`geom_smooth()` using formula = 'y ~ x'

balt_model2 <- lm(total ~ avg_age, data = top_100)
summary(balt_model2)

Call:
lm(formula = total ~ avg_age, data = top_100)

Residuals:
   Min     1Q Median     3Q    Max 
 -3165  -2586  -2294  -1076  44631 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)
(Intercept)    923.7     6588.4   0.140    0.889
avg_age         68.2      197.9   0.345    0.731

Residual standard error: 7334 on 98 degrees of freedom
Multiple R-squared:  0.001211,  Adjusted R-squared:  -0.008981 
F-statistic: 0.1188 on 1 and 98 DF,  p-value: 0.7311

1. Is there a correlation between crime counts and the offenders’ age by district?

Equation: Crime_Count = 23737.56 + 36.43*(average age)

P-value = 0.9852

Adjusted R Squared = -0.1428

From the values above, it is clear that age is not a factor in determining the number of crimes across District.

2. Is there a correlation between crime counts and the offenders’ age by type of crime?

Equation: Crime_Count = 923.7 + 68.2*(average age)

P-value = 0.7311

Adjusted R Squared = -0.008981

From the values above, it is clear that age is not a factor in determining the number of crimes across the kind of crimes.