AQI vs Mobility Project: Data Preparation

Anjelica Martinez, Joanna Rashid, Yousif Alnuaimiz
05/08/2020

# Libraries
library(easypackages)
libraries("sjmisc", "tidyverse", "lubridate", "readr", "plyr")

Introduction

The historic COVID-19 pandemic has caused nearly 50% of the world population to be subject to government “stay-at-home” and self-isolation orders. This sudden, drastic reduction in the mobility of the population has generated a natural experiment that tests the effect a near halt of mobility for individuals and drastic reduction of commercial operations would have on air quality. The metric used for this study is the mean daily mobility of individuals in a given US county.

Based on this criteria, we selected five major counties that had the highest level of mobility reduction as well as five other counties that had fewer reduction in mobility. A major county is defined as being in the top 40 most populous counties in the US, per the US Census Bureau.

The time period of this study is February 28 through March 27, the time during which some US counties enacted “shelter-in-place” and the self-isolation orders to control the community spread of COVID-19.

The five counties with the greatest reduction in citizen mobility issued “shelter-in-place” orders during this period.

The five counties with the least mobility reduction had no “shelter-in-place” orders during the studied time period and can serve as a control group. Our goal is to determine whether decrease in mobility correlates with increase in air quality and how strong of a relationship is there for these 10 counties.

Counties of Interest

Counties that decreased mobility 98-100% between Feb. 28 and Mar. 27, 2020. New York Times.

Most populous counties that decreased mobility 30% or less between between Feb. 28 and Mar. 27, 2020 New York Times.

Data Sources

Air Quality Data

We collected our air quality data from the EPA's free API service. From the website, "The AirData Air Quality Index Summary Report displays an annual summary of Air Quality Index (AQI) values for counties or core based statistical areas (CBSA). Air Quality Index is an indicator of overall air quality, because it takes into account all of the criteria air pollutants measured within a geographic area. Although AQI includes all available pollutant measurements, you should be aware that many areas have monitoring stations for some, but not all, of the pollutants." You can read the documentation here.

Mobility Data

We used the mobility statistics data collected by Descarte Labs. The data represents "the distance a typical member of a given population moves in a day, at the US admin1 (state) and admin2 (county) level)". Follow the link to the github repository to read their documentation.

Time Range Adjustment

Because the mobility dataset only has data from March 1st we decided to omit air quality data from the months of January and February. Thus, our analyses only covers from March 1st to April 18th.

Data Preparation

Mobility Data Prep

First I cleaned and tidied up the mobility index csv file from Decartes Labs.

Variables of Interest:

If you would like to read more about the Descarte Labs mobility index, you can find the documentation here.

# Import Messy Data
df <- read.csv("../Scripts/data/messy/us_mobility.csv", stringsAsFactors = FALSE)
(df <- as_tibble(df))
## # A tibble: 130,639 x 9
##    date     country_code admin_level admin1 admin2  fips samples   m50 m50_index
##    <chr>    <chr>              <int> <chr>  <chr>  <int>   <int> <dbl>     <int>
##  1 2020-03~ US                     1 Alaba~ ""         1  133826  8.33        79
##  2 2020-03~ US                     1 Alaba~ ""         1  143632 10.4         98
##  3 2020-03~ US                     1 Alaba~ ""         1  146009 10.5        100
##  4 2020-03~ US                     1 Alaba~ ""         1  149352 10.1         96
##  5 2020-03~ US                     1 Alaba~ ""         1  144109 11.0        104
##  6 2020-03~ US                     1 Alaba~ ""         1  141491 13.0        123
##  7 2020-03~ US                     1 Alaba~ ""         1  141163 11.4        107
##  8 2020-03~ US                     1 Alaba~ ""         1  138075  8.51        80
##  9 2020-03~ US                     1 Alaba~ ""         1  145037 10.9        103
## 10 2020-03~ US                     1 Alaba~ ""         1  143390 10.9        103
## # ... with 130,629 more rows

I renamed the columns to make it easier to understand what values they represented. I changed admin1 to state and admin2 to county. I then changed the date column from character format to data format.

