R has several network packages — igraph for graph algorithms, qgraph for psychometric networks, tidygraph for dplyr-style manipulation. Each does one thing well but forces you into its own data format and API. Going from a raw matrix to a filtered, annotated, publication-ready figure typically means loading three packages, converting between formats, and writing boilerplate code to stitch the results together.
cograph was designed to eliminate that friction. Every function —
plotting, centrality, community detection, filtering — accepts any major
network format directly: matrices, edge lists, igraph, statnet, qgraph,
and tna objects. No manual conversion. Centrality returns a tidy data
frame, not a list of separate calls. Community detection is one function
with 11 algorithms behind it. Statistical annotations (confidence
intervals, p-values, significance stars) render directly on the figure.
And when you need igraph or statnet for something cograph does not do,
to_igraph() and to_network() convert back
without data loss.
Beyond standard network analysis, cograph visualizes higher-order sequential pathways as simplicial blob diagrams, renders bootstrap stability results with forest plots (linear, circular, and grouped layouts), and performs motif analysis that identifies specific named node triples — not just abstract type counts.
The result is a single package that covers the full workflow from data import to publication-ready output, while remaining interoperable with the rest of the R network ecosystem.
set.seed(42)
n <- 10
states <- c("Explore", "Plan", "Monitor", "Adapt", "Reflect",
"Discuss", "Synthesize", "Evaluate", "Create", "Share")
mat <- matrix(0, n, n, dimnames = list(states, states))
# Sparse: ~30% of edges populated
edges <- sample(which(row(mat) != col(mat)), 30)
mat[edges] <- round(runif(30, 0.05, 0.5), 2)splot() is the main plotting function. One call,
publication-ready output.
Key parameters: layout, minimum,
node_fill, node_size,
edge_labels, curvature,
scale_nodes_by, theme,
tna_styling.
splot(mat, layout = "spring")
splot(mat, minimum = 0.1, edge_labels = TRUE)
splot(mat, scale_nodes_by = "betweenness")
splot(mat, theme = "dark")
splot(mat, tna_styling = TRUE)Layouts: "oval", "spring",
"circle", "grid", "mds",
"star", "bipartite", "groups", or
a custom coordinate matrix.
Themes: "default", "dark",
"minimal", "gray", "nature",
"colorblind", "viridis".
Node shapes: "circle", "square",
"triangle", "diamond",
"pentagon", "hexagon", "star",
"heart", "ellipse", "cross",
"rectangle", "pie", "donut", or
custom SVG via register_svg_shape().
| Function | Purpose |
|---|---|
splot() |
Network graph (base R) |
soplot() |
Grid/ggplot2 network |
plot_tna() / tplot() |
TNA-style wrappers with qgraph-compatible parameters |
plot_chord() |
Chord diagram (directed/undirected ribbons) |
plot_heatmap() |
Adjacency heatmap with clustering |
plot_ml_heatmap() |
Multi-layer comparison heatmap |
plot_transitions() / plot_alluvial() |
Alluvial / Sankey flow diagrams |
plot_trajectories() |
Individual trajectory tracking |
plot_compare() |
Difference network between two matrices |
plot_comparison_heatmap() |
Side-by-side heatmap comparison |
plot_mixed_network() |
Directed + undirected edges combined |
plot_bootstrap_forest() |
Bootstrap CI forest plots (linear, circular, grouped) |
plot_edge_diff_forest() |
Edge difference plots (linear, circular, chord, tile) |
plot_simplicial() |
Higher-order pathway blob overlays |
overlay_communities() |
Community blob overlays on network |
plot_mcml() |
Two-layer hierarchical cluster visualization |
plot_mtna() |
Flat multi-cluster layout |
plot_mlna() |
Stacked multilayer 3D perspective |
plot_htna() |
Multi-group heterogeneous TNA layout |
plot_robustness() |
Robustness degradation curves |
plot_permutation() /
plot_group_permutation() |
Permutation test results |
plot_simplicial(mat,
c("Explore Plan -> Monitor",
"Monitor Adapt -> Reflect",
"Discuss Synthesize -> Evaluate",
"Create Share -> Explore"),
dismantled = TRUE, ncol = 2,
title = "Higher-Order Pathways")Every function accepts six formats directly.
| Format | Example |
|---|---|
| Matrix | splot(mat) |
| Edge list | splot(data.frame(from = "A", to = "B", weight = 1)) |
| igraph | splot(igraph::make_ring(5)) |
| statnet | splot(network::network(mat)) |
| qgraph | from_qgraph(q) |
| tna | splot(tna::tna(data)) |
Conversion utilities:
| Function | Output |
|---|---|
as_cograph(x) |
cograph_network object |
to_igraph(x) |
igraph object |
to_matrix(x) |
Adjacency matrix |
to_data_frame(x) / to_df(x) |
Edge list data frame |
to_network(x) |
statnet network object |
from_qgraph(q) |
Extract qgraph styles into cograph |
Filter edges and nodes with expressions. Centrality measures are
lazy-computed inside filter_nodes().
strong <- filter_edges(mat, weight > 0.3)
get_edges(strong)
#> from to weight
#> 1 8 3 0.33
#> 2 10 3 0.49
#> 3 8 4 0.43
#> 4 10 4 0.39
#> 5 1 5 0.35
#> 6 6 5 0.35
#> 7 7 5 0.42
#> 8 2 6 0.40
#> 9 4 6 0.34
#> 10 2 8 0.49
#> 11 9 8 0.39
#> 12 3 9 0.37
#> 13 2 10 0.36top3 <- select_nodes(mat, top = 3, by = "betweenness")
get_labels(top3)
#> [1] "Plan" "Monitor" "Adapt"| Function | Purpose |
|---|---|
filter_edges(x, ...) |
Filter by weight, from, to |
filter_nodes(x, ...) |
Filter by degree, centrality, label |
select_nodes(x, ...) |
Top-N by centrality, by name, neighbors |
select_edges(x, ...) |
Top-N, involving, between, bridges, mutual |
select_neighbors(x, of) |
Ego-network extraction (multi-hop) |
select_component(x) |
Largest or named component |
select_top(x, n, by) |
Top-N nodes by any centrality |
select_bridges(x) |
Bridge edges only |
select_top_edges(x, n) |
Top-N edges by weight |
select_edges_involving(x, nodes) |
Edges touching specific nodes |
select_edges_between(x, s1, s2) |
Edges between two node sets |
subset_nodes(x, ...) /
subset_edges(x, ...) |
Base R-style subsetting |
simplify(x) |
Remove multi-edges and self-loops |
Getters and setters:
| Function | Purpose |
|---|---|
get_nodes(x) / set_nodes(x, df) |
Node data frame |
get_edges(x) / set_edges(x, df) |
Edge data frame |
get_labels(x) |
Node label vector |
n_nodes(x) / n_edges(x) |
Counts |
is_directed(x) |
Directedness |
set_groups(x) / get_groups(x) |
Group assignments |
set_layout(x, layout) |
Layout coordinates |
summarize_network(x) |
Network summary |
centrality() computes node centrality measures and
returns one tidy data frame. The built-in
student_interactions edge list works directly: no igraph
object, no matrix conversion, no setup code.
data(student_interactions)
centrality(student_interactions)
#> node degree_all strength_all closeness_all betweenness eigenvector
#> 1 Ac 33 129 0.01754386 26.342857 1.000000e+00
#> 2 Ad 20 36 0.01754386 42.541520 1.096110e-01
#> 3 Fi 24 51 0.01666667 35.721634 1.789565e-01
#> 4 Ik 14 24 0.01666667 25.844874 1.551369e-02
#> 5 Vx 26 43 0.01960784 90.717124 7.238902e-02
#> 6 Rt 20 37 0.01785714 63.135739 1.159931e-01
#> 7 Km 11 16 0.01639344 18.175108 2.804725e-02
#> 8 Gj 19 31 0.01818182 114.599049 3.265786e-02
#> 9 Bd 12 18 0.01612903 21.769264 9.607736e-03
#> 10 Ce 10 13 0.01612903 16.648629 4.473504e-03
#> 11 Oq 14 20 0.01754386 34.151726 2.293068e-02
#> 12 Ya 13 19 0.01612903 18.216122 1.758656e-02
#> 13 Mo 12 17 0.01587302 38.264502 1.003629e-01
#> 14 Hj 12 19 0.01754386 85.816522 2.013125e-02
#> 15 Tv 10 13 0.01666667 25.916306 1.320877e-02
#> 16 Eg 10 12 0.01639344 22.335171 5.783916e-03
#> 17 Pr 11 18 0.01666667 23.974060 7.602231e-02
#> 18 Qs 15 19 0.01785714 76.910851 1.511764e-02
#> 19 Xz 14 18 0.01639344 22.280159 8.533484e-03
#> 20 Np 8 8 0.01666667 12.044048 1.549052e-02
#> 21 Dg 13 13 0.01886792 29.240901 6.260099e-03
#> 22 Hk 16 25 0.01818182 72.176441 1.201845e-01
#> 23 Wy 11 16 0.01639344 34.014358 1.054705e-03
#> 24 Jl 15 18 0.01818182 67.359085 5.009801e-02
#> 25 Fh 21 55 0.01818182 78.588877 2.817489e-01
#> 26 Zb 7 8 0.01538462 9.583333 5.429715e-05
#> 27 Eh 7 13 0.01428571 34.325000 1.163185e-03
#> 28 Be 14 16 0.01851852 105.250898 2.203252e-03
#> 29 Df 8 10 0.01562500 11.026190 4.800876e-06
#> 30 Cf 12 15 0.01724138 119.109163 1.243207e-02
#> 31 Su 6 9 0.01369863 33.154401 1.028472e-04
#> 32 Ln 7 8 0.01408451 5.749708 1.376854e-02
#> 33 Gi 3 4 0.01351351 0.000000 1.280388e-17
#> 34 Uw 4 7 0.01250000 0.000000 6.480230e-20
#> pagerank
#> 1 0.285861728
#> 2 0.052985644
#> 3 0.077591140
#> 4 0.024836857
#> 5 0.057364714
#> 6 0.042552472
#> 7 0.016655998
#> 8 0.025444498
#> 9 0.014321668
#> 10 0.010679742
#> 11 0.016087378
#> 12 0.016588876
#> 13 0.031180263
#> 14 0.019051413
#> 15 0.012644206
#> 16 0.010425091
#> 17 0.022794289
#> 18 0.019784870
#> 19 0.013229134
#> 20 0.008466679
#> 21 0.010345027
#> 22 0.040383192
#> 23 0.009067435
#> 24 0.020529375
#> 25 0.070538086
#> 26 0.005635780
#> 27 0.010080122
#> 28 0.009378924
#> 29 0.004957518
#> 30 0.017877547
#> 31 0.005136500
#> 32 0.007628879
#> 33 0.005483193
#> 34 0.004411765That one call returns the default centrality table: degree, strength, closeness, betweenness, eigenvector centrality, and PageRank. Individual functions return named vectors when you want one measure only:
centrality_degree(student_interactions)
#> Ac Ad Fi Ik Vx Rt Km Gj Bd Ce Oq Ya Mo Hj Tv Eg Pr Qs Xz Np Dg Hk Wy Jl Fh Zb
#> 33 20 24 14 26 20 11 19 12 10 14 13 12 12 10 10 11 15 14 8 13 16 11 15 21 7
#> Eh Be Df Cf Su Ln Gi Uw
#> 7 14 8 12 6 7 3 4
centrality_pagerank(student_interactions)
#> Ac Ad Fi Ik Vx Rt
#> 0.285861728 0.052985644 0.077591140 0.024836857 0.057364714 0.042552472
#> Km Gj Bd Ce Oq Ya
#> 0.016655998 0.025444498 0.014321668 0.010679742 0.016087378 0.016588876
#> Mo Hj Tv Eg Pr Qs
#> 0.031180263 0.019051413 0.012644206 0.010425091 0.022794289 0.019784870
#> Xz Np Dg Hk Wy Jl
#> 0.013229134 0.008466679 0.010345027 0.040383192 0.009067435 0.020529375
#> Fh Zb Eh Be Df Cf
#> 0.070538086 0.005635780 0.010080122 0.009378924 0.004957518 0.017877547
#> Su Ln Gi Uw
#> 0.005136500 0.007628879 0.005483193 0.004411765Selected measures:
| Category | Functions |
|---|---|
| Degree | centrality_degree(),
centrality_strength(), centrality_indegree(),
centrality_outdegree(),
centrality_instrength(),
centrality_outstrength() |
| Path | centrality_betweenness(),
centrality_closeness(), centrality_harmonic(),
centrality_eccentricity() (each with in/out variants) |
| Spectral | centrality_eigenvector(),
centrality_pagerank(), centrality_authority(),
centrality_hub(), centrality_alpha(),
centrality_power(), centrality_subgraph() |
| Structural | centrality_coreness(),
centrality_constraint(),
centrality_transitivity(),
centrality_laplacian() |
| Flow | centrality_current_flow_closeness(),
centrality_current_flow_betweenness(),
centrality_load() |
| Spreading | centrality_diffusion(),
centrality_leverage(), centrality_kreach(),
centrality_voterank(),
centrality_percolation() |
Edge centrality: edge_centrality(),
edge_betweenness().
network_summary() computes up to 37 network-level
metrics.
network_summary(mat)
#> node_count edge_count density component_count diameter mean_distance min_cut
#> 1 10 30 0.333 1 0.97 0.435 1
#> centralization_degree centralization_in_degree centralization_out_degree
#> 1 0.123 0.333 0.222
#> centralization_betweenness centralization_closeness centralization_eigen
#> 1 0.149 0.238 0.479
#> transitivity reciprocity assortativity_degree hub_score authority_score
#> 1 0.423 0.111 -0.116 NA NA| Function | Purpose |
|---|---|
network_summary() |
37 metrics (density, diameter, clustering, etc.) |
network_small_world() |
Small-world coefficient |
network_rich_club() |
Rich-club coefficient |
network_global_efficiency() |
Global efficiency |
network_local_efficiency() |
Local efficiency |
degree_distribution() |
Degree histogram |
network_girth() |
Shortest cycle |
network_radius() |
Minimum eccentricity |
network_bridges() |
Bridge edges |
network_cut_vertices() |
Articulation points |
network_vertex_connectivity() |
Minimum vertices to disconnect |
network_clique_size() |
Largest complete subgraph |
11 algorithms with a consistent interface.
comms <- communities(mat, method = "walktrap")
comms
#> Community structure (walktrap)
#> Nodes: 10 | Communities: 2 | Modularity: 0.1976
#> Sizes: 5, 5
#>
#> node community
#> Explore 1
#> Plan 2
#> Monitor 2
#> Adapt 1
#> Reflect 1
#> Discuss 1
#> Synthesize 1
#> Evaluate 2
#> Create 2
#> Share 2
community_sizes(comms)
#> [1] 5 5| Function | Algorithm | Alias |
|---|---|---|
community_louvain() |
Louvain modularity | com_lv() |
community_leiden() |
Leiden (improved Louvain) | com_ld() |
community_fast_greedy() |
Fast greedy | com_fg() |
community_walktrap() |
Random walk | com_wt() |
community_infomap() |
Information flow | com_im() |
community_label_propagation() |
Label propagation | com_lp() |
community_edge_betweenness() |
Edge betweenness | com_eb() |
community_leading_eigenvector() |
Leading eigenvector | com_le() |
community_spinglass() |
Spin glass | com_sg() |
community_optimal() |
Exact optimization | com_op() |
community_fluid() |
Fluid communities | com_fl() |
Additional community functions:
| Function | Purpose |
|---|---|
community_consensus() |
Run algorithm N times, keep stable assignments |
compare_communities() |
Compare partitions (NMI, VI, Rand, adjusted Rand) |
modularity() |
Modularity score |
community_sizes() |
Size of each community |
color_communities() |
Color vector from community membership |
cluster_quality() |
Quality metrics (silhouette, Dunn index) |
cluster_significance() |
Permutation-based significance testing |
detect_communities() |
Alternative interface (returns data frame) |
Motif analysis identifies recurring 3-node patterns using the MAN classification (16 directed triad types).
mot <- motifs(mat, significance = FALSE)
mot
#> Motif Census
#> Level: aggregate | States: 10 | Pattern: triangle
#>
#> Type distribution:
#> 030T 120C 030C 120D 120U
#> 11 3 2 2 1
#>
#> Top 5 results:
#> type count
#> 030T 11
#> 120C 3
#> 030C 2
#> 120D 2
#> 120U 1| Function | Purpose |
|---|---|
motifs() |
MAN type census with significance testing |
subgraphs() |
Named node triples forming each pattern |
motif_census() |
Low-level triad census |
extract_motifs() |
Per-individual motif extraction |
extract_triads() |
Extract specific triad types |
triad_census() |
Raw 16-type triad count |
get_edge_list() |
Edge list from tna for motif input |
Plot types: plot(mot, type = "types"),
"significance", "triads",
"patterns".
Simulate network degradation under targeted and random removal.
robustness(mat, type = "vertex", measure = "betweenness", n_iter = 100)
plot_robustness(x = mat, measures = c("betweenness", "degree", "random"))| Function | Purpose |
|---|---|
robustness() |
Simulate removal attacks (vertex or edge) |
plot_robustness() |
Plot robustness curves for multiple strategies |
robustness_summary() |
AUC and summary statistics |
robustness_auc() |
Area under the robustness curve |
Backbone extraction using the disparity filter (Serrano et al. 2009).
clusters <- list(
Cognitive = c("Explore", "Plan", "Monitor", "Adapt", "Reflect"),
Social = c("Discuss", "Synthesize", "Share"),
Evaluative = c("Evaluate", "Create")
)
plot_mcml(mat, clusters, mode = "tna")
plot_mtna(mat, clusters)| Function | Architecture |
|---|---|
plot_mcml() |
Two-layer: detail nodes + summary pies |
plot_mtna() |
Flat cluster layout |
plot_mlna() |
Stacked 3D multilayer |
plot_htna() |
Multi-group heterogeneous TNA |
csum() / summarize_clusters() |
Pre-compute cluster aggregation |
as_tna() / as_mcml() |
Convert cluster summaries to tna objects |
summarize_network() / cnet() |
Extract cluster-level network (matrix aggregation) |
Construct and analyze supra-adjacency matrices for multilayer/multiplex networks.
| Function | Purpose |
|---|---|
mlna() / supra_adjacency() |
Build supra-adjacency matrix |
supra_layer() / supra_interlayer() |
Extract individual layers |
aggregate_layers() /
aggregate_weights() |
Combine layers |
plot_mlna() |
3D perspective visualization |
plot_ml_heatmap() |
Multi-layer heatmap comparison |
Detect sequential dependencies beyond first-order Markov models. Requires the Nestimate package.
| Function | Purpose |
|---|---|
build_hon() |
Higher-Order Network construction |
build_hypa() |
Path anomaly detection (hypergeometric null) |
build_mogen() |
Multi-order model selection (AIC/BIC) |
path_counts() |
k-step path frequencies |
plot_simplicial() |
Visualize pathways as blob overlays |
build_simplicial() |
Simplicial complex from cliques |
persistent_homology() |
Topological persistence across thresholds |
q_analysis() |
Multi-level structural connectivity |
verify_simplicial() |
Cross-validate via Euler-Poincare theorem |
Direct support for all tna package objects:
| Object | What splot() does |
|---|---|
tna |
Network with donut rings, TNA styling |
group_tna |
Multi-panel grid per group |
tna_bootstrap |
Stability-styled edges |
tna_permutation |
Colored difference network |
group_tna_permutation |
Multi-panel permutation results |
tna_disparity |
Backbone filter visualization |
| Function | Colors |
|---|---|
palette_viridis(n) |
Viridis scale |
palette_pastel(n) |
Soft pastel |
palette_blues(n) |
Blue gradient |
palette_reds(n) |
Red gradient |
palette_diverging(n) |
Blue-white-red |
palette_colorblind(n) |
Colorblind-safe |
palette_rainbow(n) |
Rainbow |
The sn_* functions provide a chainable builder for the
grid/ggplot2 rendering path.
mat |>
cograph() |>
sn_layout("spring") |>
sn_theme("minimal") |>
sn_nodes(size = 8, fill = "steelblue") |>
sn_edges(curvature = 0.2) |>
sn_render(title = "My Network")
mat |> cograph() |> sn_save("network.pdf")
p <- mat |> cograph() |> sn_ggplot()| Function | Purpose |
|---|---|
cograph() / as_cograph() |
Create network object |
sn_nodes() |
Node aesthetics |
sn_edges() |
Edge aesthetics |
sn_layout() |
Layout algorithm |
sn_theme() |
Visual theme |
sn_palette() |
Color palette |
sn_render() |
Render to screen |
sn_save() / sn_save_ggplot() |
Save to file |
sn_ggplot() |
Convert to ggplot2 object |
register_theme() / register_layout() /
register_shape() |
Register custom themes, layouts, shapes |
Package resources:
Blog posts:
References:
Saqr, M., López-Pernas, S., Conde, M. A., & Hernández-García, A. (2024). Social Network Analysis: A Primer, a Guide and a Tutorial in R. In Learning Analytics Methods and Tutorials. Springer. https://doi.org/10.1007/978-3-031-54464-4_15
Hernández-García, Á., Cuenca-Enrique, C., Traxler, A., López-Pernas, S., Conde-González, M. Á., & Saqr, M. (2024). Community detection in learning networks using R. In Learning Analytics Methods and Tutorials (pp. 519–540). Springer. https://doi.org/10.1007/978-3-031-54464-4_16
Saqr, M., López-Pernas, S., Törmänen, T., Kaliisa, R., Misiejuk, K., & Tikka, S. (2025). Transition Network Analysis: A Novel Framework for Modeling, Visualizing, and Identifying the Temporal Patterns of Learners and Learning Processes. In Proceedings of the 15th LAK Conference (pp. 351–361). ACM. https://doi.org/10.1145/3706468.3706513
Tikka, S., López-Pernas, S., & Saqr, M. (2025). tna: An R Package for Transition Network Analysis. Applied Psychological Measurement. https://doi.org/10.1177/01466216251348840