The function accepts either a vector of pre-computed pd values or
a matrix of posterior draws, from which pd values are computed
internally. Both direction-agnostic and directional tests are supported:
the direction argument controls which formulation is applied per
hypothesis. The adjustment is governed by the per-test prior probability
that an individual effect is null, \(\pi_0\) (argument pi0).
Equivalently, one may supply the global null \(\Pr(H_0)\) (argument
p0), the prior that all \(m\) hypotheses are null, from which
\(\pi_0 = \Pr(H_0)^{1/m}\) is recovered under independence.
Usage
pd.adjust(
pd = NULL,
draws = NULL,
p0 = NULL,
pi0 = NULL,
null.value = 0,
direction = NULL,
order = FALSE,
threshold = 0.975,
alpha = NULL,
fwer = FALSE
)Arguments
- pd
Numeric vector of pd values. For direction-agnostic tests, values must be in \([0.5, 1]\). For directional tests, values are raw one-sided probabilities in \([0, 1]\). Ignored if
drawsis supplied.- draws
Optional matrix or data frame of posterior draws (columns = parameters). If provided, pd values are computed automatically from the draws according to
directionandnull.value.- p0
Numeric scalar in \((0, 1)\). The prior probability that all hypotheses are null simultaneously (the global null, \(\Pr(H_0)\)). Supply either
pi0orp0, not both. Whenp0is given, the per-test prior is derived under independence as \(\pi_0 = \Pr(H_0)^{1/m}\).- pi0
Numeric scalar in \((0, 1)\). The per-test prior probability that an individual effect is null, applied to every test. Supply either
pi0orp0, not both.- null.value
Numeric scalar or vector. The null (reference) value against which the posterior is evaluated, specified on the scale of the posterior. A single scalar applies the same null to all parameters; a vector of length
ncol(draws)assigns a distinct null to each parameter. Ignored whenpdis supplied directly. Defaults to0.- direction
Character vector of
"greater","less", or"two.sided"(orNULL). Specifies the testing mode for each hypothesis:"greater"for a positive directional test (\(\Pr(\theta > \theta_\text{null})\)),"less"for a negative directional test (\(\Pr(\theta < \theta_\text{null})\)), and"two.sided"for direction-agnostic testing (maximum over both sides). A scalar is recycled to match the number of parameters; a mixed vector allows different modes across hypotheses. Defaults toNULL(direction-agnostic for all parameters).- order
Logical. If
TRUE, the returned rows are ordered by decreasingpd(strongest directional evidence first). IfFALSE(the default), rows are returned in input order, so the output stays aligned with the columns ofdraws(or the elements ofpd).- threshold
Numeric scalar in \((0.5, 1)\). The decision cutoff applied to pd (default
0.975, a two-sided per-test \(\alpha = 0.05\)). It does not changepd.adj; it is used only to report the equivalent adjusted threshold on the raw pd, since rejecting \(pd_{adj} > c\) is identical to rejecting raw \(pd > c^*\) with \(c^* = c\,\pi_0 / ((1-\pi_0)(1-c) + c\,\pi_0)\).- alpha
Optional numeric scalar in \((0, 1)\). A convenience parameterization of
thresholdon the error scale: when supplied, the two-sided cutoff is set to \(c = 1 - \alpha/2\) (soalpha = 0.05givesthreshold = 0.975), overriding anythresholdvalue.- fwer
Logical (default
FALSE). IfTRUE, the output also reports a family-wise cutoff on the raw pd that controls the prior-averaged FWER at the nominal level over the \(m\) tests, given \(\pi_0\). Standard corrections (e.g. Bonferroni) implicitly assume every test is null (\(\pi_0 = 1\)); here the affordable per-test rate is \([1 - (1 - \alpha)^{1/m}] / \pi_0\), relaxing the standard correction by \(1/\pi_0\) and reducing to it as \(\pi_0 \to 1\). This is a prior-averaged (Bayesian) FWER under the assumed \(\pi_0\), not strong frequentist control.
Value
An object of class pd_adjust (a data.frame with a
dedicated print.pd_adjust method), with one row per
hypothesis. Columns: pd (values used in the adjustment),
pd.adj (adjusted values), pi0 (per-test null prior
\(\pi_0\)), and m (number of tests). When
order = TRUE the rows are sorted by decreasing pd. For
direction-agnostic tests, both pd and pd.adj are bounded in
\([0.5, 1]\); for directional tests, both are on \([0, 1]\), with values
below \(0.5\) indicating that the data (and the adjustment) favoured the
opposite direction. When draws are supplied, the output additionally
includes median.est (posterior median per parameter), null.value,
and direction. The print method summarises the constant
quantities (pi0, m) in a header and displays the per-test
table; the columns themselves remain available for programmatic access.
The object also carries, as attributes, the decision cutoff threshold
(\(c\)), the equivalent adjusted cutoff on the raw pd
threshold.adj (\(c^*\)), and the corresponding two-sided per-test
Type I rates alpha.nominal (\(2(1-c)\)) and alpha.eff
(\(2(1-c^*)\)); retrieve them with, e.g., attr(x, "threshold.adj").
The cutoffs threshold.adj and threshold.fwer are exact
transformations of the decision rule, but the reported per-test rates
(alpha.eff, and the fwer calibration) use the identity
\(\alpha = 2(1 - c)\), which holds only for a diffuse within-model prior or
large \(n\) (when the null pd is approximately uniform on
\([0.5, 1]\)). Under an informative prior the true per-test rate is
smaller, so these rates are conservative and threshold.fwer controls
the FWER at or below the stated level.
Details
The adjustment follows from Bayes' theorem. Given a per-hypothesis prior \(\pi_0\) and its complement \(\pi_1 = 1 - \pi_0\), the adjusted pd is: $$ pd_{adj} = \frac{pd \pi_1}{pd \pi_1 + (1-pd)\pi_0} $$
Because the prior is conservative (\(\pi_0 > \pi_1\)), the adjustment always shrinks pd toward its lower bound.
Direction-agnostic tests ("two.sided"): pd is
defined as \(\max\!\big(\Pr(\hat\theta > \theta_\text{null}),\,
\Pr(\hat\theta < \theta_\text{null})\big)\) and is bounded in
\([0.5, 1]\) by construction. \(pd_{adj}\) is also floored at
\(0.5\), so the adjustment produces shrinkage toward \(0.5\).
Directional tests ("greater" or "less"): pd
is the raw posterior probability mass on the predicted side,
\(\Pr(\hat\theta > \theta_\text{null})\) or
\(\Pr(\hat\theta < \theta_\text{null})\), and is defined on \([0, 1]\).
Values of pd below \(0.5\) indicate that the posterior is
concentrated opposite to the predicted direction; the adjustment will
further shrink such values toward \(0\), reflecting the combined weight
of the data and the conservative prior against the hypothesis.
Mixed use of directional and direction-agnostic tests within the same call
is supported: each element of direction is handled independently,
and the same prior-odds adjustment is applied uniformly across all
hypotheses regardless of their directionality.
pd.adjust returns the per-test correction only (the marginal pd
and its adjusted value pd.adj). Family-wise summaries across hypotheses,
the cumulative joint statement and simultaneous credible intervals, are
provided separately by joint.
References
Westfall, P. H., Johnson, W. O., & Utts, J. M. (1997). A bayesian perspective on the bonferroni adjustment. Biometrika, 84(2), 419–427.
Jeffreys, H. (1938). Significance tests when several degrees of freedom arise simultaneously. Proceedings of the Royal Society of London. Series A. Mathematical and Physical Sciences, 165(921), 161–198. https://doi.org/10.1098/rspa.1938.0052
Storey, J. D. (2003). The positive false discovery rate: A bayesian interpretation and the q-value. The Annals of Statistics, 31(6), 2013–2035. https://doi.org/10.1214/aos/1074290335
Scott, J. G., & Berger, J. O. (2006). An exploration of aspects of bayesian multiple testing. Journal of Statistical Planning and Inference, 136(7), 2144–2162.
Scott, J. G., & Berger, J. O. (2010). Bayes and empirical-Bayes multiplicity adjustment in the variable- selection problem. The Annals of Statistics, 38(5), 2587–2619. https://doi.org/10.1214/10-AOS792
Examples
# From a vector of pd values
pd_values <- c(H1 = 0.999, H2 = 0.946, H3 = 0.813, H4 = 0.763,
H5 = 0.891, H6 = 0.987)
pd.adjust(pd = pd_values, p0 = 0.4)
#> Warning: some pd.adj have been floored to 0.5.
#> Prior-odds adjusted probability of direction
#> Tests: 6 | pi0 = 0.8584
#> Decision: raw pd > 0.9958 (adjusted from 0.975); effective per-test alpha
#> 0.00843 (from 0.05)
#>
#>
#> pd pd.adj direction
#> H1 0.999 0.9940 two.sided
#> H2 0.946 0.7430 two.sided
#> H3 0.813 0.5000 two.sided
#> H4 0.763 0.5000 two.sided
#> H5 0.891 0.5742 two.sided
#> H6 0.987 0.9261 two.sided
# Equivalent call specifying the per-test prior pi0 directly
pd.adjust(pd = pd_values, pi0 = 0.4^(1/6))
#> Warning: some pd.adj have been floored to 0.5.
#> Prior-odds adjusted probability of direction
#> Tests: 6 | pi0 = 0.8584
#> Decision: raw pd > 0.9958 (adjusted from 0.975); effective per-test alpha
#> 0.00843 (from 0.05)
#>
#>
#> pd pd.adj direction
#> H1 0.999 0.9940 two.sided
#> H2 0.946 0.7430 two.sided
#> H3 0.813 0.5000 two.sided
#> H4 0.763 0.5000 two.sided
#> H5 0.891 0.5742 two.sided
#> H6 0.987 0.9261 two.sided
# Simulate draws
Sigma <- matrix(0, nrow = 6, ncol = 6); diag(Sigma) <- 1
mu <- c(1, -0.1, 0.8, 0, 2, 3)
draws <- MASS::mvrnorm(n = 4000, mu = mu, Sigma = Sigma)
colnames(draws) <- c("H1", "H2", "H3", "H4", "H5", "H6")
# Per-test correction only (default)
pd.adjust(draws = draws, p0 = 0.4, null.value = 0)
#> Warning: some pd.adj have been floored to 0.5.
#> Prior-odds adjusted probability of direction
#> Tests: 6 | pi0 = 0.8584
#> Decision: raw pd > 0.9958 (adjusted from 0.975); effective per-test alpha
#> 0.00843 (from 0.05)
#>
#>
#> median.est null.value pd pd.adj direction
#> H1 0.9860 0 0.8358 0.5000 two.sided
#> H2 -0.1175 0 0.5350 0.5000 two.sided
#> H3 0.7761 0 0.7822 0.5000 two.sided
#> H4 -0.0089 0 0.5030 0.5000 two.sided
#> H5 1.9500 0 0.9762 0.8715 two.sided
#> H6 2.9866 0 0.9995 0.9970 two.sided
# Order the rows by strength of evidence
pd.adjust(draws = draws, p0 = 0.4, null.value = 0, order = TRUE)
#> Warning: some pd.adj have been floored to 0.5.
#> Prior-odds adjusted probability of direction
#> Tests: 6 | pi0 = 0.8584
#> Decision: raw pd > 0.9958 (adjusted from 0.975); effective per-test alpha
#> 0.00843 (from 0.05)
#>
#>
#> median.est null.value pd pd.adj direction
#> H6 2.9866 0 0.9995 0.9970 two.sided
#> H5 1.9500 0 0.9762 0.8715 two.sided
#> H1 0.9860 0 0.8358 0.5000 two.sided
#> H3 0.7761 0 0.7822 0.5000 two.sided
#> H2 -0.1175 0 0.5350 0.5000 two.sided
#> H4 -0.0089 0 0.5030 0.5000 two.sided
# Family-wise summaries are obtained separately with joint()
joint(draws, null.value = 0) # cumulative joint statement
#> median.est null.value pd direction joint_cum
#> H6 2.9866 0 0.9995 two.sided 0.9995
#> H5 1.9500 0 0.9762 two.sided 0.9758
#> H1 0.9860 0 0.8358 two.sided 0.8155
#> H3 0.7761 0 0.7822 two.sided 0.6398
#> H2 -0.1175 0 0.5350 two.sided 0.3420
#> H4 -0.0089 0 0.5030 two.sided 0.1735
joint(draws, interval = TRUE) # simultaneous credible intervals
#> lower est upper prob
#> H1 -1.5133941 0.985996121 3.595471 0.95
#> H2 -2.8220596 -0.117524024 2.471335 0.95
#> H3 -1.8565114 0.776129478 3.336798 0.95
#> H4 -2.5959143 -0.008943627 2.611432 0.95
#> H5 -0.7100979 1.950044477 4.707048 0.95
#> H6 0.4556620 2.986561857 5.535382 0.95
# Mix of directional and agnostic tests with parameter-specific nulls
pd.adjust(draws = draws, p0 = 0.2, null.value = c(0.2, 0, 0.2, 0, 0.5, 0.5),
direction = c("greater", "two.sided", "greater",
"two.sided", "greater", "greater"))
#> Warning: some pd.adj have been floored to 0.5.
#> Prior-odds adjusted probability of direction
#> Tests: 6 | pi0 = 0.7647
#> Decision: raw pd > 0.9922 (adjusted from 0.975); effective per-test alpha
#> 0.0157 (from 0.05)
#>
#>
#> median.est null.value pd pd.adj direction
#> H1 0.9860 0.2 0.7820 0.5246 greater
#> H2 -0.1175 0.0 0.5350 0.5000 two.sided
#> H3 0.7761 0.2 0.7230 0.4454 greater
#> H4 -0.0089 0.0 0.5030 0.5000 two.sided
#> H5 1.9500 0.5 0.9285 0.7998 greater
#> H6 2.9866 0.5 0.9948 0.9831 greater