Healthy Cities GIS Assignment

Author

Micaela T

Load the libraries and set the working directory

library(tidyverse)
library(tidyr)
setwd("~/Data 110")
cities500 <- read_csv("500CitiesLocalHealthIndicators.cdc.csv")
data(cities500)

The GeoLocation variable has (lat, long) format

Split GeoLocation (lat, long) 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

Remove the StateDesc that includes the United Sates, select Prevention as the category (of interest), filter for only measuring crude prevalence and select only 2017.

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? (can any of them be removed?)

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 in the assignment

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 new dataset “latlong_clean2” is a manageable dataset now.

For your assignment, work with a cleaned dataset where you perform your own cleaning and filtering.

1. Filter the dataset

For this assignment, I created a smaller subset of the 500 Cities dataset that focuses on Connecticut and adult prevalence. This helps make sure the dataset under 900 rows.

#Filter dataset for Connecticut cities for obesity measure
healthy_subset <- latlong_clean2 |>
  filter(
    StateAbbr == "CT",
    Measure == "Obesity among adults aged >=18 Years"
  ) |>
  rename(
    obesity_percent = Data_Value,
    cityname = CityName,
    stateabbr = StateAbbr,
    latitude = lat,
    longitude = long
  )
# Check how many rows
nrow(healthy_subset)
[1] 228
head(healthy_subset)
# 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 Hartford Census Tract    Unhealt… 0937000… Obesit…
5  2017 CT        Connecticut Waterbu… Census Tract    Unhealt… 0980000… Obesit…
6  2017 CT        Connecticut Hartford Census Tract    Unhealt… 0937000… Obesit…
# ℹ 10 more variables: Data_Value_Type <chr>, obesity_percent <dbl>,
#   PopulationCount <dbl>, latitude <dbl>, longitude <dbl>, CategoryID <chr>,
#   MeasureId <chr>, CityFIPS <dbl>, TractFIPS <dbl>, Short_Question_Text <chr>

2. Create a Barplot (Non-Map)

Next, I created a barchart that shows the obesity percentage for each Connecticut city. This helps me identify where obesity prevalence is the highest across the state of Connecticut.

#Load ggplot2 for plotting
library(ggplot2)

#Create a barchart ordered by obesity percentage
ggplot(healthy_subset, aes(x = reorder(cityname, obesity_percent), 
                           y = obesity_percent, fill = obesity_percent)) +
  geom_col() +
  coord_flip() +
  labs(title = "Adult Obesity Prevalence by City (Connecticut)",
       x = "City",
       y = "Obesity Prevalence (%)",
       caption = "Source: CDC 500 Cities (2019)") +
  theme_minimal()

3. Create a basic map

For here, I used the leaflet package to help me make a simple interactive map. Each circle represents a city, the larger circles the higher obesity prevalence.

library(leaflet)
Warning: package 'leaflet' was built under R version 4.5.2
leaflet(healthy_subset) |>
  addProviderTiles("Esri.WorldStreetMap") |>
  addCircles(
    lng = ~longitude,
    lat = ~latitude,
    radius = ~obesity_percent * 800,
    color = "#2f9c7b",
    fillColor = "#2f9c7b",
    fillOpacity = 0.5
  )

4. Refine your map to include a mouse-click tooltip

With the same map, I added pop-up boxes that shows each city’s name and obesity rate whenever you hover and click on the circles.

popup_info <- paste0(
  "<b>City: </b>", healthy_subset$cityname, "<br>",
  "<b>Obesity (%): </b>", healthy_subset$obesity_percent
)

leaflet(healthy_subset) |>
  addProviderTiles("CartoDB.Positron") |>
  addCircles(
    lng = ~longitude,
    lat = ~latitude,
    radius = ~obesity_percent * 800,
    color = "#1a3e34", #darker border outline
    fillColor = "#2f9c7b", #lighter teal fill
    fillOpacity = 0.6,
    popup = popup_info
  )

5. Write a paragraph

To start off, I filtered the dataset of Connecticut cities, I focused on adult obesity rates from the CDC’s 500 Cities dataset. You can see that the obesity percentages ranged from 25% to above 35% which shows higher rates in a few cities. The bar chart shows that the obesity levels vary across the state, with some cities showing higher prevalence than others. The interactive map highlights where obesity is more common which the larger green circles represent higher percentages. This analysis helped me see how public health issues such as obesity differ within one small state, and how GIS tools make those differences visually easier to see.