facet_wrap and assign colors to categorical variables in ggplot2

Viewed 285

I am trying to reproduce this graphic below on the COVID19 (first plot) using facet_wrap() but I cannot make the other background series visible in gray (second plot).

enter image description here

Second plot

library(dplyr)
library(httr)
library(readxl)
library(ggplot2)
library(ggrepel)
library(scales)
library(forcats)

url <- paste("https://www.ecdc.europa.eu/sites/default/files/documents/COVID-19-geographic-disbtribution-worldwide-",format(Sys.time(), "%Y-%m-%d"), ".xlsx", sep = "")
GET(url, authenticate(":", ":", type="ntlm"), write_disk(tf <- tempfile(fileext = ".xlsx")))
data <- read_excel(tf)
data$`Countries and territories` = fct_recode( data$`Countries and territories`, "Canada" ="CANADA")

days100 = data %>%
  rename(country = `Countries and territories`) %>%
  select(-Day, -Month, -Year) %>%
  arrange(country, DateRep) %>%  
  group_by(country) %>%  
  mutate(test = if_else(Cases >= 1, 
                        cumsum(Cases),0),
         logtest = if_else(test > 0, 
                           log10(test),0),
         dummy100 = if_else(test >= 100, 
                            1,0),
         num100 = if_else(dummy100 == 1, 
                          cumsum(dummy100),0),
         selec_count = if_else(country == "Ecuador",
                               1,
                               if_else(country == "Italy",
                                       2,
                                       if_else(country == "US",
                                               3,
                                               if_else(country == "China",
                                                       4,
                                                       0))))) %>%
  filter(country != 'Cases_on_an_international_conveyance_Japan',
         test >=100)

days100 = days100 %>% 
  mutate(fil_count = if_else(GeoId == "CL" | GeoId == "IT" | GeoId == "CN" | GeoId == "FR", 1, 0))

ggplot(data = days100, aes(x = num100, 
                           y = test, 
                           color = selec_count,
                           group = country)) +
  geom_line() +
  guides(color = F) +
  #scale_color_manual(values = c("1"="#5aae61", "2"="#7b3294", "3" = "red", "4" = "blue", "0"= "black")) +
  facet_wrap(~ country) +
  scale_x_continuous(expand = c(0, -1)) + 
  scale_y_continuous(trans="log10", 
                     labels = scales::comma,
                     limits = c(100, NA),
                     expand = expand_scale(mult = c(0, 0.05))) +  
  theme_bw() +
  ggrepel::geom_text_repel(data = days100 %>%
                             filter(fil_count==1 &
                                      DateRep == last(DateRep)),
                           aes(label = country))

enter image description here

Also I want to add manual colors for selec_count category so that each series can be better visualized using scale_color_manual().

Without facet_wrap()

enter image description here

1 Answers

The only way I can think of is to duplicate (with crossing or similar) the data across all available countries.

library(dplyr)
library(tidyr)
library(ggplot2)

# helpful to find the most-impacted countries with over 1000 cases
topdat <- dat %>%
  group_by(GeoId) %>%
  summarize(n=max(Cases)) %>%
  filter(n > 1000) %>%
  arrange(desc(n))

plotdat <- dat %>%
  mutate(
    `Countries and territories` =
      gsub("_", " ", 
           if_else(`Countries and territories` == "CANADA",
                   "Canada", `Countries and territories`))) %>%
  inner_join(., topdat, by = "GeoId") %>%
  arrange(DateRep) %>%
  group_by(GeoId) %>%
  filter(cumany(Cases > 100)) %>%
  mutate(
    ndays = as.numeric(difftime(DateRep, min(DateRep), units = "days")),
    ncases = cumsum(Cases),
    ndeaths = cumsum(Deaths),
    ismax = ncases == max(ncases)
  ) %>%
  crossing(., Country = unique(.$`Countries and territories`)) %>%
  mutate(
    col = case_when(
      `Countries and territories` == Country ~ 1L,
      GeoId %in% c("CN", "IT", "UK")         ~ 2L,
      TRUE                                   ~ 3L
    )
  )

firstpane <- plotdat %>%
  select(-Country) %>%
  filter(GeoId %in% c("CN", "IT", "UK")) %>%
  group_by(GeoId) %>%
  slice(which.max(ncases)) %>%
  crossing(., Country = unique(plotdat$`Countries and territories`))

ggplot(plotdat, mapping = aes(x = ndays, y = ncases, group = GeoId)) +
  geom_line(aes(color = factor(col)), data = ~ subset(., col == 3L)) +
  geom_line(aes(color = factor(col)), data = ~ subset(., col == 2L)) +
  geom_line(aes(color = factor(col)), data = ~ subset(., col == 1L)) +
  geom_text(aes(label = `Countries and territories`),
            hjust = 0, vjust = 1.2,
            data = subset(firstpane, Country == min(Country))) +
  geom_point(data = firstpane) +
  geom_point(color = "red", data = ~ subset(., ismax & col == 1L)) +
  facet_wrap(~ Country) +
  scale_y_continuous(trans = "log10", labels = scales::comma) +
  scale_color_manual(values = c("red", "gray50", "#bbbbbb88"), guide = FALSE) +
  labs(x = "Days since 100th case", y = NULL) +
  lims(x = c(1, 100))

covid plots of most-impacted countries

I did three geom_line to manually control the layering, so the red line is always on top. Otherwise, replace all three with geom_line(aes(color = factor(col))).

Related