dcvar fits Bayesian copula vector autoregressions of order one for bivariate time series. The modelling idea is to keep two questions separate: how each series is distributed on its own, and how the two series move together. A copula carries the dependence, so each margin may follow its own distribution while a single association parameter describes how the series are coupled at a given time.
The conditional mean follows a first-order vector autoregression, so each series depends on the recent past of both. The innovations are joined by a Gaussian copula, with a Clayton copula also available for the constant baseline. The association can be held constant, allowed to drift smoothly as a random walk, allowed to switch between latent regimes, or expressed as a function of covariates. In the dynamic model the autoregressive coefficients and the innovation scales may vary over time as well; in the HMM model, the regime can also govern the intercepts, autoregressive coefficients, innovation scales, and marginal families. Everything is estimated in a fully Bayesian way through Stan, which returns posterior distributions for all quantities.
Two further families, a multilevel version for panel data and a structural-equation version for latent processes, are provided as experimental extensions.
dcvar uses rstan as its default
backend.
Install dcvar from CRAN:
install.packages("dcvar")For the development version:
install.packages("remotes")
remotes::install_github("benlug/dcvar")Optionally, you can use cmdstanr as an
alternative backend:
install.packages(
"cmdstanr",
repos = c("https://stan-dev.r-universe.dev", getOption("repos"))
)
cmdstanr::install_cmdstan()For skew-normal margins, install sn:
install.packages("sn")The example below simulates a bivariate series whose association declines over time, fits the dynamic copula model, and compares it with the regime-switching and constant-association alternatives by cross-validation.
library(dcvar)
# simulate data with decreasing coupling
sim <- simulate_dcvar(
n_time = 150,
rho_trajectory = rho_decreasing(150, rho_start = 0.7, rho_end = 0.3)
)
# fit the DC-VAR model
fit <- dcvar(sim$Y_df, vars = c("y1", "y2"))
# inspect results
summary(fit)
plot_rho(fit, true_rho = sim$true_params$rho)
# compare models via leave-one-out cross-validation
fit_hmm <- dcvar_hmm(sim$Y_df, vars = c("y1", "y2"), K = 2)
fit_con <- dcvar_constant(sim$Y_df, vars = c("y1", "y2"))
dcvar_compare(dcvar = fit, hmm = fit_hmm, constant = fit_con)The core models differ in how dependence, marginal distributions, and the vector-autoregressive mean are allowed to change over time.
| Model | Function | Association over time | Status |
|---|---|---|---|
| DC-VAR | dcvar() |
Drifts smoothly as a random walk on the Fisher z scale; the autoregressive coefficients and innovation scales may also drift | Core |
| HMM Copula | dcvar_hmm() |
Switches between K latent regimes | Core |
| Constant Copula | dcvar_constant() |
Held constant (Gaussian or Clayton) | Core |
| Covariate DC-VAR | dcvar_covariate() |
A function of observed covariates on the Fisher z scale | Core |
| Multilevel | dcvar_multilevel() |
Series-specific autoregressive coefficients across many units | Experimental |
| SEM | dcvar_sem() |
Latent processes measured by observed indicators | Experimental |
Because the copula keeps the margins separate from the dependence,
every model accepts per-variable margins. Pass a length-two vector such
as margins = c("normal", "exponential") to let each series
follow a different marginal family. The single-level models
dcvar(), dcvar_hmm(), and
dcvar_constant() with a Gaussian copula support all four
families (normal, exponential, skew-normal, and gamma), used on their
own or in combination. dcvar_multilevel() and
dcvar_sem() support all four families as a single family or
in combination. dcvar_constant(copula = "clayton") provides
a Clayton-copula baseline with normal or mixed margins.
In dcvar(), the association always evolves over time.
Setting tv_phi = TRUE additionally lets the autoregressive
coefficients drift as random walks, and tv_sigma = TRUE
lets the innovation scales drift. tv_phi also accepts
"ar" or "cross" to let only the autoregressive
or only the cross-lagged coefficients vary. With both options off, the
model reduces to the constant-coefficient dynamic copula.
In dcvar_hmm(), switch = "rho" gives the
classic regime-switching copula model, where only the copula correlation
changes by latent state. Passing components such as
switch = c("rho", "mu", "phi", "sigma") fits a fuller
Markov-switching VAR(1). The HMM can also use state-specific marginal
families by passing a length-K list to
margins; hmm_state_params() extracts the
effective state-specific intercepts, VAR matrices, scale parameters, and
families.
Fitted and one-step-ahead predicted values are available for every
fitted model. For multilevel fits these are specific to each unit; for
structural-equation fits they cover both the latent states
(type = "link") and the observed indicators
(type = "response").
Leave-one-out cross-validation through loo() is
available for the single-level fits, the covariate fits, the multilevel
fits, and the naive structural-equation score fits, and
dcvar_compare() places several fits on a common predictive
scale. Comparisons that mix model families whose pointwise predictive
densities are not on the same footing are flagged with a warning.
Residual checks based on the probability integral transform,
pit_values() and pit_test(), are provided for
the single-level models. They use posterior means and are best read as
heuristic checks rather than exact calibration tests. Posterior
predictive checks through plot_ppc() are available for
normal and exponential margins; gamma and skew-normal fits do not yet
store replicated residuals on the observed margin scale.
The package also includes a constant Clayton-copula baseline, a multilevel model with exponential margins, and naive structural-equation score models that were used in the accompanying simulation studies.
If you use dcvar in your work, cite it with:
citation("dcvar")