ggraph::geom_conn_bundle() sometimes does not match other information with "from" and "to" columns correctly

Created on 15 Jun 2020  路  7Comments  路  Source: thomasp85/ggraph

Recently, I am using ggraph to create circular bundle graphs. I noticed that in ggraph::geom_conn_bundle(), if I wanted to specify information such as the color of connections, they sometimes did not match the correct connections. For example, in the following ggraph, I wanted to create 3 connections between node pairs (30,74), (34,71) and (35,70), with colors "A", "B", "C" respectively (A = red, B = green, C = blue, as shown in ggplot 0).

ggraph 1: source nodes were permuted in the ascending order: (30,74), (34,71) and (35,70), with colors "A", "B", "C" respectively, I got the correct matching.

ggraph 2: within node group re-ordering along with their color information: (30,74), (35,70) and (34,71), with colors "A", "C", "B" respectively, I got a wrong matching of the colors to the connections.

ggraph 3: across node groups re-ordering along with their color information: (34,71), (35,70) and (30,74), with colors "B", "C", "A" respectively, I got a wrong matching of the colors to the connections.

ggraph 4: across node groups re-ordering along with their color information: (34,71), (30,74) and (35,70), with colors "B", "A", "C" respectively, I got another wrong matching of the colors to the connections.

It is somewhat hard to understand if I re-order the connections with their corresponding properties (e.g. color), I get different matching of connections and their properties. I spent quite an amount of time to figure out that this problem can be solved if I permute the source nodes group in the ascending order and also the source nodes in the ascending order within each group. But I think the connection mapping should work correctly with any order of connections provided. Could you please tell me what I'm not handling correctly?

ggplot 0: revealing default color mapping to "A", "B" and "C"
enter image description here

ggraph 1: connections (30,74), (34,71) and (35,70), with colors "A", "B", "C" respectively (ordered source nodes -> correct matching)
enter image description here

ggraph 2: connections (30,74), (35,70) and (34,71), with colors "A", "C", "B" respectively (intra-group reorder -> wrong matching)
enter image description here

ggraph 3: connections (34,71), (35,70) and (30,74), with colors "B", "C", "A" respectively (inter-group reorder -> wrong matching)
enter image description here

ggraph 4: connections (34,71), (30,74) and (35,70), with colors "B", "A", "C" respectively (another inter-group reorder -> wrong matching)
enter image description here

# Libraries
library(ggraph)
library(igraph)
library(tidyverse)
library(RColorBrewer)

# create a data frame giving the hierarchical structure of your individuals
set.seed(1234)
d1 <- data.frame(from="origin", to=paste("group", seq(1,10), sep=""))
d2 <- data.frame(from=rep(d1$to, each=10), to=paste("subgroup", seq(1,100), sep="_"))
edges <- rbind(d1, d2)
edges

# create a vertices data.frame. One line per object of our hierarchy
vertices  <-  data.frame(
  name = unique(c(as.character(edges$from), as.character(edges$to))) , 
  value = runif(111)
) 
# Let's add a column with the group of each name. It will be useful later to color points
vertices$group  <-  edges$from[ match( vertices$name, edges$to ) ]
#Let's add information concerning the label we are going to add: angle, horizontal adjustement and potential flip
#calculate the ANGLE of the labels
vertices$id <- 1:nrow(vertices)
vertices

# Create a graph object
mygraph <- igraph::graph_from_data_frame( edges, vertices=vertices )

# See the color map with ggplot
tribble(~ y, ~ col,
        2, "A",
        1, "B",
        0, "C") %>%
  ggplot() +
  geom_hline(aes(yintercept=y, color=col))

# Plot ggraph 1 with conn = 30-74 (color "A"), 34-71 (color "B"), 35-70 (color "C")
ggraph(mygraph, layout = 'dendrogram', circular = TRUE) + 
  geom_node_point(aes(filter = leaf, x = x*1.05, y=y*1.05, color = group), show.legend = FALSE) +
  geom_conn_bundle(data = get_con(from = c(30, 34, 35), to = c(74, 71, 70), col = c("A", "B", "C")), alpha=1, aes(color=col), width = 0.9) +
  geom_node_text(aes(x = x*1.1, y=y*1.1, filter = leaf, label=id, angle = node_angle(x, y)), size=2.5, alpha=1) +
  theme_void() +
  coord_fixed() +
  theme(
    # legend.position="none",
    plot.margin=unit(c(0,0,0,0),"cm"),
  ) +
  expand_limits(x = c(-1.2, 1.2), y = c(-1.2, 1.2))

