Hot questions for Using Ggplot2 in ggraph

Question:

Can the letter "a" be removed from the legend associated with e.g. a fill or colour aesthetic, in a ggraph network plot, like in the simple example below ?

library(igraph)
library(ggraph)

g1 <- make_ring(6)
vertex_attr(g1) <- list(name = LETTERS[1:6],  type =rep(c("typeA", "typeB", "typeC"), 2))

ggraph(g1) + geom_node_label(aes(label = name, fill = type)) + 
  geom_edge_diagonal() + theme_graph()

In the case of geom_text, show.legend = FALSE solves it,

Remove 'a' from legend when using aesthetics and geom_text

but adding show.legend = FALSE within geom_node_label(), removes the legend completely.

is there any solution for this in ggraph?


Answer:

Answer to the original question based on comments above: The following line of code has to be added on the top of the script as per @user20650 solution.

library(grid)
GeomLabel$draw_key <- function (data, params, size) { draw_key_rect(data) }

If repel = TRUE argument is used inside geom_node_label, then in addition to the above GeomLabelRepel$draw_key <- GeomLabel$draw_key needs to be added.

Question:

I am having a little trouble understanding the difference between ggplot2 and ggraph with respect to facetting. Consider the following data:

library(igraph)
library(ggraph)
library(ggplot2)

df <- structure(list(x = c("H001", "H024", "H090", "H090", "H098", 
                     "H103", "H126", "H152", "H155", "H155", "B002", "B011", "B075", 
                     "B092", "M002", "M002", "M002", "M050", "M050", "M085", "M085", 
                     "M247", "M247", "M247"), 
               y = c("H103", "H126", "H152", "H155","H155", "H001", "H024", "H090", "H090", "H098", "B092", "B075", 
                      "B011", "B002", "M050", "M085", "M247", "M002", "M247", "M002", "M247", "M002", "M050", "M085"), 
               r = c(0.963248925980302, 0.991452643542894, 0.965947578382865, 
                     0.963234153063794, 0.962277411462605, 0.963248925980302, 
                     0.991452643542894, 0.965947578382865, 0.963234153063794, 
                     0.962277411462605, 0.960948147492217, 0.957371360458182, 
                     0.957371360458182, 0.960948147492217, 0.96976135236222, 
                     0.977435712803837, 0.997037031981303, 0.96976135236222, 
                     0.978553503235858, 0.977435712803837, 0.992741796542001, 
                     0.997037031981303, 0.978553503235858, 0.992741796542001), 
               facet_var = c("08MH", "08MH", "08MH", "08MH", "08MH", "08MH", "08MH", "08MH", "08MH", 
                             "08MH", "08HB", "08HB", "08HB", "08HB", "08NM", "08NM", "08NM", "08NM", 
                             "08NM", "08NM", "08NM", "08NM", "08NM", "08NM")), 
          class = "data.frame", row.names = c(NA, -24L), .Names = c("x", "y", "r", "facet_var")
)

If I plot the data using a simple ggplot I can facet in a way that I consider "normal":

ggplot(df, aes(x = x, y = y)) +
         geom_point() +
         facet_wrap(~facet_var)

That is each level of facet_var has some points associated with it and ONLY those point are plotted in the corresponding facet. Now if I try a similar approach using ggraph I encounter what to me is odd behaviour (though I obviously acknowledge it simply reflect a lack of understanding):

graph_cors <- graph_from_data_frame(df, directed = FALSE)

ggraph(graph_cors) +
  geom_edge_link(aes(edge_alpha = abs(r), color = r), edge_width = 2) +
  guides(edge_alpha = "none") +
  scale_edge_colour_gradientn(limits = c(-1, 1), colors = topo.colors(5)) +
  geom_node_point(color = "black", size = 5) +
  geom_node_text(aes(label = name), repel = TRUE) +
  facet_edges(~facet_var)

So that all nodes are plotted in each facet though edges respect the facetting. Can anyone outline what I'm doing wrong here?

Session Info
> sessionInfo()
R version 3.4.2 (2017-09-28)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

Matrix products: default

locale:
[1] LC_COLLATE=English_Canada.1252  LC_CTYPE=English_Canada.1252    LC_MONETARY=English_Canada.1252 LC_NUMERIC=C                   
[5] LC_TIME=English_Canada.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] ggraph_1.0.0    ggplot2_2.2.1   igraph_1.1.2    testthat_1.0.2  devtools_1.13.3

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.13          compiler_3.4.2        plyr_1.8.4            bindr_0.1             viridis_0.4.0        
 [6] tools_3.4.2           digest_0.6.12         viridisLite_0.2.0     memoise_1.1.0         tibble_1.3.4         
[11] gtable_0.2.0          pkgconfig_2.0.1       rlang_0.1.2           rstudioapi_0.7.0-9000 ggrepel_0.7.0        
[16] yaml_2.1.14           bindrcpp_0.2          gridExtra_2.3         withr_2.0.0           dplyr_0.7.4          
[21] grid_3.4.2            glue_1.1.1            R6_2.2.2              tweenr_0.1.5          udunits2_0.13        
[26] magrittr_1.5          scales_0.5.0          fortunes_1.5-4        MASS_7.3-47           units_0.4-6          
[31] assertthat_0.2.0      swtext_0.0.1          ggforce_0.1.1         colorspace_1.3-2      lazyeval_0.2.0       
[36] munsell_0.4.3         crayon_1.3.4    

Answer:

You need to assign the relevant variable as a node attribute to facet by nodes. Your facet_var variable is an edge attribute; if you assign it to each node in each dyad as node attribute, the faceting works.

facet_graph facets by two variables (e.g. an edge attribute and a node attribute). Seems like facet_nodes is what you want, but you need to send a node attribute to the argument, not an edge attribute.

Currently, your igraph object only has an edge attribute:

> graph_cors
IGRAPH UN-- 16 24 -- 
+ attr: name (v/c), r (e/n), facet_var (e/c)
+ edges (vertex names):
 [1] H001--H103 H024--H126 H090--H152 H090--H155 H098--H155 H001--H103 H024--H126 H090--H152
 [9] H090--H155 H098--H155 B002--B092 B011--B075 B011--B075 B002--B092 M002--M050 M002--M085
[17] M002--M247 M002--M050 M050--M247 M002--M085 M085--M247 M002--M247 M050--M247 M085--M247

graph_cors <- graph_from_data_frame(df, directed = FALSE)

