Healthy Cities GIS Assignment

Author

Bemnet Chernet

Load the libraries and set the working directory

library(tidyverse)
library(tidyr)
library(leaflet)

cities500 <- read_csv("500CitiesLocalHealthIndicators.cdc.csv")

The GeoLocation variable has (lat, long) format

Split GeoLocation into two columns: lat and long.

latlong <- cities500 |>
  mutate(GeoLocation = str_replace_all(GeoLocation, "[()]", "")) |>
  separate(
    GeoLocation,
    into = c("lat", "long"),
    sep = ",",
    convert = TRUE
  )

head(latlong)
# A tibble: 6 × 25
   Year StateAbbr StateDesc  CityName  GeographicLevel DataSource Category      
  <dbl> <chr>     <chr>      <chr>     <chr>           <chr>      <chr>         
1  2017 CA        California Hawthorne Census Tract    BRFSS      Health Outcom…
2  2017 CA        California Hawthorne City            BRFSS      Unhealthy Beh…
3  2017 CA        California Hayward   City            BRFSS      Health Outcom…
4  2017 CA        California Hayward   City            BRFSS      Unhealthy Beh…
5  2017 CA        California Hemet     City            BRFSS      Prevention    
6  2017 CA        California Indio     Census Tract    BRFSS      Health Outcom…
# ℹ 18 more variables: UniqueID <chr>, Measure <chr>, Data_Value_Unit <chr>,
#   DataValueTypeID <chr>, Data_Value_Type <chr>, Data_Value <dbl>,
#   Low_Confidence_Limit <dbl>, High_Confidence_Limit <dbl>,
#   Data_Value_Footnote_Symbol <chr>, Data_Value_Footnote <chr>,
#   PopulationCount <dbl>, lat <dbl>, long <dbl>, CategoryID <chr>,
#   MeasureId <chr>, CityFIPS <dbl>, TractFIPS <dbl>, Short_Question_Text <chr>

Filter the dataset

The assignment asks us to remove the United States, use crude prevalence, select 2017, Connecticut, and the Unhealthy Behaviors category.

latlong_clean <- latlong |>
  filter(StateDesc != "United States") |>
  filter(Data_Value_Type == "Crude prevalence") |>
  filter(Year == 2017) |>
  filter(StateAbbr == "CT") |>
  filter(Category == "Unhealthy Behaviors")

head(latlong_clean)
# A tibble: 6 × 25
   Year StateAbbr StateDesc   CityName   GeographicLevel DataSource Category    
  <dbl> <chr>     <chr>       <chr>      <chr>           <chr>      <chr>       
1  2017 CT        Connecticut Bridgeport Census Tract    BRFSS      Unhealthy B…
2  2017 CT        Connecticut Danbury    City            BRFSS      Unhealthy B…
3  2017 CT        Connecticut Norwalk    Census Tract    BRFSS      Unhealthy B…
4  2017 CT        Connecticut Bridgeport Census Tract    BRFSS      Unhealthy B…
5  2017 CT        Connecticut Hartford   Census Tract    BRFSS      Unhealthy B…
6  2017 CT        Connecticut Waterbury  Census Tract    BRFSS      Unhealthy B…
# ℹ 18 more variables: UniqueID <chr>, Measure <chr>, Data_Value_Unit <chr>,
#   DataValueTypeID <chr>, Data_Value_Type <chr>, Data_Value <dbl>,
#   Low_Confidence_Limit <dbl>, High_Confidence_Limit <dbl>,
#   Data_Value_Footnote_Symbol <chr>, Data_Value_Footnote <chr>,
#   PopulationCount <dbl>, lat <dbl>, long <dbl>, CategoryID <chr>,
#   MeasureId <chr>, CityFIPS <dbl>, TractFIPS <dbl>, Short_Question_Text <chr>

What variables are included?

names(latlong_clean)
 [1] "Year"                       "StateAbbr"                 
 [3] "StateDesc"                  "CityName"                  
 [5] "GeographicLevel"            "DataSource"                
 [7] "Category"                   "UniqueID"                  
 [9] "Measure"                    "Data_Value_Unit"           
[11] "DataValueTypeID"            "Data_Value_Type"           
[13] "Data_Value"                 "Low_Confidence_Limit"      
[15] "High_Confidence_Limit"      "Data_Value_Footnote_Symbol"
[17] "Data_Value_Footnote"        "PopulationCount"           
[19] "lat"                        "long"                      
[21] "CategoryID"                 "MeasureId"                 
[23] "CityFIPS"                   "TractFIPS"                 
[25] "Short_Question_Text"       

Remove the variables that will not be used

latlong_clean2 <- latlong_clean |>
  select(
    -DataSource,
    -Data_Value_Unit,
    -DataValueTypeID,
    -Low_Confidence_Limit,
    -High_Confidence_Limit,
    -Data_Value_Footnote_Symbol,
    -Data_Value_Footnote
  )

head(latlong_clean2)
# A tibble: 6 × 18
   Year StateAbbr StateDesc   CityName GeographicLevel Category UniqueID Measure
  <dbl> <chr>     <chr>       <chr>    <chr>           <chr>    <chr>    <chr>  