# Plot ggraph 2 with conn = 30-74 (color "A"), 35-70 (color "C"), 34-71 (color "B")
ggraph(mygraph, layout = 'dendrogram', circular = TRUE) + 
  geom_node_point(aes(filter = leaf, x = x*1.05, y=y*1.05, color = group), show.legend = FALSE) +
  geom_conn_bundle(data = get_con(from = c(30, 35, 34), to = c(74, 70, 71), col = c("A", "C", "B")), alpha=1, aes(color=col), width = 0.9) +
  geom_node_text(aes(x = x*1.1, y=y*1.1, filter = leaf, label=id, angle = node_angle(x, y)), size=2.5, alpha=1) +
  theme_void() +
  coord_fixed() +
  theme(
    # legend.position="none",
    plot.margin=unit(c(0,0,0,0),"cm"),
  ) +
  expand_limits(x = c(-1.2, 1.2), y = c(-1.2, 1.2))

# Plot ggraph 3 with conn = 34-71 (color "B"), 35-70 (color "C"), 30-74 (color "A")
ggraph(mygraph, layout = 'dendrogram', circular = TRUE) + 
  geom_node_point(aes(filter = leaf, x = x*1.05, y=y*1.05, color = group), show.legend = FALSE) +
  geom_conn_bundle(data = get_con(from = c(34, 35, 30), to = c(71, 70, 74), col = c("B", "C", "A")), alpha=1, aes(color=col), width = 0.9) +
  geom_node_text(aes(x = x*1.1, y=y*1.1, filter = leaf, label=id, angle = node_angle(x, y)), size=2.5, alpha=1) +
  theme_void() +
  coord_fixed() +
  theme(
    # legend.position="none",
    plot.margin=unit(c(0,0,0,0),"cm"),
  ) +
  expand_limits(x = c(-1.2, 1.2), y = c(-1.2, 1.2))

# Plot ggraph 4 with conn = 34-71 (color "B"), 30-74 (color "A"), 35-70 (color "C")
ggraph(mygraph, layout = 'dendrogram', circular = TRUE) + 
  geom_node_point(aes(filter = leaf, x = x*1.05, y=y*1.05, color = group), show.legend = FALSE) +
  geom_conn_bundle(data = get_con(from = c(34, 30, 35), to = c(71, 74, 70), col = c("B", "A", "C")), alpha=1, aes(color=col), width = 0.9) +
  geom_node_text(aes(x = x*1.1, y=y*1.1, filter = leaf, label=id, angle = node_angle(x, y)), size=2.5, alpha=1) +
  theme_void() +
  coord_fixed() +
  theme(
    # legend.position="none",
    plot.margin=unit(c(0,0,0,0),"cm"),
  ) +
  expand_limits(x = c(-1.2, 1.2), y = c(-1.2, 1.2))

My session info:

R version 3.6.0 (2019-04-26)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows Server x64 (build 17763)

Matrix products: default

locale:
[1] LC_COLLATE=Chinese (Simplified)_China.936 
[2] LC_CTYPE=Chinese (Simplified)_China.936   
[3] LC_MONETARY=Chinese (Simplified)_China.936
[4] LC_NUMERIC=C                              
[5] LC_TIME=Chinese (Simplified)_China.936    

attached base packages:
 [1] stats4    parallel  grid      stats     graphics  grDevices utils    
 [8] datasets  methods   base     

other attached packages:
 [1] shiny_1.4.0         progress_1.2.2      RColorBrewer_1.1-2 
 [4] igraph_1.2.4.2      ggraph_2.0.3        OmicsDP_1.10.0     
 [7] xcms_3.6.2          MSnbase_2.10.1      ProtGenerics_1.20.0
