JFD
JFD

Reputation: 339

Single option in scale_fill_stepsn changes color rendering in legend

I'm using ggplot's scale_fill_stepsn to generate a map with a stepped scale. When I use the option n.breaks the colors specified render properly in the legend. n.breaks calculates the breaks based on the number of breaks specified. However, when I use the option to manually specify the breaks with the same number of breaks used in n.breaks, the color rendering in the legend changes and are not rendered properly.

This does not make sense. Can this be fixed such that the legend colors in the second example look like that in the first?

library(urbnmapr)
library(ggplot2)
library(dplyr)
library(ggthemes)

# Set colors 
red   <- c(0.67, 0.75, 0.84, 0.92, 1,    1,    0.8, 0.53, 0,   0,    0,   0)
green <- c(0.25, 0.4,  0.56, 0.71, 0.86, 1,    1,   0.95, 0.9, 0.75, 0.6, 0.48)
blue  <- c(0.11, 0.18, 0.25, 0.33, 0.4,  0.45, 0.4, 0.27, 0,   0,    0,   0)

# Obtain county polygon data
states_sf <- get_urbn_map(map = "states", sf = TRUE)
counties_sf <- get_urbn_map(map = "counties", sf = TRUE)

# Assign random values of data to each count  
counties_sf$value = runif(length(counties_sf$county_fips), min=-3.0, max=3.0)

# Remove AK and HI - lower 48 only 
states_sf <- states_sf[!(states_sf$state_abbv %in% c("HI","AK")),]
counties_sf <- counties_sf[!(counties_sf$state_abbv %in% c("HI","AK")),]

# Plot county-level data with a discrete legend 

data_levels <- c(-3,-1.5, -0.8, -0.5, -0.25,-0.1,0.1,0.25,0.5,.8,1.5,3)
level_colors <- rgb(red, green, blue)

length(data_levels)
length(level_colors)


# First version - 
counties_sf %>%
  ggplot() +
  # Overlay State Outlines
  # Plot county data and fill with value
  geom_sf(mapping = aes(fill = value), color = NA) +
  geom_sf(data = states_sf, fill = NA, color = "black", size = 0.25) +
  # Remove grid lines from plot
  coord_sf(datum = NA) +
  #
  # THE FIRST OPTION of scale_fill_stepsn IS WHERE THEY ARE DIFFERENT 
  #
  scale_fill_stepsn(n.breaks=12, colors=level_colors, limits=c(-3,3), 
                    labels=scales::label_number(accuracy=0.1)) + 
  labs(title='This Data is Completely Random', 
       fill ='The Legend') + 
  theme_map() + 
  theme(legend.position = "bottom",
        legend.key.width=unit(1.5,"cm"),
        legend.box.background = element_rect(color="black", size=2),
        legend.title = element_text(face = "bold"),
        legend.spacing = unit(0.25,"cm"),
        legend.justification = "center",
        plot.title=element_text(hjust=0.5))  +
  guides(fill = guide_colorsteps(even.step=TRUE, 
                                 title.position="top", 
                                 title.hjust = 0.5,
                                 frame.colour = 'black', 
                                 barwidth=unit(250,'points'),
                                 axis.linewidth=unit(3,'points')))

This yields: enter image description here

#  
# Second version - 
counties_sf %>%
  ggplot() +
  # Overlay State Outlines
  # Plot county data and fill with value
  geom_sf(mapping = aes(fill = value), color = NA) +
  geom_sf(data = states_sf, fill = NA, color = "black", size = 0.25) +
  # Remove grid lines from plot
  coord_sf(datum = NA) +
  #
  # THE FIRST OPTION of scale_fill_stepsn IS WHERE THEY ARE DIFFERENT
  # replaced n.breaks with breaks option
  #
  scale_fill_stepsn(breaks=data_levels, colors=level_colors, limits=c(-3,3), 
                    labels=scales::label_number(accuracy=0.1)) + 
  labs(title='This Data is Completely Random', 
       fill ='The Legend') + 
  theme_map() + 
  theme(legend.position = "bottom",
        legend.key.width=unit(1.5,"cm"),
        legend.box.background = element_rect(color="black", size=2),
        legend.title = element_text(face = "bold"),
        legend.spacing = unit(0.25,"cm"),
        legend.justification = "center",
        plot.title=element_text(hjust=0.5))  +
  guides(fill = guide_colorsteps(even.step=TRUE, 
                                 title.position="top", 
                                 title.hjust = 0.5,
                                 frame.colour = 'black', 
                                 barwidth=unit(250,'points'),
                                 axis.linewidth=unit(3,'points')))

This version yields the following image. Notice the colors in the legend are now different. enter image description here

Upvotes: 2

Views: 1762

Answers (1)

teunbrand
teunbrand

Reputation: 37933

The first option evenly spaces out 12 breaks from -3 to 3 which then exactly coincide with your colours. Whereas the second option sets unevenly spaced values with the exact colours falling in between some of the breaks. The (hidden) gradient is still evenly spaced though. To have the gradient spaced as your breaks, you need to set the values argument of the scale. Simplified example below.

library(ggplot2)

df <- data.frame(
  x = runif(100),
  y = runif(100),
  z = runif(100, -3, 3)
)

level_colors <- rgb(
  red   = c(0.67, 0.75, 0.84, 0.92, 1,    1,    0.8, 0.53, 0,   0,    0,   0),
  green = c(0.25, 0.4,  0.56, 0.71, 0.86, 1,    1,   0.95, 0.9, 0.75, 0.6, 0.48),
  blue  = c(0.11, 0.18, 0.25, 0.33, 0.4,  0.45, 0.4, 0.27, 0,   0,    0,   0)
)
data_levels <- c(-3,-1.5, -0.8, -0.5, -0.25,-0.1,0.1,0.25,0.5,.8,1.5,3)

ggplot(df, aes(x, y, fill = z)) +
  geom_point(shape = 21) +
  scale_fill_stepsn(breaks=data_levels, colors=level_colors, limits=c(-3,3), 
                    values = scales::rescale(data_levels),
                    labels=scales::label_number(accuracy=0.1))

Created on 2021-04-08 by the reprex package (v1.0.0)

Upvotes: 2

Related Questions