Fits the standard ordered probit (oprobit()) or ordered logit (ologit())
model by maximum likelihood, with optional partial proportional-odds
(non-parallel, "generalized ordered") effects, a unit random intercept,
survey weights, offsets, and analytic, robust, or cluster-robust standard
errors. These are the matched baselines for the inflated models iop() and
iol(): the same engine, the same cutpoint conventions, and the same
methods, so comparisons across the four are like for like.
Usage
oprobit(
formula,
data,
parallel = TRUE,
re = NULL,
nAGQ = 15,
fe = NULL,
fe_correction = c("none", "jackknife"),
time = NULL,
weights = NULL,
offset = NULL,
se = c("analytic", "robust", "cluster", "bootstrap", "none"),
cluster = NULL,
nboot = 200,
cores = 1,
start = NULL,
maxit = 1000,
reltol = 1e-10
)
ologit(
formula,
data,
parallel = TRUE,
re = NULL,
nAGQ = 15,
fe = NULL,
fe_correction = c("none", "jackknife"),
time = NULL,
weights = NULL,
offset = NULL,
se = c("analytic", "robust", "cluster", "bootstrap", "none"),
cluster = NULL,
nboot = 200,
cores = 1,
start = NULL,
maxit = 1000,
reltol = 1e-10
)Arguments
- formula
A model formula
y ~ x1 + x2. The response may be an ordered factor, a factor (level order taken as the ordinal order), or a numeric/integer vector (sorted unique values define the order). The outcome equation has no intercept (the cutpoints absorb it), so an explicit0 +/- 1is ignored with a message and factors are coded as with an intercept; a set of columns that sums to a constant is rejected as rank deficient.- data
A data frame.
- parallel
TRUE(default; all effects parallel),FALSE(all effects category-specific), a one-sided formula naming the terms that are held parallel, e.g.parallel = ~ . - x2, or"auto": starting from the all-parallel fit, relax the term whose parallel restriction has the smallest likelihood-ratio p-value while that p-value is below 0.05 (a forward version of Stata'sgologit2, autofit; the steps are stored inobject$autofit). See alsoparallel_test().- re
Optional column name in
dataidentifying units that receive a random intercept in the outcome equation.- nAGQ
Number of adaptive Gauss–Hermite quadrature nodes per random intercept (default 15).
- fe
Optional column name in
dataidentifying units that receive a fixed effect (a dummy per unit, reference level omitted) in the outcome equation. Units whose response is constant at an extreme category have no finite fixed effect and are dropped with a message; a covariate that does not vary within units is collinear with the dummies and is refused by name (keep it withre =ormundlak()instead). Maximum-likelihood unit dummies carry incidental-parameters bias when units have few observations (Greene 2004); the fit warns when the median unit has fewer than 10. In the package's Monte Carlo (system.file("mc", package = "iop"): ordered probit, 100 units, a covariate correlated with a N(0,1) unit effect, 100 replications) the bias of that covariate's coefficient is +26 / +11 / +5 / +2.5 percent at T = 4 / 8 / 16 / 32 with unit dummies, +21 / +11 / +6 / +3 percent with a random intercept, +12 percent throughout when pooled, and within 0.3 percent at every T with themundlak()device – the recommended route for short panels; reservefe =for long ones. Computationally the dummies enter the parameter vector one per unit, and the exact-Hessian Newton polish and the covariance cost grow roughly with the square of the number of parameters (about 0.5 s at 20 units, 3 s at 100, 18 s at 300 for a plain ordered probit on 4,000 rows; the multistart of an inflated model multiplies this); for panels with many hundreds of units preferre =ormundlak().- fe_correction
With
fe,"jackknife"applies the split-panel jackknife of Dhaene and Jochmans (2015): the model is refit on the two half-panels of every unit (first and second half of the observations ordered bytime, or by row order iftimeis missing) and the common parameters are bias-corrected as2 * full - (half1 + half2) / 2, removing the leading incidental-parameters bias; the uncorrected estimates are kept inobject$coefficients_uncorrected, the half-panel estimates and log-likelihoods inobject$jackknife, and the full-sample covariance is reported: Dhaene and Jochmans (2015, Section 2) show that the split-panel jackknife removes the leading bias without changing the first-order asymptotic variance, so the full-sample covariance is the asymptotically valid one for the corrected estimator (its finite-sample variance is somewhat larger, whichse = "bootstrap"cannot assess underfe; treat the intervals as approximate). Units with a single observation do not enter the half-panels. The correction is only as good as the half-panel fits: when a covariate is identified mainly by a within-unit trend, or the half-panels are very short, the half-panel estimates are noisy and the corrected estimate inherits that noise – inspectobject$jackknife$half_coefficientsbefore reporting it.- time
Optional column name ordering the observations within units (used by
fe_correction = "jackknife").- weights
Optional weights: a column name in
dataor a numeric vector aligned to its rows. They enter as frequency weights: a fit with weight 2 equals a fit on duplicated rows, in the estimates, the model-based standard errors, the information criteria (whosenis the weight total), the Vuong statistics, and the averaged quantities of interest. Survey (sampling, probability) weights are not frequency weights: with them the default model-based standard errors are not the design-based ones, so usese = "robust"(the pseudo-maximum-likelihood sandwich) orcluster =for the primary sampling units.- offset
Optional offset on the latent scale of the outcome equation: a column name in
dataor a numeric vector.- se
Standard errors:
"analytic"(inverse observed information, default),"robust"(heteroskedasticity-consistent sandwich over observations, or over units for random-intercept fits),"cluster"(cluster-robust; needscluster),"bootstrap"(nonparametric bootstrap over the estimation rows – or over the clusters whenclusteris given, or over the units of a random-intercept fit – withnbootrefits from the full-sample estimate; the replicate estimates are kept inobject$bootandconfint.iord()can then report percentile intervals), or"none". The bootstrap is not available withfe. Replicates whose split equation (orrho) runs to a boundary are counted inobject$boot$n_boundaryand trigger a warning: their estimates inflate the bootstrap standard errors of the affected block, for which the percentile intervals are the more robust summary. For"bootstrap", a seed set before the call (set.seed()) makes the resamples reproducible; runs withcores = 1andcores > 1use different random streams.- cluster
Optional cluster identifier (a column name in
dataor a vector aligned to its rows); supplying it selectsse = "cluster"unlessseis given explicitly (withse = "bootstrap"it defines the resampling blocks). With a random intercept, clusters must nest the units.- nboot
Number of bootstrap refits for
se = "bootstrap"(default 200). Each refit costs about one fit of the model (roughly a third of a second per refit for the bundledbpZiOP), so budget a minute or two at the default on data of that size, and usecores.- cores
Number of parallel workers for the bootstrap refits (a PSOCK cluster via the parallel package; default 1).
- start
Optional starting values on the internal parameter scale (see
object$theta); rarely needed.- maxit, reltol
Controls for the BFGS stage of the optimizer, passed to
stats::optim(); the exact-Hessian Newton polish that follows runs up to 25 further iterations regardless ofmaxit(somaxitbounds the quasi-Newton phase, not the total). Results are insensitive tomaxitabove a few hundred;maxit = 0is reported as non-converged.
Value
An object of class c("oprobit", "iord") or c("ologit", "iord"):
a list with coefficients (outcome coefficients, then the cutpoints named
"a|b", then sigma_u for a random-intercept fit), vcov, loglik,
fitted.values (the n x J matrix of category probabilities), cutpoints,
converged, boundary, and the design pieces used by the methods. See
summary.iord(), predict.iord(), first_difference(),
compare_models().
Details
The latent-variable model is \(y_i^* = x_i'\beta + \epsilon_i\) with
\(y_i = j\) when \(\tau_{j-1} < y_i^* \le \tau_j\)
(\(\tau_0 = -\infty\), \(\tau_J = \infty\)), so
\(P(y_i \le j) = F(\tau_j - x_i'\beta)\), with \(F\) the standard normal
(probit) or logistic (logit) distribution function. The outcome equation has
no intercept (it is absorbed by the cutpoints), matching MASS::polr().
With parallel = FALSE, or a one-sided formula naming the terms held
parallel (all others relaxed; ~ . - x2 relaxes only x2), the relaxed
terms get a separate coefficient per cutpoint,
\(P(y_i \le j) = F(\tau_j - x_i'\beta - \tilde x_i'\beta_j)\), the partial
proportional-odds / generalized ordered model of Peterson and Harrell (1990)
and Williams (2006). Such fits can imply negative cell probabilities when the
category-specific curves cross; the fit warns when that happens at the
optimum.
With re = "unit" a random intercept \(u_g \sim N(0, \sigma_u^2)\) for the
units of that column enters the latent equation,
\(y_{ig}^* = x_{ig}'\beta + u_g + \epsilon_{ig}\), and is integrated out by
adaptive Gauss–Hermite quadrature with nAGQ nodes per unit (the mode and
curvature of each unit's integrand re-center and re-scale the nodes), as in
ordinal::clmm(). Reported probabilities and first differences are then
population-averaged (marginal over the random intercept); conditional
probabilities at \(u = 0\) and the empirical-Bayes unit effects are
available through predict.iord() and ranef().
For panels, mundlak() adds unit means of the covariates (correlated random
effects), which pairs naturally with re = or with cluster =.
Missing values, refits, and new data
Rows with a missing value in any variable of either equation (or in
weights, offsets, cluster, re, fe) are dropped before fitting –
there is no na.action argument – and object$data, fitted(),
residuals(), and predict() without newdata refer to the retained
rows (so they are not padded to the original row positions as with
na.exclude). The stored call makes update.iord() work as usual
(update(fit, . ~ . + x3), update(fit, data = subset); for the
two-part formulas update(fit, . ~ . + x3 | .) changes the outcome
equation and update(fit, . ~ . | . + z2) the inflation equation). In
predict.iord(), newdata must contain every covariate of both
equations; extra columns are ignored, unseen factor levels are an error,
and rows with missing covariates give NA predictions.
References
McKelvey, R.D. and Zavoina, W. (1975). A statistical model for the analysis of ordinal level dependent variables. Journal of Mathematical Sociology, 4, 103-120.
Peterson, B. and Harrell, F.E. (1990). Partial proportional odds models for ordinal response variables. Applied Statistics, 39, 205-217.
Williams, R. (2006). Generalized ordered logit/partial proportional odds models for ordinal dependent variables. Stata Journal, 6, 58-82.
Greene, W. (2004). The behaviour of the maximum likelihood estimator of limited dependent variable models in the presence of fixed effects. Econometrics Journal, 7, 98-119.
Dhaene, G. and Jochmans, K. (2015). Split-panel jackknife estimation of fixed-effect models. Review of Economic Studies, 82, 991-1030.
Dale, D. and Sirchenko, A. (2021). Estimation of nested and zero-inflated ordered probit models. Stata Journal, 21, 3-38.
See also
iop(), iol(), predict.iord(), first_difference(), vuong(),
parallel_test(), mundlak(), ranef(); vignette("iop"),
vignette("panels").
Other estimators:
inflated
Examples
set.seed(1)
n <- 500; x <- rnorm(n); z <- rbinom(n, 1, 0.5)
ystar <- 0.8 * x - 0.5 * z + rnorm(n)
y <- cut(ystar, c(-Inf, -0.7, 0.5, Inf), labels = c("low", "mid", "high"), ordered_result = TRUE)
d <- data.frame(y, x, z)
m1 <- oprobit(y ~ x + z, data = d)
summary(m1)
#>
#> Ordered probit
#> Call: oprobit(formula = y ~ x + z, data = d)
#> Response levels (in order): low < mid < high
#> N = 500 inference: analytic
#>
#> Outcome equation (ordered probit):
#> Estimate Std. Error z value Pr(>|z|)
#> x 0.79031 0.06294 12.556 < 2e-16 ***
#> z -0.41551 0.10750 -3.865 0.000111 ***
#>
#> Cutpoints:
#> Estimate Std. Error z value Pr(>|z|)
#> low|mid -0.67575 0.08277 -8.164 3.25e-16 ***
#> mid|high 0.50641 0.08116 6.239 4.39e-10 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> logLik = -453.26 AIC = 914.52 BIC = 931.37 df = 4
m2 <- ologit(y ~ x + z, data = d, se = "robust")
compare_models(probit = m1, logit = m2)
#> model type logLik df AIC BIC N
#> 1 probit Ordered probit -453.2582 4 914.5164 931.3748 500
#> 2 logit Ordered logit -453.6283 4 915.2565 932.1150 500
head(predict(m1, type = "prob"))
#> low mid high
#> [1,] 0.59283812 0.3289222 0.07823965
#> [2,] 0.34260009 0.4387566 0.21864332
#> [3,] 0.49387755 0.3844796 0.12164289
#> [4,] 0.06412858 0.3032328 0.63263867
#> [5,] 0.17459334 0.4225627 0.40284399
#> [6,] 0.48909882 0.3868216 0.12407962