facet_nodes <- cbind(c(df[,1], df[,2]), 
                     df[match(c(df[,1], df[,2]), df[,1]),4])

# Assign the facet_var variable to corresponding dyad pairs
V(graph_cors)$facet_node <- facet_nodes[match(V(graph_cors)$name, facet_nodes[,1]),2]

Now, the igraph object has a node attribute ("facet_node"):

> graph_cors
IGRAPH UN-- 16 24 -- 
+ attr: name (v/c), facet_node (v/c), r (e/n), facet_var (e/c)
+ edges (vertex names):
 [1] H001--H103 H024--H126 H090--H152 H090--H155 H098--H155 H001--H103 H024--H126 H090--H152
 [9] H090--H155 H098--H155 B002--B092 B011--B075 B011--B075 B002--B092 M002--M050 M002--M085
[17] M002--M247 M002--M050 M050--M247 M002--M085 M085--M247 M002--M247 M050--M247 M085--M247

# Use the facet_nodes argument with the nodal attribute
ggraph(graph_cors) +
  geom_edge_link(aes(edge_alpha = abs(r), color = r), edge_width = 2) +
  guides(edge_alpha = "none") +
  scale_edge_colour_gradientn(limits = c(-1, 1), colors = topo.colors(5)) +
  geom_node_point(color = "black", size = 5) +
  geom_node_text(aes(label = name), repel = TRUE) +
  facet_nodes(~facet_node)

Question:

I'm trying to plot a network with ggraph and I'd like to add a circle around the graph, with the edges and nodes lying centered inside the circle.

Drawing the circle works just fine with the following code (adapted from Draw a circle with ggplot2)

gg_circle <- function(r, xc, yc, color = "black", fill = NA, lty = NA, size = NA, ...) {
  x <- xc + r*cos(seq(0, pi, length.out = 100))
  ymax <- yc + r*sin(seq(0, pi, length.out = 100))
  ymin <- yc + r*sin(seq(0, -pi, length.out = 100))
  annotate("ribbon", x = x, ymin = ymin, ymax = ymax, 
           color = color, fill = fill, lty = lty, size = size, ...)
} 

But I can't manage to match the position of the network layer(s) with the position of the circle, which results in both nodes and edges lying partially outside the circle:

That's the crucial part of the code as it is right now (using highschool from ggraph as an example dataset for reproducibility purposes):

library(ggraph)
library(igraph)
graph <- graph_from_data_frame(highschool)

ggraph(graph, layout = "fr") +
  geom_edge_link() +
  geom_node_point() +
  geom_node_text(aes(label = name), 
                 check_overlap = TRUE, repel = TRUE, 
                 nudge_x = 0.1, nudge_y = 0.1) +
  gg_circle(r = 11, xc = 0, yc = 0, lty = 1, size = 0.2) +
  theme(axis.ticks.length = unit(0, "cm"),
        legend.position = "none",
        plot.margin = unit(c(0, 0, 0, 0), "cm"),  
        panel.spacing = unit(c(0, 0, 0, 0), "cm")) +
  coord_fixed()

Any ideas or suggestions on how to fix this? Thanks in advance!


Answer:

I would try to use the node positions to find acceptable values for r, xc, and yc.

Step 1. Create plot (without the circle):

set.seed(9) # for reproducibility

p <- ggraph(graph, layout = "fr") +
  geom_edge_link() +
  geom_node_point() +
  geom_node_text(aes(label = name),
                 check_overlap = TRUE,
                 repel = TRUE,
                 nudge_x = 0.1,
                 nudge_y = 0.1) +
  theme(axis.ticks.length = unit(0, "cm"),
        legend.position = "none",
        plot.margin = unit(c(0, 0, 0, 0), "cm"),  
        panel.spacing = unit(c(0, 0, 0, 0), "cm")) +
  coord_fixed()

Step 2. Get data from the plot's geom_node_point() layer (the 2nd layer in this case). Modify the gg_circle code to take this dataframe as input, & calculate the appropriate circle centre coordinates / radius:

p.positions <- layer_data(p, i = 2L)

gg_circle_from_position <- function(data, 
                                    color = "black", fill = NA, 
                                    lty = NA, size = NA, ...){

  coord.x <- data[, 'x']
  coord.y <- data[, 'y']

  xc = mean(range(coord.x))
  yc = mean(range(coord.y))
  r = max(sqrt((coord.x - xc)^2 + (coord.y - yc)^2)) * 1.05
  # expand radius by 5% so that no node sits exactly on the line;
  # increase from 1.05 to some larger number if more buffer is desired.

  # no change to this part
  x <- xc + r*cos(seq(0, pi, length.out = 100))
  ymax <- yc + r*sin(seq(0, pi, length.out = 100))
  ymin <- yc + r*sin(seq(0, -pi, length.out = 100))
  annotate("ribbon", x = x, ymin = ymin, ymax = ymax, 
           color = color, fill = fill, lty = lty, size = size, ...)

}

Step 3. Add circle to plot:

p + gg_circle_from_position(data = p.positions, lty = 1, size = 0.2)

Question:

Is it possible to draw an arrowhead in the middle of an edge using ggraph::geom_edge_link(), and if so how can this be done?

Rather than something like this with the arrowheads drawn at the ends of the edges:

library(ggraph)
library(tidygraph)
library(dplyr)

create_notable('bull') %>%
  ggraph(layout = 'graphopt') + 
  geom_edge_link(arrow = arrow(length = unit(4, 'mm')), 
                 end_cap = circle(3, 'mm')) +  
  geom_node_point(size = 5) +
  theme_graph()

I'd like to be able to achieve something like this:

I've checked the ggraph::geom_edge_link() and grid::arrow() documentation but couldn't see anything obvious about how to do this.


Answer:

I haven't used the ggraph package myself, but based on my understanding of the underlying grobs, you can try the following:

Step 1. Run the following line in your console:

trace(ggraph:::cappedPathGrob, edit = TRUE)

Step 2. In the pop-up window, change the last chunk of code from this:

if (is.null(start.cap) && is.null(end.cap)) {
  if (constant) {
    grob(x = x, y = y, id = id, id.lengths = NULL, arrow = arrow, 
         name = name, gp = gp, vp = vp, cl = "polyline")
  }
  else {
    grob(x0 = x[!end], y0 = y[!end], x1 = x[!start], 
         y1 = y[!start], id = id[!end], arrow = arrow, 
         name = name, gp = gp, vp = vp, cl = "segments")
  }
} else {
  gTree(x = x, y = y, id = id, arrow = arrow, constant = constant, 
        start = start, end = end, start.cap = start.cap, 
        start.cap2 = start.cap2, start.captype = start.captype, 
        end.cap = end.cap, end.cap2 = end.cap2, end.captype = end.captype, 
        name = name, gp = gp, vp = vp, cl = "cappedpathgrob")
}