# Rename columns
df <- dplyr::rename(df, state = admin1, county = admin2)

# Change character dates to R date format
df$date <- as.Date(df$date)

head(df)
## # A tibble: 6 x 9
##   date       country_code admin_level state county  fips samples   m50 m50_index
##   <date>     <chr>              <int> <chr> <chr>  <int>   <int> <dbl>     <int>
## 1 2020-03-01 US                     1 Alab~ ""         1  133826  8.33        79
## 2 2020-03-02 US                     1 Alab~ ""         1  143632 10.4         98
## 3 2020-03-03 US                     1 Alab~ ""         1  146009 10.5        100
## 4 2020-03-04 US                     1 Alab~ ""         1  149352 10.1         96
## 5 2020-03-05 US                     1 Alab~ ""         1  144109 11.0        104
## 6 2020-03-06 US                     1 Alab~ ""         1  141491 13.0        123

I created three dataframes for easier analysis:

Decrease Dataframe

# Create dataframe that contains counties that decreased mobility 98-100% between Feb. 28 to Mar. 27
decrease <- df[(df$county %in% c("New York County", "Arlington County", "San Francisco County", "Santa Clara County")), ]

The mobility index had a peculiar entry. For Washington, D.C. they had the state listed as Washington D.C and the county column was left blank. I changed the state value to District of Columbia and added the District of Columbia value to all the District of Columbia state entries.

dc_data<- df[(df$state == "Washington, D.C."), ]
decrease <- bind_rows(decrease, dc_data)

# Replace Washington, DC with District of Columbia
decrease$state[decrease$state == "Washington, D.C."] <- "District of Columbia"

# Add District of Columbia value
decrease[197:245, 5] <- "District of Columbia"

head(decrease)
## # A tibble: 6 x 9
##   date       country_code admin_level state county  fips samples   m50 m50_index
##   <date>     <chr>              <int> <chr> <chr>  <int>   <int> <dbl>     <int>
## 1 2020-03-01 US                     2 Cali~ San F~  6075    4513  2.66        77
## 2 2020-03-02 US                     2 Cali~ San F~  6075    4139  3.34        97
## 3 2020-03-03 US                     2 Cali~ San F~  6075    5251  3.28        95
## 4 2020-03-04 US                     2 Cali~ San F~  6075    5503  3.46       101
## 5 2020-03-05 US                     2 Cali~ San F~  6075    4693  3.42       100
## 6 2020-03-06 US                     2 Cali~ San F~  6075    4683  3.84       112

Because the AQI dataframe excludes the county suffix, I removed the 'county' suffix from all the country entries for cohesiveness and easier analysis.

# Remove County from county name
decrease$county <- gsub(" County", "", decrease$county)

# Check that District of Columbia values have been added
decrease[(decrease$county == "District of Columbia"), ]
## # A tibble: 49 x 9
##    date       country_code admin_level state county  fips samples   m50
##    <date>     <chr>              <int> <chr> <chr>  <int>   <int> <dbl>
##  1 2020-03-01 US                     1 Dist~ Distr~    11    4735  3.74
##  2 2020-03-02 US                     1 Dist~ Distr~    11    4344  2.80
##  3 2020-03-03 US                     1 Dist~ Distr~    11    5165  3.58
##  4 2020-03-04 US                     1 Dist~ Distr~    11    5349  3.66
##  5 2020-03-05 US                     1 Dist~ Distr~    11    4956  3.90
##  6 2020-03-06 US                     1 Dist~ Distr~    11    4874  4.27
##  7 2020-03-07 US                     1 Dist~ Distr~    11    4928  3.97
##  8 2020-03-08 US                     1 Dist~ Distr~    11    5380  4.51
##  9 2020-03-09 US                     1 Dist~ Distr~    11    5199  3.38
## 10 2020-03-10 US                     1 Dist~ Distr~    11    5090  3.52
## # ... with 39 more rows, and 1 more variable: m50_index <int>
# Check decrease dataframe
head(decrease)
## # A tibble: 6 x 9
##   date       country_code admin_level state county  fips samples   m50 m50_index
##   <date>     <chr>              <int> <chr> <chr>  <int>   <int> <dbl>     <int>
## 1 2020-03-01 US                     2 Cali~ San F~  6075    4513  2.66        77
## 2 2020-03-02 US                     2 Cali~ San F~  6075    4139  3.34        97
## 3 2020-03-03 US                     2 Cali~ San F~  6075    5251  3.28        95
## 4 2020-03-04 US                     2 Cali~ San F~  6075    5503  3.46       101
## 5 2020-03-05 US                     2 Cali~ San F~  6075    4693  3.42       100
## 6 2020-03-06 US                     2 Cali~ San F~  6075    4683  3.84       112

