The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
networkformat converts R objects into graph-ready format for visualization with ggraph. For tree models, the quickest path is:
as.igraph(x) — convert a model to an igraph objectas_tbl_graph(x) — convert to a tidygraph tbl_graph (for
use with ggraph)For vectors and data frames, use edgelist() and
nodelist() to build the edge/node data, then pass them to
igraph or tidygraph. If you want to work with the raw edgelists and
nodelists directly — filtering edges, computing graph statistics, or
building custom igraph objects — see
vignette("edgelist-nodelist").
The graph carries all the attributes you need for plotting — node labels, split variables, leaf predictions, observation counts, and parsed edge thresholds.
cat("Nodes:", igraph::vcount(tg), " Edges:", igraph::ecount(tg),
" Depth:", max(igraph::distances(tg, mode = "out")[1, ]), "\n")
#> Nodes: 15 Edges: 14 Depth: 4# Add a fill column: split variable for internal nodes, predicted class for leaves
tg <- tg %>%
mutate(fill_var = ifelse(is_leaf, yval, var))
# Build a palette: one color per variable + one per class
node_df <- as.data.frame(tg, what = "vertices")
vars <- setdiff(unique(node_df$var), "<leaf>")
classes <- unique(node_df$yval[node_df$is_leaf])
var_pal <- c(
setNames(c("#3B8EA5", "#D4A373"), vars[seq_len(min(length(vars), 2))]),
setNames(c("#66c2a5", "#fc8d62", "#8da0cb"), classes[seq_len(min(length(classes), 3))])
)
ggraph(tg, layout = "tree") +
geom_edge_diagonal(colour = "grey60", width = 0.5) +
geom_edge_diagonal(
aes(label = ifelse(is.na(split_point), "", paste0(split_op, split_point))),
angle_calc = "along", label_dodge = unit(3, "mm"),
label_size = 2.5, label_colour = "grey30", colour = NA) +
geom_node_label(
aes(label = label, fill = fill_var),
size = 2.5, colour = "white", fontface = "bold",
label.padding = unit(0.25, "lines"), label.r = unit(0.2, "lines")) +
scale_fill_manual(values = var_pal, name = NULL) +
theme_graph(base_family = "sans") +
theme(legend.position = "bottom") +
labs(title = "Iris classification tree",
subtitle = "Split on Sepal.Length and Sepal.Width")Internal nodes are colored by the split variable. Leaf nodes show the
predicted class. Edge labels show the split condition
(e.g. <5.45).
as_tbl_graph(rf) combines all trees into a single graph
with disconnected components — one per tree. Each node carries a
treenum attribute so you can distinguish them.
rf <- randomForest::randomForest(Species ~ ., data = iris, ntree = 3, maxnodes = 5)
# All 3 trees in one graph
tg_rf <- as_tbl_graph(rf)cat("Nodes:", igraph::vcount(tg_rf), " Edges:", igraph::ecount(tg_rf),
" Components:", igraph::count_components(tg_rf), "\n")
#> Nodes: 27 Edges: 24 Components: 3# Map integer prediction indices to class names
tg_rf <- tg_rf %>%
mutate(
pred_class = rf$classes[prediction],
display_label = ifelse(
is_leaf, pred_class,
paste0(split_var_name, "\n< ", round(split_point, 1))
),
fill_var = ifelse(is_leaf, pred_class, split_var_name)
)
# Palette: one color per predictor variable + one per predicted class
node_df <- as.data.frame(tg_rf, what = "vertices")
predictor_vars <- sort(unique(na.omit(node_df$split_var_name)))
leaf_classes <- sort(unique(na.omit(node_df$pred_class)))
var_colors <- c("#3B8EA5", "#D4A373", "#7B68AE", "#E07B54")
class_colors <- c("#66c2a5", "#fc8d62", "#8da0cb")
var_pal_rf <- c(
setNames(var_colors[seq_along(predictor_vars)], predictor_vars),
setNames(class_colors[seq_along(leaf_classes)], leaf_classes)
)
ggraph(tg_rf, layout = "tree") +
geom_edge_link(
arrow = arrow(length = unit(1.5, "mm"), type = "closed"),
end_cap = circle(5, "mm"),
colour = "grey70", width = 0.3) +
geom_node_label(
aes(label = display_label, fill = fill_var),
size = 2.5, colour = "black", fontface = "bold",
label.padding = unit(0.2, "lines"), label.r = unit(0.15, "lines"),
alpha = 0.85) +
scale_fill_manual(values = var_pal_rf, name = NULL) +
theme_graph(base_family = "sans") +
theme(legend.position = "bottom") +
labs(title = "Random forest (3 trees)",
subtitle = "Each disconnected component is one tree")All three trees are visible in a single plot. Internal nodes are colored by split variable; leaf nodes are colored by predicted class.
For a closer look at one tree:
tg1 <- as_tbl_graph(rf, treenum = 1) %>%
mutate(
pred_class = rf$classes[prediction],
display_label = ifelse(
is_leaf, pred_class,
paste0(split_var_name, "\n< ", round(split_point, 1))
),
fill_var = ifelse(is_leaf, pred_class, split_var_name)
)
ggraph(tg1, layout = "dendrogram") +
geom_edge_elbow(colour = "grey65", width = 0.4) +
geom_node_label(
aes(label = display_label, fill = fill_var),
colour = "black", size = 2.5, fontface = "bold",
label.padding = unit(0.25, "lines"), label.r = unit(0.2, "lines"),
label.size = 0.4, alpha = 0.2) +
geom_node_text(
aes(label = display_label),
colour = "black", size = 2.5, fontface = "bold") +
scale_fill_manual(values = var_pal_rf, name = NULL) +
theme_graph(base_family = "sans") +
theme(legend.position = "bottom") +
labs(title = "Tree 1 from the random forest")
#> Warning in geom_node_label(aes(label = display_label, fill = fill_var), :
#> Ignoring unknown parameters: `label.size`networkformat also handles tabular data. The bundled
courses dataset contains 13 courses across 4 departments,
with prerequisite and crosslist relationships.
library(igraph)
# Prereqs as source so arrows naturally point prereq -> course;
# crosslist is undirected, so its direction doesn't matter
all_edges <- edgelist(courses,
source_cols = c(prereq, prereq2, crosslist),
target_cols = course)
all_edges$directed <- all_edges$from_col != "crosslist"
all_edges <- all_edges[all_edges$directed | all_edges$from < all_edges$to, ]
nodes <- nodelist(courses, id_col = course)
g <- graph_from_data_frame(all_edges, vertices = nodes)
tg_courses <- as_tbl_graph(g)
cat("Nodes:", vcount(g), " Edges:", ecount(g), "\n")
#> Nodes: 13 Edges: 19
dept_pal <- c(STAT = "#5B8DB8", MATH = "#E07B54", DATA = "#6BAF6B", CS = "#9B6BB0")
ggraph(tg_courses, layout = "stress") +
geom_edge_link(
aes(filter = directed),
arrow = arrow(length = unit(2, "mm"), type = "closed"),
end_cap = circle(8, "mm"),
width = 0.5, colour = "grey45") +
geom_edge_link(
aes(filter = !directed),
end_cap = circle(6, "mm"), start_cap = circle(6, "mm"),
width = 0.8, colour = "#9B59B6", linetype = "dashed") +
geom_node_label(
aes(label = name, fill = dept),
colour = "white", fontface = "bold", size = 3,
label.padding = unit(0.3, "lines"), label.r = unit(0.2, "lines")) +
scale_fill_manual(values = dept_pal, name = "Department") +
theme_graph(base_family = "sans") +
theme(legend.position = "bottom") +
labs(title = "Course prerequisite network",
subtitle = "Arrows point from prerequisite to dependent course | Dashed = crosslisted")For the full data frame workflow — column selection,
na.rm, attr_cols, symmetric_cols,
and graph statistics — see
vignette("edgelist-nodelist").
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.