To this:

if(is.null(arrow)) {
  # same code as before, if no arrow needs to be drawn
  if (is.null(start.cap) && is.null(end.cap)) {
    if (constant) {
      grob(x = x, y = y, id = id, id.lengths = NULL, arrow = arrow, 
           name = name, gp = gp, vp = vp, cl = "polyline")
    }
    else {
      grob(x0 = x[!end], y0 = y[!end], 
           x1 = x[!start], y1 = y[!start], 
           id = id[!end], arrow = arrow, 
           name = name, gp = gp, vp = vp, cl = "segments")
    }
  } else {
    gTree(x = x, y = y, id = id, arrow = arrow, constant = constant, 
          start = start, end = end, start.cap = start.cap, 
          start.cap2 = start.cap2, start.captype = start.captype, 
          end.cap = end.cap, end.cap2 = end.cap2, end.captype = end.captype, 
          name = name, gp = gp, vp = vp, cl = "cappedpathgrob")
  }
} else {
  # split x/y/ID values corresponding to each ID into two halves; first half to
  # end with the specified arrow aesthetics; second half (with a repetition of the
  # last value from first half, so that the two halves join up) has arrow set to NULL.
  id.split = split(id, id)
  id.split = lapply(id.split, 
                    function(i) c(rep(TRUE, ceiling(length(i)/2)), 
                                  rep(FALSE, length(i) - ceiling(length(i)/2))))
  id.split = unsplit(id.split, id)
  id.first.half = which(id.split == TRUE)
  id.second.half = which(id.split == FALSE |
                           (id.split == TRUE & c(id.split[-1], FALSE) == FALSE))

  if (is.null(start.cap) && is.null(end.cap)) {
    if (constant) {
      gList(grob(x = x[id.first.half], y = y[id.first.half], id = id[id.first.half], 
                 id.lengths = NULL, arrow = arrow, 
                 name = name, gp = gp, vp = vp, cl = "polyline"),
            grob(x = x[id.second.half], y = y[id.second.half], id = id[id.second.half], 
                 id.lengths = NULL, arrow = NULL, 
                 name = name, gp = gp, vp = vp, cl = "polyline"))

    }
    else {
      # I haven't modified this chunk as I'm not familiar with ggraph,
      # & haven't managed to trigger constant == FALSE condition yet
      # to test out code modifications here
      grob(x0 = x[!end], y0 = y[!end], 
           x1 = x[!start], y1 = y[!start], 
           id = id[!end], arrow = arrow, 
           name = name, gp = gp, vp = vp, cl = "segments")
    }
  } else {
    gList(gTree(x = x[id.first.half], y = y[id.first.half], id = id[id.first.half], 
                arrow = arrow, constant = constant, 
                start = start, end = end, start.cap = start.cap, 
                start.cap2 = start.cap2, start.captype = start.captype, 
                end.cap = end.cap, end.cap2 = end.cap2, end.captype = end.captype, 
                name = name, gp = gp, vp = vp, cl = "cappedpathgrob"),
          gTree(x = x[id.second.half], y = y[id.second.half], id = id[id.second.half],
                arrow = NULL, constant = constant, 
                start = start, end = end, start.cap = start.cap, 
                start.cap2 = start.cap2, start.captype = start.captype, 
                end.cap = end.cap, end.cap2 = end.cap2, end.captype = end.captype, 
                name = name, gp = gp, vp = vp, cl = "cappedpathgrob"))
  }
}

Step 3. Run ggraph code as per normal:

set.seed(777) # set seed for reproducibility

create_notable('bull') %>%
  ggraph(layout = 'graphopt') + 
  geom_edge_link(arrow = arrow(length = unit(4, 'mm')), 
                 end_cap = circle(0, 'mm')) +
  geom_node_point(size = 5) +
  theme_graph()

# end_cap parameter has been set to 0 so that the segments join up;
# you can also refrain from specifying this parameter completely.

This effect will remain in place for the rest of your current R session (i.e. all arrowed segments created by ggraph have their arrows in the middle rather than at the end) until you run the following line:

untrace(ggraph:::cappedPathGrob)

Thereafter, normal behaviour will resume.

Question:

I am trying to plot a network graph and show only the labels for the geom_node_text that have a centrality score above a specific threshold. My code is below:

rt_tbl %>% 
mutate(centrality = centrality_authority()) %>% 
ggraph(layout = 'kk') + 
geom_edge_link(aes(), colour = "gray", alpha = 0.5) + 
geom_node_point(aes(size = centrality, colour = centrality)) + 
geom_node_text(data=subset(centrality > 0.6), aes(centrality, label=name)) + 
theme_graph(base_family = "Roboto Condensed", base_size = 13)

I encounter the following error:

Error in subset(centrality > 100) : object 'centrality' not found

My data looks like this:

# A tbl_graph: 1589 nodes and 3992 edges
# A directed multigraph with 3 components
# Node Data: 1,589 x 3 (active)
        name degree centrality
        <chr>  <dbl>      <dbl>
1   Ashinlay1970     35 0.90053429
2     WinTunMin1     25 0.66408597
3 Yaminayeyaohn1      2 0.06080755
4  TUNOO00655880      3 0.07609831
5     inewagency      8 0.21569006
6         KSwe03      4 0.12416238
# ... with 1,583 more rows

# Edge Data: 3,992 x 2
from    to
<int> <int>
1     1    48
2     1    49
3     1     1
# ... with 3,989 more rows

Answer:

I never used ggraph before and you should really provide a reproducible minimal example, but try this:

rt_tbl %>% 
    mutate(centrality = centrality_authority()) %>% 
    ggraph(layout = 'kk') + 
    geom_edge_link(aes(), colour = "gray", alpha = 0.5) + 
    geom_node_point(aes(size = centrality, colour = centrality)) + 
    geom_node_text(aes(label=ifelse(centrality > .6, name, NA))) + 
    theme_graph(base_family = "Roboto Condensed", base_size = 13)

Your subset-approach does not work because it does not look inside your rt_tbl but tries to get the object centrality, which does not exist. But it would not work anyway, because you need to give it a vector of same length as your data, but subset only returns the values that match your condition. Therefore, using ifelse is better suited for your task.