Increase Dataframe

This dataframe contains the mobility index values for counties that decreased mobility 30% or less between Feb. 28 to Mar. 27.

# Create dataframe that contains Counties that decreased mobility 30% or less between Feb. 28 to Mar. 27
increase <- df[(df$county %in% c("Miami-Dade County", "Dallas County", "Harris County", "Maricopa County", "San Bernardino County")), ]

This dataset contained several states that shared the same counties names. Here, I removed the states that we were not analyzing as well as the county suffix.

increase <- filter(increase, !(state %in% c("Alabama", "Arkansas", "Iowa", "Missouri", "Georgia")))

# Remove "county" from county name
increase$county <- gsub(" County","", increase$county)

I then combined these two datasets together to form sample_df that only contained the data for the 10 counties of interest. I kept the original intact dataframe as df in case we decided to include more counties.

# Create df with counties of interest (our sample)
sample_df <- bind_rows(decrease, increase)

I decided to rename the variables when I saved the .Rdata file in order to make it easier identification in future analyses scripts.

# Rename with clearer variables
all_mobility_data <- df
decreased_mobility <- decrease
increased_mobility <- increase
all_selected_counties <- sample_df

# Check variables
head(all_mobility_data)
## # A tibble: 6 x 9
##   date       country_code admin_level state county  fips samples   m50 m50_index
##   <date>     <chr>              <int> <chr> <chr>  <int>   <int> <dbl>     <int>
## 1 2020-03-01 US                     1 Alab~ ""         1  133826  8.33        79
## 2 2020-03-02 US                     1 Alab~ ""         1  143632 10.4         98
## 3 2020-03-03 US                     1 Alab~ ""         1  146009 10.5        100
## 4 2020-03-04 US                     1 Alab~ ""         1  149352 10.1         96
## 5 2020-03-05 US                     1 Alab~ ""         1  144109 11.0        104
## 6 2020-03-06 US                     1 Alab~ ""         1  141491 13.0        123
head(decreased_mobility)
## # A tibble: 6 x 9
##   date       country_code admin_level state county  fips samples   m50 m50_index
##   <date>     <chr>              <int> <chr> <chr>  <int>   <int> <dbl>     <int>
## 1 2020-03-01 US                     2 Cali~ San F~  6075    4513  2.66        77
## 2 2020-03-02 US                     2 Cali~ San F~  6075    4139  3.34        97
## 3 2020-03-03 US                     2 Cali~ San F~  6075    5251  3.28        95
## 4 2020-03-04 US                     2 Cali~ San F~  6075    5503  3.46       101
## 5 2020-03-05 US                     2 Cali~ San F~  6075    4693  3.42       100
## 6 2020-03-06 US                     2 Cali~ San F~  6075    4683  3.84       112
head(increased_mobility)
## # A tibble: 6 x 9
##   date       country_code admin_level state county  fips samples   m50 m50_index
##   <date>     <chr>              <int> <chr> <chr>  <int>   <int> <dbl>     <int>
## 1 2020-03-01 US                     2 Ariz~ Maric~  4013   78925  5.39        62
## 2 2020-03-02 US                     2 Ariz~ Maric~  4013   82514  8.44        97
## 3 2020-03-03 US                     2 Ariz~ Maric~  4013   84011  8.55        98
## 4 2020-03-04 US                     2 Ariz~ Maric~  4013   85357  8.67       100
## 5 2020-03-05 US                     2 Ariz~ Maric~  4013   82941  9.14       105
## 6 2020-03-06 US                     2 Ariz~ Maric~  4013   81537  9.71       111
head(all_selected_counties)
## # A tibble: 6 x 9
##   date       country_code admin_level state county  fips samples   m50 m50_index
##   <date>     <chr>              <int> <chr> <chr>  <int>   <int> <dbl>     <int>
## 1 2020-03-01 US                     2 Cali~ San F~  6075    4513  2.66        77
## 2 2020-03-02 US                     2 Cali~ San F~  6075    4139  3.34        97
## 3 2020-03-03 US                     2 Cali~ San F~  6075    5251  3.28        95
## 4 2020-03-04 US                     2 Cali~ San F~  6075    5503  3.46       101
## 5 2020-03-05 US                     2 Cali~ San F~  6075    4693  3.42       100
## 6 2020-03-06 US                     2 Cali~ San F~  6075    4683  3.84       112
# Save as Rdata file
# save(decreased_mobility, increased_mobility, all_mobility_data, all_selected_counties, file = "mobility.RData")

