0

I would like to create a waffle chart and use country flags in place of squares. Here is my current attempt using ggwaffle:

library(ggplot2)
library(emojifont)
library(ggwaffle)

iris2 <- iris
iris2$Species <- c("", "", "")

waffle_data <- waffle_iron(iris2, aes_d(group = Species))
waffle_data$label = fontawesome('fa-flag')

ggplot(waffle_data, aes(x, y, colour = group)) +
  geom_text(aes(label=label), family='fontawesome-webfont', size=7) +
  coord_equal() +
  scale_colour_waffle() +
  theme_waffle()

Created on 2023-06-18 with reprex v2.0.2

Is it possible at all with ggwaffle? I was also trying to play with ggflags but I don't see how to make these two compatible because ggflags replace points in a scatter plot.

I am also aware that Windows does not ship with country flag unicode emojis for political reasons, so base unicode wouldn't work (as in one of my previous attempts using the waffle package). So it would need to be a font that includes country flags or images thereof. Thanks for any help.

rempsyc
  • 785
  • 5
  • 24

1 Answers1

1

Using ggflags you could set the x and y coordinates to replicate a grid.

library(ggflags)
library(ggplot2)

df1 <- data.frame(x=rep(1:15, each = 10),
                  y=rep(1:10, 15),
                  country = c(rep("us", 75), rep("ca", 50), rep("gb", 25)))

ggplot(df1, aes(x, y, country=country)) + 
  geom_flag() +
  scale_country() +
  theme_void() +
  theme(legend.position = "bottom")

Created on 2023-06-18 with reprex v2.0.2

Here's a go at a function which can automate this, it's more than a bit hacky but may be useful as a starter...

library(ggflags)
library(ggplot2)



``` r
#' @param in_map_var string Vector of elements to be mapped to flags
#' @param len_x integer Length of x axis, number of flags on x axis
#' @param na_flag string ID of a flag which is nominally used for filling in the empty grid spaces, This is a hack.

waffle_map <- function(in_map_var, len_x = NA, na_flag = "ac"){

  # work out grid dimensions  
  var_count <- length(in_map_var)
  
  if(is.na(len_x)){
    x_count <- ceiling(sqrt(var_count))
  } else {
    x_count <- len_x
  }

  
  y_count <- ceiling(var_count / x_count)
  grid_count <- x_count * y_count
  
  df <- 
    data.frame(x = rep(1:y_count, each = x_count),
               y = rep(1:x_count, y_count),
               country = c(in_map_var, rep(na_flag, grid_count - var_count))
               )

  country_4legend <- unique(df$country)[unique(df$country) != na_flag]
  
  
  
  p <- 
    ggplot(df, aes(x, y, country = country)) + 
    geom_flag(size = 8) +
    scale_country(breaks = country_4legend) +
    theme_void() +
    theme(legend.position = "bottom")

    if(grid_count > var_count){
    p <- 
      p +
      geom_point(data = df[var_count:grid_count, ], aes(x, y), colour = "white", size = 10)
  }
                 
  return(p)
  
}

var_2map <- c(rep("us", 75), rep("ca", 50), rep("gb", 25))

waffle_map(var_2map, len_x = 15)


waffle_map(var_2map)

Created on 2023-06-18 with reprex v2.0.2

Peter
  • 11,500
  • 5
  • 21
  • 31
  • Very nice and straightforward. But how to add the legend back for the country names? I tried `show.legend = TRUE` and it does not change anything :/ – rempsyc Jun 18 '23 at 16:16
  • It would be cool if we could find a way to convert the original data to this sort of x and y coordinates data automatically since in my use case it is a dynamic table that updates automatically based on new data every week so I could not do it manually every time. Will try to think of something. – rempsyc Jun 18 '23 at 16:19
  • Updated to include a legend. I'll have a think about a function to generate an x, y coordinates grid. – Peter Jun 18 '23 at 16:58
  • Awesome, thanks for editing your answer! I had tried `theme(legend.position = "right")` but was missing the `scale_country()` bit before. Cool stuff – rempsyc Jun 18 '23 at 17:00
  • For future readers, note that although the `scale_country()` function is used in the `ggflags` package README, it is not documented: `> ?ggflags::scale_country No documentation for ‘scale_country’ in specified packages and libraries: you could try ‘??scale_country’` – rempsyc Jun 18 '23 at 17:05
  • 1
    Have provided a rather hacky function to manage automatic generation of a grid. Not tested this for any other cases apart from the two cases illustrated. Hopefully it provides a starter for 10. – Peter Jun 18 '23 at 18:39
  • That's pretty amazing! You could probably submit a PR to ggflags with this... Only thing is that to make it a regular waffle we would also need an `len_y` argument to make it a 10x10 square so that each flag = 1% of the data. – rempsyc Jun 18 '23 at 19:33
  • Here's a way to modify the data set within the function for this purpose: `library(dplyr) var_2map <- c(rep("us", 75), rep("ca", 50), rep("gb", 25)) length(var_2map) #> [1] 150 var_2map <- data.frame(country = var_2map) my_prop <- var_2map %>% count(country) %>% mutate(n2 = round(n/nrow(var_2map) * 100)) my_prop #> country n n2 #> 1 ca 50 33 #> 2 gb 25 17 #> 3 us 75 50 var_2map2 <- lapply(seq_len(nrow(my_prop)), \(x) { rep(my_prop$country[x], my_prop$n2[x])}) %>% unlist length(var_2map2) #> [1] 100` – rempsyc Jun 18 '23 at 20:01
  • I have promoted your great answer on Twitter: https://twitter.com/RemPsyc/status/1673444697715494913 – rempsyc Jun 26 '23 at 21:35