[10] S4Vectors_0.22.1    mzR_2.18.0          Rcpp_1.0.3         
[13] BiocParallel_1.18.1 Biobase_2.44.0      BiocGenerics_0.30.0
[16] impute_1.58.0       DMwR2_0.0.2         uuid_0.1-2         
[19] mailR_0.4.1         wkb_0.3-0           openssl_1.4.1      
[22] knitr_1.26          reshape2_1.4.3      RODBC_1.3-15       
[25] fs_1.3.1            xml2_1.2.2          yaml_2.2.0         
[28] jsonlite_1.6        data.table_1.12.8   pROC_1.16.1        
[31] randomForest_4.6-14 xgboost_0.90.0.2    ggrepel_0.8.1      
[34] gplots_3.0.1.1      ggbeeswarm_0.6.0    gridExtra_2.3      
[37] ggplotify_0.0.4     forcats_0.5.0       stringr_1.4.0      
[40] dplyr_1.0.0         purrr_0.3.3         readr_1.3.1        
[43] tidyr_1.0.0         tibble_3.0.1        ggplot2_3.2.1      
[46] tidyverse_1.3.0     magrittr_1.5       

loaded via a namespace (and not attached):
  [1] clipr_0.7.0            utf8_1.1.4             R.utils_2.9.2         
  [4] tidyselect_1.1.0       devtools_2.2.1         munsell_0.5.0         
  [7] codetools_0.2-16       preprocessCore_1.46.0  dials_0.0.6           
 [10] miniUI_0.1.1.1         withr_2.1.2            colorspace_1.4-1      
 [13] highr_0.8              rstudioapi_0.11        robustbase_0.93-5     
 [16] rJava_0.9-12           TTR_0.23-4             mzID_1.22.0           
 [19] labeling_0.3           polyclip_1.10-0        DiceDesign_1.8-1      
 [22] farver_2.0.1           rprojroot_1.3-2        vctrs_0.3.0           
 [25] generics_0.0.2         ipred_0.9-9            xfun_0.11             
 [28] R6_2.4.1               doParallel_1.0.15      graphlayouts_0.7.0    
 [31] bitops_1.0-6           lhs_1.0.2              gridGraphics_0.4-1    
 [34] assertthat_0.2.1       promises_1.1.0         scales_1.1.0          
 [37] nnet_7.3-12            beeswarm_0.2.3         gtable_0.3.0          
 [40] affy_1.62.0            processx_3.4.1         tidygraph_1.2.0       
 [43] timeDate_3043.102      rlang_0.4.6            workflows_0.1.1       
 [46] splines_3.6.0          lazyeval_0.2.2         ModelMetrics_1.2.2.1  
 [49] broom_0.5.6            BiocManager_1.30.10    modelr_0.1.6          
 [52] backports_1.1.5        httpuv_1.5.2           quantmod_0.4-15       
 [55] MassSpecWavelet_1.50.0 caret_6.0-85           usethis_1.5.1         
 [58] tools_3.6.0            lava_1.6.6             affyio_1.54.0         
 [61] ellipsis_0.3.0         sessioninfo_1.1.1      parsnip_0.1.1         
 [64] plyr_1.8.5             zlibbioc_1.30.0        ps_1.3.0              
 [67] prettyunits_1.0.2      rpart_4.1-15           viridis_0.5.1         
 [70] zoo_1.8-6              haven_2.2.0            reprex_0.3.0          
 [73] RANN_2.6.1             GPfit_1.0-8            pcaMethods_1.76.0     
 [76] whisker_0.4            packrat_0.5.0          pkgload_1.0.2         
 [79] xtable_1.8-4           mime_0.8               hms_0.5.2             
 [82] evaluate_0.14          XML_3.98-1.20          readxl_1.3.1          
 [85] IRanges_2.18.3         testthat_2.3.1         compiler_3.6.0        
 [88] KernSmooth_2.23-15     ncdf4_1.17             crayon_1.3.4          
 [91] R.oo_1.23.0            htmltools_0.4.0        later_1.0.0           
 [94] lubridate_1.7.4        DBI_1.1.0              tweenr_1.0.1          
 [97] dbplyr_1.4.2           MASS_7.3-51.4          Matrix_1.2-17         