# Check that data loads
# load("mobility.RData")

AQI Data Prep

Variables of Interest:

The API system provided by EPA required us to download each counties daily average aqi and pollutant values individually. I used an apply function (loop) to quickly process all the csv files and combine them into one dataset called df.

# Apply loop to import csv files at once
files <- list.files(path = "../Scripts/data/messy/daily_aqi_values", pattern = "*.csv", full.names = T)
df <- ldply(files, read_csv)

# Change to data format
df$Date <- as.Date(df$Date)

# Rearrange columns
df <- move_columns(df, County, .after = "Main Pollutant")

#Change to tibble format
df <- as_tibble(df)

head(df)
## # A tibble: 6 x 13
##   Date       `Overall AQI Va~ `Main Pollutant` County `Site Name (of ~
##   <date>                <dbl> <chr>            <chr>  <chr>           
## 1 0001-01-20               27 PM2.5            Arlin~ Aurora Hills Vi~
## 2 0001-04-20               30 PM2.5            Arlin~ Aurora Hills Vi~
## 3 0001-07-20               30 PM2.5            Arlin~ Aurora Hills Vi~
## 4 0001-10-20               21 PM2.5            Arlin~ Aurora Hills Vi~
## 5 NA                       29 PM2.5            Arlin~ Aurora Hills Vi~
## 6 NA                       18 PM2.5            Arlin~ Aurora Hills Vi~
## # ... with 8 more variables: `Site ID (of Overall AQI)` <chr>, `Source (of
## #   Overall AQI)` <chr>, Ozone <chr>, PM25 <chr>, CO <chr>, SO2 <chr>,
## #   PM10 <chr>, NO2 <chr>

Next I adjusted the columns to tidy format (lowercase, minimal) and renamed Overall AQI to aqi.

# Rename Columns
names(df)
##  [1] "Date"                       "Overall AQI Value"         
##  [3] "Main Pollutant"             "County"                    
##  [5] "Site Name (of Overall AQI)" "Site ID (of Overall AQI)"  
##  [7] "Source (of Overall AQI)"    "Ozone"                     
##  [9] "PM25"                       "CO"                        
## [11] "SO2"                        "PM10"                      
## [13] "NO2"
(names(df) <- c("date", "aqi", "main_pollutant",
               "county", "site_name", "site_id",
               "source", "ozone", "pm25", "co",
               "so2", "pm10", "no2"
               ))
##  [1] "date"           "aqi"            "main_pollutant" "county"        
##  [5] "site_name"      "site_id"        "source"         "ozone"         
##  [9] "pm25"           "co"             "so2"            "pm10"          
## [13] "no2"

Before saving the .Rdata file I renamed the df variable to aqi_df so that when it was imported it would not replace another variable called df.

# rename variable
aqi_df <- df

# Save Workspace
# save(aqi_df, file = "daily_aqi_and_pollutants.RData")

Merge AQI and Mobility Dataframes

