*! version 1.0 20aug2026

Title

loevh2_svy -- Design-based (survey) standard error and confidence
interval for Loevinger's H, using svyset

Syntax

loevh2_svy varlist [if] [in] [, options]

Main
table display the design-based cross tabulation of cell
percentages (via svy: tabulate) and the Pearson chi2 test

level(#) set confidence level; default is level(95)

compare test equality of Hs across sub-samples (requires by:)

meta(filename, replace|append) save each valid sub-sample's
(label, H, SE, N) row to a persistent .dta file for later meta-analysis

by is allowed; see [D] by.

No weight is specified on the loevh2_svy command line itself --
weighting, clustering, and stratification are all taken from a
previously issued svyset declaration; see Description.

Description

loevh2_svy calculates Loevinger's H coefficient (Loevinger, 1947; see
loevh2 for the full definition, properties, and references) together
with a genuinely design-based (survey) standard error and confidence
interval, honoring an active svyset declaration's sampling weight
(pweight/iweight), clustering (cluster()), and/or
stratification (strata()) -- exactly as any other svy: estimation
command would. loevh2_svy does not accept a weight on its own
command line; any weighting comes exclusively from the svyset declaration.

loevh2_svy obtains its H estimate and standard error via
svy: mean on four constructed cell-membership indicators, followed
by nlcom's delta method applied to Copas & Loeber's H formula written as
a nonlinear combination of those four cell proportions. Because svy: mean
already correctly incorporates pweight, cluster(), and/or strata()
into its variance-covariance matrix, the resulting nlcom standard error
for H is a true design-based (linearized Taylor-series) survey standard error --
analogous to how one would obtain a design-based SE for any other nonlinear
function of survey-estimated proportions.

loevh2_svy therefore requires an active svyset design with at
least one of pweight/iweight, cluster(), or strata()
genuinely set. A plain svyset _n with none of these is rejected, since
loevh2_svy would then offer no benefit over the faster, better documented
loevh2. If no svyset design (or only a degenerate one) is active,
loevh2_svy exits with an error pointing the user to loevh2 instead.

Caution: a bare pweight-only svyset (no cluster(), no strata())
is syntactically valid and accepted by loevh2_svy, but it corrects only for
unequal probability of selection in the point estimate -- it does NOT correct
standard errors for any real clustering or stratification present in the actual
sampling design. If the true design includes clustering (e.g. respondents nested
within schools, interviewers, or areas) that is not declared via cluster(),
the resulting SE/CI for H can understate the true sampling variability, sometimes
substantially. Before using pweight alone, confirm whether your sampling
design actually has a clustering/stratification structure that should be declared;
consult your data codebook/technical documentation or a survey statistician if
unsure. This risk is compounded when the weights are themselves correlated with
the outcome variables under study (see loevh2's "When to trust the results"
section, item 5, for a related, empirically documented instance of this general
phenomenon in the bootstrap-SE context).

loevh2_svy always displays a brief design summary header (number of
strata, PSUs, obs, population size, subpop size, and design df). Because
this design df is computed from subpop() (see below), it reflects the
full svyset-declared sample size, not just the observations with
non-missing var1/var2 -- so it will typically be larger than
the design df shown by a plain svy: tabulate / svy: mean on the
same two variables under the same svyset (which automatically
restricts to non-missing observations only). This is expected and correct
(see the note on subpop() below), not a discrepancy to be concerned
about. This part is followed by H with its design-based SE/CI.

If the table option is specified, it additionally displays, a design-based
cross tabulation of the two variables (cell percentages and a Pearson chi2
test), obtained via svy: tabulate and as this command
would display them. Additionally, it displays the percent of "overlap" in cell 1/1
(var1=1 & var2=1), together with its standard error and an asymmetric,
logit-transformed Wald confidence interval. r(overlap) (as a proportion),
r(se_overlap), r(lb_overlap), and r(ub_overlap) are returned
irrespective of table. By default (table not specified), neither
the cross tabulation nor the overlap summary is shown (only the design summary
header plus the H results table).

