Procrustes alignment of latent positions across time
Source:R/procrustes_align.R
procrustes_align.RdAligns dynamic latent positions across time periods to remove arbitrary rotational indeterminacy. This is essential for interpreting temporal trajectories of latent positions, since the latent space is only identified up to rotation at each time point.
Uses Procrustes rotation: at each time step, finds the orthogonal rotation matrix that best aligns the current sender coordinates to the preceding period, then applies it sequentially through the series.
Usage
procrustes_align(
object = NULL,
U = NULL,
V = NULL,
G = NULL,
return_fit = FALSE,
per_draw = FALSE,
...
)Arguments
- object
A fitted
ameorlameobject, or NULL if raw arrays are provided viaU/V/G.- U
Optional 3D array
[n, R, T]of sender latent positions. Ifobjectis provided andUis NULL, extracted fromobject$U.- V
Optional 3D array
[n, R, T]of receiver latent positions. For unipartite asymmetric models, aligned independently. For bipartite models, aligned jointly with U via the G interaction matrix.- G
Optional
[R_row, R_col]interaction matrix for bipartite models, transformed to preserve the invariant U G V'.- return_fit
Logical. If TRUE and
objectis provided, returns a modified copy of the fit object with aligned latent positions. Default FALSE.- per_draw
Logical. When TRUE, run Procrustes alignment per posterior draw rather than on the posterior-mean trajectory. This uses
object$U_full/object$V_full(the per-draw posterior cubes – populated whenposterior_optsincludes storing U/V) when present; otherwise falls back to mean-trajectory alignment and emits an informational note. Per-draw alignment is the methodologically correct treatment – rotation indeterminacy is a per-draw property, not a per-mean one – but is more memory-hungry. Default FALSE.- ...
Additional arguments (currently unused).
Value
If return_fit = FALSE (default): a list with components
U (aligned sender positions), V (aligned receiver positions,
if applicable), and G (updated interaction matrix, for bipartite).
If return_fit = TRUE: a copy of object with aligned latent
positions replacing the originals.
Details
For unipartite networks, U and V are aligned independently using separate Procrustes rotations. For symmetric networks, only U is present and aligned.
For bipartite networks, U and V are aligned jointly: separate rotation
matrices are computed for U and V, and the G interaction matrix is updated
as G_aligned = t(R_U) %*% G %*% R_V to preserve the product
U %*% G %*% t(V).
If U is a 2D matrix (static model with a single time point), it is returned unchanged with an informational message.
See also
latent_positions for extracting aligned positions as
a tidy data frame, uv_plot for visualizing latent positions
Examples
# \donttest{
data(YX_bin_list)
# YX_bin_list$Y stores latent-scale values; threshold to 0/1 first
Y_bin <- lapply(YX_bin_list$Y, function(y) 1 * (y > 0))
fit <- lame(Y_bin, Xdyad = YX_bin_list$X, R = 2,
family = "binary", dynamic_uv = TRUE,
burn = 5, nscan = 5, odens = 1,
verbose = FALSE)
aligned <- procrustes_align(fit)
str(aligned$U) # aligned 3D array [n, R, T]
#> num [1:50, 1:2, 1:4] -0.15053 0.23513 -0.07296 0.00135 -0.09851 ...
#> - attr(*, "dimnames")=List of 3
#> ..$ : chr [1:50] "303" "304" "317" "321" ...
#> ..$ : NULL
#> ..$ : NULL
# }