--- title: "3. Ecosystem compatibility: TraMineR, tna, and Nestimate" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{3. Ecosystem compatibility: TraMineR, tna, and Nestimate} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") library(transitiontrees) has_tna <- requireNamespace("tna", quietly = TRUE) has_nest <- requireNamespace("Nestimate", quietly = TRUE) ``` `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. ```{r data} data(engagement) class(engagement) dim(engagement) ``` ## Route A -- wide sequence data, directly Hand the wide frame straight to `context_tree()`. ```{r wide} tree_wide <- context_tree(engagement, max_depth = 2L, min_count = 5L) tree_wide$alphabet n_nodes(tree_wide) ``` 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. ```{r tna, eval = has_tna} library(tna) model_tna <- tna(engagement) class(model_tna) tree_tna <- context_tree(model_tna, max_depth = 2L, min_count = 5L) tree_tna ``` ## 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: ```{r nestimate, eval = has_nest} library(Nestimate) model_nest <- build_tna(engagement) class(model_nest) tree_nest <- context_tree(model_nest, max_depth = 2L, min_count = 5L) tree_nest ``` ## They agree Because all three objects wrap the *same* sequences, the fitted trees are identical -- same alphabet, same nodes, same observation count. ```{r agree, eval = has_tna && has_nest} identical(tree_wide$nodes, tree_tna$nodes) identical(tree_tna$nodes, tree_nest$nodes) 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)) ``` 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. ```{r pure-graph, eval = has_nest, error = TRUE} graph_only <- build_tna(engagement) graph_only$data <- NULL # strip the stored sequences context_tree(graph_only) # -> informative error, not a fabricated fit ``` `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.