1  2017 CT        Connecticut Bridgep… Census Tract    Unhealt… 0908000… Obesit…
2  2017 CT        Connecticut Danbury  City            Unhealt… 918430   Obesit…
3  2017 CT        Connecticut Norwalk  Census Tract    Unhealt… 0955990… Obesit…
4  2017 CT        Connecticut Bridgep… Census Tract    Unhealt… 0908000… Curren…
5  2017 CT        Connecticut Hartford Census Tract    Unhealt… 0937000… Obesit…
6  2017 CT        Connecticut Waterbu… Census Tract    Unhealt… 0980000… Obesit…
# ℹ 10 more variables: Data_Value_Type <chr>, Data_Value <dbl>,
#   PopulationCount <dbl>, lat <dbl>, long <dbl>, CategoryID <chr>,
#   MeasureId <chr>, CityFIPS <dbl>, TractFIPS <dbl>, Short_Question_Text <chr>

The cleaned dataset is now manageable for the investigation.

Step 1: Create my own subset

For my investigation, I chose to study current smoking prevalence in Connecticut in 2017. I filtered the cleaned dataset to keep only the Current Smoking measure.

# Keep only observations about current smoking
smoking_ct <- latlong_clean2 |>
  filter(Short_Question_Text == "Current Smoking")

head(smoking_ct)
# A tibble: 6 × 18
   Year StateAbbr StateDesc   CityName GeographicLevel Category UniqueID Measure
  <dbl> <chr>     <chr>       <chr>    <chr>           <chr>    <chr>    <chr>  
1  2017 CT        Connecticut Bridgep… Census Tract    Unhealt… 0908000… Curren…
2  2017 CT        Connecticut Waterbu… Census Tract    Unhealt… 0980000… Curren…
3  2017 CT        Connecticut New Bri… Census Tract    Unhealt… 0950370… Curren…
4  2017 CT        Connecticut Bridgep… Census Tract    Unhealt… 0908000… Curren…
5  2017 CT        Connecticut Hartford Census Tract    Unhealt… 0937000… Curren…
6  2017 CT        Connecticut Waterbu… Census Tract    Unhealt… 0980000… Curren…
# ℹ 10 more variables: Data_Value_Type <chr>, Data_Value <dbl>,
#   PopulationCount <dbl>, lat <dbl>, long <dbl>, CategoryID <chr>,
#   MeasureId <chr>, CityFIPS <dbl>, TractFIPS <dbl>, Short_Question_Text <chr>

Check the number of observations.

# The assignment requires no more than 900 observations
nrow(smoking_ct)
[1] 228

The subset contains 228 observations, which is below the assignment limit of 900 observations.

Step 2: Create a plot

I created a histogram to show the distribution of current smoking prevalence.

ggplot(smoking_ct, aes(x = Data_Value)) +
  geom_histogram(
    bins = 15,
    na.rm = TRUE
  ) +
  labs(
    title = "Current Smoking Prevalence in Connecticut",
    subtitle = "2017 CDC 500 Cities Data",
    x = "Current Smoking Prevalence (%)",
    y = "Number of Observations"
  ) +
  theme_minimal()

Step 3: Create a map

The map shows the geographic locations of the observations in the Current Smoking subset.

leaflet(smoking_ct) |>
  setView(
    lng = -72.7,
    lat = 41.6,
    zoom = 8
  ) |>
  addProviderTiles("Esri.WorldStreetMap") |>
  addCircles(
    data = smoking_ct,
    lng = ~long,
    lat = ~lat,
    radius = ~sqrt(Data_Value) * 1000,
    fillOpacity = 0.5,
    stroke = TRUE
  )

Step 4: Refine the map with a mouse-click tooltip

The popup shows more information when a point is clicked.

popup_smoking <- paste0(
  "<b>City:</b> ", smoking_ct$CityName,
  "<br><b>Geographic Level:</b> ", smoking_ct$GeographicLevel,
  "<br><b>Year:</b> ", smoking_ct$Year,
  "<br><b>Measure:</b> ", smoking_ct$Short_Question_Text,
  "<br><b>Smoking Prevalence:</b> ",
  round(smoking_ct$Data_Value, 1), "%"
)

leaflet(smoking_ct) |>
  setView(
    lng = -72.7,
    lat = 41.6,
    zoom = 8
  ) |>
  addProviderTiles("Esri.WorldStreetMap") |>
  addCircles(
    data = smoking_ct,
    lng = ~long,
    lat = ~lat,
    radius = ~sqrt(Data_Value) * 1000,
    fillOpacity = 0.5,
    stroke = TRUE,
    popup = popup_smoking
  )

Step 5: Describe the plots

The histogram shows the distribution of current smoking prevalence in the Connecticut observations from 2017. The values range from 8.2% to 30.9%, so there is a noticeable difference between the lower and higher observations. The map shows where the observations are located across Connecticut. The circles make it possible to see the geographic pattern of the data, and larger circles represent higher smoking prevalence. When I click on a circle, the popup shows the city, geographic level, year, measure, and smoking prevalence. Overall, the histogram helps me understand the distribution of smoking prevalence, while the map helps me see where the observations are located.