Title: | Fit Dynamic Structural Equation Models |
---|---|
Description: | Applies dynamic structural equation models to time-series data with generic and simplified specification for simultaneous and lagged effects. Methods are described in Thorson et al. (2024) "Dynamic structural equation models synthesize ecosystem dynamics constrained by ecological mechanisms." |
Authors: | James Thorson [aut, cre] |
Maintainer: | James Thorson <[email protected]> |
License: | GPL-3 |
Version: | 1.5.0 |
Built: | 2025-02-13 16:56:50 UTC |
Source: | https://github.com/james-thorson-noaa/dsem |
Convert dsem to phylopath output
as_fitted_DAG( fit, lag = 0, what = c("Estimate", "Std_Error", "p_value"), direction = 1 )
as_fitted_DAG( fit, lag = 0, what = c("Estimate", "Std_Error", "p_value"), direction = 1 )
fit |
Output from |
lag |
which lag to output |
what |
whether to output estimates |
direction |
whether to include one-sided arrows |
Convert output to format supplied by est_DAG
Convert output from package dsem to sem
as_sem(object, lag = 0)
as_sem(object, lag = 0)
object |
Output from |
lag |
what lag to extract and visualize |
Convert output to format supplied by sem
Data used to demonstrate and test ecosystem synthesis
data(bering_sea)
data(bering_sea)
Calculates the conditional Akaike Information criterion (cAIC).
cAIC(object, what = c("cAIC", "EDF"))
cAIC(object, what = c("cAIC", "EDF"))
object |
Output from |
what |
Whether to return the cAIC or the effective degrees of freedom (EDF) for each group of random effects. |
cAIC is designed to optimize the expected out-of-sample predictive
performance for new data that share the same random effects as the
in-sample (fitted) data, e.g., spatial interpolation. In this sense,
it should be a fast approximation to optimizing the model structure
based on k-fold crossvalidation.
By contrast, AIC
calculates the
marginal Akaike Information Criterion, which is designed to optimize
expected predictive performance for new data that have new random effects,
e.g., extrapolation, or inference about generative parameters.
cAIC also calculates as a byproduct the effective degrees of freedom, i.e., the number of fixed effects that would have an equivalent impact on model flexibility as a given random effect.
Both cAIC and EDF are calculated using Eq. 6 of Zheng Cadigan Thorson 2024.
Note that, for models that include profiled fixed effects, these profiles are turned off.
Either the cAIC, or the effective degrees of freedom (EDF) by group of random effects
**Deriving the general approximation to cAIC used here**
Zheng, N., Cadigan, N., & Thorson, J. T. (2024). A note on numerical evaluation of conditional Akaike information for nonlinear mixed-effects models (arXiv:2411.14185). arXiv. doi:10.48550/arXiv.2411.14185
**The utility of EDF to diagnose hierarchical model behavior**
Thorson, J. T. (2024). Measuring complexity for hierarchical models using effective degrees of freedom. Ecology, 105(7), e4327 doi:10.1002/ecy.4327
classify_variables
is copied from sem:::classifyVariables
classify_variables(model)
classify_variables(model)
model |
SEM model |
Copied from package 'sem' under licence GPL (>= 2) with permission from John Fox
Tagged-list defining exogenous and endogenous variables
Converts equations to arrow-and-lag notation expected by dsem
convert_equations(equations)
convert_equations(equations)
equations |
Specification for time-series structural equation model structure
including lagged or simultaneous effects. See Details section in
|
The function modifies code copied from package 'sem' under licence GPL (>= 2) with permission from John Fox.
For specifyEquations, each input line is either a regression equation or the specification of a variance or covariance. Regression equations are of the form y = par1*x1 + par2*x2 + ... + park*xk where y and the xs are variables in the model (either observed or latent), and the pars are parameters. If a parameter is given as a numeric value (e.g., 1) then it is treated as fixed. Note that no error variable is included in the equation; error variances are specified via either the covs argument, via V(y) = par (see immediately below), or are added automatically to the model when, as by default, endog.variances=TRUE. A regression equation may be split over more than one input by breaking at a +, so that + is either the last non-blank character on a line or the first non-blank character on the subsequent line.
Variances are specified in the form V(var) = par and covariances in the form C(var1, var2) = par, where the vars are variables (observed or unobserved) in the model. The symbols V and C may be in either lower- or upper-case. If par is a numeric value (e.g., 1) then it is treated as fixed. In conformity with the RAM model, a variance or covariance for an endogenous variable in the model is an error variance or covariance.
To set a start value for a free parameter, enclose the numeric start value in parentheses after the parameter name, as parameter(value).
Fits a dynamic structural equation model
dsem( sem, tsdata, family = rep("fixed", ncol(tsdata)), estimate_delta0 = FALSE, prior_negloglike = NULL, control = dsem_control(), covs = colnames(tsdata) )
dsem( sem, tsdata, family = rep("fixed", ncol(tsdata)), estimate_delta0 = FALSE, prior_negloglike = NULL, control = dsem_control(), covs = colnames(tsdata) )
sem |
Specification for time-series structural equation model structure
including lagged or simultaneous effects. See Details section in
|
tsdata |
time-series data, as outputted using |
family |
Character-vector listing the distribution used for each column of |
estimate_delta0 |
Boolean indicating whether to estimate deviations from equilibrium in initial year as fixed effects, or alternatively to assume that dynamics start at some stochastic draw away from the stationary distribution |
prior_negloglike |
A user-provided function that takes as input the vector of fixed effects out$obj$par
returns the negative log-prior probability. For example
|
control |
Output from |
covs |
optional: a character vector of one or more elements, with each element giving a string of variable names, separated by commas. Variances and covariances among all variables in each such string are added to the model. Warning: covs="x1, x2" and covs=c("x1", "x2") are not equivalent: covs="x1, x2" specifies the variance of x1, the variance of x2, and their covariance, while covs=c("x1", "x2") specifies the variance of x1 and the variance of x2 but not their covariance. These same covariances can be added manually via argument 'sem', but using argument 'covs' might save time for models with many variables. |
A DSEM involves (at a minimum):
a matrix where column
for variable c is
a time-series;
a user-supplied specification for the path coefficients, which
define the precision (inverse covariance) for a matrix of state-variables
and see
make_dsem_ram
for more details on the math involved.
The model also estimates the time-series mean for each variable.
The mean and precision matrix therefore define a Gaussian Markov random field for
:
Users can the specify
a distribution for measurement errors (or assume that variables are measured without error) using
argument family
. This defines the link-function and distribution
for each time-series
:
dsem
then estimates all specified coefficients, time-series means , and distribution
measurement errors
via maximizing a log-marginal likelihood, while
also estimating state-variables
.
summary.dsem
then assembles estimates and standard errors in an easy-to-read format.
Standard errors for fixed effects (path coefficients, exogenoux variance parameters, and measurement error parameters)
are estimated from the matrix of second derivatives of the log-marginal likelihod,
and standard errors for random effects (i.e., missing or state-space variables) are estimated
from a generalization of this method (see sdreport
for details).
An object (list) of class 'dsem'. Elements include:
TMB object from MakeADFun
RAM parsed by make_dsem_ram
SEM structure parsed by make_dsem_ram
as intermediate description of model linkages
The list of inputs passed to MakeADFun
The output from nlminb
The output from sdreport
Objects useful for package function, i.e., all arguments passed during the call
Total time to run model
**Introducing the package, its features, and comparison with other software (to cite when using dsem):**
Thorson, J. T., Andrews, A., Essington, T., Large, S. (2024). Dynamic structural equation models synthesize ecosystem dynamics constrained by ecological mechanisms. Methods in Ecology and Evolution. doi:10.1111/2041-210X.14289
# Define model sem = " # Link, lag, param_name cprofits -> consumption, 0, a1 cprofits -> consumption, 1, a2 pwage -> consumption, 0, a3 gwage -> consumption, 0, a3 cprofits -> invest, 0, b1 cprofits -> invest, 1, b2 capital -> invest, 0, b3 gnp -> pwage, 0, c2 gnp -> pwage, 1, c3 time -> pwage, 0, c1 " # Load data data(KleinI, package="AER") TS = ts(data.frame(KleinI, "time"=time(KleinI) - 1931)) tsdata = TS[,c("time","gnp","pwage","cprofits",'consumption', "gwage","invest","capital")] # Fit model fit = dsem( sem=sem, tsdata = tsdata, estimate_delta0 = TRUE, control = dsem_control(quiet=TRUE) ) summary( fit ) plot( fit ) plot( fit, edge_label="value" )
# Define model sem = " # Link, lag, param_name cprofits -> consumption, 0, a1 cprofits -> consumption, 1, a2 pwage -> consumption, 0, a3 gwage -> consumption, 0, a3 cprofits -> invest, 0, b1 cprofits -> invest, 1, b2 capital -> invest, 0, b3 gnp -> pwage, 0, c2 gnp -> pwage, 1, c3 time -> pwage, 0, c1 " # Load data data(KleinI, package="AER") TS = ts(data.frame(KleinI, "time"=time(KleinI) - 1931)) tsdata = TS[,c("time","gnp","pwage","cprofits",'consumption', "gwage","invest","capital")] # Fit model fit = dsem( sem=sem, tsdata = tsdata, estimate_delta0 = TRUE, control = dsem_control(quiet=TRUE) ) summary( fit ) plot( fit ) plot( fit, edge_label="value" )
Define a list of control parameters. Note that
the format of this input is likely to change more rapidly than that of
dsem
dsem_control( nlminb_loops = 1, newton_loops = 1, trace = 0, eval.max = 1000, iter.max = 1000, getsd = TRUE, quiet = FALSE, run_model = TRUE, gmrf_parameterization = c("separable", "projection"), constant_variance = c("conditional", "marginal", "diagonal"), use_REML = TRUE, profile = NULL, parameters = NULL, map = NULL, getJointPrecision = FALSE, extra_convergence_checks = TRUE, lower = -Inf, upper = Inf )
dsem_control( nlminb_loops = 1, newton_loops = 1, trace = 0, eval.max = 1000, iter.max = 1000, getsd = TRUE, quiet = FALSE, run_model = TRUE, gmrf_parameterization = c("separable", "projection"), constant_variance = c("conditional", "marginal", "diagonal"), use_REML = TRUE, profile = NULL, parameters = NULL, map = NULL, getJointPrecision = FALSE, extra_convergence_checks = TRUE, lower = -Inf, upper = Inf )
nlminb_loops |
Integer number of times to call |
newton_loops |
Integer number of Newton steps to do after running
|
trace |
Parameter values are printed every 'trace' iteration
for the outer optimizer. Passed to
'control' in |
eval.max |
Maximum number of evaluations of the objective function
allowed. Passed to 'control' in |
iter.max |
Maximum number of iterations allowed. Passed to 'control' in
|
getsd |
Boolean indicating whether to call |
quiet |
Boolean indicating whether to run model printing messages to terminal or not; |
run_model |
Boolean indicating whether to estimate parameters (the default), or instead to return the model inputs and compiled TMB object without running; |
gmrf_parameterization |
Parameterization to use for the Gaussian Markov random field, where the default 'separable' constructs a precision matrix that must be full rank, and the alternative 'projection' constructs a full-rank and IID precision for variables over time, and then projects this using the inverse-cholesky of the precision, where this projection can be rank-deficient. |
constant_variance |
Whether to specify a constant conditional variance
|
use_REML |
Boolean indicating whether to treat non-variance fixed effects as random, either to motigate bias in estimated variance parameters or improve efficiency for parameter estimation given correlated fixed and random effects |
profile |
Parameters to profile out of the likelihood (this subset will be appended to |
parameters |
list of fixed and random effects, e.g., as constructed by |
map |
list of fixed and mirrored parameters, constructed by |
getJointPrecision |
whether to get the joint precision matrix. Passed
to |
extra_convergence_checks |
Boolean indicating whether to run extra checks on model convergence. |
lower |
vectors of lower bounds, replicated to be as long as start and passed to |
upper |
vectors of upper bounds, replicated to be as long as start and passed to |
An S3 object of class "dsem_control" that specifies detailed model settings, allowing user specification while also specifying default values
Fits a dynamic structural equation model
dsemRTMB( sem, tsdata, family = rep("fixed", ncol(tsdata)), estimate_delta0 = FALSE, log_prior = function(p) 0, control = dsem_control(), covs = colnames(tsdata) )
dsemRTMB( sem, tsdata, family = rep("fixed", ncol(tsdata)), estimate_delta0 = FALSE, log_prior = function(p) 0, control = dsem_control(), covs = colnames(tsdata) )
sem |
Specification for time-series structural equation model structure
including lagged or simultaneous effects. See Details section in
|
tsdata |
time-series data, as outputted using |
family |
Character-vector listing the distribution used for each column of |
estimate_delta0 |
Boolean indicating whether to estimate deviations from equilibrium in initial year as fixed effects, or alternatively to assume that dynamics start at some stochastic draw away from the stationary distribution |
log_prior |
A user-provided function that takes as input the list of
parameters |
control |
Output from |
covs |
optional: a character vector of one or more elements, with each element giving a string of variable names, separated by commas. Variances and covariances among all variables in each such string are added to the model. Warning: covs="x1, x2" and covs=c("x1", "x2") are not equivalent: covs="x1, x2" specifies the variance of x1, the variance of x2, and their covariance, while covs=c("x1", "x2") specifies the variance of x1 and the variance of x2 but not their covariance. These same covariances can be added manually via argument 'sem', but using argument 'covs' might save time for models with many variables. |
dsemRTMB
is interchangeable with dsem
, but uses RTMB
instead of TMB for estimation. Both are provided for comparison and
real-world comparison.
An object (list) of class 'dsem', fitted using RTMB
# Define model sem = " # Link, lag, param_name cprofits -> consumption, 0, a1 cprofits -> consumption, 1, a2 pwage -> consumption, 0, a3 gwage -> consumption, 0, a3 cprofits -> invest, 0, b1 cprofits -> invest, 1, b2 capital -> invest, 0, b3 gnp -> pwage, 0, c2 gnp -> pwage, 1, c3 time -> pwage, 0, c1 " # Load data data(KleinI, package="AER") TS = ts(data.frame(KleinI, "time"=time(KleinI) - 1931)) tsdata = TS[,c("time","gnp","pwage","cprofits",'consumption', "gwage","invest","capital")] # Fit model fit = dsemRTMB( sem=sem, tsdata = tsdata, estimate_delta0 = TRUE, control = dsem_control(quiet=TRUE) )
# Define model sem = " # Link, lag, param_name cprofits -> consumption, 0, a1 cprofits -> consumption, 1, a2 pwage -> consumption, 0, a3 gwage -> consumption, 0, a3 cprofits -> invest, 0, b1 cprofits -> invest, 1, b2 capital -> invest, 0, b3 gnp -> pwage, 0, c2 gnp -> pwage, 1, c3 time -> pwage, 0, c1 " # Load data data(KleinI, package="AER") TS = ts(data.frame(KleinI, "time"=time(KleinI) - 1931)) tsdata = TS[,c("time","gnp","pwage","cprofits",'consumption', "gwage","invest","capital")] # Fit model fit = dsemRTMB( sem=sem, tsdata = tsdata, estimate_delta0 = TRUE, control = dsem_control(quiet=TRUE) )
Data used to demonstrate and test cross-lagged (vector autoregressive) models
data(isle_royale)
data(isle_royale)
Data extracted from file "Data_wolves_moose_Isle_Royale_June2019.csv" available at https://www.isleroyalewolf.org and obtained 2023-06-23. Reproduced with permission from John Vucetich, and generated by the Wolves and Moose of Isle Royale project.
Vucetich, JA and Peterson RO. 2012. The population biology of Isle Royale wolves and moose: an overview. https://www.isleroyalewolf.org
list_parameters
lists all fixed and random effects
list_parameters(Obj, verbose = TRUE)
list_parameters(Obj, verbose = TRUE)
Obj |
Compiled TMB object |
verbose |
Boolean, whether to print messages to terminal |
Tagged-list of fixed and random effects, returned invisibly and printed to screen
Extract the (marginal) log-likelihood of a dsem model
## S3 method for class 'dsem' logLik(object, ...)
## S3 method for class 'dsem' logLik(object, ...)
object |
Output from |
... |
Not used |
object of class logLik
with attributes
val |
log-likelihood |
df |
number of parameters |
Returns an object of class logLik. This has attributes
"df" (degrees of freedom) giving the number of (estimated) fixed effects
in the model, abd "val" (value) giving the marginal log-likelihood.
This class then allows AIC
to work as expected.
Calculates quantile residuals using the predictive distribution from a jacknife (i.e., leave-one-out predictive distribution)
loo_residuals( object, nsim = 100, what = c("quantiles", "samples", "loo"), track_progress = TRUE, ... )
loo_residuals( object, nsim = 100, what = c("quantiles", "samples", "loo"), track_progress = TRUE, ... )
object |
Output from |
nsim |
Number of simulations to use if |
what |
whether to return quantile residuals, or samples from the leave-one-out predictive distribution of data, or a table of leave-one-out predictions and standard errors for the latent state |
track_progress |
whether to track runtimes on terminal |
... |
Not used |
Conditional quantile residuals cannot be calculated when using family = "fixed"
, because
state-variables are fixed at available measurements and hence the conditional distribution is a Dirac
delta function. One alternative is to use leave-one-out residuals, where we calculate the predictive distribution
for each state value when dropping the associated observation, and then either use that as the
predictive distribution, or sample from that predictive distribution and then calculate
a standard quantile distribution for a given non-fixed family. This appraoch is followed here.
It is currently only implemented when all variables follow family = "fixed"
, but
could be generalized to a mix of families upon request.
A matrix of residuals, with same order and dimensions as argument tsdata
that was passed to dsem
.
Make the text string for a dynamic factor analysis expressed using arrow-and-lag notation for DSEM.
make_dfa(variables, n_factors, factor_names = paste0("F", seq_len(n_factors)))
make_dfa(variables, n_factors, factor_names = paste0("F", seq_len(n_factors)))
variables |
Character string of variables (i.e., column names of |
n_factors |
Number of factors. |
factor_names |
Optional character-vector of factor names,
which must match NA columns in |
A text string to be passed to dsem
make_dsem_ram
converts SEM arrow notation to ram
describing SEM parameters
make_dsem_ram( sem, times, variables, covs = variables, quiet = FALSE, remove_na = TRUE )
make_dsem_ram( sem, times, variables, covs = variables, quiet = FALSE, remove_na = TRUE )
sem |
Specification for time-series structural equation model structure
including lagged or simultaneous effects. See Details section in
|
times |
A character vector listing the set of times in order |
variables |
A character vector listing the set of variables |
covs |
A character vector listing variables for which to estimate a standard deviation |
quiet |
Boolean indicating whether to print messages to terminal |
remove_na |
Boolean indicating whether to remove NA values from RAM (default) or not.
|
RAM specification using arrow-and-lag notation
Each line of the RAM specification for make_dsem_ram
consists of four (unquoted) entries,
separated by commas:
This is a simple formula, of the form
A -> B
or, equivalently, B <- A
for a regression
coefficient (i.e., a single-headed or directional arrow);
A <-> A
for a variance or A <-> B
for a covariance
(i.e., a double-headed or bidirectional arrow). Here, A
and
B
are variable names in the model. If a name does not correspond
to an observed variable, then it is assumed to be a latent variable.
Spaces can appear freely in an arrow specification, and
there can be any number of hyphens in the arrows, including zero: Thus,
e.g., A->B
, A --> B
, and A>B
are all legitimate
and equivalent.
An integer specifying whether the linkage
is simultaneous (lag=0
) or lagged (e.g., X -> Y, 1, XtoY
indicates that X in time T affects Y in time T+1), where
only one-headed arrows can be lagged. Using positive values to indicate lags
then matches the notational convention used in package dynlm.
The name of the regression coefficient, variance,
or covariance specified by the arrow. Assigning the same name to two or
more arrows results in an equality constraint. Specifying the parameter name
as NA
produces a fixed parameter.
start value for a free parameter or value of a fixed parameter.
If given as NA
(or simply omitted), the model is provide a default
starting value.
Lines may end in a comment following #. The function extends code copied from package 'sem' under licence GPL (>= 2) with permission from John Fox.
Simultaneous autoregressive process for simultaneous and lagged effects
This text then specifies linkages in a multivariate time-series model for variables
with dimensions
for
times and
variables.
make_dsem_ram
then parses this text to build a path matrix with
dimensions
, where element
represents the impact of
on
, where
and
. This path matrix defines a simultaneous equation
where is a matrix of exogenous errors with covariance
,
where
is the Cholesky of exogenous covariance. This
simultaneous autoregressive (SAR) process then results in
having covariance:
Usefully, computing the inverse-covariance (precision) matrix does not require inverting
:
Example: univariate first-order autoregressive model
This simultaneous autoregressive (SAR) process across variables and times
allows the user to specify both simutanous effects (effects among variables within
year ) and lagged effects (effects among variables among years
).
As one example, consider a univariate and first-order autoregressive process where
.
with independent errors. This is specified by passing
sem = "X -> X, 1, rho \n X <-> X, 0, sigma"
to make_dsem_ram
.
This is then parsed to a RAM:
heads | to | from | paarameter | start |
1 | 2 | 1 | 1 | <NA> |
1 | 3 | 2 | 1 | <NA> |
1 | 4 | 3 | 1 | <NA> |
2 | 1 | 1 | 2 | <NA> |
2 | 2 | 2 | 2 | <NA> |
2 | 3 | 3 | 2 | <NA> |
2 | 4 | 4 | 2 | <NA> |
Rows of this RAM where heads=1
are then interpreted to construct the path matrix , where column "from"
in the RAM indicates column number in the matrix, column "to" in the RAM indicates row number in the matrix:
While rows where heads=2
are interpreted to construct the Cholesky of exogenous covariance
and column "parameter" in the RAM associates each nonzero element of those
two matrices with an element of a vector of estimated parameters:
with two estimated parameters . This then results in covariance:
Which converges on the stationary covariance for an AR1 process for times :
except having a lower pointwise variance for the initial times, which arises as a "boundary effect".
Similarly, the arrow-and-lag notation can be used to specify a SAR representing a conventional structural equation model (SEM), cross-lagged (a.k.a. vector autoregressive) models (VAR), dynamic factor analysis (DFA), or many other time-series models.
A reticular action module (RAM) describing dependencies
# Univariate AR1 sem = " X -> X, 1, rho X <-> X, 0, sigma " make_dsem_ram( sem=sem, variables="X", times=1:4 ) # Univariate AR2 sem = " X -> X, 1, rho1 X -> X, 2, rho2 X <-> X, 0, sigma " make_dsem_ram( sem=sem, variables="X", times=1:4 ) # Bivariate VAR sem = " X -> X, 1, XtoX X -> Y, 1, XtoY Y -> X, 1, YtoX Y -> Y, 1, YtoY X <-> X, 0, sdX Y <-> Y, 0, sdY " make_dsem_ram( sem=sem, variables=c("X","Y"), times=1:4 ) # Dynamic factor analysis with one factor and two manifest variables # (specifies a random-walk for the factor, and miniscule residual SD) sem = " factor -> X, 0, loadings1 factor -> Y, 0, loadings2 factor -> factor, 1, NA, 1 X <-> X, 0, NA, 0.01 # Fix at negligible value Y <-> Y, 0, NA, 0.01 # Fix at negligible value " make_dsem_ram( sem=sem, variables=c("X","Y","factor"), times=1:4 ) # ARIMA(1,1,0) sem = " factor -> factor, 1, rho1 # AR1 component X -> X, 1, NA, 1 # Integrated component factor -> X, 0, NA, 1 X <-> X, 0, NA, 0.01 # Fix at negligible value " make_dsem_ram( sem=sem, variables=c("X","factor"), times=1:4 ) # ARIMA(0,0,1) sem = " factor -> X, 0, NA, 1 factor -> X, 1, rho1 # MA1 component X <-> X, 0, NA, 0.01 # Fix at negligible value " make_dsem_ram( sem=sem, variables=c("X","factor"), times=1:4 )
# Univariate AR1 sem = " X -> X, 1, rho X <-> X, 0, sigma " make_dsem_ram( sem=sem, variables="X", times=1:4 ) # Univariate AR2 sem = " X -> X, 1, rho1 X -> X, 2, rho2 X <-> X, 0, sigma " make_dsem_ram( sem=sem, variables="X", times=1:4 ) # Bivariate VAR sem = " X -> X, 1, XtoX X -> Y, 1, XtoY Y -> X, 1, YtoX Y -> Y, 1, YtoY X <-> X, 0, sdX Y <-> Y, 0, sdY " make_dsem_ram( sem=sem, variables=c("X","Y"), times=1:4 ) # Dynamic factor analysis with one factor and two manifest variables # (specifies a random-walk for the factor, and miniscule residual SD) sem = " factor -> X, 0, loadings1 factor -> Y, 0, loadings2 factor -> factor, 1, NA, 1 X <-> X, 0, NA, 0.01 # Fix at negligible value Y <-> Y, 0, NA, 0.01 # Fix at negligible value " make_dsem_ram( sem=sem, variables=c("X","Y","factor"), times=1:4 ) # ARIMA(1,1,0) sem = " factor -> factor, 1, rho1 # AR1 component X -> X, 1, NA, 1 # Integrated component factor -> X, 0, NA, 1 X <-> X, 0, NA, 0.01 # Fix at negligible value " make_dsem_ram( sem=sem, variables=c("X","factor"), times=1:4 ) # ARIMA(0,0,1) sem = " factor -> X, 0, NA, 1 factor -> X, 1, rho1 # MA1 component X <-> X, 0, NA, 0.01 # Fix at negligible value " make_dsem_ram( sem=sem, variables=c("X","factor"), times=1:4 )
parse_path
is copied from sem::parse.path
parse_path(path)
parse_path(path)
path |
text to parse |
Copied from package 'sem' under licence GPL (>= 2) with permission from John Fox
Tagged-list defining variables and direction for a specified path coefficient
Plot from a fitted dsem
model
## S3 method for class 'dsem' plot( x, y, edge_label = c("name", "value", "value_and_stars"), digits = 2, style = c("igraph", "ggraph"), ... )
## S3 method for class 'dsem' plot( x, y, edge_label = c("name", "value", "value_and_stars"), digits = 2, style = c("igraph", "ggraph"), ... )
x |
Output from |
y |
Not used |
edge_label |
Whether to plot parameter names, estimated values, or estimated values along with stars indicating significance at 0.05, 0.01, or 0.001 levels (based on two-sided Wald tests) |
digits |
integer indicating the number of decimal places to be used |
style |
Whether to make a graph using |
... |
arguments passed to |
This function coerces output from a graph and then plots the graph.
Invisibly returns the output from graph_from_data_frame
which was passed to plot.igraph
for plotting.
Predict variables given new (counterfactual) values of data, or for future or past times
## S3 method for class 'dsem' predict(object, newdata = NULL, type = c("link", "response"), ...)
## S3 method for class 'dsem' predict(object, newdata = NULL, type = c("link", "response"), ...)
object |
Output from |
newdata |
optionally, a data frame in which to look for variables with which to predict. If omitted, the fitted data are used to create predictions. If desiring predictions after the fitted data, the user must append rows with NAs for those future times. Similarly, if desiring predictions given counterfactual values for time-series data, then those individual observations can be edited while keeping other observations at their original fitted values. |
type |
the type of prediction required. The default is on the scale of the linear predictors; the alternative "response" is on the scale of the response variable. Thus for a Poisson-distributed variable the default predictions are of log-intensity and type = "response" gives the predicted intensity. |
... |
Not used |
A matrix of predicted values with dimensions and order corresponding to
argument newdata
is provided, or tsdata
if not.
Predictions are provided on either link or response scale, and
are generated by re-optimizing random effects condition on MLE
for fixed effects, given those new data.
Prints output from fitted dsem model
## S3 method for class 'dsem' print(x, ...)
## S3 method for class 'dsem' print(x, ...)
x |
Output from |
... |
Not used |
No return value, called to provide clean terminal output when calling fitted object in terminal.
read_model
converts SEM arrow notation to model
describing SEM parameters
read_model(sem, times, variables, covs = NULL, quiet = FALSE)
read_model(sem, times, variables, covs = NULL, quiet = FALSE)
sem |
Specification for time-series structural equation model structure
including lagged or simultaneous effects. See Details section in
|
times |
A character vector listing the set of times in order |
variables |
A character vector listing the set of variables |
covs |
A character vector listing variables for which to estimate a standard deviation |
quiet |
Boolean indicating whether to print messages to terminal |
See make_dsem_ram
for details
Calculate deviance or response residuals for dsem
## S3 method for class 'dsem' residuals(object, type = c("deviance", "response"), ...)
## S3 method for class 'dsem' residuals(object, type = c("deviance", "response"), ...)
object |
Output from |
type |
which type of residuals to compute (only option is |
... |
Not used |
A matrix of residuals, with same order and dimensions as argument tsdata
that was passed to dsem
.
Data used to demonstrate and test trophic cascades options
data(sea_otter)
data(sea_otter)
Simulate from a fitted dsem
model
## S3 method for class 'dsem' simulate( object, nsim = 1, seed = NULL, variance = c("none", "random", "both"), resimulate_gmrf = FALSE, ... )
## S3 method for class 'dsem' simulate( object, nsim = 1, seed = NULL, variance = c("none", "random", "both"), resimulate_gmrf = FALSE, ... )
object |
Output from |
nsim |
number of simulated data sets |
seed |
random seed |
variance |
whether to ignore uncertainty in fixed and random effects, include estimation uncertainty in random effects, or include estimation uncertainty in both fixed and random effects |
resimulate_gmrf |
whether to resimulate the GMRF based on estimated or
simulated random effects (determined by argument |
... |
Not used |
This function conducts a parametric bootstrap, i.e., simulates new data conditional upon estimated values for fixed and random effects. The user can optionally simulate new random effects conditional upon their estimated covariance, or simulate new fixed and random effects conditional upon their imprecision.
Note that simulate
will have no effect on states x_tj
for which there
is a measurement and when those measurements are fitted using family="fixed"
, unless
resimulate_gmrf=TRUE
. In this latter case, the GMRF is resimulated given
estimated path coefficients
Simulated data, either from obj$simulate
where obj
is the compiled
TMB object, first simulating a new GMRF and then calling obj$simulate
.
Plot from a fitted dsem
model
stepwise_selection(model_options, model_shared, quiet = FALSE, ...)
stepwise_selection(model_options, model_shared, quiet = FALSE, ...)
model_options |
character-vector containing sem elements that could be included or dropped depending upon their parsimony |
model_shared |
character-vector containing sem elements that must be included regardless of parsimony |
quiet |
whether to avoid displaying progress to terminal |
... |
arguments passed to |
This function conducts stepwise (i.e., forwards and backwards) model selection using marginal AIC, while forcing some model elements to be included and selecting among others.
An object (list) that includes:
the string with the selected SEM model
a list showing the AIC and whether each model_options
is included or not
# Simulate x -> y -> z set.seed(101) x = rnorm(100) y = 0.5*x + rnorm(100) z = 1*y + rnorm(100) tsdata = ts(data.frame(x=x, y=y, z=z)) # define candidates model_options = c( "y -> z, 0, y_to_z", "x -> z, 0, x_to_z" ) # define paths that are required model_shared = " x -> y, 0, x_to_y " # Do selection step = stepwise_selection( model_options = model_options, model_shared = model_shared, tsdata = tsdata, quiet = TRUE ) # Check selected model cat(step$model)
# Simulate x -> y -> z set.seed(101) x = rnorm(100) y = 0.5*x + rnorm(100) z = 1*y + rnorm(100) tsdata = ts(data.frame(x=x, y=y, z=z)) # define candidates model_options = c( "y -> z, 0, y_to_z", "x -> z, 0, x_to_z" ) # define paths that are required model_shared = " x -> y, 0, x_to_y " # Do selection step = stepwise_selection( model_options = model_options, model_shared = model_shared, tsdata = tsdata, quiet = TRUE ) # Check selected model cat(step$model)
summarize parameters from a fitted dynamic structural equation model
## S3 method for class 'dsem' summary(object, ...)
## S3 method for class 'dsem' summary(object, ...)
object |
Output from |
... |
Not used |
A DSEM is specified using "arrow and lag" notation, which specifies the set of
path coefficients and exogenous variance parameters to be estimated. Function dsem
then estimates the maximum likelihood value for those coefficients and parameters
by maximizing the log-marginal likelihood. Standard errors for parameters are calculated
from the matrix of second derivatives of this log-marginal likelihood (the "Hessian matrix").
However, many users will want to associate individual parameters and standard errors
with the path coefficients that were specified using the "arrow and lag" notation.
This task is complicated in
models where some path coefficients or variance parameters are specified to share a single value a priori,
or were assigned a name of NA and hence assumed to have a fixed value a priori (such that
these coefficients or parameters have an assigned value but no standard error).
The summary
function therefore compiles the MLE for coefficients (including duplicating
values for any path coefficients that assigned the same value) and standard error
estimates, and outputs those in a table that associates them with the user-supplied path and parameter names.
It also outputs the z-score and a p-value arising from a two-sided Wald test (i.e.
comparing the estimate divided by standard error against a standard normal distribution).
Returns a data.frame summarizing estimated path coefficients, containing columns:
The parsed path coefficient
The lag, where e.g. 1 means the predictor in time t effects the response in time t+1
Parameter name
Start value if supplied, and NA otherwise
Parameter number
Variable in path treated as predictor
Variable in path treated as response
Whether the path is one-headed or two-headed
Maximum likelihood estimate
Estimated standard error from the Hessian matrix
Estimate divided by Std_Error
P-value associated with z_value using a two-sided Wald test
Calculate the p-value for a test of d-separation (Experimental)
test_dsep( object, n_time = NULL, n_burnin = NULL, what = c("pvalue", "CIC", "all"), test = c("wald", "lr") )
test_dsep( object, n_time = NULL, n_burnin = NULL, what = c("pvalue", "CIC", "all"), test = c("wald", "lr") )
object |
object from |
n_time |
how many times to include when defining the set of conditional independence relationships. If missing, this value is taken from the maximum lag that's included in the model plus one. |
n_burnin |
how many times to include prior to |
what |
whether to just get the p-value, an information criterion based on the conditional independence test, or a named list with these two and other intermediate calculations (used for diagnosing test behavior) |
test |
whether to test each conditional-independence relationship using a (univariate) wald test or a (multivariate) likelihood ratio test. The likelihood-ratio test might be more accurate given estimation covariance and also faster (does not require standard errors), but also is not used by phylopath and therefore less supported by previous d-dsep testing applications. |
A user-specified SEM implies a set of conditional independence relationships among variables, which can be fitted individually, extracting the slope and associated p-value, and then combining these p-values to define a model-wide (omnibus) p-value for the hypothesis that a given data set arises from the specified model. This test is modified from package:phylopath. However it is unclear exactly how to define the set of conditional-independence assumptions in a model with temporal autocorrelation, and the test was not developed for uses when data are missing. At the time of writing, the function is hightly experimental.
Note that the method is not currently designed to deal with two-headed arrows among variables (i.e., exogenous covariance).
A p-value representing the weight of evidence that the data arises from the specified model, where a low p-value indicates significant evidence for rejecting this hypothesis.
Shipley, B. (2000). A new inferential test for path models based on directed acyclic graphs. Structural Equation Modeling, 7(2), 206-218. doi:10.1207/S15328007SEM0702_4
# Simulate data set set.seed(101) a = rnorm( 100 ) b = 0.5*a + rnorm(100) c = 1*a + rnorm(100) d = 1*b - 0.5*c + rnorm(100) tsdata = ts(data.frame(a=a, b=b, c=c, d=d)) # fit wrong model wrong = dsem( tsdata = tsdata, sem = " a -> d, 0, a_to_d b -> d, 0, b_to_d c -> d, 0, c_to_d " ) test_dsep( wrong ) # fit right model right = dsem( tsdata = tsdata, sem = " a -> b, 0, a_to_b a -> c, 0, a_to_c b -> d, 0, b_to_d c -> d, 0, c_to_d " ) test_dsep( right )
# Simulate data set set.seed(101) a = rnorm( 100 ) b = 0.5*a + rnorm(100) c = 1*a + rnorm(100) d = 1*b - 0.5*c + rnorm(100) tsdata = ts(data.frame(a=a, b=b, c=c, d=d)) # fit wrong model wrong = dsem( tsdata = tsdata, sem = " a -> d, 0, a_to_d b -> d, 0, b_to_d c -> d, 0, c_to_d " ) test_dsep( wrong ) # fit right model right = dsem( tsdata = tsdata, sem = " a -> b, 0, a_to_b a -> c, 0, a_to_c b -> d, 0, b_to_d c -> d, 0, c_to_d " ) test_dsep( right )
TMBAIC
calculates AIC for a given model fit
TMBAIC(opt, k = 2, n = Inf)
TMBAIC(opt, k = 2, n = Inf)
opt |
the output from |
k |
the penalty on additional fixed effects (default=2, for AIC) |
n |
the sample size, for use in AICc calculation (default=Inf, for which AICc=AIC) |
AIC, where a parsimonious model has a AIC relative to other candidate models
Calculate a data frame of total effects, representing the estimated effect of every variable on every other variable and any time-lag from 0 (simultaneous effects) to a user-specified maximum lag.
total_effect(object, n_lags = 4)
total_effect(object, n_lags = 4)
object |
Output from |
n_lags |
Number of lags over which to calculate total effects |
Total effects are taken from the Leontief matrix ,
where
is the path matrix across variables and times.
A data frame listing the time-lag (lag), variable that is undergoing some exogenous change (from), and the variable being impacted (to), along with the total effect (total_effect) including direct and indirect pathways, and the partial "direct" effect (direct_effect)
extract the covariance of fixed effects, or both fixed and random effects.
## S3 method for class 'dsem' vcov(object, which = c("fixed", "random", "both"), ...)
## S3 method for class 'dsem' vcov(object, which = c("fixed", "random", "both"), ...)
object |
output from |
which |
whether to extract the covariance among fixed effects, random effects, or both |
... |
ignored, for method compatibility |
A square matrix containing the estimated covariances among the parameter estimates in the model.
The dimensions dependend upon the argument which
, to determine whether fixed, random effects,
or both are outputted.