INTRODUCTION

The firearm suicide epidemic has been growing, with the firearm suicide rate increasing 12.5% over the last decade (2010-2019). While females are more likely than males to attempt suicide, males are four times more likely to die by suicide. Across all demographics, males have higher rates of firearm suicide and suicide overall. This is primarily due to the fact that males are more likely to use a more lethal suicide attempt method, such as firearms. Suicide attempts among males are eight times more likely to involve firearms than attempts among females. Firearm suicide rates vary substantially across the country. Suicide rates (both overall and by firearm) are generally higher in places where household firearm ownership is more common. In 2019, New Jersey had the lowest firearm suicide rate, while Wyoming had the highest. I am very interested in answering the following questions:

To answer these question, I combined two separate data sources. To obtain the median household income and poverty rates, I used a US Census API for all years for which firearms-related suicide data are available. For firearm-related suicide and ownership data, I used the database compiled by the Rand Corp. that includes those data by state for the years 1981 – 2002.

The motivation behind this project is one of a personal nature for me, as a good friend in our circle was having mental health issues and ended up taking his own life with a firearm. In order to extend what was learned in class, I used ggmap as my new tool to show suicide rates across the entirety of the United States. I then combined this with other visualizations to address the questions I set forth to answer.

Data import, cleaning, & joining

Load the requisite libraries:

library(jsonlite)
library(readxl)
library(ggplot2)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.1     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ lubridate 1.9.2     ✔ tibble    3.2.1
## ✔ purrr     1.0.1     ✔ tidyr     1.3.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter()  masks stats::filter()
## ✖ purrr::flatten() masks jsonlite::flatten()
## ✖ dplyr::lag()     masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)
library(ggmap)
## ℹ Google's Terms of Service: <https://mapsplatform.google.com>
## ℹ Please cite ggmap if you use it! Use `citation("ggmap")` for details.
library(viridis)
## Loading required package: viridisLite
library(forcats)

Relational data source

Import the Rand Corp. gun-related suicide rate data from an excel file:

suicide = read_excel("TL-354-State-Level Estimates of Household Firearm Ownership.xlsx", sheet = 2)
head(suicide)
## # A tibble: 6 × 20
##     FIP  Year STATE   HFR HFR_se universl permit Fem_FS_S Male_FS_S BRFSS GALLUP
##   <dbl> <dbl> <chr> <dbl>  <dbl>    <dbl>  <dbl>    <dbl>     <dbl> <dbl>  <dbl>
## 1     1  1980 Alab… 0.608  0.031        0      0    0.824     0.834    -9  0.554
## 2     1  1981 Alab… 0.597  0.047        0      0    0.692     0.831    -9 -9    
## 3     1  1982 Alab… 0.661  0.036        0      0    0.772     0.821    -9 -9    
## 4     1  1983 Alab… 0.586  0.038        0      0    0.688     0.819    -9  0.611
## 5     1  1984 Alab… 0.624  0.036        0      0    0.71      0.776    -9 -9    
## 6     1  1985 Alab… 0.644  0.031        0      0    0.756     0.835    -9  0.612
## # ℹ 9 more variables: GSS <dbl>, PEW <dbl>, HuntLic <dbl>, GunsAmmo <dbl>,
## #   BackChk <dbl>, PewQChng <dbl>, BS1 <dbl>, BS2 <dbl>, BS3 <dbl>
dim(suicide)
## [1] 1850   20
str(suicide)
## tibble [1,850 × 20] (S3: tbl_df/tbl/data.frame)
##  $ FIP      : num [1:1850] 1 1 1 1 1 1 1 1 1 1 ...
##  $ Year     : num [1:1850] 1980 1981 1982 1983 1984 ...
##  $ STATE    : chr [1:1850] "Alabama" "Alabama" "Alabama" "Alabama" ...
##  $ HFR      : num [1:1850] 0.608 0.597 0.661 0.586 0.624 0.644 0.567 0.609 0.606 0.627 ...
##  $ HFR_se   : num [1:1850] 0.031 0.047 0.036 0.038 0.036 0.031 0.038 0.036 0.031 0.031 ...
##  $ universl : num [1:1850] 0 0 0 0 0 0 0 0 0 0 ...
##  $ permit   : num [1:1850] 0 0 0 0 0 0 0 0 0 0 ...
##  $ Fem_FS_S : num [1:1850] 0.824 0.692 0.772 0.688 0.71 ...
##  $ Male_FS_S: num [1:1850] 0.834 0.831 0.821 0.819 0.776 ...
##  $ BRFSS    : num [1:1850] -9 -9 -9 -9 -9 -9 -9 -9 -9 -9 ...
##  $ GALLUP   : num [1:1850] 0.554 -9 -9 0.611 -9 ...
##  $ GSS      : num [1:1850] 0.584 -9 0.655 -9 0.627 ...
##  $ PEW      : num [1:1850] -9 -9 -9 -9 -9 -9 -9 -9 -9 -9 ...
##  $ HuntLic  : num [1:1850] 0.291 0.295 0.291 0.285 0.282 ...
##  $ GunsAmmo : num [1:1850] -0.509 -0.619 -0.527 -0.713 -0.733 ...
##  $ BackChk  : num [1:1850] -9 -9 -9 -9 -9 -9 -9 -9 -9 -9 ...
##  $ PewQChng : num [1:1850] 0 0 0 0 0 0 0 0 0 0 ...
##  $ BS1      : num [1:1850] 0 1 2 3 4 5 6 7 7.91 8.73 ...
##  $ BS2      : num [1:1850] 0 0 0 0 0 0 0 0 0.09 0.27 ...
##  $ BS3      : num [1:1850] 0 0 0 0 0 0 0 0 0 0 ...

