tna
A methodologically rigorous framework for modeling behavioral processes as networks with the full power of graph theory and modern network science techniques. Supports multiple model types — first-order Markov, frequency-based, co-occurrence, and attention-based — with confirmatory testing at every level: bootstrapped edge significance, centrality stability coefficients, and permutation-based group comparisons with effect sizes and multiple-comparison corrections.
Install
install.packages("tna")
# or dev version:
devtools::install_github("sonsoleslp/tna")
All analysis objects have print(), summary(), and plot() methods.
Tutorials
Build Models
Plotting
Analysis
Validation
Data Preparation
prepare_data()
Convert long-format event logs into wide-format sequences. This is typically your first step before building a TNA model.
prepare_data(data, actor, time, action, order, time_threshold = 900,
custom_format = NULL, is_unix_time = FALSE,
unix_time_unit = "seconds", unused_fn = dplyr::first)| Parameter | Description | Default |
|---|---|---|
data | Long-format data frame with actor, time, action columns | — |
actor | Column name for actor/sequence ID | — |
time | Column name for timestamps | — |
action | Column name for states/actions | — |
time_threshold | Max seconds between events in same session | 900 |
$sequence_data, $statistics, $data.Example
results <- prepare_data(
group_regulation_long,
actor = "Actor", time = "Time", action = "Action"
)
print(results$statistics)
Building Models
tna() / ftna() / ctna() / atna()
The recommended way to build a TNA model. Each shortcut creates a model of a specific type from sequence data — no need to specify type manually. tna() builds a first-order Markov transition probability matrix where each row sums to 1, representing the likelihood of transitioning from one state to the next. The other shortcuts build frequency, co-occurrence, and attention-weighted models respectively.
tna(x, ...)
ftna(x, ...)
ctna(x, ...)
atna(x, ...)| Function | Model Type | Description |
|---|---|---|
tna() | "relative" | Transition probabilities (rows sum to 1). The standard TNA model |
ftna() | "frequency" | Raw transition counts. Required for plot_mosaic() and plot_associations() |
ctna() | "co-occurrence" | Co-occurrence within sequences, regardless of order |
atna() | "attention" | Exponential decay-weighted downstream pairs |
| Parameter | Description |
|---|---|
x | data.frame (wide format), stslist, matrix, or tna_data object |
... | Additional arguments passed to build_model(): scaling, cols, params, inits, begin_state, end_state |
tna object with $weights (transition matrix), $inits (initial state probabilities), $labels (state names), $data (original data).Examples
# Build a standard TNA model (transition probabilities)
model <- tna(group_regulation)
print(model)
summary(model)
# Build other model types
model_f <- ftna(group_regulation) # frequency counts
model_c <- ctna(group_regulation) # co-occurrence
model_a <- atna(group_regulation) # attention-weighted
build_model()
The general-purpose model constructor. Use this when you need model types beyond the four shortcuts (e.g., n-gram, gap, window, reverse), or when you need fine-grained control over params. For the common cases, prefer tna(), ftna(), ctna(), atna() above.
build_model(x, type = "relative", scaling = character(0L), cols = everything(),
params = list(), inits, begin_state, end_state)| Parameter | Description | Default |
|---|---|---|
x | data.frame (wide), stslist, matrix, or tna_data | — |
type | "relative", "frequency", "co-occurrence", "n-gram", "gap", "window", "reverse", "attention" | "relative" |
scaling | "minmax", "max", "rank", or empty vector | character(0) |
params | List: n_gram, max_gap, window_size, weighted, direction, decay, lambda, time, duration | list() |
tna object with $weights, $inits, $labels, $data.Examples
# Using build_model with explicit type
model <- build_model(group_regulation, type = "relative")
# Shortcut functions (preferred)
model <- tna(group_regulation) # type = "relative"
model_f <- ftna(group_regulation) # type = "frequency"
model_c <- ctna(group_regulation) # type = "co-occurrence"
model_a <- atna(group_regulation) # type = "attention"
print(model)
summary(model)
Model Types
| Type | Description |
|---|---|
"relative" | Transition probabilities (rows sum to 1). Default |
"frequency" | Raw transition counts |
"co-occurrence" | Co-occurrence within sequences |
"n-gram" | Higher-order transitions |
"gap" | Non-adjacent transitions, weighted by gap |
"window" | Transitions within sliding window |
"reverse" | Reverse order (reply networks) |
"attention" | Exponential decay-weighted downstream pairs |
group_model() / group_tna()
Build a TNA model for each group. Accepts manual group assignments, mhmm objects, or tna_clustering results. Most tna functions accept group_tna directly — no need to specify groups again.
group_model(x, group, type = "relative", scaling = character(0L),
groupwise = FALSE, cols = everything(), params = list(), na.rm = TRUE, ...)Examples
# From mixture Markov model
gmodel <- group_model(engagement_mmm)
print(gmodel)
summary(gmodel)
# Manual groups
group <- c(rep("High", 1000), rep("Low", 1000))
gmodel <- group_model(group_regulation, group = group)
Plotting
All TNA analysis objects have plot() methods returning qgraph or ggplot2 objects.
plot()
Plot a TNA model as a transition network.
plot(x, labels, colors, pie, cut, show_pruned = TRUE, pruned_edge_color = "pink",
edge.color = NA, edge.labels = TRUE, edge.label.position = 0.65,
layout = "circle", layout_args = list(), scale_nodes, scaling_factor = 0.5,
mar = rep(5, 4), theme = "colorblind", ...)| Parameter | Description | Default |
|---|---|---|
layout | "circle", "spring", or custom matrix | "circle" |
scale_nodes | Centrality measure name to scale node size (e.g., "OutStrength") | — |
show_pruned | Show pruned edges in a different color | TRUE |
edge.labels | Display edge weight labels | TRUE |
theme | qgraph color theme | "colorblind" |
Examples
model <- tna(group_regulation)
plot(model)
plot(model, layout = "spring", scale_nodes = "OutStrength")
# Group model — side-by-side panels
gmodel <- group_model(engagement_mmm)
plot(gmodel)
plot_frequencies()
Bar plot of state frequency distribution. Works on tna and group_tna.
Examples
model <- tna(group_regulation)
plot_frequencies(model)
# Group comparison
gmodel <- group_model(engagement_mmm)
plot_frequencies(gmodel)
plot_mosaic()
Mosaic plot with chi-square test results. Requires frequency model (ftna()).
ftna(), not tna().Example
model_f <- ftna(group_regulation)
plot_mosaic(model_f)
plot_sequences()
Sequence index plots or state distribution plots. Pass group_tna directly for group comparison.
plot_sequences(x, group, type = "index", scale = "proportion",
geom = "bar", include_na = FALSE, colors, na_color = "white",
sort_by, show_n = TRUE, border, title, legend_title, xlab, ylab,
tick = 5, ncol = 2L, ...)data.frame method also accepts a cols parameter to select columns.| Parameter | Description | Default |
|---|---|---|
type | "index" or "distribution" | "index" |
scale | "proportion" or "count" | "proportion" |
geom | "bar" or "tile" | "bar" |
sort_by | Column name or "multidimensional" to sort sequences | — |
Examples
plot_sequences(group_regulation)
plot_sequences(group_regulation, type = "distribution")
# Group comparison — pass group_tna directly
gmodel <- group_model(engagement_mmm)
plot_sequences(gmodel)
plot_compare()
Difference network between two models. Green = x greater, red = y greater.
plot_compare(x, y, ...)Example
model_a <- tna(group_regulation[1:1000, ])
model_b <- tna(group_regulation[1001:2000, ])
plot_compare(model_a, model_b)
plot_associations()
Association network. Requires frequency model (ftna()).
ftna(), not tna().Example
model_f <- ftna(group_regulation)
plot_associations(model_f)
hist()
Histogram of edge weight distribution.
Example
model <- tna(group_regulation)
hist(model)
Additional Plot Methods
| Object | plot() produces |
|---|---|
tna_centralities | Lollipop charts by measure |
tna_communities | Network colored by community |
tna_cliques | Individual clique subnetworks |
tna_bootstrap | Significant edges only |
tna_permutation | Significant edge differences |
tna_stability | Stability curves with CS-coefficients |
tna_comparison | Heatmap or difference viz |
tna_sequence_comparison | Pattern comparison chart |
group_tna | Side-by-side network plots |
Centrality Measures
centralities()
Calculate centrality measures. Works on tna, group_tna, and matrix.
centralities(x, loops = FALSE, normalize = FALSE, measures)| Measure | Description |
|---|---|
OutStrength | Total weight of outgoing edges |
InStrength | Total weight of incoming edges |
ClosenessIn / ClosenessOut / Closeness | Closeness centrality variants |
Betweenness | Geodesic betweenness |
BetweennessRSP | Randomized shortest paths betweenness |
Diffusion | Diffusion centrality |
Clustering | Signed clustering coefficient |
Examples
model <- tna(group_regulation)
cm <- centralities(model)
print(cm)
plot(cm, ncol = 3, reorder = TRUE)
# On group_tna directly
gmodel <- group_model(engagement_mmm)
centralities(gmodel)
betweenness_network()
Build network with edge betweenness as weights.
betweenness_network(x, directed, ...)tna object with betweenness-weighted edges.Example
model <- tna(group_regulation)
bn <- betweenness_network(model)
print(bn)
estimate_cs()
Centrality stability via subset sampling.
estimate_cs(x, loops = FALSE, normalize = FALSE,
measures = c("InStrength", "OutStrength", "Betweenness"),
iter = 1000, method = "pearson",
drop_prop = seq(0.1, 0.9, by = 0.1),
threshold = 0.7, certainty = 0.95, progressbar = FALSE)tna_stability object with CS-coefficients per measure.Example
model <- tna(group_regulation)
cs <- estimate_cs(model, measures = c("InStrength", "OutStrength"), iter = 100)
print(cs)
plot(cs)
Network Structure
communities()
Detect communities using 7 igraph algorithms. Works on tna and group_tna.
communities(x, methods, gamma = 1)| Parameter | Description | Default |
|---|---|---|
methods | Character vector of igraph community detection methods | All 7 methods |
gamma | Resolution parameter for modularity-based methods | 1 |
Examples
model <- tna(group_regulation)
comm <- communities(model)
print(comm)
plot(comm, method = "spinglass")
# On group_tna directly
gmodel <- group_model(engagement_mmm)
communities(gmodel)
cliques()
Identify cliques (complete subgraphs) of a given size.
cliques(x, size = 2, threshold = 0, sum_weights = FALSE, ...)Example
model <- tna(group_regulation)
cliq <- cliques(model, size = 2)
print(cliq)
plot(cliq, n = 3, ask = FALSE)
Comparison
compare()
Compare two TNA models with comprehensive metrics.
# tna method
compare(x, y, scaling = "none", ...)
# group_tna method
compare(x, i, j, ...)Example
gmodel <- group_model(engagement_mmm)
comp <- compare(gmodel, i = 1, j = 2)
print(comp)
plot(comp)
compare_sequences()
Compare subsequence patterns between groups. Pass group_tna directly — groups are already defined.
# For group_tna (groups already defined)
compare_sequences(x, sub, min_freq = 5L, correction = "bonferroni", ...)Example
gmodel <- group_model(engagement_mmm)
comp_seq <- compare_sequences(gmodel)
print(head(comp_seq, 10))
plot(comp_seq)
permutation_test()
Permutation tests for edge weight and centrality differences.
permutation_test(x, y, adjust = "none", iter = 1000, paired = FALSE,
level = 0.05, measures = character(0), ...)Example
model_x <- tna(group_regulation[1:200, ])
model_y <- tna(group_regulation[1001:1200, ])
perm <- permutation_test(model_x, model_y, iter = 100)
print(perm)
plot(perm)
Validation
bootstrap()
Bootstrap transition networks for confidence intervals and significance.
bootstrap(x, iter = 1000, level = 0.05, method = "stability",
threshold, consistency_range = c(0.75, 1.25))| Parameter | Description | Default |
|---|---|---|
iter | Number of bootstrap iterations | 1000 |
level | Significance level | 0.05 |
method | "stability" or "significance" | "stability" |
Example
model <- tna(group_regulation)
boot <- bootstrap(model, iter = 100)
print(boot)
plot(boot)
bootstrap_cliques()
Bootstrap cliques to assess stability.
bootstrap_cliques(x, size = 2L, threshold = 0, iter = 1000, level = 0.05,
consistency_range = c(0.75, 1.25), ...)Example
model <- tna(group_regulation)
bc <- bootstrap_cliques(model, size = 2, iter = 100)
print(bc)
prune()
Remove weak edges. Four methods available.
prune(x, method = "threshold", threshold = 0.1, lowest = 0.05,
level = 0.5, boot = NULL, ...)| Method | Description |
|---|---|
"threshold" | Remove edges below absolute threshold |
"lowest" | Remove lowest proportion of edges |
"disparity" | Statistical disparity filter |
"bootstrap" | Remove non-significant edges (requires boot) |
Examples
model <- tna(group_regulation)
pruned_t <- prune(model, method = "threshold", threshold = 0.1)
pruned_p <- prune(model, method = "lowest", lowest = 0.05)
pruned_d <- prune(model, method = "disparity", level = 0.5)
pruning_details(pruned_t)
plot(pruned_t)
# Restore and reapply
restored <- deprune(pruned_t)
repruned <- reprune(restored)
Clustering
cluster_sequences()
Cluster sequences using string distance-based dissimilarity.
cluster_sequences(data, k, dissimilarity = "hamming", method = "pam",
na_syms = c("*", "%"), weighted = FALSE, lambda = 1, ...)| Parameter | Description | Default |
|---|---|---|
k | Number of clusters | — |
dissimilarity | "hamming", "osa", "lv", "dl", "lcs", etc. | "hamming" |
method | "pam" or "ward" | "pam" |
"osa" instead of "hamming".tna_clustering object. Pass directly to group_model().Example
result <- cluster_sequences(group_regulation[1:200, ], k = 3, dissimilarity = "osa")
print(result)
# Pass directly to group_model
gmodel_clust <- group_model(result)
plot(gmodel_clust)
rename_groups()
Rename groups in a group_tna object.
rename_groups(x, new_names)Example
gmodel <- group_model(engagement_mmm)
gmodel_renamed <- rename_groups(gmodel, c("A", "B", "C"))
names(gmodel_renamed) # "A" "B" "C"
mmm_stats()
Extract statistics from a mixture Markov model.
mmm_stats(x, level = 0.05)Example
mmm_stats(engagement_mmm)
Summary & Conversion
summary()
Comprehensive summary of a TNA model. Works on tna and group_tna.
Examples
model <- tna(group_regulation)
summary(model)
gmodel <- group_model(engagement_mmm)
summary(gmodel)
as.igraph()
Convert a TNA model to an igraph object for use with igraph functions.
as.igraph(x, mode = "directed", ...)Example
model <- tna(group_regulation)
g <- as.igraph(model)
print(g)
simulate()
Simulate sequence data from a TNA model (requires type = "relative").
simulate(object, nsim = 1, seed = NULL, max_len = 100L, na_range = c(0L, 0L), ...)Example
model <- tna(group_regulation)
sim <- simulate(model, nsim = 5, seed = 123, max_len = 10)
print(sim)
sna()
Build a social network analysis model from edge-list data (from, to, weight).
sna(x, aggregate = sum, ...)Example
set.seed(42)
d <- data.frame(
from = sample(LETTERS[1:4], 100, replace = TRUE),
to = sample(LETTERS[1:4], 100, replace = TRUE),
weight = rexp(100)
)
model_sna <- sna(d)
import_data()
Transform wide-format feature data into long-format sequence data.
import_data(data, cols, id_cols, window_size = 1, replace_zeros = TRUE)import_onehot()
Import one-hot encoded data as co-occurrence network.
import_onehot(data, cols, window = 1L)References & Tutorials
Updated Tutorials
| Tutorial | Link |
|---|---|
| An Updated Comprehensive Tutorial on Transition Network Analysis (TNA) | sonsoles.me/posts/tna-tutorial |
| TNA Group Analysis: Comparing Groups and Discovering Clusters | sonsoles.me/posts/tna-group |
| TNA Clustering: Discovering Latent Subgroups from Sequence Data | sonsoles.me/posts/tna-clustering |
| TNA Model Comparison: A Complete Guide to compare() Metrics | sonsoles.me/posts/tna-compare |
Advanced Learning Analytics Methods (Book 2, Springer 2026)
Saqr, M. & Lopez-Pernas, S. (Eds.) (2026). Advanced Learning Analytics Methods: AI, Precision and Complexity. Springer. Open Access. doi:10.1007/978-3-031-95365-1
| Ch. | Title | Tutorial |
|---|---|---|
| 15 | Saqr, M., Lopez-Pernas, S., & Tikka, S. Mapping Relational Dynamics with Transition Network Analysis: A Primer and Tutorial. pp. 371–411. doi:10.1007/978-3-031-95365-1_15 |
Online tutorial |
| 16 | Saqr, M., Lopez-Pernas, S., & Tikka, S. Capturing the Breadth and Dynamics of the Temporal Processes with Frequency Transition Network Analysis: A Primer and Tutorial. doi:10.1007/978-3-031-95365-1_16 |
Online tutorial |
| 17 | Lopez-Pernas, S., Tikka, S., & Saqr, M. Mining Patterns and Clusters with Transition Network Analysis: A Heterogeneity Approach. doi:10.1007/978-3-031-95365-1_17 |
Online tutorial |
Learning Analytics Methods and Tutorials (Book 1, Springer 2024)
Saqr, M. & Lopez-Pernas, S. (Eds.). Learning Analytics Methods and Tutorials: A Practical Guide Using R. Springer. Open Access. doi:10.1007/978-3-031-54464-4
| Ch. | Title |
|---|---|
| 10 | Saqr, M., Lopez-Pernas, S., Helske, S., Durand, M., Murphy, K., Studer, M., & Ritschard, G. Sequence Analysis in Education: Principles, Technique, and Tutorial with R. pp. 321–354. doi:10.1007/978-3-031-54464-4_10 |
| 12 | Helske, J., Helske, S., Saqr, M., Lopez-Pernas, S., & Murphy, K. A Modern Approach to Transition Analysis and Process Mining with Markov Models in Education. doi:10.1007/978-3-031-54464-4_12 |
| 13 | Lopez-Pernas, S., Saqr, M., Helske, S., & Murphy, K. Multichannel Sequence Analysis in Educational Research: An Introduction and Tutorial with R. doi:10.1007/978-3-031-54464-4_13 |
Papers
Saqr, M., Lopez-Pernas, S., Tormanen, 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. Proceedings of the 15th International Learning Analytics and Knowledge Conference (LAK ’25), 351–361. ACM.
doi:10.1145/3706468.3706513 · arXiv:2411.15486
Tikka, S., Lopez-Pernas, S., & Saqr, M. (2025). tna: An R Package for Transition Network Analysis. Applied Psychological Measurement (online ahead of print).
doi:10.1177/01466216251348840
Misiejuk, K., Kaliisa, R., Lopez-Pernas, S., & Saqr, M. (2026). Expanding the Quantitative Ethnography Toolkit with Transition Network Analysis: Exploring Methodological Synergies and Boundaries. In Advances in Quantitative Ethnography. ICQE 2025, CCIS vol. 2677. Springer.
doi:10.1007/978-3-032-12229-2_10
Lopez-Pernas, S., Misiejuk, K., Tikka, S., & Saqr, M. (2026). Role Dynamics in Student-AI Collaboration: A Heterogeneous Transition Network Analysis Approach. SSRN preprint.
doi:10.2139/ssrn.6082190
Package Vignettes
| Vignette | Link |
|---|---|
| Getting started with tna | sonsoles.me/tna/articles/tna.html |
| A showcase of the main tna functions | sonsoles.me/tna/articles/complete_tutorial.html |
| How to prepare data for tna | sonsoles.me/tna/articles/prepare_data.html |
| Frequency-based TNA | sonsoles.me/tna/articles/ftna.html |
| Attention TNA | sonsoles.me/tna/articles/atna.html |
| Finding cliques and communities | sonsoles.me/tna/articles/communities_and_cliques.html |
| Using grouped sequence data | sonsoles.me/tna/articles/grouped_sequences.html |
Tools & Apps
| Resource | Link |
|---|---|
| CRAN package | cran.r-project.org/package=tna |
| GitHub repository | github.com/sonsoleslp/tna |
| Documentation site | sonsoles.me/tna |
| TNA Web (Shiny app) | sonsoleslp.shinyapps.io/tna-app |
| JTNA (Jamovi plugin) | github.com/sonsoleslp/jTNA |
| LA Methods companion site | lamethods.org |
| UEF TNA resource hub | sites.uef.fi/learning-analytics/tna |
tna v1.1.0 | MIT License | Mohammed Saqr, Santtu Tikka, Sonsoles Lopez-Pernas