Ross Boehme - DATA608 - Story 3

Assignment: Do stricter gun laws reduce firearm gun deaths? The CDC publishes firearm mortality for each state per 100,000 persons. Each state’s firearm control laws can be categorized as very strict to very lax. The purpose of this Story is to answer the question, “Do stricter firearm control laws help reduce firearm mortality?”

Notes: - You may not use the same desktop application that you have used for a previous story. - If you use color in your visuals you must use an accessible color palette.

library(httr)
## Warning: package 'httr' was built under R version 4.2.3
library(jsonlite)
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.4.0     ✔ purrr   1.0.1
## ✔ tibble  3.1.8     ✔ dplyr   1.1.0
## ✔ tidyr   1.3.0     ✔ stringr 1.5.0
## ✔ readr   2.1.4     ✔ forcats 0.5.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter()  masks stats::filter()
## ✖ purrr::flatten() masks jsonlite::flatten()
## ✖ dplyr::lag()     masks stats::lag()
library(plotly)
## Warning: package 'plotly' was built under R version 4.2.3
## 
## Attaching package: 'plotly'
## 
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## 
## The following object is masked from 'package:httr':
## 
##     config
## 
## The following object is masked from 'package:stats':
## 
##     filter
## 
## The following object is masked from 'package:graphics':
## 
##     layout
library(ggplot2)
library(maps)
## Warning: package 'maps' was built under R version 4.2.3
## 
## Attaching package: 'maps'
## 
## The following object is masked from 'package:purrr':
## 
##     map
library(patchwork)
## Warning: package 'patchwork' was built under R version 4.2.3

Retrieving data, confirming it’s a dataframe, and showing the initial structure. Currently contains extra columns, longitudinal data, and deaths unrelated to guns.

df <- fromJSON(content(GET("https://data.cdc.gov/resource/489q-934x.json"), "text"))

df <- as.data.frame(df)