API data source

I chose to use a US Census API that contained state-wide data on median household income and poverty rates, by year. To access the API, I had to register for a key with the US Census Bureau. I also wrote a for loop to iterate over all of the available years (1995 - 2016) to import and collate those data into a single dataframe that I could later join with the gun-related suicide data. Note: Census data only available from 1995 forward.

years = (unique(suicide$Year))
years = years[years > 1994]
years = as.character(years)
header <- c("Median_Household_Income", "All_Ages_Poverty_Count", "All_Ages_Poverty_Rate", "All_Ages_Poverty_Universe", "State_Name", "State_Abbr", "Year", "State_FIPS")
census_all = data.frame(matrix(ncol = length(header), nrow = 0))
for (y in 1:length(years)) {
  year <- years[y]
  print(year)
  key <- "44427d638aa029da61ae1cce5abe89e33be89f8e"
  get <- "SAEMHI_PT&&SAEPOVALL_PT&SAEPOVRTALL_PT&SAEPOVU_ALL&NAME&STABREV"
  for.statemt <- "state:*&time=" 
  # url <- "https://api.census.gov/data/timeseries/poverty/saipe?get=SAEMHI_PT&&SAEPOVALL_PT&NAME&for=state:*&time=2018"
  base.url <- "https://api.census.gov/data/timeseries/poverty/saipe"
  url <- paste(base.url, "?get=", get, "&for=", for.statemt, year, sep = "")
   key.path <- paste("&key=", key, sep = "")
  path <- paste(url, key.path, sep = "")
  
  census <- as.data.frame(fromJSON(path))
  census <- census[-1, ]
  
  names(census) <- header
  census_all = rbind(census_all, census)
}
## [1] "1995"
## [1] "1996"
## [1] "1997"
## [1] "1998"
## [1] "1999"
## [1] "2000"
## [1] "2001"
## [1] "2002"
## [1] "2003"
## [1] "2004"
## [1] "2005"
## [1] "2006"
## [1] "2007"
## [1] "2008"
## [1] "2009"
## [1] "2010"
## [1] "2011"
## [1] "2012"
## [1] "2013"
## [1] "2014"
## [1] "2015"
## [1] "2016"
## Drop All_Ages_Poverty_Universe since it was an empty column:
census_all <- census_all %>% select(-All_Ages_Poverty_Universe)

## Fix the first 3 columns, which came in as character:  
census_all <- census_all %>% 
  mutate(Median_Household_Income = as.numeric(Median_Household_Income), 
         All_Ages_Poverty_Count = as.numeric(All_Ages_Poverty_Count),
         All_Ages_Poverty_Rate = as.numeric(All_Ages_Poverty_Rate))

