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.

v1.1.0 CRAN Markov Models R ≥ 4.1.0

Install

install.packages("tna")
# or dev version:
devtools::install_github("sonsoleslp/tna")

All analysis objects have print(), summary(), and plot() methods.

Tutorials

Comprehensive TNA Tutorial Group Analysis Clustering Model Comparison Book Ch. 15: TNA Primer Book Ch. 16: FTNA Book Ch. 17: Clusters

Build Models

tna() · ftna() · ctna() · atna() · group_model() · sna()

Plotting

plot() · plot_frequencies() · plot_mosaic() · plot_sequences() · plot_compare() · hist()

Analysis

centralities() · communities() · cliques() · compare() · permutation_test()

Validation

bootstrap() · prune() · estimate_cs() · cluster_sequences()

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)
ParameterDescriptionDefault
dataLong-format data frame with actor, time, action columns
actorColumn name for actor/sequence ID
timeColumn name for timestamps
actionColumn name for states/actions
time_thresholdMax seconds between events in same session900
Returns: List with $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, ...)
FunctionModel TypeDescription
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
ParameterDescription
xdata.frame (wide format), stslist, matrix, or tna_data object
...Additional arguments passed to build_model(): scaling, cols, params, inits, begin_state, end_state
Returns: A 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)
ParameterDescriptionDefault
xdata.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 vectorcharacter(0)
paramsList: n_gram, max_gap, window_size, weighted, direction, decay, lambda, time, durationlist()
Returns: A 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

TypeDescription
"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", ...)
ParameterDescriptionDefault
layout"circle", "spring", or custom matrix"circle"
scale_nodesCentrality measure name to scale node size (e.g., "OutStrength")
show_prunedShow pruned edges in a different colorTRUE
edge.labelsDisplay edge weight labelsTRUE
themeqgraph 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()).

Requires integer-valued weight matrix. Use 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, ...)
The data.frame method also accepts a cols parameter to select columns.
ParameterDescriptionDefault
type"index" or "distribution""index"
scale"proportion" or "count""proportion"
geom"bar" or "tile""bar"
sort_byColumn 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()).

Requires integer-valued weight matrix. Use 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

Objectplot() produces
tna_centralitiesLollipop charts by measure
tna_communitiesNetwork colored by community
tna_cliquesIndividual clique subnetworks
tna_bootstrapSignificant edges only
tna_permutationSignificant edge differences
tna_stabilityStability curves with CS-coefficients
tna_comparisonHeatmap or difference viz
tna_sequence_comparisonPattern comparison chart
group_tnaSide-by-side network plots

Centrality Measures

centralities()

Calculate centrality measures. Works on tna, group_tna, and matrix.

centralities(x, loops = FALSE, normalize = FALSE, measures)
MeasureDescription
OutStrengthTotal weight of outgoing edges
InStrengthTotal weight of incoming edges
ClosenessIn / ClosenessOut / ClosenessCloseness centrality variants
BetweennessGeodesic betweenness
BetweennessRSPRandomized shortest paths betweenness
DiffusionDiffusion centrality
ClusteringSigned 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, ...)
Returns: A 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)
Returns: A 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)
ParameterDescriptionDefault
methodsCharacter vector of igraph community detection methodsAll 7 methods
gammaResolution parameter for modularity-based methods1
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))
ParameterDescriptionDefault
iterNumber of bootstrap iterations1000
levelSignificance level0.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, ...)
MethodDescription
"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, ...)
ParameterDescriptionDefault
kNumber of clusters
dissimilarity"hamming", "osa", "lv", "dl", "lcs", etc."hamming"
method"pam" or "ward""pam"
For variable-length sequences, use "osa" instead of "hamming".
Returns: A 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

TutorialLink
An Updated Comprehensive Tutorial on Transition Network Analysis (TNA)sonsoles.me/posts/tna-tutorial
TNA Group Analysis: Comparing Groups and Discovering Clusterssonsoles.me/posts/tna-group
TNA Clustering: Discovering Latent Subgroups from Sequence Datasonsoles.me/posts/tna-clustering
TNA Model Comparison: A Complete Guide to compare() Metricssonsoles.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.TitleTutorial
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

VignetteLink
Getting started with tnasonsoles.me/tna/articles/tna.html
A showcase of the main tna functionssonsoles.me/tna/articles/complete_tutorial.html
How to prepare data for tnasonsoles.me/tna/articles/prepare_data.html
Frequency-based TNAsonsoles.me/tna/articles/ftna.html
Attention TNAsonsoles.me/tna/articles/atna.html
Finding cliques and communitiessonsoles.me/tna/articles/communities_and_cliques.html
Using grouped sequence datasonsoles.me/tna/articles/grouped_sequences.html

Tools & Apps

ResourceLink
CRAN packagecran.r-project.org/package=tna
GitHub repositorygithub.com/sonsoleslp/tna
Documentation sitesonsoles.me/tna
TNA Web (Shiny app)sonsoleslp.shinyapps.io/tna-app
JTNA (Jamovi plugin)github.com/sonsoleslp/jTNA
LA Methods companion sitelamethods.org
UEF TNA resource hubsites.uef.fi/learning-analytics/tna

tna v1.1.0 | MIT License | Mohammed Saqr, Santtu Tikka, Sonsoles Lopez-Pernas