head(df)
##   year_and_quarter                   time_period
## 1          2021 Q1 12 months ending with quarter
## 2          2021 Q1 12 months ending with quarter
## 3          2021 Q1 12 months ending with quarter
## 4          2021 Q1 12 months ending with quarter
## 5          2021 Q1 12 months ending with quarter
## 6          2021 Q1 12 months ending with quarter
##                        cause_of_death    rate_type               unit
## 1                          All causes Age-adjusted Deaths per 100,000
## 2                   Alzheimer disease Age-adjusted Deaths per 100,000
## 3                            COVID-19 Age-adjusted Deaths per 100,000
## 4                              Cancer Age-adjusted Deaths per 100,000
## 5 Chronic liver disease and cirrhosis Age-adjusted Deaths per 100,000
## 6  Chronic lower respiratory diseases Age-adjusted Deaths per 100,000
##   rate_overall rate_sex_female rate_sex_male rate_alaska rate_alabama
## 1        866.3           716.3        1040.4       779.2       1123.4
## 2         32.1            36.8          24.8        28.2         51.2
## 3        120.7              94         153.9        44.4        160.2
## 4          142           122.8         167.7         143        160.5
## 5         13.9             9.8          18.3        23.6         17.2
## 6         33.8            30.9          37.5        28.4         50.4
##   rate_arkansas rate_arizona rate_california rate_colorado rate_connecticut
## 1        1043.1        853.2           767.7         760.9            784.3
## 2          44.1         31.3              41            36             20.6
## 3         130.6        142.1           128.5          83.6            138.9
## 4         160.7        125.5           128.5         123.4            132.1
## 5          15.9         18.2            14.9          18.1             12.7
## 6          57.5         34.5            26.3          36.6             22.5
##   rate_district_of_columbia rate_delaware rate_florida rate_georgia rate_hawaii
## 1                     904.8         866.2        757.3        957.4       585.9
## 2                      11.1          35.4         19.7         45.9        22.4
## 3                     148.6         106.8         80.6        130.5        20.7
## 4                       136         149.1        134.4        145.8       124.1
## 5                         9          10.6         13.4         12.5         8.6
## 6                      18.9            32         31.2         39.5        16.9
##   rate_iowa rate_idaho rate_illinois rate_indiana rate_kansas rate_kentucky
## 1     848.3      789.6         861.5        981.8       901.2        1064.2
## 2      29.7         37          28.3           32        24.2          31.7
## 3     119.9       79.2         121.3        132.8       121.8         109.6
## 4     145.4        135         148.2        160.6       148.1           177
## 5      11.9         16          11.5         14.4        15.4          17.5
## 6      35.9       36.8          31.3         49.5        40.9          52.7
##   rate_louisiana rate_massachusetts rate_maryland rate_maine rate_michigan
## 1         1080.3                768         847.9      785.3         911.2
## 2           45.3               17.8            16       27.6          36.3
## 3          146.7                126           111       36.1         101.7
## 4          158.1              132.8         139.5      157.5         155.5
## 5           11.1               11.5           9.6       14.2          14.5
## 6           38.9               25.2          25.3       36.8          38.5
##   rate_minnesota rate_missouri rate_mississippi rate_montana
## 1          737.8         956.2             1197        836.6
## 2           32.9          33.9             58.3         22.6
## 3           85.1           113            171.5         91.8
## 4          136.4         157.4              172          136
## 5             14          12.9             16.3         21.6
## 6           28.3          43.4             56.7         38.8
##   rate_north_carolina rate_north_dakota rate_nebraska rate_new_hampshire
## 1               893.7             835.5         822.7              744.9
## 2                36.8              37.6            32               25.8
## 3                99.3             132.8         101.3               60.9
## 4                 146             136.7         143.3              143.8
## 5                13.1              17.9            14               12.5
## 6                35.4              32.1          40.2               30.5
##   rate_new_jersey rate_new_mexico rate_nevada rate_new_york rate_ohio
## 1           853.1           952.7       895.2         817.6     983.2
## 2            22.2            25.9        27.5            14      37.5
## 3           172.6           144.1       133.4         174.2     122.2
## 4           129.8             130       143.1         125.7     157.6
## 5             9.7            36.8        16.6           8.3      13.1
## 6            22.8              38        40.6          22.9      40.4
##   rate_oklahoma rate_oregon rate_pennsylvania rate_rhode_island
## 1        1063.5       751.4             895.1             828.9
## 2          36.6        36.4              23.5                32
## 3         157.8        37.1               123             138.4
## 4         167.7       146.9             150.8             141.3
## 5          18.9        16.5              10.4              13.7
## 6          56.3        32.4              30.2              24.1
##   rate_south_carolina rate_south_dakota rate_tennessee rate_texas rate_utah
## 1               981.9             882.7         1056.8        922     771.2
## 2                  40              37.4           42.8       44.9      41.1
## 3               126.6             145.8          122.5      162.3      68.7
## 4               150.5               147          161.9        137     116.2
## 5                16.3                29           16.8       16.4      10.7
## 6                41.9              33.6           47.6       34.3      30.2
##   rate_virginia rate_vermont rate_washington rate_wisconsin rate_west_virginia
## 1         824.8        737.9           714.8          825.8             1096.9
## 2          28.3         34.3              42           31.7               35.2
## 3            92         21.5            46.5           86.1               94.2
## 4         144.3        151.9           139.8          146.9              176.3
## 5          11.8           12              15           12.8               17.4
## 6          29.9           33            27.5           32.4                 56
##   rate_wyoming rate_age_1_4 rate_age_5_14 rate_age_15_24 rate_age_25_34
## 1          854         <NA>          <NA>           <NA>           <NA>
## 2         32.1         <NA>          <NA>           <NA>           <NA>
## 3           84         <NA>          <NA>           <NA>           <NA>
## 4          138         <NA>          <NA>           <NA>           <NA>
## 5         29.1         <NA>          <NA>           <NA>           <NA>
## 6         51.2         <NA>          <NA>           <NA>           <NA>
##   rate_age_35_44 rate_age_45_54 rate_age_55_64 rate_65_74 rate_age_75_84
## 1           <NA>           <NA>           <NA>       <NA>           <NA>
## 2           <NA>           <NA>           <NA>       <NA>           <NA>
## 3           <NA>           <NA>           <NA>       <NA>           <NA>
## 4           <NA>           <NA>           <NA>       <NA>           <NA>
## 5           <NA>           <NA>           <NA>       <NA>           <NA>
## 6           <NA>           <NA>           <NA>       <NA>           <NA>
##   rate_age_85_plus
## 1             <NA>
## 2             <NA>
## 3             <NA>
## 4             <NA>
## 5             <NA>
## 6             <NA>

Initial filtering of dataframe