[100] cli_2.0.2              vsn_3.52.0             R.methodsS3_1.7.1     
[103] gdata_2.18.0           gower_0.2.1            pkgconfig_2.0.3       
[106] rvcheck_0.1.6          sp_1.3-2               recipes_0.1.9         
[109] MALDIquant_1.19.3      foreach_1.4.7          vipor_0.4.5           
[112] multtest_2.40.0        prodlim_2019.11.13     rvest_0.3.5           
[115] callr_3.4.0            digest_0.6.23          rmarkdown_2.0         
[118] cellranger_1.1.0       curl_4.0               gtools_3.8.1          
[121] lifecycle_0.2.0        nlme_3.1-143           desc_1.2.0            
[124] viridisLite_0.3.0      askpass_1.1            limma_3.40.6          
[127] fansi_0.4.0            pillar_1.4.3           lattice_0.20-38       
[130] pkgbuild_1.0.6         fastmap_1.0.1          httr_1.4.1            
[133] DEoptimR_1.0-8         survival_2.44-1.1      remotes_2.1.0         
[136] glue_1.4.1             xts_0.11-2             iterators_1.0.12      
[139] ggforce_0.3.1          class_7.3-15           stringi_1.4.3         
[142] memoise_1.1.0          caTools_1.17.1.2 

Most helpful comment

Thanks zhuxr11, do you mean ordering them in the vertices and the hierarchy files? I am trying to figure this out too. I have a reproducible example in stackoverflow here.

Let's look at an example. If the nodes in the vertex data.frame is defined as (for safety, I suggest arrange the group in the ascending order, since I did not explore whether this has an effect on property mapping):

name    group
C           group_1
B           group_1
A           group_2

And you have a connection like this:

from    to    weight
B         A      0.5
A         C      0.6
C         B      0.3

Now look at column from. Since C and B are from group_1, they should go before A, which is from group_2. And within group_1, B should go before C (in the alphabetical order). In this sense, you can try following order:

from    to    weight
B         A      0.5
C         B      0.3
A         C      0.6

Not sure whether this can help or not.

All 7 comments

I agree with you that the order of the items in the connect dataframe should not impact the color/transparency of the connections being made. I thought that reordering the connect dataframe in an ascending order had solved the issue for me, but not quite, some connections are still colored wrong. I just submitted a new issue regarding this.

I agree with you that the order of the items in the connect dataframe should not impact the color/transparency of the connections being made. I thought that reordering the connect dataframe in an ascending order had solved the issue for me, but not quite, some connections are still colored wrong. I just submitted a new issue regarding this.

Thanks for bringing attention to this issue. Simply sorting the names of source nodes does NOT necessarily solve the problem. What I discovered for correct mapping is that you first need to permute the source nodes group in the ascending order and then the source nodes in the ascending order within each group. I spent quite an amount of time figuring this out, and hope this can help.

Thanks zhuxr11, do you mean ordering them in the vertices and the hierarchy files? I am trying to figure this out too. I have a reproducible example in stackoverflow here.

Thanks zhuxr11, do you mean ordering them in the vertices and the hierarchy files? I am trying to figure this out too. I have a reproducible example in stackoverflow here.

Let's look at an example. If the nodes in the vertex data.frame is defined as (for safety, I suggest arrange the group in the ascending order, since I did not explore whether this has an effect on property mapping):

name    group
C           group_1
B           group_1
A           group_2

And you have a connection like this:

from    to    weight
B         A      0.5
A         C      0.6
C         B      0.3

Now look at column from. Since C and B are from group_1, they should go before A, which is from group_2. And within group_1, B should go before C (in the alphabetical order). In this sense, you can try following order:

from    to    weight
B         A      0.5
C         B      0.3
A         C      0.6

Not sure whether this can help or not.

aaaaaha, I understand what you mean now. Zhuxr11 you are a life saver! I will try this out now and let you know how it went. Many many thanks!

aaaaaha, I understand what you mean now. Zhuxr11 you are a life saver! I will try this out now and let you know how it went. Many many thanks!

My pleasure~

Happy to report your solution worked beautifully :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

akitepowell picture akitepowell  路  10Comments

jalapic picture jalapic  路  3Comments

zephyris picture zephyris  路  4Comments

daniel-munro picture daniel-munro  路  12Comments

mbojan picture mbojan  路  6Comments