head(census_all)
##   Median_Household_Income All_Ages_Poverty_Count All_Ages_Poverty_Rate
## 2                   27357                 752097                  17.6
## 3                   42255                  60735                  10.1
## 4                   31736                 722660                  16.3
## 5                   26515                 455776                  18.2
## 6                   36767                5215575                  16.5
## 7                   37235                 392938                  10.4
##   State_Name State_Abbr Year State_FIPS
## 2    Alabama         AL 1995         01
## 3     Alaska         AK 1995         02
## 4    Arizona         AZ 1995         04
## 5   Arkansas         AR 1995         05
## 6 California         CA 1995         06
## 7   Colorado         CO 1995         08
# View(census_all)
# table(census_all$Year)

Joining the suicide and census dataframes

Next, I merged the suicide rate data with the census data by year. First, I converted year to character type in the suicide dataframe. I also chose to rename the “State_Name” column that was imported from the Census API to “STATE” to make the join simpler. I performed a right_join on the dataframes because I wanted all of the suicide data retained.

suicide$Year <- as.character(suicide$Year)

census_all <- census_all %>% 
  rename(STATE = State_Name)

suicide_census <- right_join(suicide, census_all, by = c("Year", "STATE"))
head(suicide_census)
## # A tibble: 6 × 25
##     FIP Year  STATE   HFR HFR_se universl permit Fem_FS_S Male_FS_S BRFSS GALLUP
##   <dbl> <chr> <chr> <dbl>  <dbl>    <dbl>  <dbl>    <dbl>     <dbl> <dbl>  <dbl>
## 1     1 1995  Alab… 0.554  0.047        0      0    0.640     0.786    -9 -9    
## 2     1 1996  Alab… 0.568  0.031        0      0    0.699     0.8      -9  0.503
## 3     1 1997  Alab… 0.557  0.029        0      0    0.697     0.782    -9  0.594
## 4     1 1998  Alab… 0.577  0.036        0      0    0.658     0.795    -9 -9    
## 5     1 1999  Alab… 0.537  0.036        0      0    0.698     0.802    -9  0.520
## 6     1 2000  Alab… 0.538  0.026        0      0    0.616     0.760    -9  0.576
## # ℹ 14 more variables: GSS <dbl>, PEW <dbl>, HuntLic <dbl>, GunsAmmo <dbl>,
## #   BackChk <dbl>, PewQChng <dbl>, BS1 <dbl>, BS2 <dbl>, BS3 <dbl>,
## #   Median_Household_Income <dbl>, All_Ages_Poverty_Count <dbl>,
## #   All_Ages_Poverty_Rate <dbl>, State_Abbr <chr>, State_FIPS <chr>

Computing the average suicide rate from the male and female rates:

suicide_census$ave_sr = rowMeans(suicide_census[,8:9])

Importing state longitude and latitude data for mapping

These data were downloaded from a publicly available site that gave the center long,lat coordinates for every state in the US.

st_geo = read.csv("state_lat_lon.csv")

This was then joined to the merged suicide-census dataframe to make a complete dataframe with necessary geographic information for mapping.

suicide_census_geo = full_join(suicide_census, st_geo, by = "STATE")
head(suicide_census_geo)
## # A tibble: 6 × 29
##     FIP Year  STATE   HFR HFR_se universl permit Fem_FS_S Male_FS_S BRFSS GALLUP
##   <dbl> <chr> <chr> <dbl>  <dbl>    <dbl>  <dbl>    <dbl>     <dbl> <dbl>  <dbl>
## 1     1 1995  Alab… 0.554  0.047        0      0    0.640     0.786    -9 -9    
## 2     1 1996  Alab… 0.568  0.031        0      0    0.699     0.8      -9  0.503
## 3     1 1997  Alab… 0.557  0.029        0      0    0.697     0.782    -9  0.594
## 4     1 1998  Alab… 0.577  0.036        0      0    0.658     0.795    -9 -9    
## 5     1 1999  Alab… 0.537  0.036        0      0    0.698     0.802    -9  0.520
## 6     1 2000  Alab… 0.538  0.026        0      0    0.616     0.760    -9  0.576
## # ℹ 18 more variables: GSS <dbl>, PEW <dbl>, HuntLic <dbl>, GunsAmmo <dbl>,
## #   BackChk <dbl>, PewQChng <dbl>, BS1 <dbl>, BS2 <dbl>, BS3 <dbl>,
## #   Median_Household_Income <dbl>, All_Ages_Poverty_Count <dbl>,
## #   All_Ages_Poverty_Rate <dbl>, State_Abbr.x <chr>, State_FIPS <chr>,
## #   ave_sr <dbl>, State_Abbr.y <chr>, lat <dbl>, lon <dbl>

ANALYSIS