Haleluya Tesfamariam - Hate Crimes Data (Week 3 Homework)

Load Data

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(knitr)
library(ggplot2)
setwd("C:/Users/Selomie/Downloads")
setwd("C:/Users/Selomie/Downloads/")
nypop <- read_csv("nyc_census_pop_2020.csv")
Rows: 62 Columns: 4
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (2): Area Name, Population Percent Change
num (2): 2020 Census Population, Population Change

ℹ 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.
hatecrimes <- read_csv("NYPD_Hate_Crimes_19-26.csv")
Rows: 4029 Columns: 14
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (9): Record Create Date, Patrol Borough Name, County, Law Code Category ...
dbl (4): Full Complaint ID, Complaint Year Number, Month Number, Complaint P...
lgl (1): Arrest Date

ℹ 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.

Clean Up the Data

names(hatecrimes) <- tolower(names(hatecrimes))
names(hatecrimes) <- gsub(" ","",names(hatecrimes)) #gsub turns "<space>" to <> for the variable row "names"
head(hatecrimes)
# A tibble: 6 × 14
  fullcomplaintid complaintyearnumber monthnumber recordcreatedate
            <dbl>               <dbl>       <dbl> <chr>           
1         2.02e14                2019           1 1/23/2019       
2         2.02e14                2019           2 2/25/2019       
3         2.02e14                2019           2 2/27/2019       
4         2.02e14                2019           4 4/16/2019       
5         2.02e14                2019           6 6/20/2019       
6         2.02e14                2019           7 7/31/2019       
# ℹ 10 more variables: complaintprecinctcode <dbl>, patrolboroughname <chr>,
#   county <chr>, lawcodecategorydescription <chr>, offensedescription <chr>,
#   pdcodedescription <chr>, biasmotivedescription <chr>,
#   offensecategory <chr>, arrestdate <lgl>, arrestid <chr>

Bias Motive

bias_count <- hatecrimes |>
  select(biasmotivedescription) |> #selecting the column
  group_by(biasmotivedescription) |> #making bias the variable of interest
  count() |>
  arrange(desc(n)) #sorts the counts by highest to lowest
head(bias_count) #gives us the 6 row summary
# A tibble: 6 × 2
# Groups:   biasmotivedescription [6]
  biasmotivedescription          n
  <chr>                      <int>
1 ANTI-JEWISH                 1906
2 ANTI-MALE HOMOSEXUAL (GAY)   489
3 ANTI-ASIAN                   401
4 ANTI-BLACK                   315
5 ANTI-OTHER ETHNICITY         168
6 ANTI-MUSLIM                  156

Visualization

ggplot(hatecrimes, aes(x = biasmotivedescription))+
  geom_bar()

Filtering for Top 10 Target Groups

bias_count |>
  head(10) |>
  ggplot(aes(x=biasmotivedescription, y = n)) +
  geom_col()

Arrange by Height & Rotate

bias_count |>
  head(10) |>
  ggplot(aes(x=reorder(biasmotivedescription, n), y = n)) +
  geom_col() +
  coord_flip()

Cosmetic Edits: Title, Captions, Color, etc.

bias_count |> #calls data
  head(10) |> #excludes non top 10
  ggplot(aes(x=reorder(biasmotivedescription, n),y=n)) + #flips graph
  geom_col(fill = "blue") + #fills bar with blue
  coord_flip()+ #arranges text below to appear flipped
  labs(x = "",
       y = "Counts of Hate Crime Types By Motive",
       title = "Bar Graph of Hate Crimes from 2019-2026",
       subtitle ="Counts based on the hatecrime motive",
       caption = "Source: NY State Division of Criminal Justice Services")

theme_minimal
function (base_size = 11, base_family = "", header_family = NULL, 
    base_line_size = base_size/22, base_rect_size = base_size/22, 
    ink = "black", paper = "white", accent = "#3366FF") 
{
    theme_bw(base_size = base_size, base_family = base_family, 
        header_family = header_family, base_line_size = base_line_size, 
        base_rect_size = base_rect_size, ink = ink, paper = paper, 
        accent = accent) %+replace% theme(axis.ticks = element_blank(), 
        axis.text.x.bottom = element_text(margin = margin(t = 0.45 * 
            base_size)), axis.text.x.top = element_text(margin = margin(b = 0.45 * 
            base_size)), axis.text.y.left = element_text(margin = margin(r = 0.45 * 
            base_size)), axis.text.y.right = element_text(margin = margin(l = 0.45 * 
            base_size)), legend.background = element_blank(), 
        legend.key = element_blank(), panel.background = element_blank(), 
        panel.border = element_blank(), strip.background = element_blank(), 
        plot.background = element_rect(fill = paper, colour = NA), 
        complete = TRUE)
}
<bytecode: 0x0000026b0df71548>
<environment: namespace:ggplot2>

Filtering Data For Select Groups by County

