Reputation: 587
I have a tree diagram which I want to display horizontally. I had been using igraph and inspiration from this solution, but I am updating my code to {ggraph}
hoping for a cleaner aesthetic and better control over edge labels.
In {igraph}
I am able to manipulate the l_igraph
matrix directly to transpose x
and y
coordinates. However, when I do this to the dataframe produced by tidygraph::create_layout()
and plot it I get an error to do about breaking some expectation of object class.
Thanks in advance for your recommendations.
# dependencies
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(stringr)
library(ggplot2)
library(igraph)
#>
#> Attaching package: 'igraph'
#> The following objects are masked from 'package:dplyr':
#>
#> as_data_frame, groups, union
#> The following objects are masked from 'package:stats':
#>
#> decompose, spectrum
#> The following object is masked from 'package:base':
#>
#> union
library(tidygraph)
#>
#> Attaching package: 'tidygraph'
#> The following object is masked from 'package:igraph':
#>
#> groups
#> The following object is masked from 'package:stats':
#>
#> filter
library(ggraph)
# edgelist
df_graph <- tibble::tibble(from = rep("parent", 8),
to = stringr::str_c("child ", letters[1:8]))
# igraph
g_igraph <- igraph::graph_from_data_frame(df_graph, directed = FALSE)
l_igraph <-
igraph::layout_as_tree(
g_igraph,
root = igraph::get.vertex.attribute(g_igraph, "name") %>% stringr::str_detect(., "parent") %>% which(.)) %>%
.[,2:1]
igraph::plot.igraph(g_igraph, layout = l_igraph,
vertex.size = 8,
vertex.color = "grey90",
# vertex label attrs
vertex.label.cex = 0.7,
vertex.label.font = 2,
vertex.label.color = "#000000",
vertex.label.bg.color = "#FFFFFF",
# edge label attrs
edge.label.font = 2,
edge.label.color = "#000000",
# edge arrow attrs
edge.arrow.size = 1.5,
edge.arrow.width = 0.5)
# tidygraph / ggraph
g_tidy <- tidygraph::as_tbl_graph(g_igraph)
# ggraph top-down tree
l_tidy <- ggraph::create_layout(
g_tidy,
layout = 'tree',
root = igraph::get.vertex.attribute(g_tidy, "name") %>% stringr::str_detect(., "parent") %>% which(.)
)
ggraph::ggraph(l_tidy) +
ggraph::geom_edge_link() +
ggraph::geom_node_point()
# ggraph horizontal tree, attempted to modify xy coords like igraph example above
xy <- dplyr::select(l_tidy, x:y)
l_tidy_revised <- l_tidy %>%
tidygraph::mutate(y = xy$x,
x = xy$y)
ggraph::ggraph(l_tidy_revised) +
ggraph::geom_edge_link() +
ggraph::geom_node_point()
#> Error in `.register_graph_context()`:
#> ! `graph` must be a <tbl_graph> object
#> Backtrace:
#> ▆
#> 1. ├─base::tryCatch(...)
#> 2. │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#> 3. │ ├─base (local) tryCatchOne(...)
#> 4. │ │ └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#> 5. │ └─base (local) tryCatchList(expr, names[-nh], parentenv, handlers[-nh])
#> 6. │ └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#> 7. │ └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#> 8. ├─base::withCallingHandlers(...)
#> 9. ├─base::saveRDS(...)
#> 10. ├─base::do.call(...)
#> 11. ├─base (local) `<fn>`(...)
#> 12. └─global `<fn>`(input = base::quote("fussy-agama_reprex.R"))
#> 13. └─rmarkdown::render(input, quiet = TRUE, envir = globalenv(), encoding = "UTF-8")
#> 14. └─knitr::knit(knit_input, knit_output, envir = envir, quiet = quiet)
#> 15. └─knitr:::process_file(text, output)
#> 16. ├─base::withCallingHandlers(...)
#> 17. ├─knitr:::process_group(group)
#> 18. └─knitr:::process_group.block(group)
#> 19. └─knitr:::call_block(x)
#> 20. └─knitr:::block_exec(params)
#> 21. └─knitr:::eng_r(options)
#> 22. ├─knitr:::in_input_dir(...)
#> 23. │ └─knitr:::in_dir(input_dir(), expr)
#> 24. └─knitr (local) evaluate(...)
#> 25. └─evaluate::evaluate(...)
#> 26. └─evaluate:::evaluate_call(...)
#> 27. ├─evaluate (local) handle(...)
#> 28. │ └─base::try(f, silent = TRUE)
#> 29. │ └─base::tryCatch(...)
#> 30. │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#> 31. │ └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#> 32. │ └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#> 33. ├─base::withCallingHandlers(...)
#> 34. ├─base::withVisible(value_fun(ev$value, ev$visible))
#> 35. └─knitr (local) value_fun(ev$value, ev$visible)
#> 36. └─knitr (local) fun(x, options = options)
#> 37. ├─base::withVisible(knit_print(x, ...))
#> 38. ├─knitr::knit_print(x, ...)
#> 39. └─knitr:::knit_print.default(x, ...)
#> 40. └─evaluate (local) normal_print(x)
#> 41. ├─base::print(x)
#> 42. └─ggplot2:::print.ggplot(x)
#> 43. ├─ggplot2::ggplot_build(x)
#> 44. └─ggraph:::ggplot_build.ggraph(x)
#> 45. └─tidygraph::.register_graph_context(attr(plot$data, "graph"), free = TRUE)
#> 46. └─cli::cli_abort("{.arg graph} must be a {.cls tbl_graph} object")
#> 47. └─rlang::abort(...)
# check class for layout object(s)
# l_tidy is not a tbl_graph object, but still plots
tidygraph::is.tbl_graph(l_tidy)
#> [1] FALSE
class(l_tidy)
#> [1] "layout_tbl_graph" "layout_ggraph" "data.frame"
# l_tidy_revised has same classes as l_tidy, does not plot
tidygraph::is.tbl_graph(l_tidy_revised)
#> [1] FALSE
class(l_tidy_revised)
#> [1] "layout_tbl_graph" "layout_ggraph" "data.frame"
Created on 2023-02-08 with reprex v2.0.2
Upvotes: 1
Views: 112
Reputation: 587
It turns out that {ggraph}
will accept an igraph layout in its initial call, giving me the desired layout transpose.
Graph & layout objects are copied from OP.
# dependencies
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(stringr)
library(ggplot2)
library(igraph)
#>
#> Attaching package: 'igraph'
#> The following objects are masked from 'package:dplyr':
#>
#> as_data_frame, groups, union
#> The following objects are masked from 'package:stats':
#>
#> decompose, spectrum
#> The following object is masked from 'package:base':
#>
#> union
library(tidygraph)
#>
#> Attaching package: 'tidygraph'
#> The following object is masked from 'package:igraph':
#>
#> groups
#> The following object is masked from 'package:stats':
#>
#> filter
library(ggraph)
# edgelist
df_graph <- tibble::tibble(from = rep("parent", 8),
to = stringr::str_c("child ", letters[1:8]))
# igraph
g_igraph <-
igraph::graph_from_data_frame(df_graph, directed = FALSE)
l_igraph <-
igraph::layout_as_tree(
g_igraph,
root = igraph::get.vertex.attribute(g_igraph, "name") %>% stringr::str_detect(., "parent") %>% which(.)
) %>%
.[, 2:1]
# tidygraph / ggraph
g_tidy <- tidygraph::as_tbl_graph(g_igraph)
# ggraph top-down tree
l_tidy <- ggraph::create_layout(
g_tidy,
layout = 'tree',
root = igraph::get.vertex.attribute(g_tidy, "name") %>% stringr::str_detect(., "parent") %>% which(.)
)
# undesired top-down layout
ggraph::ggraph(l_tidy) +
ggraph::geom_edge_link() +
ggraph::geom_node_point()
# desired transpose layout
ggraph::ggraph(graph = g_tidy, layout = l_igraph) +
ggraph::geom_edge_link() +
ggraph::geom_node_point()
Created on 2023-02-08 with reprex v2.0.2
Upvotes: 1