Edit:

BTW this is a minimal reproducible example (at least I now know how to use ggraph):

library(tidygraph)
library(ggraph)

rt_tble <- tidygraph::create_star(10) %>% 
    mutate(centrality = centrality_authority(), 
           name = LETTERS[1:10])

ggraph(graph = rt_tble) +
    geom_edge_link() +
    geom_node_point(aes(size = centrality, colour = centrality)) + 
    geom_node_text(aes(label = ifelse(centrality > .6, as.character(name), NA_character_)))

I had to use as.character(name) instead of name or levels(name) (as I did before), maybe you would have to change that in my solution above as well...

But regarding that, have a look at @Alper Yilmaz solution below.

Question:

I'd like to use ggraph package, but loading it, I got this error:

library(ggraph)
Error: package or namespace load failed for ‘ggraph’:
 object ‘scale_type’ is not exported by 'namespace:ggplot2'

What does it mean? How can I fix it? I'm not finding any answer on the Internet. Thanks in advance. I am running on Windows 10 this version of R:

    > shortRversion()
[1] "R-3.5.1_2018-07-02"

Answer:

I was able to fix this specific error by reinstalling the latest ggplot2 version.

remove.packages("ggplot2")
install.packages("ggplot2")

It seems that ggraph depends on this current version of ggplot2.

Question:

I'm pretty new to ggraph and strugling a bit to get the legend to properly display the colors of the nodes. I have the following example data set:

nodes <- data.frame( ID =  c( 2, 3, 4, 5, 6, 7 ),  
                     cl = c( "A", "B", "A", "A", "C", "B" ), 
                     ty = c( 1, 1, 0, 0, 0, 1 ), 
                     assets = c( 20000000, 10000, 500000, 10000, 150, 50 )
                    )


edges <- data.frame( from = c( 2, 5, 4, 6, 7, 4, 3 ), 
                     to = c( 3, 4, 3, 5, 5, 3, 2 ), 
                     we = c( 1, 1, 3, 2, 1, 1, 3 ), 
                     pa = c( 0, 0, 1, 0, 1, 0, 0 ))

Based on these data I tried to plot the graph:

library( 'tidygraph' )
library( 'igraph' )
library( 'ggraph' )

graph <- graph_from_data_frame( edges, vertices = nodes, directed = TRUE ) %>% as_tbl_graph()


ggraph( graph, layout = 'fr' ) + 
  # Create edge layer
    geom_edge_link0( aes( width = we, color = factor( pa )), 
                          arrow = arrow( angle = 10, length = unit( 0.15, "inches" ),
                                         ends = "last", type = "closed" )) +
    scale_edge_width( range = c( 0.2, 2.2 )) +
    scale_edge_color_grey( start = 0.4, end = 0.8 ) +
  # Create node layer
    geom_node_point( aes( shape = factor( ty ), fill = cl, size = log( assets ))) +
  # Title and legend
    labs( edge_width = "Power", edge_color = "Ownertype" ) +
    ggtitle( "Title" ) +
    theme( legend.key = element_rect( fill = "white", colour = "black" ), 
    legend.title = element_text(face = "bold" )) +
    scale_size_continuous( name = "Assets", range = c( 3, 6 ), breaks = c( 5, 10, 15 )) +
    scale_shape_manual( name = "Same branch", values = c( 21, 23 ), labels = c( "no", "yes" )) +
    scale_fill_brewer( name = "Sector", palette = "Dark2" ) 

I have two issues with the legend under heading 'Sector':

  1. The color keys are not displayed, they are all black. This happens everytime I let both the color and the shape of the nodes vary.
  2. The color keys are too small, so that it is really hard to distinguish the colors (once they are there).

Unfortunately all my attempts to solve these two problems have been unsuccessful.


Answer:

By default, the legend guide for points doesn't use a shape that supports a fill color. You need to set such a shape for the guide:

+ guides(fill = guide_legend(override.aes = list(size = 5, shape = 21)))

Question:

Context

I am using ggraph to arrange nodes (leaves of a tree) in a circular dendrogram and then add connections between some of the nodes (using hierarchical bundling using geom_conn_bundle):

library(ggraph)
library(igraph)

# Example data
edges <- data.frame(from="root", to=paste("leaf", seq(1,100), sep=""))
vertices <- data.frame(name = unique(c(as.character(edges$from), as.character(edges$to))) ) 
tree <- graph_from_data_frame( edges, vertices=vertices )

# Drawing nodes
pr <- ggraph(tree, layout = "dendrogram", circular = TRUE) + 
    geom_edge_diagonal(alpha = 0.2)

# Example connection
pr <- pr + geom_conn_bundle(
    data = get_con(from = 23, to = 42),
    alpha=0.8, 
    width=3, 
    colour="skyblue", 
    tension = 0.9
)
print(pr)

This nicely displays a nearly transparent dendrogram and some (in this example one) connections in skyblue.

Problem / Desired output

What I'd like though, is the direction of the connection being indicated by a color gradient (i.e. starting with green, slowly changing into red) instead of showing the connection in just one color (skyblue). How can I achive such a color gradient using R and ggraph's geom_conn_bundle?

The following excerpt from Holten (2006) can serve of an example of how I'd like the connections to look:


Answer:

Several of the ggraph geoms for drawing edges, including geom_conn_bundle and geom_edge_diagonal, have a calculated index stat. It's a number from 0 to 1 of how far along the edge a point is. Note that the simplified versions of these geoms (geom_*0) don't calculate it. Some mentions of it are in this blog post by the ggraph author.

In this case, map the index stat(index) to color inside your bundle's aes, then set a gradient scale with (scale_edge_color_gradient, not scale_color_gradient as I initially tried).

In the example picture, I can't tell whether the width is also scaled, but the same would work, e.g. edge_width = stat(index).

library(ggraph)
library(igraph)

ggraph(tree, layout = "dendrogram", circular = TRUE) + 
  geom_edge_diagonal(alpha = 0.2) + 
  geom_conn_bundle(aes(color = stat(index)),
    data = get_con(from = 23, to = 42),
    alpha=0.8, 
    width=3,
    # colour="skyblue", 
    tension = 0.9
  ) +
  scale_edge_color_gradient(low = "green", high = "red")

Created on 2019-03-09 by the reprex package (v0.2.1)

Question:

I am trying to plot the co-occurrences of bigrams (from Text Mining with R)like this:

Or like this:

But following exactly the same codes given in the book, my plots are missing much of the lines and colors. Not sure if it is because I have missed out some important steps or I'm missing certain packages.

Below is a simpler version for illustration:

library(dplyr)
library(ggplot2)
library(igraph)
library(ggraph)

terms <- sample(letters[1:10],50,replace=T)
count <- sample(1:50,25,replace=T)

bigrams <- data_frame(term1=terms[1:25],term2=terms[26:50],occur=count) %>%
  arrange(desc(occur)) %>%
  graph_from_data_frame()

a <- grid::arrow(type = "closed", length = unit(.15, "inches"))

And I'm getting plots that's just not right (even the legend is not shown properly):

ggraph(bigrams, layout = "fr") +
  geom_edge_link(aes(edge_alpha = occur), show.legend = FALSE,  
                 arrow = a, end_cap = circle(.07, 'inches')) +
  geom_node_point(color = "lightblue", size = 5) +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
  theme_void()

ggraph(bigrams, layout = "fr") +
  geom_edge_link(aes(edge_alpha = occur, edge_width = occur), edge_colour = "cyan4") +
  geom_node_point(size = 5) +
  geom_node_text(aes(label = name), repel = TRUE, 
                 point.padding = unit(0.2, "lines")) +
  theme_void()

Ok this is funny but removing the theme_void() solves all. I suppose it does something different when the book is been written. However the legend in the second graph is still not showing, so there is still something wrong:


Answer:

I've found the ggraph package nice but with some issues. To me, your code work, if you zoom on the plot in RStudio. However, I advice you some small mods, that make the plot without zooming:

ggraph(bigrams, layout = "fr") +
  geom_edge_link(aes(width = occur),          # seems the alpha creates problem with legend
                 colour = "cyan4") +
  geom_node_point(size = 5) +
  scale_edge_width(range = c(0.2, 2)) +       # rescale the edges
  geom_node_text(aes(label = name), repel = TRUE, point.padding = unit(0.2, "lines"))+
 theme_graph()                                # made for graph

If you want the alpha, you can try this, but I noticed you see the legend only zooming in RStudio:


The data are the same of yours, but with set.seed(1).

Question:

I'm facing this issue: I got some data like these:

library(tidyverse)
library(tidygraph)
library(ggraph)
library(ggrepel)

edges <- data.frame(a=c('k','k','k','k','k','z','z'),
                    b=c('b','b','b','b','c','b','c'), costant = 1)
  a b costant
1 k b       1
2 k b       1
3 k b       1
4 k b       1
5 k c       1
6 z b       1
7 z c       1

Now I would lik to have a graph with ggraph that have nodes and edges with weights. So I worked this way:

# first I calculated the edges weights
edges1 <- edges%>% group_by(a,b) %>% summarise(weight = sum(costant))
> edges1
# A tibble: 4 x 3
# Groups:   a [?]
  a     b     weight
  <fct> <fct>  <dbl>
1 k     b          4
2 k     c          1
3 z     b          1
4 z     c          1

Then the nodes:

nodes <- rbind(data.frame(word = edges$a, n = 1),data.frame(word = edges$b, n = 1)) %>%
 group_by(word) %>%
summarise(n = sum(n))
> nodes
# A tibble: 4 x 2
  word      n
  <fct> <dbl>
1 k         5
2 z         2
3 b         5
4 c         2

Till now, everything works fine. Now, following this as example:

tidy <- tbl_graph(nodes = nodes, edges = edges1, directed = T)
tidy <- tidy %>% 
  activate(edges) %>% 
  arrange(desc(weight)
)

Suddently I plotted the graph:

ggraph(tidy, layout = "gem") + 
  geom_node_point(aes(size=n)) +
  geom_edge_link(aes(width = weight), alpha = 0.8) + 
  scale_edge_width(range = c(0.2, 2)) +
  geom_text_repel(aes(x = x, y=y , label=word)) 

But the result is this:

And I cannot figure out why there is a line between k and z, because that edges does not exists. Thank in advance.


Answer:

It seems it's due to the fact that tbl_graph converts edge1 tibble's nodes from factor to integer by as.integer without considering the nodes tibble, this is source of the error. If we pre-convert the edge node's to integers correctly it will work as expected.

edges <- data.frame(a=c('k','k','k','k','k','z','z'),
                    b=c('b','b','b','b','c','b','c'), costant = 1)
edges1 <- edges%>% group_by(a,b) %>% summarise(weight = sum(costant))

nodes <- rbind(data.frame(word = edges$a, n = 1),data.frame(word = edges$b, n = 1)) %>%
  group_by(word) %>%
  summarise(n = sum(n))

edges2 <- edges1 # save edges with factor node labels into edge2
# convert 'from' and 'to' factor columns to integer columns correctly 
# with the nodes tibble's corresponding matched index values 
edges1$a <- match(edges1$a, nodes$word) 
edges1$b <- match(edges1$b, nodes$word)

tidy <- tbl_graph(nodes = nodes, edges = edges1, directed = T)
tidy <- tidy %>% 
  activate(edges) %>% 
  arrange(desc(weight)
  ) 

ggraph(tidy, layout = "gem") + 
   geom_node_point(aes(size=n)) +
   geom_edge_link(aes(width = weight), arrow = arrow(length = unit(4, 'mm')), end_cap = circle(3, 'mm'), alpha = 0.8) + 
   scale_edge_width(range = c(0.2, 2)) +
   geom_text_repel(aes(x = x, y=y , label=word)) 

edges2 # compare the edges in the following tibble with the next figure
# A tibble: 4 x 3
# Groups:   a [?]
    a     b     weight
  <fct> <fct>  <dbl>
#1 k     b       4
#2 k     c       1
#3 z     b       1
#4 z     c       1

Question:

I'm trying to create 3D plots of simulated tree roots in R. Here is an example of a root system growing over time:

This is essentially a 3D network of cylinders, where the cylinder diameter (and, optionally, color) represents the size of the root. The available data includes:

  • x, y, z of the root centroid
  • direction of "parent" root (e.g. +x, -x, +y, -y, +z, -z), although this information could be captured in several different ways, including by calculating the x, y, z of the parent directly prior to plotting.
  • size of root

Example 3D data is here, but here is my first attempt at it in just 2D using ggplot2::geom_spoke:

dat <- data.frame(x = c(0,1,-1,0,1,-1),
              y = c(-1,-1,-1,-2,-2,-2),
              biomass = c(3,1.5,1.5,1,1,1), 
              parent.dir = c("+y","-x","+x","+y","+y","+y"))
