cibr
cibr

Reputation: 473

Manipulate the extreme values used to develop fill in a map

I use ggplot2 and a package called rnaturalearth to develop a map of European countries, with different colour for each country dependent on each country's score.

The problem: I want to define which value ggplot2 uses when defining colours for each country. Now, ggplot2 uses the lowest and highest values to define the extremes of the scale; I want to define the extremes by myself (that is, use lower and higher extreme values and thereby manipulate the colours).

Data:

Data <- structure(list(iso_a2 = structure(c("AT", "BE", "CH", "CZ", "DE", 
"DK", "ES", "FI", "FR", "GB", "GR", "HU", "IE", "IT", "LU", "NL", 
"NO", "PL", "PT", "SE", "SI"), label = "Country", format.stata = "%2s"), 
    values = c(0.498210763764124, 0.39731454174295, 1.28199768407053, 
    -0.645219093750782, 0.0505441315876436, 1.91304897270232, 
    0.0802428116303039, 1.40711329859048, 0.0627266707521129, 
    0.191581882192026, 0.181001731032278, 0.356882787996765, 
    0.168525492955127, 0.276086485962419, 1.12477750307519, 0.892265586989629, 
    1.22957941831254, -0.942690852748792, -0.412722832958249, 
    1.34589962077675, -0.484403677107108)), row.names = c(NA, 
-21L), class = c("data.frame"))

This is the code I'm currently running:

# Load packages
library(tidyverse)
library(rnaturalearth)

# Use map from rnaturalearth, restrict to European countries
world  <- ne_countries(scale = "medium", returnclass = "sf")
Europe <- world[which(world$continent == "Europe"), ]

# Add Data to the map
Data <- merge(Europe, Data, by = "iso_a2", all = TRUE)

# Draw map with colours
ggplot(Data) +
    geom_sf(aes(fill = -values), size = 0.25) +
    coord_sf(xlim = c(-10.8, 32), ylim = c(35, 71.3), expand=FALSE) +
    theme(legend.position = "none") +
    theme(
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      axis.ticks = element_blank(),
      panel.grid.major = element_blank(),
      panel.background = element_blank()) +
    scale_fill_continuous(na.value = "grey90")

The result is:

Resulting map

I would like to manipulate these colours not by defining the colours per se, but by manipulating the scale ggplot2 uses (now presumable going from the lowest to the highest value in data$values).

Manipulating the colours directly by using scale_fill_gradient would not be a good idea since I am developing several maps and the whole idea is that a specific value should result in the same colour independently of what values are represented in the specific map.


EDIT. I have decided to use a simple solution, adding two rows to the data frame with constant extreme values across all maps.

Something like:


df_high <- data.frame("SJ",  1.63, 2.2)
df_low  <- data.frame("AZ", -1.5, -2.02)

names(df_high) <- c("cntry", "values_1", "values_2")
names(df_low)  <- c("cntry", "values_1", "values_2")

new_map_2010 <- rbind(old_map_2010, df_high, df_low)
new_map_2012 <- rbind(old_map_2012, df_high, df_low)

This is not a programmer's solution, but it is easier for me than what appears to be the alternative solution. Consequently, I am not able to decide whether the solution below by @Pedro Alencar works or not.

So PLEASE, if anyone tries Pedro Alencar's suggestion and finds it works, please tell me and I will accept his solution. For now, I can only upvote it.

(My own solution can easily be adapted by using code to identify the highest and lowest values across all data frames, then add new rows with these values - and add these specific rows to all data frames. All done with code, but still not really a programmer's solution...)

Upvotes: 2

Views: 204

Answers (1)

Pedro Alencar
Pedro Alencar

Reputation: 1089

Create a data frame with values (rounded) and colours:

df_colors <- data.frame(values_round = seq(round(min(Data$values, na.rm = T),2), 
                                    round(max(Data$values, na.rm = T),2),
                                    by = 0.01))
df_colors$color <- grDevices::colorRampPalette(brewer.pal(9, "Blues"))(nrow(df_colors))
df_colors$values_round <- as.integer(df_colors$values_round*100)

See that I turned the values to integer. This is because I was facing some rounding errors.

Then create a new column values_round in your original data frame (Data) and left_join with df_colors:

Data$values_round <- as.integer(round(Data$values, 2)*100)
Data <- left_join(Data, df_colors, by = 'values_round')

Now you can plot:

ggplot(Data) +
  geom_sf(aes(fill = color), size = 0.25) +
  coord_sf(xlim = c(-10.8, 32), ylim = c(35, 71.3), expand=FALSE) +
  theme(legend.position = "none") +
  theme(
    axis.text.x = element_blank(),
    axis.text.y = element_blank(),
    axis.ticks = element_blank(),
    panel.grid.major = element_blank(),
    panel.background = element_blank()) +
  scale_colour_identity(aesthetics = "fill")

Note that you'll need to identify the maximum and minimum for your entire dataset in order to get a fixed df_colors.

Upvotes: 1

Related Questions