#Only looking at firearm deaths at yearly cadence; not interested in rate per age in this analysis
#Looking at only most recent full year of data (2022)
firearm <- df %>%
  filter(cause_of_death == "Firearm-related injury" & 
        time_period == "12 months ending with quarter" &
        rate_type == "Crude" &
        year_and_quarter == "2022 Q4")

firearm <- firearm %>%
  select(-starts_with("rate_age"), -rate_overall, -rate_sex_female, -rate_sex_male,
         -rate_65_74, -cause_of_death, -time_period, -rate_type, -unit, -year_and_quarter)

My dataframe now only contains the average firearm related deaths for 2022 per state. To better organize this dataframe (for more efficient data access), I’ll rename the states to their abbreviations and pivot the df longer.

#Create vector to map state names to abbreviations
state.abb <- c(
  "alabama" = "AL", "alaska" = "AK", "arizona" = "AZ", "arkansas" = "AR", "california" = "CA",
  "colorado" = "CO", "connecticut" = "CT", "delaware" = "DE", "district_of_columbia" = "DC",
  "florida" = "FL", "georgia" = "GA", "hawaii" = "HI", "idaho" = "ID", "illinois" = "IL", "indiana" = "IN",
  "iowa" = "IA", "kansas" = "KS", "kentucky" = "KY", "louisiana" = "LA", "maine" = "ME", "maryland" = "MD",
  "massachusetts" = "MA", "michigan" = "MI", "minnesota" = "MN", "mississippi" = "MS", "missouri" = "MO",
  "montana" = "MT", "nebraska" = "NE", "nevada" = "NV", "new_hampshire" = "NH", "new_jersey" = "NJ",
  "new_mexico" = "NM", "new_york" = "NY", "north_carolina" = "NC", "north_dakota" = "ND", "ohio" = "OH",
  "oklahoma" = "OK", "oregon" = "OR", "pennsylvania" = "PA", "rhode_island" = "RI", "south_carolina" = "SC",
  "south_dakota" = "SD", "tennessee" = "TN", "texas" = "TX", "utah" = "UT", "vermont" = "VT",
  "west_virginia" = "WV", "virginia" = "VA", "washington" = "WA", "wisconsin" = "WI", "wyoming" = "WY"
)

#Replace state names with abbreviations in column names
new_colnames <- colnames(firearm)
for (state in names(state.abb)) {
  new_colnames <- gsub(state, state.abb[state], new_colnames, ignore.case = TRUE)
}
colnames(firearm) <- new_colnames

colnames(firearm) <- gsub("^rate_", "", colnames(firearm))

#Pivot longer
firearm <- pivot_longer(firearm, everything(), names_to = "state", values_to = "mortality_rate")

#Add column "year" to specify this is for 2022 data
firearm$year <- "2022"

#Assign double data type to injury_rate column
firearm$mortality_rate <- as.numeric(firearm$mortality_rate)

Showing cleaned firearm injury rate dataframe for 2022 per state (and D.C.)

colnames(firearm) <- c("state_abbr", "mortality_rate", "year")

head(firearm)
## # A tibble: 6 × 3
##   state_abbr mortality_rate year 
##   <chr>               <dbl> <chr>
## 1 AK                   22.4 2022 
## 2 AL                   25.2 2022 
## 3 AR                   21.9 2022 
## 4 AZ                   20.9 2022 
## 5 CA                    8.9 2022 
## 6 CO                   17.7 2022

I couldn’t easily find API-accessible information on gun law strength rankings. The most credible source I found was the Gifford rankings. Uploaded to my personal Github then read below. Manually added District of Columbia (DC) which wasn’t in the Gifford rankings but would be an “A” according to their “strongest gun laws in the country” (Everytown for Gun Safety).

safety <- read_csv('https://raw.githubusercontent.com/rossboehme/DATA608/main/giffords_2024_gun_law_strength_rankings.csv',show_col_types=FALSE)

safety <- as.data.frame(safety)

#Showing initial dataframe
head(safety)
##   gun_law_strength       state gun_law_grade Gun Death Rate (Ranked)
## 1                1  California             A                      44
## 2                2  New Jersey             A                      47
## 3                3 Connecticut             A                      45
## 4                4    Illinois            A-                      32
## 5                5    New York            A-                      46
## 6                6      Hawaii            A-                      48
##   Gun Death Rate (Per 100K)
## 1                       8.7
## 2                       5.0
## 3                       7.0
## 4                      14.4
## 5                       5.3
## 6                       4.5

