---
title: "Regulation networks from Dynalytics event data"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Regulation networks from Dynalytics event data}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
has_tna <- requireNamespace("tna", quietly = TRUE)
has_cograph <- requireNamespace("cograph", quietly = TRUE)
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7,
                      fig.height = 6, warning = FALSE, message = FALSE,
                      eval = has_tna)
library(psychnets)
if (has_tna) {
  data("group_regulation_long", package = "tna")
  event_log <- data.frame(group_regulation_long)
}
```

## Using the Dynalytics event-data grammar

*psychnets* forms the psychometric network analysis component of the Dynalytics ecosystem and adopts the shared event-data grammar used throughout the framework. Event records consist of an actor identifying who performed an event, an action identifying what occurred, and, where applicable, a time or session variable indicating when the event took place. Data organized according to this common structure can therefore be supplied directly to `psychnet()` without intermediate restructuring.

When the `actor` argument is specified, `psychnet()` automatically interprets the input as an event log. By convention, the action variable is assumed to be named `"Action"` unless otherwise specified, allowing event data generated by other Dynalytics packages to be analysed directly. During preprocessing, the event log is transformed into an actor-by-action matrix by aggregating the frequency with which each actor performs each recorded action. This matrix forms the input to the selected psychometric network estimator, allowing event data to be converted into a network representation through a single estimation call with all required preprocessing performed internally.


## What a network from event data represents

A network estimated from event data represents statistical relationships among
action frequencies rather than relationships among individual events. Network
estimation requires a data matrix in which each observation has one value for
every node. In `psychnets`, this representation is obtained by converting the
event log into an actor-by-action frequency matrix, where actions constitute the
network nodes and the corresponding action counts form the variables. Edges in
the resulting network represent conditional associations between action
frequencies after controlling for the frequencies of all remaining actions in
the model.

For example, consider the actions *planning* and *monitoring*. A positive edge
between these nodes indicates that actors who engage more frequently in planning
also tend to engage more frequently in monitoring than would be expected after
accounting for variation in all other recorded actions. Conversely, the absence
of an edge indicates that the estimation procedure did not identify a unique
conditional association between the frequencies of these actions once the
remaining action frequencies were considered. Importantly, these edges quantify
conditional statistical dependencies among action frequencies and should not be
interpreted as evidence of causal relationships between actions.

The definition of the observational unit is a critical methodological
consideration because it determines the interpretation of the estimated network.
When each actor's complete event history is treated as a single observation, the
resulting network characterizes between-person differences in action frequencies.
Alternatively, dividing each actor's event history into multiple sessions
produces repeated observations within individuals. In this case, person-centered
session frequencies can be used to estimate within-person networks that capture
the extent to which actions co-vary relative to each individual's typical pattern
of activity. These alternative aggregation strategies address distinct research
questions and should therefore be selected according to the substantive aims of
the analysis.

## The event log

The `group_regulation_long` data from the optional `tna` package contain 27,533
events from 2,000 students. Each row records `Actor`, achievement level
(`Achiever`), group, course, time, and `Action`. The nine action categories are
`adapt`, `cohesion`, `consensus`, `coregulate`, `discuss`, `emotion`, `monitor`,
`plan`, and `synthesis`.

```{r data-preview}
head(event_log)
```

The event log is already in the Dynalytics long format. Repeated rows for the
same actor represent different actions in that actor's activity history.

## Estimating one actor-level network with `psychnet()`

`psychnet()` recognizes the event log when the `actor` argument is supplied. It
uses the shared actor-action grammar, performs the required conversion
internally, and fits a Gaussian graphical model. The default method is the EBIC
graphical lasso.

```{r fit-actor-network}
actor_net <- psychnet(data = event_log, actor = "Actor")
actor_net
```

The fitted network contains 9 action nodes and 28 edges. The selected penalty is
$\lambda = 0.008001$ at $\gamma = 0.5$.

## Inspecting actor-level edges with `summary()`

`summary()` prints the fitted model and returns an edge table with `from`, `to`,
and `weight`. Each weight is a partial correlation between two action counts
after the other seven action counts are considered.

```{r summarize-actor-network}
summary(actor_net)
```

The largest positive edge joins `consensus` and `plan` ($r = 0.434$). Students
who plan more also tend to produce more consensus events above and beyond their
other action frequencies. The `cohesion`-`emotion` edge is also positive
($r = 0.348$).

The largest negative edge joins `discuss` and `plan` ($r = -0.154$). Higher
discussion frequency is associated with lower planning frequency after the
other action counts are taken into account. Several retained edges are very
small, including `coregulate`-`plan` (-0.007) and
`coregulate`-`synthesis` (0.008), so their substantive interpretation should be
cautious.

## Checking numerical optimality with `certificate()`

`certificate()` returns `method`, `certificate`, `kind`, and `certified`. For
the graphical lasso, the certificate is the largest KKT stationarity residual.

```{r certify-actor-network}
certificate(actor_net)
```

The residual is $3.19 \times 10^{-11}$ and `certified` is `TRUE`. It indicates
that the fitted precision matrix satisfies the optimization conditions to
numerical precision. This diagnostic checks computation, not sampling stability
or causal validity.

## Visualizing the actor-level network

`cograph::splot()` draws the fitted object when `cograph` is available.
Psychological styling distinguishes positive and negative edges and adds the
stored predictability rings.

```{r plot-actor-network, eval = has_tna && has_cograph}
cograph::splot(actor_net, psych_styling = TRUE)
```

The layout provides orientation but has no statistical distance scale. Exact
interpretation should use the edge table.

## Estimating one network per achievement level

`psychnet()` estimates one network per level when `group` names a grouping
column. It returns a `psychnet_group` object. Here, separate networks are fitted
for high- and low-achieving students.

```{r fit-group-networks}
achievement_nets <- psychnet(data = event_log, actor = "Actor", group = "Achiever")
achievement_nets
```

The high-achieving network contains 25 edges across 1,000 students. The
low-achieving network contains 27 edges across 1,000 students. Edge counts alone
do not show that one group has a stronger network, so the weight summaries must
also be examined.

`summary()` returns one row per group with `group`, `nodes`, `edges`, and
`mean_abs_weight`.

```{r summarize-group-networks}
summary(achievement_nets)
```

The mean absolute edge weight is 0.114 in both groups. The typical retained-edge
magnitude is therefore similar even though the selected structures differ by two
edges.

`net_centralities()` applies to every network in the group object. It returns
strength and expected influence for each action within each achievement level.

```{r group-centrality}
net_centralities(achievement_nets)
```

`consensus` has the largest strength in both groups: 1.448 among high achievers
and 1.200 among low achievers. Among low achievers, `discuss` has the
second-largest strength at 1.081. These are descriptive differences between the
estimated networks. Formal claims about group differences require a network
comparison procedure.

```{r plot-group-networks, eval = has_tna && has_cograph, fig.width = 11, fig.height = 5.5}
cograph::splot(achievement_nets, psych_styling = TRUE)
```

## Estimating a person-centered session network

`psychnet()` can infer sessions from gaps in an event-time column. With
`time = "Time"` and `time_threshold = 300`, a pause longer than five minutes
starts a new session. The time values define occasions only. They are not used
to estimate lagged or temporal effects.

When `standardize = TRUE`, the default, `psychnet()` calculates action
frequencies for every actor-session and subtracts each actor's mean frequencies.
It then fits one pooled network to these person-centered session values. The
result describes which deviations from an actor's usual action profile tend to
occur together across sessions.

```{r fit-within-network}
within_net <- psychnet(data = event_log, actor = "Actor", time = "Time", time_threshold = 300)
within_net
```

This person-centered network is the within-actor component of the covariance
decomposition. It is not a separate network for each actor, a temporal network,
or a random-effects model. The fitted network contains 14 edges, compared with
28 in the actor-level network. Its selected penalty is $\lambda = 0.0288$.

```{r summarize-within-network}
summary(within_net)
```

The largest person-centered edge joins `cohesion` and `emotion` ($r = 0.148$).
In sessions where a student produces more cohesion events than that student's
usual level, emotion events also tend to be above the student's usual level,
after the other action deviations are considered. The `discuss`-`plan` edge is
negative ($r = -0.074$). These edges answer a different question from the
actor-level edges and should not be compared as if they shared the same unit of
variation.

```{r certify-within-network}
certificate(within_net)
```

The KKT residual is $1.30 \times 10^{-12}$ and is certified, indicating numerical
optimality to numerical precision.

```{r plot-within-network, eval = has_tna && has_cograph}
cograph::splot(within_net, psych_styling = TRUE)
```

## Keeping the within- and between-actor networks

`psychnet()` returns a `psychnet_multilevel` object when session-based event data
are fitted with `standardize = FALSE`. The object contains the pooled
person-centered network and the between-actor network. The between network
relates actors' average action profiles. The word multilevel here refers to the
within-between covariance decomposition, not to a fitted random-effects model.

```{r fit-multilevel-networks}
multilevel_nets <- psychnet(data = event_log, actor = "Actor", time = "Time", time_threshold = 300, standardize = FALSE)
multilevel_nets
```

The analysis contains 2,000 actors and 12,305 inferred occasions. The
within-actor network has 14 edges, and the between-actor network has 24. The
larger between-actor edge count means that more conditional associations are
retained among students' average action profiles than among their session-level
deviations.

`cograph::splot()` recognizes the multilevel object and displays the two networks
as a grid.

```{r plot-multilevel-networks, eval = has_tna && has_cograph, fig.width = 11, fig.height = 5.5}
cograph::splot(multilevel_nets, psych_styling = TRUE)
```

## Reporting the analysis

A report should identify the Dynalytics actor, action, and time or session
columns; define the occasion; state the session-gap threshold; distinguish
actor-level, person-centered, and between-actor questions; and report the network
estimator and its settings. The report should state explicitly that inferred
sessions do not form a temporal network or a random-effects model.

The present analysis passed 27,533 events from 2,000 students directly to
`psychnet()` using the actor-action grammar. The actor-level graphical-lasso
network contained 28 edges. Networks by achievement level contained 25 and 27
edges with similar mean absolute weights. A five-minute session rule produced
12,305 occasions, a 14-edge person-centered network, and a 24-edge between-actor
network.

## How event counts and levels are constructed

For actor $a$, occasion $s$, and action $j$, the frequency variable is the
number of matching events,

$$
V_{asj}=\sum_e I(A_e=a,\ S_e=s,\ J_e=j).
$$

Without sessions, each actor contributes one frequency vector. With sessions,
each actor contributes several vectors. Session inference sorts events within an
actor by time and starts a new occasion when the gap exceeds the selected
threshold.

For nested occasions, the actor mean for action $j$ is

$$
\bar V_{a\cdot j}=\frac{1}{n_a}\sum_s V_{asj},
$$

and the within-actor deviation is

$$
V^{W}_{asj}=V_{asj}-\bar V_{a\cdot j}.
$$

The between-actor network is estimated from the actor means. The within-actor
network is estimated from the deviations. Person centering with
`standardize = TRUE` returns only the within component. Each resulting network
uses the same Gaussian graphical estimator as a cross-sectional frequency
network.
