This project analyzes the crime data from the City of Chicago for May 26 and 27, 2024 to understand patterns and trends in criminal activity. I chose this topic because I visited Chicago last year and was warned about the high crime rate there, so, I decided to analyze the crime data to find out the facts myself. As a tourist, I want to find out:
For this analysis, we use a dataset provided by the City of Chicago which includes records of reported crimes. The dataset contains information such as the type of crime, the date, and the location.
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.1 ✔ tibble 3.2.1
## ✔ lubridate 1.9.3 ✔ tidyr 1.3.1
## ✔ purrr 1.0.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
dataset_url <- "https://data.cityofchicago.org/resource/ijzp-q8t2.csv"
crime_data <- read.csv(dataset_url, check.names = FALSE, stringsAsFactors = FALSE, header = TRUE)
#Displaying the data
head(crime_data)
## id case_number date block iucr
## 1 13476551 JH283367 2024-05-29T00:00:00.000 035XX N PINE GROVE AVE 0810
## 2 13477420 JH284435 2024-05-29T00:00:00.000 061XX S PRAIRIE AVE 0420
## 3 13476295 JH282996 2024-05-29T00:00:00.000 088XX S ADA ST 0486
## 4 13476461 JH283196 2024-05-29T00:00:00.000 053XX N CUMBERLAND AVE 0560
## 5 13483428 JH291862 2024-05-29T00:00:00.000 015XX W 81ST ST 1220
## 6 13477931 JH285093 2024-05-29T00:00:00.000 042XX N WOLCOTT AVE 0820
## primary_type description
## 1 THEFT OVER $500
## 2 BATTERY AGGRAVATED - KNIFE / CUTTING INSTRUMENT
## 3 BATTERY DOMESTIC BATTERY SIMPLE
## 4 ASSAULT SIMPLE
## 5 DECEPTIVE PRACTICE THEFT OF LOST / MISLAID PROPERTY
## 6 THEFT $500 AND UNDER
## location_description arrest domestic beat district ward
## 1 STREET false false 1925 19 46
## 2 APARTMENT true true 311 3 20
## 3 RESIDENCE false true 2222 22 21
## 4 STREET false false 1614 16 41
## 5 APARTMENT false false 612 6 17
## 6 RESIDENCE - YARD (FRONT / BACK) false false 1912 19 47
## community_area fbi_code x_coordinate y_coordinate year
## 1 6 06 1171599 1923832 2024
## 2 40 04B 1179093 1864377 2024
## 3 71 08B 1168875 1846163 2024
## 4 76 08A 1119314 1934514 2024
## 5 71 11 NA NA 2024
## 6 5 06 1163062 1928197 2024
## updated_on latitude longitude
## 1 2024-06-05T15:41:03.000 41.94646 -87.64465
## 2 2024-06-05T15:41:03.000 41.78314 -87.61893
## 3 2024-06-05T15:41:03.000 41.73339 -87.65692
## 4 2024-06-05T15:41:03.000 41.97676 -87.83661
## 5 2024-06-05T15:41:03.000 NA NA
## 6 2024-06-05T15:41:03.000 41.95862 -87.67591
## location
## 1 \n, \n(41.946458557, -87.64465454)
## 2 \n, \n(41.783142005, -87.618927866)
## 3 \n, \n(41.733387234, -87.656915358)
## 4 \n, \n(41.976763216, -87.836613764)
## 5
## 6 \n, \n(41.958620269, -87.675910698)
We will use several libraries for data manipulation, visualization, and analysis, including tidyverse for data manipulation and ggplot2 for visualization.
library(tidyverse)
library(ggplot2)
The dataset includes fields such as the date of the crime, the primary type of crime, the description, and the location.
# Summary statistics of the dataset
summary(crime_data)
## id case_number date block
## Min. : 28141 Length:1000 Length:1000 Length:1000
## 1st Qu.:13475193 Class :character Class :character Class :character
## Median :13475804 Mode :character Mode :character Mode :character
## Mean :13422351
## 3rd Qu.:13476384
## Max. :13484068
##
## iucr primary_type description location_description
## Length:1000 Length:1000 Length:1000 Length:1000
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## arrest domestic beat district
## Length:1000 Length:1000 Min. : 111.0 Min. : 1.00
## Class :character Class :character 1st Qu.: 529.2 1st Qu.: 5.00
## Mode :character Mode :character Median :1113.0 Median :11.00
## Mean :1162.3 Mean :11.39
## 3rd Qu.:1723.0 3rd Qu.:17.00
## Max. :2535.0 Max. :25.00
##
## ward community_area fbi_code x_coordinate
## Min. : 1.00 Min. : 1.00 Length:1000 Min. :1097306
## 1st Qu.: 9.00 1st Qu.:22.00 Class :character 1st Qu.:1154308
## Median :22.00 Median :32.00 Mode :character Median :1167592
## Mean :22.81 Mean :36.39 Mean :1165716
## 3rd Qu.:34.00 3rd Qu.:53.00 3rd Qu.:1176794
## Max. :50.00 Max. :77.00 Max. :1204787
## NA's :11
## y_coordinate year updated_on latitude
## Min. :1816604 Min. :2024 Length:1000 Min. :41.65
## 1st Qu.:1859197 1st Qu.:2024 Class :character 1st Qu.:41.77
## Median :1893721 Median :2024 Mode :character Median :41.86
## Mean :1886939 Mean :2024 Mean :41.85
## 3rd Qu.:1909988 3rd Qu.:2024 3rd Qu.:41.91
## Max. :1950563 Max. :2024 Max. :42.02
## NA's :11 NA's :11
## longitude location
## Min. :-87.92 Length:1000
## 1st Qu.:-87.71 Class :character
## Median :-87.66 Mode :character
## Mean :-87.67
## 3rd Qu.:-87.63
## Max. :-87.53
## NA's :11
# Verifying correct column names as data set
colnames(crime_data)
## [1] "id" "case_number" "date"
## [4] "block" "iucr" "primary_type"
## [7] "description" "location_description" "arrest"
## [10] "domestic" "beat" "district"
## [13] "ward" "community_area" "fbi_code"
## [16] "x_coordinate" "y_coordinate" "year"
## [19] "updated_on" "latitude" "longitude"
## [22] "location"
# Verifying rows
head(crime_data)
## id case_number date block iucr
## 1 13476551 JH283367 2024-05-29T00:00:00.000 035XX N PINE GROVE AVE 0810
## 2 13477420 JH284435 2024-05-29T00:00:00.000 061XX S PRAIRIE AVE 0420
## 3 13476295 JH282996 2024-05-29T00:00:00.000 088XX S ADA ST 0486
## 4 13476461 JH283196 2024-05-29T00:00:00.000 053XX N CUMBERLAND AVE 0560
## 5 13483428 JH291862 2024-05-29T00:00:00.000 015XX W 81ST ST 1220
## 6 13477931 JH285093 2024-05-29T00:00:00.000 042XX N WOLCOTT AVE 0820
## primary_type description
## 1 THEFT OVER $500
## 2 BATTERY AGGRAVATED - KNIFE / CUTTING INSTRUMENT
## 3 BATTERY DOMESTIC BATTERY SIMPLE
## 4 ASSAULT SIMPLE
## 5 DECEPTIVE PRACTICE THEFT OF LOST / MISLAID PROPERTY
## 6 THEFT $500 AND UNDER
## location_description arrest domestic beat district ward
## 1 STREET false false 1925 19 46
## 2 APARTMENT true true 311 3 20
## 3 RESIDENCE false true 2222 22 21
## 4 STREET false false 1614 16 41
## 5 APARTMENT false false 612 6 17
## 6 RESIDENCE - YARD (FRONT / BACK) false false 1912 19 47
## community_area fbi_code x_coordinate y_coordinate year
## 1 6 06 1171599 1923832 2024
## 2 40 04B 1179093 1864377 2024
## 3 71 08B 1168875 1846163 2024
## 4 76 08A 1119314 1934514 2024
## 5 71 11 NA NA 2024
## 6 5 06 1163062 1928197 2024
## updated_on latitude longitude
## 1 2024-06-05T15:41:03.000 41.94646 -87.64465
## 2 2024-06-05T15:41:03.000 41.78314 -87.61893
## 3 2024-06-05T15:41:03.000 41.73339 -87.65692
## 4 2024-06-05T15:41:03.000 41.97676 -87.83661
## 5 2024-06-05T15:41:03.000 NA NA
## 6 2024-06-05T15:41:03.000 41.95862 -87.67591
## location
## 1 \n, \n(41.946458557, -87.64465454)
## 2 \n, \n(41.783142005, -87.618927866)
## 3 \n, \n(41.733387234, -87.656915358)
## 4 \n, \n(41.976763216, -87.836613764)
## 5
## 6 \n, \n(41.958620269, -87.675910698)
# summary of data set by providing dimensions
str(crime_data)
## 'data.frame': 1000 obs. of 22 variables:
## $ id : int 13476551 13477420 13476295 13476461 13483428 13477931 13477597 13476780 13477155 13478047 ...
## $ case_number : chr "JH283367" "JH284435" "JH282996" "JH283196" ...
## $ date : chr "2024-05-29T00:00:00.000" "2024-05-29T00:00:00.000" "2024-05-29T00:00:00.000" "2024-05-29T00:00:00.000" ...
## $ block : chr "035XX N PINE GROVE AVE" "061XX S PRAIRIE AVE" "088XX S ADA ST" "053XX N CUMBERLAND AVE" ...
## $ iucr : chr "0810" "0420" "0486" "0560" ...
## $ primary_type : chr "THEFT" "BATTERY" "BATTERY" "ASSAULT" ...
## $ description : chr "OVER $500" "AGGRAVATED - KNIFE / CUTTING INSTRUMENT" "DOMESTIC BATTERY SIMPLE" "SIMPLE" ...
## $ location_description: chr "STREET" "APARTMENT" "RESIDENCE" "STREET" ...
## $ arrest : chr "false" "true" "false" "false" ...
## $ domestic : chr "false" "true" "true" "false" ...
## $ beat : int 1925 311 2222 1614 612 1912 1135 313 122 2512 ...
## $ district : int 19 3 22 16 6 19 11 3 1 25 ...
## $ ward : int 46 20 21 41 17 47 28 20 42 36 ...
## $ community_area : int 6 40 71 76 71 5 29 42 32 19 ...
## $ fbi_code : chr "06" "04B" "08B" "08A" ...
## $ x_coordinate : int 1171599 1179093 1168875 1119314 NA 1163062 1157861 1181638 1173971 1135571 ...
## $ y_coordinate : int 1923832 1864377 1846163 1934514 NA 1928197 1895062 1863877 1899133 1914247 ...
## $ year : int 2024 2024 2024 2024 2024 2024 2024 2024 2024 2024 ...
## $ updated_on : chr "2024-06-05T15:41:03.000" "2024-06-05T15:41:03.000" "2024-06-05T15:41:03.000" "2024-06-05T15:41:03.000" ...
## $ latitude : num 41.9 41.8 41.7 42 NA ...
## $ longitude : num -87.6 -87.6 -87.7 -87.8 NA ...
## $ location : chr "\n, \n(41.946458557, -87.64465454)" "\n, \n(41.783142005, -87.618927866)" "\n, \n(41.733387234, -87.656915358)" "\n, \n(41.976763216, -87.836613764)" ...
We handle missing values by replacing them with “Unknown” where appropriate. The date column is converted to a suitable format for analysis.
# Converting the date column into the proper date format
crime_data$date <- as.POSIXct(crime_data$date, format="%Y-%m-%dT%H:%M:%OS")
colSums(is.na(crime_data))
## id case_number date
## 0 0 0
## block iucr primary_type
## 0 0 0
## description location_description arrest
## 0 0 0
## domestic beat district
## 0 0 0
## ward community_area fbi_code
## 0 0 0
## x_coordinate y_coordinate year
## 11 11 0
## updated_on latitude longitude
## 0 11 11
## location
## 0
crime_data <- crime_data %>%
mutate(across(everything(), ~ifelse(is.na(.), "Unknown", .)))
crime_data <- crime_data %>%
filter(!is.na(date))
# cleaned data
head(crime_data)
## id case_number date block iucr
## 1 13476551 JH283367 1716966000 035XX N PINE GROVE AVE 0810
## 2 13477420 JH284435 1716966000 061XX S PRAIRIE AVE 0420
## 3 13476295 JH282996 1716966000 088XX S ADA ST 0486
## 4 13476461 JH283196 1716966000 053XX N CUMBERLAND AVE 0560
## 5 13483428 JH291862 1716966000 015XX W 81ST ST 1220
## 6 13477931 JH285093 1716966000 042XX N WOLCOTT AVE 0820
## primary_type description
## 1 THEFT OVER $500
## 2 BATTERY AGGRAVATED - KNIFE / CUTTING INSTRUMENT
## 3 BATTERY DOMESTIC BATTERY SIMPLE
## 4 ASSAULT SIMPLE
## 5 DECEPTIVE PRACTICE THEFT OF LOST / MISLAID PROPERTY
## 6 THEFT $500 AND UNDER
## location_description arrest domestic beat district ward
## 1 STREET false false 1925 19 46
## 2 APARTMENT true true 311 3 20
## 3 RESIDENCE false true 2222 22 21
## 4 STREET false false 1614 16 41
## 5 APARTMENT false false 612 6 17
## 6 RESIDENCE - YARD (FRONT / BACK) false false 1912 19 47
## community_area fbi_code x_coordinate y_coordinate year
## 1 6 06 1171599 1923832 2024
## 2 40 04B 1179093 1864377 2024
## 3 71 08B 1168875 1846163 2024
## 4 76 08A 1119314 1934514 2024
## 5 71 11 Unknown Unknown 2024
## 6 5 06 1163062 1928197 2024
## updated_on latitude longitude
## 1 2024-06-05T15:41:03.000 41.946458557 -87.64465454
## 2 2024-06-05T15:41:03.000 41.783142005 -87.618927866
## 3 2024-06-05T15:41:03.000 41.733387234 -87.656915358
## 4 2024-06-05T15:41:03.000 41.976763216 -87.836613764
## 5 2024-06-05T15:41:03.000 Unknown Unknown
## 6 2024-06-05T15:41:03.000 41.958620269 -87.675910698
## location
## 1 \n, \n(41.946458557, -87.64465454)
## 2 \n, \n(41.783142005, -87.618927866)
## 3 \n, \n(41.733387234, -87.656915358)
## 4 \n, \n(41.976763216, -87.836613764)
## 5
## 6 \n, \n(41.958620269, -87.675910698)
crime_data$date <- as.POSIXct(crime_data$date, format="%Y-%m-%d %H:%M:%S")
crime_data <- crime_data %>%
mutate(date_hour = format(date, "%Y-%m-%d %H:00:00"))
hourly_counts <- crime_data %>%
count(date_hour) %>%
mutate(date_hour = as.POSIXct(date_hour, format="%Y-%m-%d %H:%M:%S"))
hourly_counts <- hourly_counts %>%
mutate(day = format(date_hour, "%Y-%m-%d"))
# Plotting graph
ggplot(hourly_counts, aes(x = date_hour, y = n, fill = day)) +
geom_bar(stat = "identity", color = "black") +
geom_text(aes(label = format(date_hour, "%I %p")), vjust = -0.5, size = 2.5) +
theme_minimal() +
labs(title = "Crimes Over Time by Hour", x = "Date and Hour", y = "Count of Crimes") +
theme(axis.text.x = element_blank(), axis.ticks.x = element_blank()) +
scale_fill_manual(values = c("2024-05-27" = "#FFB6C1", "2024-05-28" = "#ADD8E6", "2024-05-29" = "#98FB98")) +
guides(fill = guide_legend(title = "Day"))
The crimes are generally high throughout the day except in the night from 1 AM to 9 AM.
We conduct a frequency analysis to identify the most common types of crimes.
# Frequency of crime types
crime_type_freq <- crime_data %>%
group_by(primary_type) %>%
summarize(Frequency = n()) %>%
arrange(desc(Frequency))
# Displaying the top crime types
head(crime_type_freq, 10)
## # A tibble: 10 × 2
## primary_type Frequency
## <chr> <int>
## 1 BATTERY 192
## 2 THEFT 190
## 3 ASSAULT 128
## 4 CRIMINAL DAMAGE 111
## 5 OTHER OFFENSE 76
## 6 MOTOR VEHICLE THEFT 71
## 7 BURGLARY 46
## 8 WEAPONS VIOLATION 37
## 9 DECEPTIVE PRACTICE 35
## 10 ROBBERY 30
We visualize the distribution of different types of crimes using a plot.
# Plotting graph
ggplot(crime_data, aes(x = primary_type)) +
geom_bar(fill = "blue", color = "black") +
theme_minimal() +
labs(title = "Crimes by Type", x = "Type of Crime", y = "Count") +
theme(axis.text.x = element_text(angle = 90, hjust = 1))
The following crimes are more frequent (Evident from the statistical analysis and graph): 1. Theft 2. Battery 3. Assault 4. Criminal Damage 5. Motor Vehicle Theft
Although stalking is also a concern for tourists, but the number of reported incidents are low.
We explore the distribution of crimes by location description.
ggplot(crime_data, aes(x = location_description)) +
geom_bar(fill = "green", color = "black") +
theme_minimal() +
labs(title = "Crimes by Location", x = "Location Description", y = "Count") +
theme(axis.text.x = element_text(angle = 90, hjust = 1))
Most number of crimes take place in the following areas: 1) Apartment 2) Street 3) Parking lot/ Garage (non residential) 4) Residence 5) Sidewalk 6) Small Retail Store
# Filtering the data to include only Theft, Assault, and Battery
filtered_crime_data <- crime_data %>%
filter(primary_type %in% c("THEFT", "ASSAULT", "BATTERY"))
location_counts <- filtered_crime_data %>%
count(location_description) %>%
filter(n > 0) %>%
filter(location_description %in% c("STREET", "PARKING LOT / GARAGE (NON RESIDENTIAL)", "SIDEWALK", "SMALL RETAIL STORE"))
# Plotting
ggplot(location_counts, aes(x = reorder(location_description, n), y = n)) +
geom_bar(stat = "identity", fill = "green", color = "black") +
theme_minimal() +
labs(title = "Crimes by Location (Theft, Assault, Battery)", x = "Location Description", y = "Count") +
theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 6))
coord_flip()
## <ggproto object: Class CoordFlip, CoordCartesian, Coord, gg>
## aspect: function
## backtransform_range: function
## clip: on
## default: FALSE
## distance: function
## expand: TRUE
## is_free: function
## is_linear: function
## labels: function
## limits: list
## modify_scales: function
## range: function
## render_axis_h: function
## render_axis_v: function
## render_bg: function
## render_fg: function
## setup_data: function
## setup_layout: function
## setup_panel_guides: function
## setup_panel_params: function
## setup_params: function
## train_panel_guides: function
## transform: function
## super: <ggproto object: Class CoordFlip, CoordCartesian, Coord, gg>
The tourists have to stay vigilant at all these four locations.
crime_data$date <- as.POSIXct(crime_data$date, format="%Y-%m-%d %H:%M:%S")
# Filtering data for Theft, Assault, and Battery
filtered_crime_data <- crime_data %>%
filter(primary_type %in% c("THEFT", "ASSAULT", "BATTERY")) %>%
mutate(date_hour = format(date, "%Y-%m-%d %H:00:00"))
hourly_counts <- filtered_crime_data %>%
count(date_hour) %>%
mutate(date_hour = as.POSIXct(date_hour, format="%Y-%m-%d %H:%M:%S"))
hourly_counts <- hourly_counts %>%
mutate(day = format(date_hour, "%Y-%m-%d"))
# Plotting the graph
ggplot(hourly_counts, aes(x = date_hour, y = n, fill = day)) +
geom_bar(stat = "identity", color = "black") +
scale_x_datetime(date_breaks= "1 hour", date_labels = "%I %p") + # Format x-axis to show hour
theme_minimal() +
labs(title = "Crimes Over Time by Hour for Theft, Assault, and Battery", x = "Hour", y = "Count of Crimes") +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) + # Rotate x-axis labels
scale_fill_manual(values = c("2024-05-27" = "pink", "2024-05-28" = "blue", "2024-05-29" = "purple")) +
guides(fill = guide_legend(title = "Day"))
These three crimes are generally high throughout the day except in the night from 1 AM to 7 AM. It is therefore important to stay vigilant when outside at all time.
# Calculate the counts of crimes by district
district_counts <- crime_data %>%
count(district) %>%
filter(n > 0)
# Create the plot
ggplot(district_counts, aes(x = reorder(as.factor(district), -n), y = n)) +
geom_bar(stat = "identity", fill = "blue", color = "black") +
theme_minimal() +
labs(title = "Number of Crimes by District", x = "District", y = "Count of Crimes") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
District 16 and 20 are safe relative to others.
# Convert date column to POSIXct if it's not already
crime_data$date <- as.POSIXct(crime_data$date, format="%Y-%m-%d %H:%M:%S")
# Filtering data for district 20
district_20_data <- crime_data %>%
filter(district == 20)
# Aggregate the counts of crimes by community area for Theft, Assault, and Battery
community_area_counts <- district_20_data %>%
filter(primary_type %in% c("THEFT", "ASSAULT", "BATTERY")) %>%
count(community_area)
# Create a complete sequence of community areas
all_community_areas <- unique(district_20_data$community_area)
# Ensure all community areas are represented
community_area_counts <- community_area_counts %>%
complete(community_area = all_community_areas, fill = list(n = 0))
# Plotting the graph
ggplot(community_area_counts, aes(x = as.factor(community_area), y = n)) +
geom_bar(stat = "identity", fill = "lightblue", color = "black") +
geom_text(aes(label = n), vjust = -0.5, color = "black") +
theme_minimal() +
labs(title = "Number of Crimes by Community Area in District 20",
x = "Community Area", y = "Count of Crimes") +
theme(axis.text.x = element_text(angle = 90, hjust = 1))
The safest place for tourists to stay in Chicago is Community Area 2 in District 20 where no crime of battery, theft and assault are reported on the given dates.
What is the nature of most common crimes that tourists should be concerned about? Conclusion: The most frequent types of crimes concerning the tourists are theft, battery, and assault.
What are the places where these crimes are mostly committed? Conclusion: These crimes are mostly committed on the streets, sidewalks, small retail stores, and non-residential parking lots. All these areas are generally full of tourists.
When are these crimes committed during the day? Is there a pattern to guide the tourists? Conclusion: These crimes are being committed throughout the day except in the night (1 AM - 7 AM). The tourists therefore must stay vigilant when outdoors.
Which is the safest community area to stay in Chicago? Conclusion: The safest place for tourists to stay in Chicago is Community Area 2 in District 20 where no crime of battery, theft and assault are reported on the given dates.
Which districts have high crime rate that tourists have to be aware about? Conclusion: The top 5 districts with highest reported crimes are: 3, 12, 11, 6, and 8.
In conclusion, our analysis of Chicago’s crime data revealed significant insights into the types and locations of crimes, as well as their temporal patterns. These findings can inform law enforcement strategies, policy-making, and community initiatives aimed at reducing crime and enhancing public safety. By understanding where and when crimes are most likely to occur, stakeholders can develop more effective approaches to crime prevention and resource allocation, ultimately contributing to a safer Chicago.