While not the most technically challenging, I thought this project was an illustration of how relatively simple data can still be interesting to work with.
I wrote a small piece for the journal ‘Judicature’ in which I presented the qualitative results of a series of focus groups I held with judges from around the country. We primarily discussed autonomous vehicles and state and federal legislation and regulations.
To supplement the article, I wanted to create a map that accounted for the current state laws regarding autonomous vehicle use across the United States. To do this, I gathered data from a third-party website (https://www.ncsl.org/transportation/autonomous-vehicles-state-bill-tracking-database) and input it into a csv file. Let’s load the data.
<- read.csv("G:/Users/Chris/Desktop/Autonomous Vehicles/Map Data for Article/AVMapData.csv", header = TRUE, check.names=FALSE)
mapdata print(head(mapdata))
## ST state AllowPubRoads StatesAllow
## 1 AL Alabama 1 Legal w/ Safety Driver
## 2 AK Alaska 0 No Legislation
## 3 AZ Arizona 1 Legal
## 4 AR Arkansas 1 Legal w/ Safety Driver
## 5 CA California 1 Legal
## 6 CO Colorado 1 Legal w/ Safety Driver
Above we can see what the data in the spreadsheet looks like. We have four columns of data: one with the state abbreviation, one with the state name, one dichotomous variable regarding whether autonomous vehicles are allowed on public roads or not, and one StatesAllow column.
str(mapdata)
## 'data.frame': 51 obs. of 4 variables:
## $ ST : chr "AL" "AK" "AZ" "AR" ...
## $ state : chr "Alabama" "Alaska" "Arizona" "Arkansas" ...
## $ AllowPubRoads: int 1 0 1 1 1 1 1 1 1 1 ...
## $ StatesAllow : chr "Legal w/ Safety Driver" "No Legislation" "Legal" "Legal w/ Safety Driver" ...
unique(mapdata$StatesAllow)
## [1] "Legal w/ Safety Driver" "No Legislation" "Legal"
We can see we have 51 observations (50 states plus DC), that the StatesAllow column has three levels (nominally), and that columns 1, 3, and 4 are currently character datatypes but should be factor datatypes. Let’s fix that last problem right now.
<- c(1,3,4)
colnames <- lapply(mapdata[colnames] , factor)
mapdata[colnames] str(mapdata)
## 'data.frame': 51 obs. of 4 variables:
## $ ST : Factor w/ 51 levels "AK","AL","AR",..: 2 1 4 3 5 6 7 9 8 10 ...
## $ state : chr "Alabama" "Alaska" "Arizona" "Arkansas" ...
## $ AllowPubRoads: Factor w/ 2 levels "0","1": 2 1 2 2 2 2 2 2 2 2 ...
## $ StatesAllow : Factor w/ 3 levels "Legal","Legal w/ Safety Driver",..: 2 3 1 2 1 2 2 2 2 1 ...
We’re going to need some libraries to make our map.
library(usmap)
library(ggplot2)
From here we can easily plot an initial map with our data. Using the function ‘plot_usmap’, we can incorporate our own mapdata and plot the factor levels in the ‘StatesAllow’ column.
plot_usmap(regions = "states", data=mapdata, values='StatesAllow') + scale_fill_brewer(palette = 5)
This works for a basic map, and you can even incorporate color palettes.
So if you’re in a pinch, this will do! But, it’s not so easy to edit the individual elements of the plot if one wanted to using this function.
If we do a bit more legwork, we can have much more flexibility in how we deal with our image. Using the “githubinstall” package, we’re going to install the package “fiftystater” from a github repository.
We could also use the package “maps”, but “fiftystater” allows us to incorporate Alaska and Hawaii into our contiguous map in a really nice way.
#library(githubinstall)
#githubinstall("fiftystater") #you should remove the initial hashtags if you're installing fiftystater for the first time.
library(fiftystater)
head(fifty_states)
## long lat order hole piece id group
## 1 -85.07007 31.98070 1 FALSE 1 alabama Alabama.1
## 2 -85.11515 31.90742 2 FALSE 1 alabama Alabama.1
## 3 -85.13557 31.85488 3 FALSE 1 alabama Alabama.1
## 4 -85.13156 31.78381 4 FALSE 1 alabama Alabama.1
## 5 -85.13017 31.77885 5 FALSE 1 alabama Alabama.1
## 6 -85.11529 31.73157 6 FALSE 1 alabama Alabama.1
The library “fiftystater” allows us access to the latitude and longitude coordinates for each area of our map using the “fifty_states” dataframe. We can perform a join on our two dataframes, but first we need to create a common column to join on. Fortunately, we just have to make our state data lowercase and name the new column ‘id’.
library(dplyr)
$id <- tolower(mapdata$state)
mapdata<- left_join(fifty_states, mapdata)
fifty_states_av head(fifty_states_av)
## long lat order hole piece id group ST state
## 1 -85.07007 31.98070 1 FALSE 1 alabama Alabama.1 AL Alabama
## 2 -85.11515 31.90742 2 FALSE 1 alabama Alabama.1 AL Alabama
## 3 -85.13557 31.85488 3 FALSE 1 alabama Alabama.1 AL Alabama
## 4 -85.13156 31.78381 4 FALSE 1 alabama Alabama.1 AL Alabama
## 5 -85.13017 31.77885 5 FALSE 1 alabama Alabama.1 AL Alabama
## 6 -85.11529 31.73157 6 FALSE 1 alabama Alabama.1 AL Alabama
## AllowPubRoads StatesAllow
## 1 1 Legal w/ Safety Driver
## 2 1 Legal w/ Safety Driver
## 3 1 Legal w/ Safety Driver
## 4 1 Legal w/ Safety Driver
## 5 1 Legal w/ Safety Driver
## 6 1 Legal w/ Safety Driver
Now we have one dataframe with our latitude and logitude coordinates for each state, along with our StatesAllow factor with our three levels. Now it’s just a matter of using ggplot to print out our map.
<- ggplot(data = fifty_states_av,
avmap aes(x = long, y = lat,
group = group, fill = StatesAllow))
<- avmap + geom_polygon(color = "gray90", size = 0.1) +
avmap coord_map(projection = "albers", lat0 = 39, lat1 = 45)
avmap
That’s not terrible, but the colors aren’t the best, and I’d like a title for the map. Let’s specify a color palette to use in our map for our fill. You can use a different color palette than I did, and if you want some inspiration, try the website Coolors.co.
<- c("#2B7A78", "#3AAFA9", "#5D5C61") av_colors
And now let’s incorporate those colors in the map, and add the title.
<- avmap + scale_fill_manual(values = alpha(av_colors, 1)) +
avmap labs(title = "AV Legislation by State", fill = NULL)
avmap
It’s looking better! Using the traditional commands in ggplot, we can arrange the elements of the image better than if we had stuck with the plot_usmap function from above. Let’s exercise that ability below, and design our map how we’d like it to look!
<- avmap + theme_bw() + theme(legend.text=element_text(size=16), legend.position = 'bottom') +
avmap theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.y=element_blank(),axis.title.y=element_blank(),
axis.text.y=element_blank(), axis.ticks.x=element_blank(), plot.title = element_text(size=26, hjust = 0.5),
legend.background = element_rect(fill = "transparent"), legend.box.background = element_rect(fill = "transparent"))
avmap
There we are! I’ve changed or removed many of the background elements for the plot, as well as changing the size and positioning of the title and the legend text. Now we can save the image, specifying a width and height for the image, and thus the aspect ratio of the image as well. For this project, I used an image size of 12 x 8 inches, and I made the background of the image transparent.
ggsave(avmap, filename = "G:/Users/Chris/Desktop/Autonomous Vehicles/Map Data for Article/avmap.png",
width = 12, height = 8, bg = "transparent")
And finally, here is what our exported image looks like!