I have a data frame that looks like this. It contains the sunflower seed productivity of each country. I want to add next to this data polygon data so I can plot it with ggplot2.
I was told to use this site: https://observablehq.com/@ladataviz/wip-voronoi-data-generator, I want to understand how I can create polygons and plot a circular voronoi diagram.
I have created a similar post in the past, but my question here is very different. I want to find a way to create the polygon data
df <- data.frame(country = c("Ukraine", "Russia", "Argentina", "China", "Romania", "Other"),
prod = c(11.0, 10.6, 3.1, 2.4, 2.1, 15.3))
df
#> country prod
#> 1 Ukraine 11.0
#> 2 Russia 10.6
#> 3 Argentina 3.1
#> 4 China 2.4
#> 5 Romania 2.1
#> 6 Other 15.3
Created on 2023-01-20 with reprex v2.0.2
If add polygons to my data should look like this:
x y path split group value
1 472.0117 220.08122253 0 Ukraine Ukraine 11
2 471.8336 217.18476868 1 Ukraine Ukraine 11
3 471.6556 214.28833008 2 Ukraine Ukraine 11
4 471.4776 211.39187622 3 Ukraine Ukraine 11
5 471.2996 208.49542236 4 Ukraine Ukraine 11
6 471.1216 205.59896851 5 Ukraine Ukraine 11
I want my data to look like this.
Having become hooked by this problem, I have written a small package to address it which you can install via
devtools::install_github("AllanCameron/VoronoiPlus")
It can deal with Voronoi maps (as in the question on this page) with a call to voronoi_map
, which takes the weights and group labels. It can also take an arbitrary shape to act as the boundary of the tiles, though defaults to the unit circle if this is missing.
library(VoronoiPlus)
res <- voronoi_map(values = df$prod, groups = df$country)
plot(res)
You can extract the polygons as a data frame from this object with:
polys <- get_polygons(res)
head(polys)
#> geom x y group value
#> 1 1 -0.6436006 -0.7649495 Ukraine 11
#> 2 1 -0.6691306 -0.7431448 Ukraine 11
#> 3 1 -0.7071068 -0.7071068 Ukraine 11
#> 4 1 -0.7431448 -0.6691306 Ukraine 11
#> 5 1 -0.7771460 -0.6293204 Ukraine 11
#> 6 1 -0.8090170 -0.5877853 Ukraine 11
The package can also handle arbitrarily nested groups to produce a genuine treemap via the voronoi_treemap
function, which employs a formula interface (the weights on the left and grouping variables on the right)
df$region <- c("Europe", "Europe", "Other", "Other", "Europe", "Other")
dat <- voronoi_treemap(prod ~ region + country, data = df)
head(dat)
#> x y group value parent level
#> 1 0.6657716 -0.7460137 Europe 23.7 root 1
#> 2 0.6293204 -0.7771460 Europe 23.7 root 1
#> 3 0.5877853 -0.8090170 Europe 23.7 root 1
#> 4 0.5446390 -0.8386706 Europe 23.7 root 1
#> 5 0.5000000 -0.8660254 Europe 23.7 root 1
#> 6 0.4539905 -0.8910065 Europe 23.7 root 1
This allows a nested treemap as follows:
library(tidyverse)
ggplot(dat[dat$level == 2,], aes(x, y, label = group)) +
geom_polygon(aes(fill = parent)) +
geom_polygon(fill = "white", aes(group = group, alpha = group),
color = "black") +
geom_text(data = . %>% group_by(group) %>%
summarize(x = mean(x), y = mean(y))) +
scale_alpha_discrete(guide = "none") +
coord_equal() +
theme_void()
The algorithm used is also a brute-force method at present, though a little different to the one demonstrated by Robert Hijmans. I am working on a more directed method to improve convergence times.
A major caveat is that the package is in a nascent stage and has not been properly tested or documented at the time of writing.