dat$parent.dir <- as.numeric(as.character(factor(dat$parent.dir, 
                                             levels = c("-x", "+x", "-y", "+y"),
                                             labels = c(pi, 0, pi*3/2, pi/2)))) 

ggplot(dat, aes(x = x, y = y)) +
  geom_point(x = 0, y = 0, size = 20) +
  geom_spoke(radius = 1,
         aes(angle = parent.dir,
             size  = biomass)) + 
  coord_equal()

I prefer a solution based in the ggplot2 framework, but I realize that there are not a ton of 3D options for ggplot2. One interesting approach could be to creatively utilize the concept of network graphs via the ggraph and tidygraph packages. While those packages only operate in 2D as far as I know, their developer has also had some interesting related ideas in 3D that could also be applied.

The rgl library in seems to be the go-to for 3D plots in R, but an rgl solution just seems so much more complex and lacks the other benefits of ggplot2, such as faceting by year as in the example, easily adjusting scales, etc.

Example data is here:


Answer:

I don't understand the format of your data so I'm sure this isn't the display you want, but it shows how to draw a bunch of cylinders in rgl:

root <- read.csv("~/temp/root.csv")
segments <- data.frame(row.names = unique(root$parent.direction),
                       x = c(-1,0,1,0,0),
                       y = c(0,1,0,0,-1),
                       z = c(0,0,0,0.2,0))
library(rgl)
open3d()
for (i in seq_len(nrow(root))) {
        rbind(root[i,2:4], 
              root[i,2:4] - segments[root$parent.direction[i],]) %>%
        cylinder3d(radius = root$size[i]^0.3, closed = -2, sides = 20) %>%
        shade3d(col = "green")
}
decorate3d() 

This gives the following display (rotatable in the original):

You can pass each cylinder through addNormals if you want it to look smooth, or use sides = <some big number> in the cylinder3d to make them look rounder.

Question:

I'm experimenting with the outstanding ggraph library to depict some really-hard to depict interrelationships for a scientific work. Specifically, I want to show SNP-SNP interactions in a genetic locus. It would be very nice if I plotted the interactions as curved nodes of a graph, where the SNPs are positioned in a linear fashion according to their genetic positions. The geom_edge_arc() aesthetics from the ggraph library would be ideal. However, I cannot put the nodes in an order according to the positions.

Here is an example

library(igraph)
library(tidyverse)
library(ggraph)

set.seed(10)
nodes <- tibble(nodes = paste("SNP",seq(1:10)), pos = sample(c(10000:20000),10))
edges <- expand.grid(nodes$nodes,nodes$nodes) %>% 
              mutate(interaction = rnorm(100)) %>%
              filter(abs(interaction)>1)

gr <- graph_from_data_frame(edges, vertices = nodes)

ggraph(gr, 'linear', circular=F) +
  geom_edge_arc(aes(edge_width=interaction)) 

The nodes are evenly spaced here, as "factors". However, I wanted to place them on the x coordinate as specified by the pos variable (which in turn becomes an attribute of the nodes). Adding + geom_node_point(aes(x=pos))to the ggplot object doesn't result in a correct rendering. I could probably do the plot with "basic" ipgraph too, but I like ggraph and ggplot2, and it would be an elegant and easy way to plot with this.

Kind regards, and thanks in advance,

Robert


Answer:

Not sure if this is still relevant, but there are two ways to solve this.

As noted by @axeman, you can use the manual layout, and basically pass the x and y coordinates to it:

ggraph(gr, 
       layout = 'manual', 
       node.position = data_frame(y = rep(0, length(nodes$pos)), x = nodes$pos)) +
  geom_edge_arc(aes(edge_width=interaction))

The othe way is to overrride the x aes inside geom_edge_arc. To be able to pass a node attribute to an aes we need to use geom_edge_arc2:

ggraph(gr, 'linear', circular=F) +
  geom_edge_arc2(aes(edge_width=interaction, x = node.pos))

Created on 2018-05-30 by the reprex package (v0.2.0).

Question:

Been reading Tidytext Mining with R by Julia Silge and David Robinson - https://www.tidytextmining.com/nasa.html - and stumped on how to have the node size adjust in relation to frequency (n). Tried the following code...

 library(widyr)
 set.seed(1234)
 title_word_pairs %>%
 filter(n >= 250) %>%
 graph_from_data_frame() %>%
 ggraph(layout = "fr") +
 geom_edge_link(aes(edge_alpha = n, edge_width = n), edge_colour = 
 "royalblue") +
 geom_node_point(aes(size = n)) + scale_size(range = c(2,10)) +
 geom_node_text(aes(label = name), repel = TRUE,
            point.padding = unit(0.2, "lines")) +
 theme_void()

...and receive this error...

 Error: Column `size` must be a 1d atomic vector or a list
 Call `rlang::last_error()` to see a backtrace

Any thoughts or ideas would be appreciated.


Answer:

The issue is that this frequency n is for edges, not vertices. So geom_edge_link finds n because n is an edge attribute, while geom_node_point doesn't find n because it's not among vertex attributes.

So then we wish to construct another variable that would actually be the vertex frequency.

subt <- title_word_pairs %>%
  filter(n >= 250)
vert <- subt %>% gather(item, word, item1, item2) %>%
  group_by(word) %>% summarise(n = sum(n))

subt %>%
  graph_from_data_frame(vertices = vert) %>%
  ggraph(layout = "fr") +
  geom_edge_link(aes(edge_alpha = n, edge_width = n), edge_colour = "royalblue") +
  geom_node_point(aes(size = n)) + scale_size(range = c(2,10)) +
  geom_node_text(aes(label = name), repel = TRUE, point.padding = unit(0.2, "lines")) +
  theme_void()

Here subt is the same as before, then vert contains two columns: vertices (words) and their frequency in subt as a sum or relevant edge frequencies. Lastly, I added vertices = vert as to pass this vertex attribute.

Question:

Reading Tidytext Mining with R --https://www.tidytextmining.com/nasa.html -- I have the following question:

By default the node text color is black and I've been able to adjust the color globally but is it possible to have the default color black but other color(s) based off key words?

library(ggplot2)
library(igraph)
library(ggraph)