# Load .Rdata files
load("../Scripts/data/tidy/mobility.RData")
load("../Scripts/data/tidy/daily_aqi_and_pollutants.RData")

# Create mobility dataframe with selected values
mobility_df <- all_selected_counties %>%
  select(date, county, m50, m50_index)

# Create aqi dataframe with selected values
aqi_df <- aqi_df %>%
  filter(date >= "2020/03/01") %>%
  select(date, county, aqi, main_pollutant, ozone, pm25, co, so2, pm10, no2)

# Combine dataframes together and match by date and county
df <- merge(mobility_df, aqi_df, by = c("date", "county"))

Next, I seperated the counties by mobility percentage as we did previously but this time with mobility and aqi values included. Finally, I saved it as a the final .Rdata file to use for our analyses.

# counties that decreased by <30%
high_mobility <- df[(df$county %in% c("New York", "Arlington", "San Francisco", "Santa Clara", "District of Columbia")), ]

# counties that decreased by 98-100%
low_mobility <- df[(df$county %in% c("Miami-Dade", "Dallas", "Harris", "Maricopa", "San Bernardino")), ]

# save the final dataset to use for analysis
# save(df, low_mobility, high_mobility, file = "data/tidy/tidy_mobility_vs_aqi.RData")

head(df)
##         date               county   m50 m50_index aqi main_pollutant ozone pm25
## 1 2020-03-01            Arlington 3.270        71  37          Ozone    37    .
## 2 2020-03-01 District of Columbia 3.739       102  39          Ozone    39   26
## 3 2020-03-01             Maricopa 5.387        62  44          Ozone    44   43
## 4 2020-03-01           Miami-Dade 5.467        68  47          Ozone    47   30
## 5 2020-03-01             New York 0.462        23  35          Ozone    35    .
## 6 2020-03-01       San Bernardino 4.508        57  47          PM2.5    47   47
##     co  so2 pm10  no2
## 1 <NA> <NA> <NA> <NA>
## 2 <NA> <NA> <NA> <NA>
## 3    7    1   17   29
## 4 <NA> <NA> <NA> <NA>
## 5    2 <NA>    . <NA>
## 6 <NA> <NA> <NA> <NA>
head(high_mobility)
##         date               county   m50 m50_index aqi main_pollutant ozone pm25
## 1 2020-03-01            Arlington 3.270        71  37          Ozone    37    .
## 2 2020-03-01 District of Columbia 3.739       102  39          Ozone    39   26
## 5 2020-03-01             New York 0.462        23  35          Ozone    35    .
## 7 2020-03-01        San Francisco 2.663        77  36          Ozone    36   20
## 8 2020-03-01          Santa Clara 4.088        57  36          Ozone    36   29
## 9 2020-03-02            Arlington 4.594       100  44          Ozone    44    .
##     co  so2 pm10  no2
## 1 <NA> <NA> <NA> <NA>
## 2 <NA> <NA> <NA> <NA>
## 5    2 <NA>    . <NA>
## 7 <NA> <NA> <NA> <NA>
## 8 <NA> <NA> <NA> <NA>
## 9 <NA> <NA> <NA> <NA>
head(low_mobility)
##          date         county   m50 m50_index aqi main_pollutant ozone pm25   co
## 3  2020-03-01       Maricopa 5.387        62  44          Ozone    44   43    7
## 4  2020-03-01     Miami-Dade 5.467        68  47          Ozone    47   30 <NA>
## 6  2020-03-01 San Bernardino 4.508        57  47          PM2.5    47   47 <NA>
## 10 2020-03-02         Dallas 9.311       100  50          PM2.5    23   50 <NA>
## 12 2020-03-02         Harris 8.717        97  48          PM2.5    37   48 <NA>
## 13 2020-03-02       Maricopa 8.438        97  41          Ozone    41   26    5
##     so2 pm10  no2
## 3     1   17   29
## 4  <NA> <NA> <NA>
## 6  <NA> <NA> <NA>
## 10 <NA> <NA> <NA>
## 12 <NA> <NA> <NA>
## 13    1   19   24