I have a data.frame
where each row defines a composition of a group, and I would like to plot them all as pie charts on a cartesian plane.
Here is the example composition data.frame
:
library(dplyr)
set.seed(1)
composition.df <- data.frame(x = runif(10, 0, 100), y = runif(10, 0, 100),
y.a_1 = as.integer(runif(10, 0, 100)), y.a_2 = as.integer(runif(10, 0, 100)), y.a_3 = as.integer(runif(10, 0, 100)), y.a_4 = as.integer(runif(10, 0, 100)),
y.b_1 = as.integer(runif(10, 0, 100)), y.b_2 = as.integer(runif(10, 0, 100)), y.b_3 = as.integer(runif(10, 0, 100)), y.b_4 = as.integer(runif(10, 0, 100)),
o.a_1 = as.integer(runif(10, 0, 100)), o.a_3 = as.integer(runif(10, 0, 100)), o.a_4 = as.integer(runif(10, 0, 100)),
o.b_1 = as.integer(runif(10, 0, 100)), o.b_3 = as.integer(runif(10, 0, 100)), o.b_4 = as.integer(runif(10, 0, 100))) %>%
dplyr::mutate(size = runif(10, 0.002, 0.01)*(y.a_1 + y.a_2 + y.a_3 + y.a_4 + y.b_1 + y.b_2 + y.b_3 + y.b_4 + o.a_1 + o.a_3 + o.a_4 + o.b_1 + o.b_3 + o.b_4))
Each group/pie is assigned to one of three classes:
composition.df$class <- sample(c("A", "B", "C"), nrow(composition.df), replace = T)
In addition, some of the groups/pies are connected by edges/segments. Here I'm creating an example edges data.frame
:
edge.idx.df <- t(combn(1:nrow(composition.df), 2)) %>% as.data.frame() %>%
filter(V1 != V2) %>%
sample_n(20)
edge.df <- do.call(rbind, lapply(1:nrow(edge.idx.df), function(i)
data.frame(xstart = composition.df$x[edge.idx.df$V1[i]], ystart = composition.df$y[edge.idx.df$V1[i]], xend = composition.df$x[edge.idx.df$V2[i]], yend = composition.df$y[edge.idx.df$V2[i]])
)) %>% mutate(weight = runif(nrow(edge.idx.df), 0, 1))
In order to plot composition.df
as pie charts I'm following this post, where I'm converting composition.df
to a polygons data.frame
:
composition2polygon <- function(x, y, size, samples, n, rownum, class)
{
angles <- c(0,2*pi*cumsum(n)/sum(n))
do.call("rbind",Map(function(a1,a2,s){
xvals <- c(0,sin(seq(a1,a2,len=30))*size,0)+x
yvals <- c(0, cos(seq(a1,a2,len=30))*size,0)+y
data.frame(x=xvals,y=yvals,sample=s,rownum=rownum,class=class)
}, head(angles,-1),tail(angles,-1),samples))
}
polygon.df <- composition.df %>%
mutate(r = row_number()) %>%
rowwise() %>%
group_map(~ with(.x, composition2polygon(x, y,
size, c("y.a_1", "y.a_2", "y.a_3", "y.a_4", "y.b_1", "y.b_2", "y.b_3", "y.b_4", "o.a_1", "o.a_3", "o.a_4", "o.b_1", "o.b_3", "o.b_4"),
c(y.a_1, y.a_2, y.a_3, y.a_4, y.b_1, y.b_2, y.b_3, y.b_4, o.a_1, o.a_3, o.a_4, o.b_1, o.b_3, o.b_4), r, class))) %>%
bind_rows() %>%
mutate(animal = gsub("\\.a","",sample) %>% gsub("\\.b","",.)) %>% unique()
And setting the sample
, animal
, and class
columns as factor
s in order to color code them
polygon.df$sample <- factor(polygon.df$sample, levels = c("y.a_1", "y.a_2", "y.a_3", "y.a_4", "y.b_1", "y.b_2", "y.b_3", "y.b_4", "o.a_1", "o.a_3", "o.a_4", "o.b_1", "o.b_3", "o.b_4"))
polygon.df$animal <- factor(polygon.df$animal, levels = c("y_1", "y_2", "y_3", "y_4", "o_1", "o_3", "o_4"))
polygon.df$population <- factor(gsub("^y\\.","",polygon.df$sample) %>% gsub("^o\\.","",.) %>% gsub("_\\d+$","",.),levels=c("a","b"))
polygon.df$class <- factor(polygon.df$class,levels=c("A","B","C"))
Plotting only the polygons
works fine:
library(ggplot2)
ggplot(polygon.df, aes(x, y, color = class, fill = animal, group = interaction(sample, rownum))) +
geom_polygon(linewidth = 1, linetype = "dotted") +
scale_fill_manual(values = c(y_1="#66C2A5", y_2="#FC8D62", y_3="#8DA0CB", y_4="#E78AC3", o_1="#A6D854", o_3="#FFD92F", o_4="#E5C494")) +
scale_color_manual(values = c(A="blue", B="red", C="green")) +
guides(pattern = guide_legend(override.aes = list(fill = "white"))) + coord_equal() + theme_void()
But if I try to add the edges
with this:
ggplot(polygon.df, aes(x, y, color = class, fill = animal, group = interaction(sample, rownum))) +
geom_polygon(linewidth = 1, linetype = "dotted") +
geom_segment(aes(x = edge.df$xstart, y = edge.df$ystart, xend = edge.df$xend, yend = edge.df$yend),
color = "darkgray", linewidth = edge.df$weight, alpha = 0.1, inherit.aes = F) +
scale_fill_manual(values = c(y_1="#66C2A5", y_2="#FC8D62", y_3="#8DA0CB", y_4="#E78AC3", o_1="#A6D854", o_3="#FFD92F", o_4="#E5C494")) +
scale_color_manual(values = c(A="blue", B="red", C="green")) +
guides(pattern = guide_legend(override.aes = list(fill = "white"))) + coord_equal() + theme_void()
I get this error:
Error in `geom_segment()`:
! Problem while computing aesthetics.
ℹ Error occurred in the 2nd layer.
Caused by error in `check_aesthetics()`:
! Aesthetics must be either length 1 or the same as the data (4340)
✖ Fix the following mappings: `x`, `y`, `xend`, and `yend`
Run `rlang::last_trace()` to see where the error occurred.
Any idea how to add the edges
?
Instead of passing the columns to the aesthetics using edge.df$...
, use the data=
argument and get rid of edge.df$...
:
library(ggplot2)
ggplot(
polygon.df,
aes(x, y,
color = class, fill = animal,
group = interaction(sample, rownum)
)
) +
geom_polygon(linewidth = 1, linetype = "dotted") +
geom_segment(
data = edge.df,
aes(
x = xstart, y = ystart, xend = xend, yend = yend,
linewidth = weight
),
color = "darkgray", alpha = 0.1, inherit.aes = FALSE
) +
scale_fill_manual(
values = c(
y_1 = "#66C2A5", y_2 = "#FC8D62",
y_3 = "#8DA0CB", y_4 = "#E78AC3",
o_1 = "#A6D854", o_3 = "#FFD92F",
o_4 = "#E5C494"
)
) +
scale_color_manual(
values = c(A = "blue", B = "red", C = "green")
) +
coord_equal() +
theme_void()