Cleaning gun safety law rankings:
- Only relevant data (state names and grades from A to F)
- No spaces in column names   - Mapping grades to Likert scale where 1 = most lax (“F” grade) and 5 = strictest (“A” grade)
- Manually adding D.C. rankings which weren’t in my original source

safety <- safety %>% select(gun_law_grade, state)

safety <- safety %>%
  mutate(strictness_rating = case_when(
    str_detect(gun_law_grade, "A") ~ 5,
    str_detect(gun_law_grade, "B") ~ 4,
    str_detect(gun_law_grade, "C") ~ 3,
    str_detect(gun_law_grade, "D") ~ 2,
    TRUE ~ 1
  ))

safety$state_abbr <- sapply(safety$state, function(x) {
  switch(x,
          "Alabama" = "AL", "Alaska" = "AK", "Arizona" = "AZ", "Arkansas" = "AR", "California" = "CA",
  "Colorado" = "CO", "Connecticut" = "CT", "Delaware" = "DE", "District of Columbia" = "DC",
  "Florida" = "FL", "Georgia" = "GA", "Hawaii" = "HI", "Idaho" = "ID", "Illinois" = "IL", "Indiana" = "IN",
  "Iowa" = "IA", "Kansas" = "KS", "Kentucky" = "KY", "Louisiana" = "LA", "Maine" = "ME", "Maryland" = "MD",
  "Massachusetts" = "MA", "Michigan" = "MI", "Minnesota" = "MN", "Mississippi" = "MS", "Missouri" = "MO",
  "Montana" = "MT", "Nebraska" = "NE", "Nevada" = "NV", "New Hampshire" = "NH", "New Jersey" = "NJ",
  "New Mexico" = "NM", "New York" = "NY", "North Carolina" = "NC", "North Dakota" = "ND", "Ohio" = "OH",
  "Oklahoma" = "OK", "Oregon" = "OR", "Pennsylvania" = "PA", "Rhode Island" = "RI", "South Carolina" = "SC",
  "South Dakota" = "SD", "Tennessee" = "TN", "Texas" = "TX", "Utah" = "UT", "Vermont" = "VT",
  "West Virginia" = "WV", "Virginia" = "VA", "Washington" = "WA", "Wisconsin" = "WI", "Wyoming" = "WY",
         NA) 
})

#Display the updated dataframe
safety <- safety %>% select(state, state_abbr, strictness_rating)

#Manually add DC
safety <- rbind(safety, data.frame(state = "District of Columbia", state_abbr = "DC", strictness_rating = 5))

#Showing final "safety" dataframe
head(safety)
##         state state_abbr strictness_rating
## 1  California         CA                 5
## 2  New Jersey         NJ                 5
## 3 Connecticut         CT                 5
## 4    Illinois         IL                 5
## 5    New York         NY                 5
## 6      Hawaii         HI                 5

Now I’ll merge my firearm mortality dataframe with my gun safety rating dataframe.

merged <- merge(firearm, safety, by = "state_abbr")

