# 3. Ecosystem compatibility: TraMineR, tna, and Nestimate

`transitiontrees` is built to slot into the wider `mohsaqr` sequence-analysis ecosystem (`TraMineR`, `tna`, `Nestimate`, `cograph`). You do not have to re-export or re-format your data: `context_tree()` takes wide sequence data directly, and it also accepts a fitted transition/network object (`tna`, `Nestimate`), auto-detected by its S3 class – reading the sequences the object already carries and fitting the suffix tree on exactly those, over the same state set.

It also speaks `TraMineR`’s missing-data convention with no dependency on the package: in wide input, the void code `%` and the missing code `*` (alongside `NA` and `""`) are treated as gaps, never as states. So a matrix exported from a `TraMineR` state-sequence via `as.matrix()` drops straight in.

This vignette shows the hand-offs on one shared dataset and confirms they agree. The `tna` and `Nestimate` sections only run if those packages are installed; the wide-data section needs no extra package.

## The shared dataset

The bundled `engagement` object is a wide `data.frame`: 1000 weekly engagement sequences over three states, with `NA` marking dropout.

    data(engagement)
    class(engagement)
    #> [1] "data.frame"
    dim(engagement)
    #> [1] 1000   25

## Route A – wide sequence data, directly

Hand the wide frame straight to `context_tree()`.

    tree_wide <- context_tree(engagement, max_depth = 2L, min_count = 5L)
    tree_wide$alphabet
    #> [1] "Active"     "Average"    "Disengaged"
    n_nodes(tree_wide)
    #> [1] 13

A `TraMineR` user reaches this same tree with `context_tree(as.matrix(seq))` – the void/missing codes are dropped automatically, so there is no need to declare the alphabet or strip the void by hand.

## 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.2
    #> ------------------------------------------------------
    #>   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 objects are masked _by_ '.GlobalEnv':
    #>
    #>     engagement, group_regulation_long
    #> The following objects are masked from 'package:transitiontrees':
    #>
    #>     engagement, 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_wide$nodes, tree_tna$nodes)
    #> [1] TRUE
    identical(tree_tna$nodes, tree_nest$nodes)
    #> [1] TRUE

    data.frame(
      route   = c("wide", "tna", "Nestimate"),
      n_nodes = c(n_nodes(tree_wide), n_nodes(tree_tna), n_nodes(tree_nest)),
      nobs    = c(model_fit(tree_wide)$nobs, model_fit(tree_tna)$nobs,
                  model_fit(tree_nest)$nobs))
    #>       route n_nodes  nobs
    #> 1      wide      13 24555
    #> 2       tna      13 24555
    #> 3 Nestimate      13 24555

The 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.