I am trying to fill a US map where each state is filled in by mean salary (In the default colour scale). I have the shapefile, and a dataframe which looks like this (Data falsified):
data <- structure(list(State = c("Arkansas",
"Iowa",
"California",
"Idaho"),
MeanSalary = c(50000,60000,62000,55000)),
row.names=1:4, class = "data.frame")
Here is my code:
library(tidyverse)
library(rgdal)
map <- readOGR(dsn = ".", layer = "usamap")
PlotData <- merge(map, data, by = "State")
This all works so far. I can also make an empty map:
map_base <- ggplot(data = PlotData, mapping=(aes(x=long, y = lat, group = group)) +
geom_polygon(color = "black", fill = NA)
map_base
However, I can't fill in the map with the values.
map_base <- ggplot(data = PlotData, mapping=(aes(x=long, y = lat, group = group)) +
geom_polygon(color = "black", fill = PlotData$MeanSalary)
map_base
I get this error:
Error: Aesthetics must be either length 1 or the same as the data (2834334): fill
What am I getting wrong?
Here I provided two solutions to plot polygons using ggplot2.
Solution 1: geom_sf
sf class is the next generation spatial data class in R. geom_sf can plot the sf object. To do this, we need to convert the sp object to sf object. Here I used the state spatial polygons from the USAboundaries package as an example.
library(tidyverse)
library(sf)
library(USAboundaries)
# Get the state data
state <- us_states()
# Check the class
class(state)
# [1] "sf" "data.frame"
# Create example data frame
data <- structure(list(State = c("Arkansas",
"Iowa",
"California",
"Idaho"),
MeanSalary = c(50000,60000,62000,55000)),
row.names=1:4, class = "data.frame")
# Merge data to state and filter for these records
state_filter <- state %>%
left_join(data, by = c("name" = "State")) %>%
# Remove Hawaii, Alaska, and Puerto Rico to just focus on the rest states
filter(!name %in% c("Hawaii", "Alaska", "Puerto Rico"))
# Plot the data
ggplot(state_filter) +
geom_sf(aes(fill = MeanSalary))

Solution 2: ggspatial package
The ggspatial package can plot the sp object. So if you don't want to work with sf object, using ggspatial could be an option.
library(tidyverse)
library(sf)
library(USAboundaries)
library(sp)
library(ggspatial)
# Convert the sf object to sp object
state_filter_sp <- as(state_filter, "Spatial")
# Plot the data
ggplot() +
annotation_spatial(state_filter_sp) +
layer_spatial(state_filter_sp, aes(fill = MeanSalary))

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With