3. Ecosystem compatibility: TraMineR, tna, and Nestimate
Source:vignettes/ecosystem-compatibility.Rmd
ecosystem-compatibility.Rmdtransitiontrees is built to slot into the wider
Dynalytics
ecosystem (tna, Nestimate,
cograph) as well as TraMineR. You do
not have to re-export or re-format your data:
context_tree() accepts a fitted state-sequence or
transition/network object directly, auto-detected by
its S3 class. It reads the sequences the object already carries and fits
the suffix tree on exactly those, over the same state set.
This vignette shows the three hand-offs on one shared dataset and
confirms they agree. The tna and Nestimate
sections only run if those packages are installed; the
TraMineR state-sequence section runs on the bundled data
with no extra package.
The shared dataset
The bundled engagement object is a TraMineR
state-sequence (stslist): 1000 weekly engagement sequences
over three states.
Route A – a state-sequence (stslist) object
Hand the stslist straight to
context_tree().
tree_sts_raw <- context_tree(engagement, max_depth = 2L, min_count = 5L)
tree_sts_raw$alphabet
#> [1] "%" "Active" "Average" "Disengaged"Note the alphabet: a TraMineR sequence object encodes
gaps/missing with a void code (here %),
and taken literally that void looks like a fourth state. The clean fix
is to pass the object’s declared alphabet so the void is
treated as end-of-sequence, not a state:
tree_sts <- context_tree(engagement, max_depth = 2L, min_count = 5L,
alphabet = attr(engagement, "alphabet"))
tree_sts$alphabet
#> [1] "Active" "Average" "Disengaged"
n_nodes(tree_sts)
#> [1] 13That is the tree to use. The tna and
Nestimate objects below carry the clean label set
internally, so they decode the void for you automatically – the two
routes converge on this same tree.
Route B – a tna transition-network object
A tna model carries its underlying sequences in its
$data slot. context_tree() reads them and
decodes through the model’s label set.
library(tna)
#> 'tna' package version 1.2.3
#> ------------------------------------------------------
#> Tikka, S., López-Pernas, S., and Saqr, M. (2025).
#> tna: An R Package for Transition Network Analysis.
#> Applied Psychological Measurement.
#> https://doi.org/10.1177/01466216251348840
#> ------------------------------------------------------
#> Please type 'citation("tna")' for more citation information.
#> See the package website at https://sonsoles.me/tna/
#>
#> Attaching package: 'tna'
#> The following object is masked from 'package:transitiontrees':
#>
#> group_regulation_long
model_tna <- tna(engagement)
class(model_tna)
#> [1] "tna"
tree_tna <- context_tree(model_tna, max_depth = 2L, min_count = 5L)
tree_tna
#> <transitiontrees> 13 nodes, depth <= 2, 3 states [unpruned]
#> alphabet : Active, Average, Disengaged
#> fit on : 1000 sequences, 24555 observations
#> smoothing: floor(ymin=0.001, rule=interpolate) min_count = 5
#> (start) n=24555 -> Active (0.51)
#> |-- Active n=11894 -> Active (0.86)
#> | |-- Active n=9766 -> Active (0.86)
#> | |-- Average n=1473 -> Active (0.86)
#> | `-- Disengaged n=312 -> Active (0.85)
#> |-- Average n=5063 -> Average (0.55)
#> | |-- Active n=1031 -> Average (0.54)
#> | |-- Average n=2670 -> Average (0.54)
#> | `-- Disengaged n=1043 -> Average (0.57)
#> `-- Disengaged n=6598 -> Disengaged (0.79)
#> |-- Active n=581 -> Disengaged (0.82)
#> |-- Average n=719 -> Disengaged (0.73)
#> `-- Disengaged n=4960 -> Disengaged (0.79)Route C – a Nestimate network object
Nestimate::build_tna() (and the other
build_*() constructors) return a netobject
that likewise carries the sequence frame and a $nodes label
table. Same hand-off:
library(Nestimate)
#>
#> Attaching package: 'Nestimate'
#> The following objects are masked from 'package:tna':
#>
#> cluster_data, group_regulation_long, plot_mosaic
model_nest <- build_tna(engagement)
class(model_nest)
#> [1] "netobject" "cograph_network"
tree_nest <- context_tree(model_nest, max_depth = 2L, min_count = 5L)
tree_nest
#> <transitiontrees> 13 nodes, depth <= 2, 3 states [unpruned]
#> alphabet : Active, Average, Disengaged
#> fit on : 1000 sequences, 24555 observations
#> smoothing: floor(ymin=0.001, rule=interpolate) min_count = 5
#> (start) n=24555 -> Active (0.51)
#> |-- Active n=11894 -> Active (0.86)
#> | |-- Active n=9766 -> Active (0.86)
#> | |-- Average n=1473 -> Active (0.86)
#> | `-- Disengaged n=312 -> Active (0.85)
#> |-- Average n=5063 -> Average (0.55)
#> | |-- Active n=1031 -> Average (0.54)
#> | |-- Average n=2670 -> Average (0.54)
#> | `-- Disengaged n=1043 -> Average (0.57)
#> `-- Disengaged n=6598 -> Disengaged (0.79)
#> |-- Active n=581 -> Disengaged (0.82)
#> |-- Average n=719 -> Disengaged (0.73)
#> `-- Disengaged n=4960 -> Disengaged (0.79)They agree
Because all three objects wrap the same sequences, the fitted trees are identical – same alphabet, same nodes, same observation count.
identical(tree_sts$nodes, tree_tna$nodes)
#> [1] TRUE
identical(tree_tna$nodes, tree_nest$nodes)
#> [1] TRUE
data.frame(
route = c("stslist", "tna", "Nestimate"),
n_nodes = c(n_nodes(tree_sts), n_nodes(tree_tna), n_nodes(tree_nest)),
nobs = c(model_fit(tree_sts)$nobs, model_fit(tree_tna)$nobs,
model_fit(tree_nest)$nobs))
#> route n_nodes nobs
#> 1 stslist 13 24555
#> 2 tna 13 24555
#> 3 Nestimate 13 24555The tree shares one symbol space with the source model, so the whole
pathway API – common_pathways(),
divergent_pathways(), bootstrap_pathways(),
the plots – composes onto an already-estimated network with no
conversion step.
The boundary: sequences, never aggregated transitions
The hand-off works only when the object actually carries its sequences. A pure graph projection – nodes, edges and weights with the raw sequences nulled out (an aggregated transition network) – is rejected, on purpose: the original sequences cannot be recovered from edge weights, so fabricating them would be silently wrong. The same invariant rejects a bare numeric transition matrix.
graph_only <- build_tna(engagement)
graph_only$data <- NULL # strip the stored sequences
context_tree(graph_only) # -> informative error, not a fabricated fit
#> Error:
#> ! This netobject/cograph_network object carries no sequence data anywhere (no usable $data / $sequences / $seqdata / embedded netobject). It looks like a pure graph - an aggregated transition network - and transitiontrees fits on raw sequences, not on aggregated transitions: the original sequences cannot be recovered from edge weights (the same reason numeric transition matrices are rejected). Pass a sequence-bearing object instead, e.g. a wide-format transition/network object that still carries its raw sequences, or the original wide sequence data.frame.transitiontrees fits on raw sequences. If you have only
an aggregated network, go back to the sequence data it was built
from.
Group objects
The grouped constructors compose too. context_tree()
recognises a group_tna / netobject_group (a
named list of per-group models) and fits one tree per group, returning a
transitiontrees_group that prune_tree(),
compare_trees(), and compare_groups() consume
directly – see the Advanced analysis vignette for the group
workflow.