Unlike H itself (which can be negative, and for which the analogous logit
construction was found to perform poorly -- see
"Why no asymmetric-CI option is offered" below --
the overlap proportion is a genuine bounded parameter in [0,1], the textbook
use case for a logit-transformed CI: the CI is obtained by applying
nlcom's delta method to logit (overlap) and back-transforming the
resulting Wald interval with the inverse-logit function, guaranteeing
a CI that always stays within [0,1] and is naturally asymmetric.

Internally, loevh2_svy uses svy, subpop() rather than filtering
the estimation sample with a plain if condition, so that any
if/in/by: restriction is handled the statistically
correct way for a complex survey design (retaining the full PSU/strata
information for variance estimation while restricting point estimation to
the relevant subpopulation), rather than potentially biasing the variance
estimate by silently dropping PSU/stratum information for excluded
observations.

The underlying nonlinear H expression (matching loevh2's own H formula),
and its "swap" convention -- which assigns the cell 1/0 role to whichever
off-diagonal cell belongs to the variable with the smaller marginal
probability of being 1 (i.e. based on comparing the two margins P(var1=1)
vs. P(var2=1)) is:

H = 1 - p_10 / [(p_11 + p_10) × (1 - (p_11 + p_01))]

where p_11, p_10, p_01 (and implicitly p_00 = 1-p_11-p_10-p_01) are the four
joint cell proportions of the two binary variables, estimated via svy: mean.

Options

table displays the design-based cross tabulation of cell percentages
(and a Pearson chi2 test), obtained via svy: tabulate. Additionally,
it displays the percent of "overlap" in cell 1/1 (var1=1 & var2=1),
together with its standard error (sqrt(p*(1-p)/N)) and an asymmetric,
logit-transformed Wald confidence interval. By default (table not specified),
this cross tabulation and percent overlap are not shown. r(overlap),
r(se_overlap), r(lb_overlap), and r(ub_overlap) are returned
irrespective of table.

level(#) specifies the confidence level, as a percentage, for the confidence
interval. The default is level(95) or as set by set level.

compare requires by: (or bysort:). After H has been estimated
(with its design-based SE) for all sub-samples specified with by, a test
of the equality of the resulting Hs is performed (Copas & Loeber, 1990, Eq. 16),
using each sub-sample's design-based H and SE from loevh2_svy
in place of loevh2's large-sample SE. Results are stored in r(Hbar),
r(chi2), r(df), r(p_chi2), and r(H_SE_N) (a k×3 matrix of
each sub-sample's H, design-based SE, and N) -- see loevh2's
Description for the formulas. As with loevh2,
sub-samples with a degenerate 2×2 table or a missing by-value are excluded/skipped,
with r(lastgroup) indicating which sub-sample the top-level
r(loevh)/r(se)/etc. belong to.

meta(filename, replace|append) saves each valid sub-sample's (label,
H, SE, N) row to a persistent Stata dataset filename_svy.dta (note
the automatically appended _svy suffix -- see loevh2's own
meta() option for the full rationale of
this suffix convention, which keeps design-based results from being mixed
with plain loevh2- or loevh2_boot-sourced results in the
same pooled file even when the SAME base filename is supplied to more
than one of these commands). The SE column saved here is
loevh2_svy's own design-based (delta-method/nlcom) standard
error. In addition to the descriptive columns documented for loevh2's
meta() option, the saved rows also record
the active svyset design used to compute them: the weight type
and expression (svy_wtype, svy_wexp), cluster and strata
variable names (svy_cluster, svy_strata), and the finite
population correction (svy_fpc) -- for reference only, so that
pooled results from several different survey designs (or several
different loevh2 / loevh2_boot / loevh2_svy runs) can
later be told apart.

Otherwise, meta() behaves exactly as documented for loevh2: without
compare, exactly one row is saved per call (or per by-group, if
by: is used without compare); with compare, all valid
sub-samples' rows (matching r(H_SE_N)) are saved together, once, after the
last by-group has been processed. Exactly one of replace or append
must be specified, and a companion filename_svy.do pooling/meta-setup
script is (re)written on every call -- see loevh2's meta()
option for full details on the saved columns, the companion script, and replace/append
semantics.

NOTE: Running the companion pooling script (the .do file written
alongside the .dta file, e.g. myfile.do) will replace the dataset
currently in memory with the pooled meta-analysis results (it begins with
use "myfile.dta", clear). If you run this script in the middle of a longer
analysis session, be sure to reload your own working dataset (and re-issue any
svyset, if applicable) afterward before continuing, or enclose "do
myfile.do" between "preserve" and "restore".

Examples

Set up a complex survey design (for all following examples!)

. svyset psu [pweight=wt], strata(region)

Basic use

. loevh2_svy item1 item2

With a 99% confidence level

. loevh2_svy item1 item2, level(99)

By-group analysis, testing equality of Hs across strata

. bysort region: loevh2_svy item1 item2, compare

Same as above and saving H, SE, and N to loev_meta.dta

. bysort region: loevh2_svy item1 item2, compare meta(loev_meta, replace)

pweight only, no clustering/stratification (however, see "Caution" above)

. svyset [pweight=wt]

. loevh2_svy item1 item2

Stored results

loevh2_svy stores the following in r():

Scalars

r(loevh) Loevinger's H coefficient (missing if 2×2 table is degenerate
or nlcom / svy: mean fails); with by, this H and the follwing
r-returns until r(ub_overlap) are for the sub-sample identified by r(lastgroup)

r(se) standard error (design-based)

r(lb) lower bound of confidence interval

r(ub) upper bound of confidence interval

r(level) confidence level

r(N) number of observations

r(overlap) percent overlap in cell 1/1 (var1=1 & var2=1)

r(se_overlap) design-based standard error

r(lb_overlap) lower bound of confidence interval (logit-transformed)

r(ub_overlap) upper bound of confidence interval (logit-transformed)

r(Hbar) weighted average H across sub-samples (only with compare)

r(Hbar_se) pooled (inverse-variance-weighted) standard error of r(Hbar)
(only with compare)

r(chi2) chi2 test statistic for equality of Hs across sub-samples (only with compare)

r(df) degrees of freedom of the chi2 test (only with compare)

r(p_chi2) p-value of the chi2 test (only with compare)

r(Hbar_N) total number of observations summed across all valid sub-samples
(only with compare)

Matrices

r(H_SE_N) k×3 matrix of each sub-sample's H, design-based SE,
and N (only with compare)

Macros

r(var1) name of row variable

r(var2) name of column variable

r(se_type) "svy, nlcom (delta method)"

r(group) by-group variable(s) (if by: was used)

r(lastgroup) value or category label of the sub-sample to
which r(loevh), r(se), r(lb), r(ub), and r(N)
belong (if by: was used)

r(error) "degenerate" if the 2×2 table has a zero cell
or margin, "missing_byvar" if a by-group was skipped because its
by-value is missing, "svy_failed" if svy: mean itself failed,
or "nlcom_failed" if nlcom failed (e.g. a singular Jacobian at
or very near a boundary cell proportion)

Degenerate tables and estimation failures

A 2×2 table in which any cell or margin is exactly zero makes H undefined;
loevh2_svy detects this (before ever calling svy: mean)
and returns missing values with r(error) = "degenerate", together with a
warning, rather than aborting with a hard error.

Because loevh2_svy additionally depends on svy: mean and
nlcom succeeding (the latter requiring, in particular, a non-singular
Jacobian of the H expression evaluated at the estimated cell proportions),
two further failure modes are possible even when the 2×2 table is not
degenerate in the row-count sense above: r(error) = "svy_failed"
(the svy: mean call itself failed) and r(error) = "nlcom_failed"
(nlcom's delta method failed, most likely because an estimated cell
proportion is at or extremely close to a boundary value under the given
design). In both cases loevh2_svy displays an explanatory error
message and returns missing values rather than terminating with an
uncaught error.

Why no asymmetric-CI option is offered

loevh2's small option produces an asymmetric confidence
interval using a closed-form log-odds-ratio ("relative risk") construction
specific to a 2×2 table (Copas & Loeber, 1990, Eqs. 20-23) -- it is
not a delta-method/Wald construction at all, and has no direct nlcom
analog. Since loevh2_svy is built entirely around svy: mean +
nlcom -- a delta-method (Wald-type) machinery from the ground up --
there is no straightforward way to reproduce Eqs. 20-23 within this framework: that
formula's derivation does not go through a design-based variance-covariance
matrix of estimated proportions, and simply re-implementing it with
svy-adjusted cell proportions would not, by itself, correctly propagate
the survey design's clustering/stratification into the resulting CI (Eqs. 20-23
assume a simple random-sample / fixed-margins setting, not a general design-based
covariance structure). So no direct analog of small is available
under loevh2_svy.

A logit- or log-transformed Wald CI for H (the natural nlcom-based candidate,
exactly analogous to what IS implemented for r(overlap)) was considered and
deliberately not implemented: unlike overlap, H is not confined to [0,1] and
treats H=0 as a substantively meaningful interior point, not a boundary; a
logit/log transform is undefined or numerically unstable exactly in that important
region, and an earlier Monte Carlo simulation confirmed that this transform badly
under-covers for small |H| (see loevh2's
"When to trust the results").

See also

loevh2 calculates (asymptotic) standard errors for Loevinger's H for large and small
samples (pweight not allowed)

loevh2_boot provides bootstrap confidence intervals for Loevinger's H,
which may be more robust when sample sizes are small or asymptotic assumptions
are not met -- see also loevh2's "When to trust the results"
section for when this actually holds.

loevh (if installed) by Jean-Benoit Hardouin provides Loevinger's H coefficient for multiple
items (see ssc describe loevh).

Author

Dirk Enzmann (University of Hamburg) with AI assistance (Claude/Anthropic)

Acknowledgments

Daniel Klein's suggestion to use nlcom to calculate H with population
weighting was the key idea behind writing loevh2_svy.

References

Copas, J. B., & Loeber, R. (1990). Relative improvement over chance (RIOC) for 2×2
tables. British Journal of Mathematical and Statistical Psychology, 43(2),
293–307. https://doi.org/10.1111/j.2044-8317.1990.tb00942.x

Loevinger, J. A. (1947). A systematic approach to the construction and evaluation of
tests of ability. Psychological Monographs, 61(4), i–49.
https://doi.org/10.1037/h0093565