hate_county <- hatecrimes |>
  filter(biasmotivedescription %in% c("ANTI-JEWISH", "ANTI-MALE HOMOSEXUAL (GAY)", "ANTI-ASIAN", "ANTI-BLACK"))|>
  group_by(county) |>
  count(biasmotivedescription)|>
  arrange(desc(n))
hate_county
# A tibble: 20 × 3
# Groups:   county [5]
   county   biasmotivedescription          n
   <chr>    <chr>                      <int>
 1 KINGS    ANTI-JEWISH                  798
 2 NEW YORK ANTI-JEWISH                  651
 3 QUEENS   ANTI-JEWISH                  289
 4 NEW YORK ANTI-MALE HOMOSEXUAL (GAY)   237
 5 NEW YORK ANTI-ASIAN                   228
 6 KINGS    ANTI-MALE HOMOSEXUAL (GAY)   120
 7 KINGS    ANTI-BLACK                    99
 8 BRONX    ANTI-JEWISH                   92
 9 QUEENS   ANTI-MALE HOMOSEXUAL (GAY)    91
10 KINGS    ANTI-ASIAN                    80
11 NEW YORK ANTI-BLACK                    79
12 QUEENS   ANTI-ASIAN                    78
13 RICHMOND ANTI-JEWISH                   76
14 QUEENS   ANTI-BLACK                    75
15 BRONX    ANTI-MALE HOMOSEXUAL (GAY)    35
16 RICHMOND ANTI-BLACK                    35
17 BRONX    ANTI-BLACK                    27
18 BRONX    ANTI-ASIAN                    10
19 RICHMOND ANTI-MALE HOMOSEXUAL (GAY)     6
20 RICHMOND ANTI-ASIAN                     5

Combining Totals from Counties and Years

hate2 <- hatecrimes |>
  filter(biasmotivedescription %in% c("ANTI-JEWISH", "ANTI-MALE HOMOSEXUAL (GAY)", "ANTI-ASIAN", "ANTI-BLACK"))|>
  group_by(complaintyearnumber, county) |>
  count(biasmotivedescription)|>
  arrange(desc(n))
hate2
# A tibble: 127 × 4
# Groups:   complaintyearnumber, county [35]
   complaintyearnumber county   biasmotivedescription     n
                 <dbl> <chr>    <chr>                 <int>
 1                2024 KINGS    ANTI-JEWISH             152
 2                2024 NEW YORK ANTI-JEWISH             136
 3                2025 KINGS    ANTI-JEWISH             136
 4                2019 KINGS    ANTI-JEWISH             128
 5                2023 KINGS    ANTI-JEWISH             126
 6                2022 KINGS    ANTI-JEWISH             125
 7                2023 NEW YORK ANTI-JEWISH             124
 8                2025 NEW YORK ANTI-JEWISH             110
 9                2022 NEW YORK ANTI-JEWISH             104
10                2021 NEW YORK ANTI-ASIAN               84
# ℹ 117 more rows

Select Group Crimes Plotted Together

#dodge puts the graphs next to one another by year since our grouping is done by the variable "complaintyearnumber"
ggplot(data = hate2)+
  geom_bar(aes(x=complaintyearnumber, y=n, fill = biasmotivedescription),
           position = "dodge", stat = "identity") +
  labs(fill = "Hate Crime Type",
       y = "Number of Hate Crime Incidents",
       title = "Hate Crime Type in NY Counties Between 2010-16",
       caption = "Source: NY State Division of Criminal Justice Services")

Select Group Hate Crime Count by County

ggplot(data = hate2)+
  geom_bar(aes(x=county, y=n, fill = biasmotivedescription),
           position = "dodge", stat = "identity") +
  labs(fill = "Hate Crime Type",
       y = "Number of Hate Crime Incidents",
       title = "Hate Crime Type in NY Counties Between 2010-16",
       caption = "Source: NY State Division of Criminal Justice Services")

County Segregated Visualization

ggplot(data = hate2) +
  geom_bar(aes(x=complaintyearnumber, y=n, fill=biasmotivedescription),
           position= "dodge", stat = "identity") +
  facet_wrap(~county) + #Splits data by county
  labs(fill = "Hate Crime Type",
       y = "Number of Hate Crime Incidents",
       title = "Hate Crime Type in NY Counties Between 2010-2016",
       caption = "Source: NY State Division of Criminal Justice Services")

Check Correlation w/Population Density

nypop$`Area Name` <-gsub(" County", "", nypop$`Area Name`)
nypop2 <- nypop |>
  rename(county = `Area Name`)|>
  select(county, `2020 Census Population`)
head(nypop2)
# A tibble: 6 × 2
  county      `2020 Census Population`
  <chr>                          <dbl>
1 Albany                        314848
2 Allegany                       46456
3 Bronx                        1472654
4 Broome                        198683
5 Cattaraugus                    77042
6 Cayuga                         76248

Join Rate Calculations (per 100k)