print(merged)
##    state_abbr mortality_rate year                state strictness_rating
## 1          AK           22.4 2022               Alaska                 1
## 2          AL           25.2 2022              Alabama                 1
## 3          AR           21.9 2022             Arkansas                 1
## 4          AZ           20.9 2022              Arizona                 1
## 5          CA            8.9 2022           California                 5
## 6          CO           17.7 2022             Colorado                 5
## 7          CT            6.9 2022          Connecticut                 5
## 8          DC           22.9 2022 District of Columbia                 5
## 9          DE           12.2 2022             Delaware                 4
## 10         FL           14.5 2022              Florida                 2
## 11         GA           19.8 2022              Georgia                 1
## 12         HI            4.6 2022               Hawaii                 5
## 13         IA           11.5 2022                 Iowa                 1
## 14         ID           17.4 2022                Idaho                 1
## 15         IL           14.3 2022             Illinois                 5
## 16         IN           17.7 2022              Indiana                 2
## 17         KS           16.8 2022               Kansas                 1
## 18         KY           18.6 2022             Kentucky                 1
## 19         LA           27.6 2022            Louisiana                 1
## 20         MA            3.8 2022        Massachusetts                 5
## 21         MD           13.2 2022             Maryland                 5
## 22         ME           12.9 2022                Maine                 2
## 23         MI           15.0 2022             Michigan                 4
## 24         MN            9.8 2022            Minnesota                 4
## 25         MO           24.1 2022             Missouri                 1
## 26         MS           28.8 2022          Mississippi                 1
## 27         MT           24.4 2022              Montana                 1
## 28         NC           17.0 2022       North Carolina                 3
## 29         ND           16.0 2022         North Dakota                 1
## 30         NE           12.4 2022             Nebraska                 3
## 31         NH           11.2 2022        New Hampshire                 2
## 32         NJ            5.1 2022           New Jersey                 5
## 33         NM           27.0 2022           New Mexico                 3
## 34         NV           19.4 2022               Nevada                 4
## 35         NY            5.3 2022             New York                 5
## 36         OH           15.6 2022                 Ohio                 2
## 37         OK           19.8 2022             Oklahoma                 1
## 38         OR           15.4 2022               Oregon                 5
## 39         PA           15.0 2022         Pennsylvania                 4
## 40         RI            3.4 2022         Rhode Island                 4
## 41         SC           20.9 2022       South Carolina                 2
## 42         SD           15.5 2022         South Dakota                 1
## 43         TN           21.0 2022            Tennessee                 1
## 44         TX           15.4 2022                Texas                 1
## 45         UT           13.2 2022                 Utah                 1
## 46         VA           15.1 2022             Virginia                 4
## 47         VT           13.0 2022              Vermont                 4
## 48         WA           13.1 2022           Washington                 5
## 49         WI           14.1 2022            Wisconsin                 3
## 50         WV           17.5 2022        West Virginia                 1
## 51         WY           21.3 2022              Wyoming                 1

I will create an initial visualization to show the association between Firearm Deaths Per 100K people and Firearm Law Strictness. There appears to be a general inverse relationship: the higher the gun law strictness (5 is highest), the lower the mortality rate.

ggplot(data = merged, aes(x = strictness_rating, y = mortality_rate)) +
  geom_point(aes(color = 'U.S. State'), size = 3) +
  geom_smooth(method = "lm", se = FALSE, aes(color = "Trend")) +
  guides(color = guide_legend(override.aes = list(linetype = 1), title = "")) +
  labs(title = "Firearm Deaths vs. Firearm Law Strictness by U.S. State",
       x = "Strictness Rating (5 is strictest)",
       y = "Deaths Per 100K People") +
  theme(plot.title = element_text(face = "bold",hjust=0.5))
## `geom_smooth()` using formula = 'y ~ x'

Next I’ll bring in state information from the maps package so I can create a heatmap as requested.

#Retrieve state info from maps package
state_info <- map_data("state")

#Merge mortality/law data with map data
merged$region <- tolower(merged$state)

gun_map <- left_join(state_info, merged, by = "region")

For my U.S. heatmap, I will use accessible color palettes (for people with color blindness) as defined by David Nichols. Red/blue is more visible than red/green.

In this combined plot, I juxtapose the Firearm Deaths Per State map against the Firearm Law Strictness Per State map. There appears to be an association between higher firearm mortality rates (per 100K population) and more lax gun laws, both of which are displayed in red. Likewise, lower mortality rates (blue) show up in states with stricter gun laws (blue).

p1 <- ggplot(gun_map, aes(x = long, y = lat, group = group, fill = mortality_rate)) +
  geom_polygon() +
  scale_fill_gradient(low = "#005AB5", high = '#DC3220') +
  coord_map() +
  theme_void() +
  labs(title = "Firearm Deaths Per State",
       caption = "",
       fill = "Mortality Rate
       (Per 100K People)") +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5), 
    legend.title.align = 0.5, 
    legend.position = "bottom",  
    legend.box = "vertical"
  )

p2 <- ggplot(gun_map, aes(x = long, y = lat, group = group, fill = strictness_rating)) +
  geom_polygon() +
  scale_fill_gradient(low = "#DC3220", high = '#005AB5') + 
  coord_map() +
  theme_void() +
  labs(title = "Firearm Law Strictness Per State",
       caption = "",
       fill = "Strictness Rating
       (5 is strictest)") +
 theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.title.align = 0.5, 
    legend.position = "bottom", 
    legend.box = "vertical"
  )

combined_plot <- p1 / p2 + plot_layout(heights = c(2, 2)) 
combined_plot