set.seed(1234)
title_word_pairs %>%
 filter(n >= 250) %>%
 graph_from_data_frame() %>%
 ggraph(layout = "fr") +
 geom_edge_link(aes(edge_alpha = n, edge_width = n)
 , edge_colour = "cyan4") +
 geom_node_point(size = 5) +
 geom_node_text(aes(label = name), repel = TRUE
 , point.padding = unit(0.2, "lines"), colour="red") +
 theme_void()

In the above image "land" and "data" would be red and all other text would be black.


Answer:

Without a reproducible example I went through the link and made a small dataset to illustrate my solution.

Using ggplot data to create a list of colors:

Here, I created the igraph without nodes, text, etc. and then used its data to make a list of desired colors.

library(dplyr)
library(widyr)
library(ggplot2)
library(igraph)
library(ggraph)

title_word_pairs1 <- structure(list(item1 = c("phase", "ges", "phase", "1", "phase", 
                                              "phase", "ges", "disc", "phase", "phase"), 
                                    item2 = c("ii", "disc", "system", "version", "space",
                                              "based", "degree", "degree", "low", "power"), 
                                    n = c(2498, 1201, 948, 678, 637, 601, 582, 582, 480, 441)), 
                                row.names = c(NA, -10L), class = c("tbl_df", "tbl", data.frame"))

set.seed(1)
g <- title_word_pairs1 %>%
  filter(nnn >= 250) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr")

mcolor <- g$data %>% mutate(mcolor = if_else(name%in%c("low", "space"), "blue", "black")) %>% select(mcolor)


g +
  geom_edge_link(aes(edge_alpha = n, edge_width = n)
                 , edge_colour = "cyan4") +
  geom_node_point(size = 5) +
  geom_node_text(aes(label = name), repel = TRUE
                 , point.padding = unit(0.2, "lines"), colour=mcolor$mcolor) +
  theme_void() + theme(legend.position="none")

Created on 2019-05-19 by the reprex package (v0.2.1)

Manipulating ggplot_build object colors to desired colors:

What I basically do is making the plot and then manipulating the ggplot object to get the desired colors.

library(dplyr)
library(widyr)
library(ggplot2)
library(igraph)
library(ggraph)


title_word_pairs1 <- structure(list(item1 = c("phase", "ges", "phase", "1", "phase", 
                                              "phase", "ges", "disc", "phase", "phase"), 
                                    item2 = c("ii", "disc", "system", "version", "space",
                                              "based", "degree", "degree", "low", "power"), 
                                    n = c(2498, 1201, 948, 678, 637, 601, 582, 582, 480, 441)), 
                                row.names = c(NA, -10L), class = c("tbl_df", "tbl", data.frame"))

set.seed(1)
g <- title_word_pairs1 %>%
  filter(n >= 250) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
  geom_edge_link(aes(edge_alpha = n, edge_width = n)
                 , edge_colour = "cyan4") +
  geom_node_point(size = 5) +
  geom_node_text(aes(label = name), repel = TRUE
                 , point.padding = unit(0.2, "lines"), colour="red") +
  theme_void() + theme(legend.position="none")


g

gg <- ggplot_build(g)

gg$data[[3]] <- gg$data[[3]] %>%
                             mutate(colour = if_else(label%in%c("low", "space"), "blue", "black"))

gt <- ggplot_gtable(gg)

plot(gt)

Created on 2019-05-18 by the reprex package (v0.2.1)

Question:

The first image is a hand drawn (using MS word) image of a graph. The second image is an attempt to generate the same graph using ggraph.

Following is the code I used to automatically draw the graph when given the node-edge connections (the code was adopted from this thread). I want to move the x and y axis of the ggraph as shown in the hand drawn graph (image 1). Reverse the x axis numbering from top to bottom and move the y axis to the top. How do I go about it?

library(igraph)
library(tidyverse)
library(ggraph)

V <- read.table(text = "x        y
                2 1
                4 2
                4 4
                2 5
                6 4
                3 7
                8 6", 
    header = T) %>%
  rownames_to_column("name")

E <- matrix(c(0,    1,    0,    0,    0, 0, 0,
              0,    0,    1,    0,    0, 0, 0,
              0,    0,    0,    1,    1, 0, 0,
              0,    0,    0,    0,    0, 1, 0,
              0,    0,    0,    0,    0, 0, 1,
              0,    0,    0,    1,    0, 0, 0,
              0,    0,    0,    0,    1, 0, 0), nrow = 7, byrow = T) %>%
  data.frame() %>% 
  rename_all(list(function(x) 1:7)) %>% 
  rownames_to_column(var = "from") %>% 
  gather(to, val, 2:6) %>% 
  filter(val == 1) %>%
  select(from, to)

g <- graph_from_data_frame(E, vertices = V, directed = F)

png("C:\\Users\\Yasoda\\Downloads\\rplot.png", width = 450, height = 450)

ggraph(g) + 
  geom_edge_link(edge_width = 1.3) + 
  geom_node_label(aes(label = name),label.r = unit(0.75, "lines"), 
                  label.size = 0.65, label.padding = unit(0.55,"lines"), show.legend = F) +
  ggtitle("My plot") +
  coord_flip() +
  expand_limits(x = 0, y = 0) +
  scale_x_continuous(expand = c(0, 0), limits = c(0, 9), breaks = c(0:9), minor_breaks = NULL) + 
  scale_y_continuous(expand = c(0, 0),limits = c(0, 9), breaks = c(0:9), minor_breaks = NULL) +
  theme_minimal() 


dev.off()

I tried using scale_x_reverse() but it distorts the layout and gives the warning "Scale for 'x' is already present.Adding another scale for 'x', which will replace the existing scale.". Also I tried the position = "top" option in the scale_y_continuous and it doesn't make a difference either.


Answer:

ggraph(g) + 
  geom_edge_link(edge_width = 1.3) + 
  geom_node_label(aes(label = name),label.r = unit(0.75, "lines"), 
                  label.size = 0.65, label.padding = unit(0.55,"lines"), show.legend = F) +
  ggtitle("My plot") +
  coord_flip() +
  expand_limits(x = 0, y = 0) +

  # Using scale_x_reverse and swapping the limits
  scale_x_reverse(expand = c(0, 0), limits = c(9, 0), breaks = c(0:9), minor_breaks = NULL) + 
  # switching y position to "right" (pre-flip)
  scale_y_continuous(expand = c(0, 0),limits = c(0, 9), breaks = c(0:9), minor_breaks = NULL, position = "right") +
  theme_minimal() 

Question:

I started playing with the ggraph package since it looks very promising in terms of extra features it adds to the available plots of network graphs (see this tutorial). However, testing something fairly trivial, I'm already stuck. What I basically want to do is reproduce a simple dendrogram:

ArrestsDen <- as.dendrogram(hclust(dist(USArrests[1:5,])))
plot(ArrestsDen)

But this is how it looks in ggraph:

library(ggraph)
ggraph(ArrestsDen, 'dendrogram') +
  geom_edge_elbow()

Instead of axis tick labels, it shows just 0,1,2 etc.. I already tried the usual ggplot2 "tricks" but without success:

ggraph(ArrestsDen, 'dendrogram') +
  geom_edge_elbow() +
  scale_x_discrete(labels = labels)

# create labels manually
labs <- labels(ArrestsDen)
names(labs) <- as.character(1:length(labels(ArrestsDen)))
class(labs)

ggraph(ArrestsDen, 'dendrogram') +
  geom_edge_elbow() +
  scale_x_discrete(labels = labs)

What am I missing?


Answer:

You could try that:

  ggraph(ArrestsDen, 'dendrogram') +
    geom_edge_elbow() + 
    theme_bw() + 
    scale_x_continuous(breaks = c(0, 1, 2, 3, 4), label=c("Ark","Arizona","Cal", "Alab", "Alaska"))

Question:

I try to plot a bipartite network with the edges of the same color than one of their nodes. For example, let's take an movie/actor bipartite graph as an exemple, with 7 movies and 15 actors, and each actor has a nationality. I want to have the edge between an actor and a movie of the same color than the nationality of the actor.

NG1 <- 7
NG2 <- 15
Nat <- sample(x = c("French", "English", "Italian", "American", "Chinese"), size = NG2, replace = T)
G <- graph.empty(NG1+NG2)

[Here, head(Nat) returns "Italian" "English" "American" "French" "French" "French"] The code to create the edgelist:

E1 <- sample(x=1:NG1, size = 30, replace = T)
E2 <- sample(x=(NG1+1):(NG1+NG2), size = 30, replace = T)
EL <- c(rbind(E1, E2))
G <- add_edges(G, EL, nat = Nat[E2-NG1])

[Here, head(EL) returns 1 14 3 13 2 15] The different aes arguments:

GROUP <- c(rep("Movie", NG1), rep("Act", NG2))
COL <- c(rep("Movie", NG1), Nat)
TXT <- c(as.character(1:NG1), letters[1:NG2])

And now the ggraph instructions:

 ggraph(G, layout = 'kk') + 
      geom_node_point(aes(col = COL, shape = GROUP, size = 7)) + 
      geom_edge_link(aes(col = nat)) +
      geom_node_text(aes(label = TXT), size = 4)

As you can see in the bottom, actor a, which is Italien has a blue node, but is connected with a pink edge with movie 7... How can I specify the color palette for nodes (here 6 colors) and edges (the 5 first colors of the nodes)?

I hope that I have made clear.


Answer:

After two hours, I finally found a solution !

I used the function gg_color_hue defined here to emulate the 6 colors used for the nodes and then:

+ scale_edge_colour_manual(values = cols[1:5])

Question:

what data format is necessary to create ggraph circlepack layout? It seems to require a hierarchy.

I tried normally vertices and nodes, which apparently doesn't work.

library(ggraph)
library(igraph)
edges=data.frame(from=c('a','b','c'), to= c('c','a','d'))
vertices=data.frame(nodes=c('a','b','c','d'), weight=c(1,2,3,4))
graph <- graph_from_data_frame(edges, vertices = vertices)
ggraph(graph, 'circlepack', weight = 'size') + 
geom_node_circle(size = 0.25, n = 50) + 
coord_fixed()

I then tried a dendrogram object which doesn't work either. If i want to show several groups with sub-items in packed circle, how shall i build the graph object?

the data frame is more like this

df <- data.frame(group=c("a","a","b","b","b"),    subitem=c("x","y","z,"u","v"), size=c(6,2,3,2,5))

Answer:

A circlepack layout models a hierarchical/tree-like structure with one root and no cycles. To model your df as a circlepack layout, you have to consider that a and b in the group column are both roots. If we add a root to the df, and have both a and b be children of that root, we can visualize it as a circlepack:

library(ggraph)
library(igraph)
library(dplyr)


df <- data.frame(group=c("root", "root", "a","a","b","b","b"),    
                 subitem=c("a", "b", "x","y","z","u","v"), 
                 size=c(0, 0, 6,2,3,2,5))

# create a dataframe with the vertices' attributes

vertices <- df %>% 
  distinct(subitem, size) %>% 
  add_row(subitem = "root", size = 0)

graph <- graph_from_data_frame(df, vertices = vertices)

ggraph(graph, layout = "circlepack", weight = 'size') + 
  geom_node_circle(aes(fill =depth)) +
# adding geom_text to see which circle is which node 
  geom_text(aes(x = x, y = y, label = paste(name, "size=", size))) +
  coord_fixed()

Question:

I have generated a very simple network plot with a circular layout using ggraph. The angle of node labels changes along the edge of the circle to make it easier to read these. However, the labels are truncated and adjusting margins seems to make no difference. I played around with ggsave, egg and have not had much joy.

Here is my reproducible code. Maybe somebody can help me. Thanks in advance:

# load network packages

require(tidygraph)
require(igraph)

# fetch data from github

githubURL <- "https://github.com/aterhorst/data/raw/master/network.RDS"
network <- readRDS(url(githubURL))

# compute label angles

require(pracma)

lo <- layout.circle(network)
angle <- as_tibble(cart2pol(lo)) %>% mutate(degree = phi * 180/pi)

# generate plot

require(ggraph)

ggraph(network, layout = "circle") +
geom_edge_link() +
geom_node_point() +
geom_node_text(aes(label = name), 
               size = 2, 
               hjust = ifelse(lo[,1] > 0, -0.2, 1.2),
               angle = case_when(lo[,2] > 0 & lo[,1] > 0 ~ angle$degree, 
                                 lo[,2] < 0 & lo[,1] > 0 ~ angle$degree,
                                 lo[,1] == 1 ~angle$degree,
                                 TRUE ~ angle$degree - 180)) +
theme_graph()

Answer:

dropping theme_graph(), i found that the axis is -1 to 1 for both x and y, and it doesnt seem to fit the labels. So I added coord_cartesian() to expand the limit of the plot. Have below added to ggraph()

+ coord_cartesian(xlim=c(-1.2,1.2), ylim=c(-1.2,1.2))