datajoin <- left_join(hate2, nypop2, by=c("county"))
datajoin
# A tibble: 127 × 5
# Groups:   complaintyearnumber, county [35]
   complaintyearnumber county biasmotivedescription     n 2020 Census Populati…¹
                 <dbl> <chr>  <chr>                 <int>                  <dbl>
 1                2024 KINGS  ANTI-JEWISH             152                     NA
 2                2024 NEW Y… ANTI-JEWISH             136                     NA
 3                2025 KINGS  ANTI-JEWISH             136                     NA
 4                2019 KINGS  ANTI-JEWISH             128                     NA
 5                2023 KINGS  ANTI-JEWISH             126                     NA
 6                2022 KINGS  ANTI-JEWISH             125                     NA
 7                2023 NEW Y… ANTI-JEWISH             124                     NA
 8                2025 NEW Y… ANTI-JEWISH             110                     NA
 9                2022 NEW Y… ANTI-JEWISH             104                     NA
10                2021 NEW Y… ANTI-ASIAN               84                     NA
# ℹ 117 more rows
# ℹ abbreviated name: ¹​`2020 Census Population`
hate_new <- hate2 |>
  mutate(county = as_factor(str_to_lower(as.character(county))))
nypop_new <- nypop2 |>
  mutate(county = as_factor(str_to_lower(as.character(county))))
head(hate_new)
# A tibble: 6 × 4
# Groups:   complaintyearnumber, county [6]
  complaintyearnumber county   biasmotivedescription     n
                <dbl> <fct>    <chr>                 <int>
1                2024 kings    ANTI-JEWISH             152
2                2024 new york ANTI-JEWISH             136
3                2025 kings    ANTI-JEWISH             136
4                2019 kings    ANTI-JEWISH             128
5                2023 kings    ANTI-JEWISH             126
6                2022 kings    ANTI-JEWISH             125
datajoinrate <- datajoin |>
  mutate(rate = n/`2020 Census Population`* 100000) |>
  arrange(desc(rate))
datajoinrate
# A tibble: 127 × 6
# Groups:   complaintyearnumber, county [35]
   complaintyearnumber county biasmotivedescription     n 2020 Census Populati…¹
                 <dbl> <chr>  <chr>                 <int>                  <dbl>
 1                2024 KINGS  ANTI-JEWISH             152                     NA
 2                2024 NEW Y… ANTI-JEWISH             136                     NA
 3                2025 KINGS  ANTI-JEWISH             136                     NA
 4                2019 KINGS  ANTI-JEWISH             128                     NA
 5                2023 KINGS  ANTI-JEWISH             126                     NA
 6                2022 KINGS  ANTI-JEWISH             125                     NA
 7                2023 NEW Y… ANTI-JEWISH             124                     NA
 8                2025 NEW Y… ANTI-JEWISH             110                     NA
 9                2022 NEW Y… ANTI-JEWISH             104                     NA
10                2021 NEW Y… ANTI-ASIAN               84                     NA
# ℹ 117 more rows
# ℹ abbreviated name: ¹​`2020 Census Population`
# ℹ 1 more variable: rate <dbl>

Questions

  1. Write about the positive and negative aspects of this hatecrimes dataset.

    The dataset is useful because it provides raw data that is incredibly detailed and specific. It includes information about the year, county, type of offense, and bias motive, which makes it possible to look at hate crimes from several different angles. I also like that the data can be grouped by both time and location, allowing patterns to be compared between different counties and years. However, there are some important limitations. The tutorial asks for data between “2010-2016,” while the actual dataset includes entries from 2019-2026. The dataset would be improved if it actually spanned 2010-2026, since having a longer timeframe could make it easier to identify long-term patterns instead of only more recent ones. Another major issue is that hate crimes can go unreported or unrecorded. The tutorial itself points out that the FBI relies on local law enforcement agencies to collect and submit hate crime data, meaning the dataset may not include every hate crime that actually occurred. Because of this, the numbers should not necessarily be treated as a complete representation of hate crimes in New York. Overall, the dataset is detailed and useful, but its limitations should be considered when interpreting the results.

  2. List 2 different paths you could hypothetically like to study about this dataset at some future point.

One path I would be interested in studying is whether individual hate crimes can have multiple bias motives at the same time. For example, I would want to see if a crime could be classified as both anti-Asian and anti-male homosexual. It would be interesting to see which combinations of bias motives occur most often and whether certain combinations are more common in specific counties or years. This could show patterns that would not be visible if every bias motive was studied separately.

A second path I would like to study is the rate of hate crimes compared to the population of the group being targeted. The current analysis mostly looks at the number of incidents, but a county with a larger population would naturally have more opportunities for incidents to occur. I would want to use demographic population data to calculate rates for specific groups, such as anti-Asian hate crimes compared to the Asian population or anti-Black hate crimes compared to the Black population. This would make it possible to compare counties more proportionally instead of only comparing their total number of incidents. It could also reveal patterns that are hidden when only looking at raw counts.