thomasp85 / ggraph

Grammar of Graph Graphics

Home Page:https://ggraph.data-imaginist.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support for outlined node text (shadowtext)

pbreheny opened this issue · comments

First of all, thank you for the excellent work on this package! Enhancement suggestion here, which is to allow users access to the shadowtext package, either as an option to geom_node_text() (as with repel) or as a separate function. It makes a huge difference in readability. Compare these two figures:

Code is trivial, just replace the existing geom with geom=shadowtext::GeomShadowText, which is how I made the above figure.

I like this! You can actually simply use geom_shadowtext() since the layout coordinates of nodes are stored in a "hidden" data.frame.

library(igraph)
library(ggraph)

g <- sample_gnp(50, 0.7)
V(g)$name <- sapply(1:50, function(x) paste0(sample(LETTERS, 4), collapse = ""))
E(g)$weight <- runif(ecount(g))

ggraph(g,"stress") +
  geom_edge_link0(aes(edge_color = weight, edge_width = weight), show.legend = FALSE) +
  geom_node_point(size = 8, color = "#44a6c6") +
  # geom_node_text(aes(label=name),fontface="bold")+
  shadowtext::geom_shadowtext(aes(x, y, label = name), color = "black", size = 4, bg.colour = "white") +
  scale_edge_color_continuous(low = "grey66", high = "black") +
  scale_edge_width(range = c(0.1, 0.5)) +
  theme_graph() +
  coord_fixed()

Created on 2022-07-05 by the reprex package (v2.0.1)

Oh that's very cool...I didn't know it was possible to do this sort of thing with hidden data frames and ggplot.

By the way, one limitation of the above workaround is that it doesn't work with filter:

library(igraph)
library(ggraph)

g <- sample_gnp(50, 0.7)
V(g)$name <- sapply(1:50, function(x) paste0(sample(LETTERS, 4), collapse = ""))
V(g)$centrality <- eigen_centrality(g)$vector
E(g)$weight <- runif(ecount(g))

ggraph(g,"stress") +
  geom_edge_link0(aes(edge_color = weight, edge_width = weight), show.legend = FALSE) +
  geom_node_point(size = 8, color = "#44a6c6") +
  # geom_node_text(aes(label=name),fontface="bold")+
  shadowtext::geom_shadowtext(aes(x, y, label = name, filter = centrality > 0.8), color = "black", size = 4, bg.colour = "white") +
  scale_edge_color_continuous(low = "grey66", high = "black") +
  scale_edge_width(range = c(0.1, 0.5)) +
  theme_graph() +
  coord_fixed()

produces

Warning message:
Ignoring unknown aesthetics: filter 

and the figure looks the same (all nodes are labeled; no filter is applied).

filters do not exist for regular ggplot geoms. You need to do some more hacking to get this to work. I think it is best to create a new variable name_filtered which is the empty string if the filter is not fulfiled

library(igraph)
library(ggraph)

g <- sample_gnp(50, 0.7)
V(g)$name <- sapply(1:50, function(x) paste0(sample(LETTERS, 4), collapse = ""))
V(g)$centrality <- eigen_centrality(g)$vector
V(g)$name_filtered <- ifelse(V(g)$centrality>0.8,V(g)$name,"")
E(g)$weight <- runif(ecount(g))

ggraph(g,"stress") +
  geom_edge_link0(aes(edge_color = weight, edge_width = weight), show.legend = FALSE) +
  geom_node_point(size = 8, color = "#44a6c6") +
  shadowtext::geom_shadowtext(aes(x, y, label = name_filtered), 
                              color = "black", size = 4, bg.colour = "white") +
  scale_edge_color_continuous(low = "grey66", high = "black") +
  scale_edge_width(range = c(0.1, 0.5)) +
  theme_graph() +
  coord_fixed()

Actually, I just learned that geom_text_repel() accepts arguments bg.r and bg.color, allowing it to replicate the functionality of shadowtext. So, the following works -- note that I'm specifying repel=TRUE so that ggraph() uses geom_text_repel(), but setting force=0 so that it doesn't actually do any repelling.

ggraph(g,"stress") +
  geom_edge_link0(aes(edge_color = weight, edge_width = weight), show.legend = FALSE) +
  geom_node_point(size = 8, color = "#44a6c6") +
  geom_node_text(aes(label=name, filter = centrality > 0.8), repel=TRUE, bg.color="white", bg.r=0.15, force=0) +
  scale_edge_color_continuous(low = "grey66", high = "black") +
  scale_edge_width(range = c(0.1, 0.5)) +
  theme_graph() +
  coord_fixed()

I guess with this, we don't really need a modification to the package itself to accomplish what I originally wanted. This issue could be closed, although I'll let you do that (maybe you want to add an example like this to the documentation or something?).

Didn't know this either and this seems like an elegant enough solution for this problem