
Copula endogeneity correction of Liengaard et al. (2025) (JAMS)
Source:R/copreg-jams.R
CopRegJAMS.RdGeneralised Gaussian copula estimator of Liengaard, Becker, Bennedsen,
Heiler, Taylor and Ringle (2025). Endogenous regressors are transformed to
normal scores and combined with the (also transformed) continuous
exogenous regressors through the inverse of their empirical
variance-covariance matrix (Equation 17) to form the copula control terms
added to the structural model (Equation 19). The estimator's distinctive
feature is that this copula structure may be estimated separately within
the joint categories formed by the discrete (factor or character)
exogenous regressors (Equations 20-21), controlled by conditional.
Discrete exogenous regressors always stay in the structural model as
ordinary controls; conditional only decides whether they also split
the copula terms.
Usage
CopRegJAMS(
formula,
data,
conditional = TRUE,
cdf = "ecdf.adj",
ties = "max",
nboots = 199,
subset = NULL,
contrasts = NULL,
parallel = FALSE,
ncores = NULL,
verbose = interactive()
)Arguments
- formula
A two-part
Formulaof the formy ~ endogenous_1 + endogenous_2 | exogenous_1 + exogenous_2. Terms before the bar are endogenous and receive a copula correction; terms after it are exogenous and receive none. Anythinglm()accepts is allowed, including interactions, polynomials, factors and- 1to drop the intercept. Copula terms are only generated for first-order endogenous terms (matching \(d_P\) in Equation 17 of the source paper); giving an interaction or a power of an endogenous regressor its own position before the bar produces a warning.- data
A
data.framecontaining the variables named informula. Missing values are handled byna.omit.- conditional
Controls which exogenous regressors split the copula structure into categories (Equations 20-21).
TRUE(the default) uses the joint categories of all factor and character regressors in the exogenous part offormula: two binary variables give four cells, not two.FALSEimposes one common copula structure across the whole sample (Equation 18). A character vector instead names the specific exogenous variables whose categories the structure may vary over, which is useful when the joint number of cells implied byTRUEwould be too large for the sample. Under all three settings the discrete regressors remain in the structural model as ordinary controls and are never copula-transformed themselves: \(W\) in Equation 17 is always the continuous exogenous vector.- cdf
Character string naming the estimator of the marginal CDF used in the copula transformation. Defaults to
"ecdf.adj", the adjusted empirical CDF proposed in the source paper.- ties
Character string controlling how ties are broken in the CDF estimate:
"max"(the default) for the counting function, or"average"for midranks.- nboots
Number of bootstrap replicates used to compute standard errors.
- subset
Optional logical vector selecting a subset of
data, as inlm().- contrasts
Optional contrasts specification passed to
model.matrix.- parallel
FALSE(the default),TRUE,"multicore"or"snow"to spread the bootstrap replicates across workers.- ncores
Number of workers to use when
parallelis notFALSE.NULL(the default) uses one less than the number of detected cores.- verbose
Logical; show a progress bar while bootstrapping. Defaults to
interactive().
Value
An object of class "copreg": a list including
coefficients,std.error,vcovPoint estimates, bootstrap standard errors and bootstrap covariance matrix for the structural coefficients and the copula terms.
rho,rho.seEstimated correlation(s) between the structural residual and each endogenous regressor's copula data, with bootstrap standard errors.
boot,boot.rho,boot.olsThe raw bootstrap replicates underlying the above.
ols.coefficients,ols.std.errorThe uncorrected OLS benchmark, estimated on the same bootstrap resamples.
iconStandard error inflation of the corrected estimator relative to uncorrected OLS.
fitted.values,residualsFitted values and residuals of the structural model (copula terms excluded).
copula.terms,copula.data,copula.groupsThe copula control terms actually added to the regression, the underlying normal scores of the endogenous regressors, and (for JAMS) which endogenous regressor and cell each copula column belongs to.
diagnosticsIdentification and specification diagnostics used by
validity().call,method,cdf,ties,nBook-keeping: the matched call, an estimator label, the CDF and tie method used, and the sample size.
Methods exist for print(), summary, coef(),
vcov(), residuals(), fitted(), predict(),
confint(), nobs(), formula(), update() and
validity(). For JAMS specifically, summary() additionally
reports a bootstrap Wald test of the joint significance of the copula
terms and, whenever conditional produces more than one cell, a
second bootstrap Wald test of whether the copula terms differ across
those cells.
References
Liengaard, B. D., J.-M. Becker, M. Bennedsen, P. Heiler, L. N. Taylor, and C. M. Ringle (2025). Dealing with regression models' endogeneity by means of an adjusted estimator for the Gaussian copula approach. Journal of the Academy of Marketing Science 53, 279-299.
Examples
set.seed(1)
n <- 300
z <- factor(sample(c("a", "b"), n, replace = TRUE))
w <- rnorm(n)
u <- rnorm(n)
p <- 0.6 * u + rnorm(n)
y <- 1 + 2 * p + 0.5 * w + (z == "b") + u
dat <- data.frame(y = y, p = p, w = w, z = z)
fit <- CopRegJAMS(y ~ p | w + z, data = dat, nboots = 25)
summary(fit)
#>
#> Copula endogeneity correction: JAMS (Liengaard et al. 2025)
#>
#> Call:
#> CopRegJAMS(formula = y ~ p | w + z, data = dat, nboots = 25)
#>
#> Residuals of the augmented regression (u = xi - C gamma):
#> Min 1Q Median 3Q Max
#> -2.33260 -0.54857 -0.06364 0.71824 2.83336
#>
#> Residuals of the structural model (xi = y - mu - P alpha - W beta):
#> Min 1Q Median 3Q Max
#> -2.964652 -0.743361 -0.002778 0.693468 3.099066
#>
#> Coefficients:
#> Estimate Std. Error z value Pr(>|z|)
#> (Intercept) 1.13409 0.08556 13.255 < 2e-16 ***
#> p 2.92685 0.37752 7.753 8.98e-15 ***
#> w 0.42374 0.07180 5.902 3.60e-09 ***
#> zb 0.70984 0.11570 6.135 8.50e-10 ***
#> p_cop.za -0.60715 0.48989 -1.239 0.215
#> p_cop.zb -0.46754 0.45689 -1.023 0.306
#> ---
#> Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#>
#> Endogeneity: rho(P*, xi*) is the correlation between the normal score
#> of an endogenous regressor and that of the structural error, xi* = xi / sigma.
#> Estimate Std. Error z value Pr(>|z|)
#> rho(p*, xi*) -0.5180 0.3666 -1.413 0.158
#>
#> Joint significance of the copula terms (bootstrap Wald test):
#> chi-squared = 2.335 on 2 df, p = 0.3112
#> Copula structure constant across the 2 categories of the discrete
#> regressors (bootstrap Wald test): chi-squared = 1.751 on 1 df,
#> p = 0.1858. A small p favours the grouped estimation.
#>
#> Fit, on 294 residual degrees of freedom:
#> augmented structural
#> Residual standard error 0.9062 1.0647
#> R-squared 0.9286 0.9014
#> Adjusted R-squared 0.9274 0.8997
#> sigma above is the standard error of the structural model, the one
#> entering xi* = xi / sigma.
#> Standard errors from 25 bootstrap replicates; cdf = "ecdf.adj", ties = "max".
#> Pr(>|z|) in both tables: Wald test using the normal approximation,
#> z = Estimate / Std. Error, with the bootstrap standard error.
#> See confint(object, type = "percentile") for bootstrap percentile intervals.
#>
#> --- Identification diagnostics ------------------------------------
#>
#> Non-normality of the endogenous regressors (small p = non-normal, good):
#> AD AD p KS p
#> p 0.1927 0.8943 0.9966
#>
#> Correlation of the copula terms with the exogenous regressors
#> (this estimator does not assume it is zero; it projects the correlation
#> out in the first stage):
#> max |corr| with p (Holm) joint R2 joint p
#> p 0.07532 w 0.3869 0.005728 0.4261
#> full matrix in summary(object)$diagnostics$exog.correlation.matrix
#>
#> Collinearity of the copula terms (omega near 0 = weakly identified):
#> corr(P, C) omega
#> p_cop.za 0.9975 0.01504
#> p_cop.zb 0.9883 0.01640
#>
# \donttest{
fit_unconditional <- CopRegJAMS(y ~ p | w + z, data = dat,
conditional = FALSE, nboots = 25)
summary(fit_unconditional)
#>
#> Copula endogeneity correction: JAMS (Liengaard et al. 2025), unconditional
#>
#> Call:
#> CopRegJAMS(formula = y ~ p | w + z, data = dat, conditional = FALSE,
#> nboots = 25)
#>
#> Residuals of the augmented regression (u = xi - C gamma):
#> Min 1Q Median 3Q Max
#> -2.33667 -0.55235 -0.05705 0.68338 2.79300
#>
#> Residuals of the structural model (xi = y - mu - P alpha - W beta):
#> Min 1Q Median 3Q Max
#> -5.07144 -1.26545 -0.09888 1.25522 4.95035
#>
#> Coefficients:
#> Estimate Std. Error z value Pr(>|z|)
#> (Intercept) 1.11151 0.11754 9.456 < 2e-16 ***
#> p 3.68561 0.85953 4.288 1.80e-05 ***
#> w 0.37062 0.09483 3.908 9.29e-05 ***
#> zb 0.83224 0.08889 9.363 < 2e-16 ***
#> p_cop -1.47954 1.06890 -1.384 0.166
#> ---
#> Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#>
#> Endogeneity: rho(P*, xi*) is the correlation between the normal score
#> of an endogenous regressor and that of the structural error, xi* = xi / sigma.
#> Estimate Std. Error z value Pr(>|z|)
#> rho(p*, xi*) -0.8561 0.6395 -1.339 0.181
#>
#> Fit, on 295 residual degrees of freedom:
#> augmented structural
#> Residual standard error 0.9050 1.7632
#> R-squared 0.9285 0.7287
#> Adjusted R-squared 0.9276 0.7250
#> sigma above is the standard error of the structural model, the one
#> entering xi* = xi / sigma.
#> Standard errors from 25 bootstrap replicates; cdf = "ecdf.adj", ties = "max".
#> Pr(>|z|) in both tables: Wald test using the normal approximation,
#> z = Estimate / Std. Error, with the bootstrap standard error.
#> See confint(object, type = "percentile") for bootstrap percentile intervals.
#>
#> --- Identification diagnostics ------------------------------------
#>
#> Non-normality of the endogenous regressors (small p = non-normal, good):
#> AD AD p KS p
#> p 0.1927 0.8943 0.9966
#>
#> Correlation of the copula terms with the exogenous regressors
#> (this estimator does not assume it is zero; it projects the correlation
#> out in the first stage):
#> max |corr| with p (Holm) joint R2 joint p
#> p 0.09902 zb 0.1738 0.01545 0.09911
#> full matrix in summary(object)$diagnostics$exog.correlation.matrix
#>
#> Collinearity of the copula terms (omega near 0 = weakly identified):
#> corr(P, C) omega
#> p_cop 0.997 0.001903
#>
# }