--- title: "Interoperability with tna and Nestimate" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Interoperability with tna and Nestimate} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>", message = FALSE, warning = FALSE, dpi = 150, fig.width = 7, fig.height = 5.6, out.width = "100%", fig.align = "center") set.seed(2026) library(lagseq) options(digits = 3) has <- function(p) requireNamespace(p, quietly = TRUE) ``` `lagseq` belongs to the Dynalytics ecosystem alongside `tna` (transition network analysis), Nestimate (network estimation), `cograph` (rendering), and `TraMineR`. The ecosystem separates estimation from rendering and analysis, and the packages share a common data grammar and object schemas. In practice this means two things: `lsa()` can read sequences straight out of those packages' objects, and a fitted `lsa` model converts back into them in one call. # Into lagseq: fit from another package's object `lsa()` recognises sequence-bearing objects from `tna` (`tna`, `tna_data`, `tna_seq_data`), Nestimate, and `TraMineR` (`stslist`). It pulls the sequences out and fits, so no manual extraction is needed. ## A tna object `tna` and `lsa()` share the same long-format grammar (`actor` / `action` / `time` / `order` / `session`). Prepare the data with `tna`, build a model, and hand either object to `lsa()`. ```{r tna-in, eval = has("tna")} log <- tna::group_regulation_long prepared <- tna::prepare_data(log, actor = "Actor", action = "Action", time = "Time") tna_model <- tna::tna(prepared) lsa(prepared) # fit from the prepared sequence object lsa(tna_model) # or from a built tna model -- same sequences, same fit ``` ## A TraMineR state sequence object A `TraMineR` `stslist` (the standard state-sequence object) is read directly. ```{r tramineR-in, eval = has("TraMineR")} sts <- TraMineR::seqdef(engagement) lsa(sts) ``` # Out of lagseq: hand a fit to another toolkit A fitted model is a directed weighted network, so it converts to the native object of either network package. Estimation stays in `lagseq`; the downstream analysis runs in the toolkit built for it. ```{r fit} fit <- lsa(engagement) ``` ## To a tna network `lsa_to_tna()` returns a `tna` object. Choose the edge weight with `weights` (`"prob"` for a transition-probability network, `"count"` for a frequency one). From there, tna's analysis verbs apply. ```{r to-tna, eval = has("tna")} tn <- lsa_to_tna(fit, weights = "prob") tna::centralities(tn) |> head() ``` # Shared rendering: the transition network is a tna model Because estimation and rendering are separate layers, the transition network view of an `lsa` fit *is* a `tna` model: `lsa_to_tna()` builds it on the fly and tna's own plot method renders it (coloured nodes, initial-probability arcs, weighted directed edges). ```{r render, eval = has("tna")} plot(fit, type = "network", weights = "prob") ``` The residual network, by contrast, is rendered by `cograph` and keeps the lag-sequential meaning (each edge a tested departure from independence). One fit, two rendering layers, one shared object model. # One grammar across the ecosystem The practical payoff is a single mental model. The arguments that sequence a raw log are the same in `lsa()` and in `tna::prepare_data()`: ```r tna::prepare_data(log, actor =, action =, time =, order =, session =) lsa(log, actor =, action =, time =, order =, session =) ``` So an analysis can move between transition-network tooling and lag-sequential testing without reshaping the data, and an object built in one package is valid input to the other.