I have a data frame like this:
structure(list(x = c(65.11, 65.11, 65.11, 43.72, 43.72, 43.72,
43.72, 43.72, 43.72, 43.72, 43.72, 59.89, 59.89, 59.89, 59.89,
36.24, 36.24, 36.24, 36.24, 67.88, 37.89, 37.89, 37.89, 56.05,
56.05, 56.05, 60.16, 60.16, 60.16, 30.92, 30.92, 30.92, 47.55,
47.55, 47.55), y = c(32.17, 32.17, 32.17, 56.09, 56.09, 56.09,
56.09, 56.09, 56.09, 56.09, 56.09, 15.64, 15.64, 15.64, 15.64,
81.61, 81.61, 81.61, 81.61, 56.96, 21.69, 21.69, 21.69, 86.47,
86.47, 86.47, 68.31, 68.31, 68.31, 51.56, 51.56, 51.56, 43.44,
43.44, 43.44), xend = c(59.89, 60.16, 43.72, 59.89, 37.89, 56.05,
60.16, 65.11, 36.24, 30.92, 47.55, 37.89, 65.11, 67.88, 30.92,
37.89, 56.05, 30.92, 47.55, 60.16, 43.72, 59.89, 30.92, 43.72,
36.24, 60.16, 43.72, 67.88, 47.55, 43.72, 36.24, 37.89, 59.89,
37.89, 43.72), yend = c(15.64, 68.31, 56.09, 15.64, 21.69, 86.47,
68.31, 32.17, 81.61, 51.56, 43.44, 21.69, 32.17, 56.96, 51.56,
21.69, 86.47, 51.56, 43.44, 68.31, 56.09, 15.64, 51.56, 56.09,
81.61, 68.31, 56.09, 56.96, 43.44, 56.09, 81.61, 21.69, 15.64,
21.69, 56.09), node = c("484", "484", "484", "309", "309", "309",
"309", "309", "309", "309", "309", "740", "740", "740", "740",
"151", "151", "151", "151", "11", "26", "26", "26", "991", "991",
"991", "731", "731", "731", "714", "714", "714", "99", "99",
"99")), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA,
-35L))
I can plot the graph like below:
df %>%
ggplot() +
geom_point(aes(x, y), shape = 21, size = 5, stroke = 1, colour = 'black', fill = 'blue') +
geom_segment(aes(x = x, y = y, xend = xend, yend = yend),
colour = 'red')
But this is in fact directed graph, so each segments has two directed components overlapping. I have tried to fix it with position_dodge2
, but the following attempt is not as neat as needed:
df %>%
ggplot() +
geom_point(aes(x, y), shape = 21, size = 5, stroke = 1, colour = 'black', fill = 'blue') +
geom_segment(aes(x = x, y = y, xend = xend, yend = yend),
colour = 'red',
position = position_dodge2(width = 3))
I'd love to make the overlapping segments parallel, probably dodging their ends a little from the node position. How can I achieve this?
If you don't mind a bit of data wrangling, you could use the 'ggraph' package to do ggplot2 graphs. The approach below assumes that a node can be unique identified by x or xend positions.
library(ggraph)
library(igraph)
nodes <- unique(df$x, df$xend)
elist <- cbind(df$node[match(df$x, nodes)],
df$node[match(df$xend, nodes)])
gra <- graph_from_edgelist(elist)
lay <- create_layout(gra, "auto")
lay$x <- df$x[match(lay$name, df$node)]
lay$y <- df$y[match(lay$name, df$node)]
ggraph(lay) +
geom_edge_parallel(end_cap = circle(.5), start_cap = circle(.5),
colour = "red",
arrow = arrow(length = unit(2, 'mm'), type = 'closed')) +
geom_node_point(shape = 21, size = 5, stroke = 1, colour = 'black', fill = 'blue')
EDIT: I found it easiest to manipulate edge aesthetics at the graph stage, while node aesthetics can be easily manipulated at the layout stage. Example:
gra <- graph_from_edgelist(elist)
E(gra)$size <- sample(1:10, length(E(gra)), replace = T)
lay <- create_layout(gra, "auto")
lay$x <- df$x[match(lay$name, df$node)]
lay$y <- df$y[match(lay$name, df$node)]
lay$size <- sample(1:10, nrow(lay), replace = T)
ggraph(lay) +
geom_edge_parallel(end_cap = circle(.5), start_cap = circle(.5),
colour = "red", aes(edge_width = size),
arrow = arrow(length = unit(2, 'mm'), type = 'closed')) +
geom_node_point(shape = 21, aes(size = size), stroke = 1, colour = 'black', fill = 'blue')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With