| Title: | Consistent API for Hypothesis Testing |
|---|---|
| Description: | Provides a consistent API for hypothesis testing built on principles from 'Structure and Interpretation of Computer Programs': data abstraction, closure (combining tests yields tests), and higher-order functions (transforming tests). Implements z-tests, Wald tests, likelihood ratio tests, Fisher's method for combining p-values, and multiple testing corrections. Designed for use by other packages that want to wrap their hypothesis tests in a consistent interface. |
| Authors: | Alexander Towell [aut, cre] (ORCID: <https://orcid.org/0000-0001-6443-9897>) |
| Maintainer: | Alexander Towell <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.0.0 |
| Built: | 2026-05-15 08:35:40 UTC |
| Source: | https://github.com/queelius/hypothesize |
Applies a multiple testing correction to a hypothesis test or vector of tests, returning adjusted test object(s).
adjust_pval(x, method = "bonferroni", n = NULL)adjust_pval(x, method = "bonferroni", n = NULL)
x |
A |
method |
Character. Adjustment method (see Details). Default is
|
n |
Integer. Total number of tests in the family. If |
When performing multiple hypothesis tests, the probability of at least one false positive (Type I error) increases. Multiple testing corrections adjust p-values to control error rates across the family of tests.
This function demonstrates the higher-order function pattern: it takes a hypothesis test as input and returns a transformed hypothesis test as output. The adjusted test retains all original properties but with a corrected p-value.
For a single test: a hypothesis_test object of subclass
adjusted_test with the adjusted p-value. For a list of tests: a list
of adjusted test objects.
The returned object contains:
Original test statistic (unchanged)
Adjusted p-value
Original degrees of freedom (unchanged)
The method used
The unadjusted p-value
Number of tests in the family
The method parameter accepts any method supported by stats::p.adjust():
"bonferroni"Multiplies p-values by . Controls
family-wise error rate (FWER). Conservative.
"holm"Step-down Bonferroni. Controls FWER. Less conservative than Bonferroni while maintaining strong control.
"BH" or "fdr"
Benjamini-Hochberg procedure. Controls false discovery rate (FDR). More powerful for large-scale testing.
"hochberg"Step-up procedure. Controls FWER under independence.
"hommel"More powerful than Hochberg but computationally intensive.
"BY"Benjamini-Yekutieli. Controls FDR under arbitrary dependence.
"none"No adjustment (identity transformation).
This function exemplifies transforming hypothesis tests:
adjust_pval : hypothesis_test -> hypothesis_test
The output can be used with all standard generics (pval(), test_stat(),
is_significant_at(), etc.) and can be further composed.
stats::p.adjust() for the underlying adjustment,
fisher_combine() for combining (not adjusting) p-values
# Single test adjustment (must specify n) w <- wald_test(estimate = 2.0, se = 0.8) pval(w) # Original p-value w_adj <- adjust_pval(w, method = "bonferroni", n = 10) pval(w_adj) # Adjusted (multiplied by 10, capped at 1) w_adj$original_pval # Can still access original # Adjusting multiple tests at once tests <- list( wald_test(estimate = 2.5, se = 0.8), wald_test(estimate = 1.2, se = 0.5), wald_test(estimate = 0.8, se = 0.9) ) # BH (FDR) correction - n is inferred from list length adjusted <- adjust_pval(tests, method = "BH") vapply(adjusted, pval, numeric(1)) # Adjusted p-values # Compare methods vapply(tests, pval, numeric(1)) # Original vapply(adjust_pval(tests, method = "bonferroni"), pval, numeric(1)) vapply(adjust_pval(tests, method = "BH"), pval, numeric(1))# Single test adjustment (must specify n) w <- wald_test(estimate = 2.0, se = 0.8) pval(w) # Original p-value w_adj <- adjust_pval(w, method = "bonferroni", n = 10) pval(w_adj) # Adjusted (multiplied by 10, capped at 1) w_adj$original_pval # Can still access original # Adjusting multiple tests at once tests <- list( wald_test(estimate = 2.5, se = 0.8), wald_test(estimate = 1.2, se = 0.5), wald_test(estimate = 0.8, se = 0.9) ) # BH (FDR) correction - n is inferred from list length adjusted <- adjust_pval(tests, method = "BH") vapply(adjusted, pval, numeric(1)) # Adjusted p-values # Compare methods vapply(tests, pval, numeric(1)) # Original vapply(adjust_pval(tests, method = "bonferroni"), pval, numeric(1)) vapply(adjust_pval(tests, method = "BH"), pval, numeric(1))
Negates a hypothesis test by transforming its p-value: .
The complement test rejects when the original test fails to reject.
complement_test(test)complement_test(test)
test |
A |
The complement is the NOT operation in the Boolean algebra of hypothesis
tests. Together with intersection_test() (AND) and union_test() (OR),
it forms a complete algebra where De Morgan's laws hold by construction.
A hypothesis_test object with "complemented_test" prepended
to the class vector. The original class hierarchy is preserved.
The pre-complement p-value
The input test object
If the original test checks "is different from
?" (rejecting when the difference is large), the
complement checks "is close to ?"
(rejecting when the difference is small). This connects to the
Two One-Sided Tests (TOST) procedure used in bioequivalence studies.
Double complement is identity:
complement_test(complement_test(t)) has the same p-value as t
De Morgan's law:
union_test(a, b) = complement_test(intersection_test(complement_test(a), complement_test(b)))
intersection_test(), union_test()
w <- wald_test(estimate = 3.0, se = 1.0) pval(w) # small pval(complement_test(w)) # large # Double complement recovers the original pval(complement_test(complement_test(w))) == pval(w)w <- wald_test(estimate = 3.0, se = 1.0) pval(w) # small pval(complement_test(w)) # large # Double complement recovers the original pval(complement_test(complement_test(w))) == pval(w)
Extracts a confidence interval from a hypothesis test object, exploiting the fundamental duality between hypothesis tests and confidence intervals.
## S3 method for class 'hypothesis_test' confint(object, parm = NULL, level = 0.95, ...) ## S3 method for class 'wald_test' confint(object, parm = NULL, level = 0.95, ...) ## S3 method for class 'z_test' confint(object, parm = NULL, level = 0.95, ...)## S3 method for class 'hypothesis_test' confint(object, parm = NULL, level = 0.95, ...) ## S3 method for class 'wald_test' confint(object, parm = NULL, level = 0.95, ...) ## S3 method for class 'z_test' confint(object, parm = NULL, level = 0.95, ...)
object |
A |
parm |
Ignored (for compatibility with generic). |
level |
Numeric. Confidence level (default 0.95). |
... |
Additional arguments (ignored). |
Hypothesis tests and confidence intervals are two views of the same
underlying inference. For a test of at level
, the confidence interval contains exactly
those values of that would not be rejected.
This duality means:
A 95% CI contains all values where the two-sided test has p > 0.05
The CI boundary is where p = 0.05 exactly
Inverting a test "inverts" it into a confidence set
A named numeric vector with elements lower and upper.
Confidence intervals are currently implemented for:
wald_test: Uses
z_test: Uses
Tests without stored estimates (like lrt or fisher_combined_test)
cannot produce confidence intervals directly.
# Wald test stores estimate and SE, so CI is available w <- wald_test(estimate = 2.5, se = 0.8) confint(w) # 95% CI confint(w, level = 0.99) # 99% CI # The duality: 2.5 is in the CI, and testing H0: theta = 2.5 # would give p = 1 (not rejected) wald_test(estimate = 2.5, se = 0.8, null_value = 2.5) # z-test also supports confint set.seed(1) z <- z_test(rnorm(50, mean = 10, sd = 2), mu0 = 9, sigma = 2) confint(z)# Wald test stores estimate and SE, so CI is available w <- wald_test(estimate = 2.5, se = 0.8) confint(w) # 95% CI confint(w, level = 0.99) # 99% CI # The duality: 2.5 is in the CI, and testing H0: theta = 2.5 # would give p = 1 (not rejected) wald_test(estimate = 2.5, se = 0.8, null_value = 2.5) # z-test also supports confint set.seed(1) z <- z_test(rnorm(50, mean = 10, sd = 2), mu0 = 9, sigma = 2) confint(z)
Extract the degrees of freedom from a hypothesis test
dof(x, ...) ## S3 method for class 'hypothesis_test' dof(x, ...)dof(x, ...) ## S3 method for class 'hypothesis_test' dof(x, ...)
x |
a hypothesis test object |
... |
additional arguments to pass into the method |
Numeric degrees of freedom.
w <- wald_test(estimate = 2.5, se = 0.8) dof(w)w <- wald_test(estimate = 2.5, se = 0.8) dof(w)
Combines p-values from independent hypothesis tests into a single omnibus test using Fisher's method.
fisher_combine(...)fisher_combine(...)
... |
|
Fisher's method is a meta-analytic technique for combining evidence from multiple independent tests of the same hypothesis (or related hypotheses). It demonstrates a key principle: combining hypothesis tests yields a hypothesis test (the closure property).
Given independent p-values , Fisher's
statistic is:
Under the global null hypothesis (all individual nulls are true), this
follows a chi-squared distribution with degrees of freedom.
A hypothesis_test object of subclass fisher_combined_test
containing:
Fisher's chi-squared statistic
P-value from distribution
Degrees of freedom ()
Number of tests combined
Vector of the individual p-values
If is a valid p-value under , then .
Therefore . The sum of independent
chi-squared random variables is also chi-squared with summed degrees of
freedom, giving .
A significant combined p-value indicates that at least one of the individual null hypotheses is likely false, but does not identify which one(s). Fisher's method is sensitive to any deviation from the global null, making it powerful when effects exist but liberal when assumptions are violated.
This function exemplifies the closure property from SICP: the operation
of combining hypothesis tests produces another hypothesis test. The result
can be further combined, adjusted, or analyzed using the same generic
methods (pval(), test_stat(), is_significant_at(), etc.).
adjust_pval() for multiple testing correction (different goal)
# Scenario: Three independent studies test the same drug effect # Study 1: p = 0.08 (trend, not significant) # Study 2: p = 0.12 (not significant) # Study 3: p = 0.04 (significant at 0.05) # Combine using raw p-values combined <- fisher_combine(0.08, 0.12, 0.04) combined is_significant_at(combined, 0.05) # Stronger evidence together # Or combine hypothesis_test objects directly t1 <- wald_test(estimate = 1.5, se = 0.9) t2 <- wald_test(estimate = 0.8, se = 0.5) set.seed(1) t3 <- z_test(rnorm(30, mean = 0.3), mu0 = 0, sigma = 1) fisher_combine(t1, t2, t3) # The result is itself a hypothesis_test, so it composes # (though combining non-independent tests is invalid)# Scenario: Three independent studies test the same drug effect # Study 1: p = 0.08 (trend, not significant) # Study 2: p = 0.12 (not significant) # Study 3: p = 0.04 (significant at 0.05) # Combine using raw p-values combined <- fisher_combine(0.08, 0.12, 0.04) combined is_significant_at(combined, 0.05) # Stronger evidence together # Or combine hypothesis_test objects directly t1 <- wald_test(estimate = 1.5, se = 0.9) t2 <- wald_test(estimate = 0.8, se = 0.5) set.seed(1) t3 <- z_test(rnorm(30, mean = 0.3), mu0 = 0, sigma = 1) fisher_combine(t1, t2, t3) # The result is itself a hypothesis_test, so it composes # (though combining non-independent tests is invalid)
Constructs a hypothesis test object that implements the hypothesize API.
This is the base constructor used by specific test functions like lrt(),
wald_test(), and z_test().
hypothesis_test(stat, p.value, dof, superclasses = NULL, ...)hypothesis_test(stat, p.value, dof, superclasses = NULL, ...)
stat |
Numeric. The test statistic. |
p.value |
Numeric. The p-value (probability of observing a test
statistic as extreme as |
dof |
Numeric. Degrees of freedom. Use |
superclasses |
Character vector. Additional S3 classes to prepend,
creating a subclass of |
... |
Additional named arguments stored in the object for introspection (e.g., input data, null hypothesis value). |
The hypothesis_test object is the fundamental data abstraction in this
package. It represents the result of a statistical hypothesis test and
provides a consistent interface for extracting results.
This design follows the principle of data abstraction: the internal
representation (a list) is hidden behind accessor functions (pval(),
test_stat(), dof(), is_significant_at()).
An S3 object of class hypothesis_test (and any superclasses),
which is a list containing at least stat, p.value, dof, plus
any additional arguments passed via ....
To create a new type of hypothesis test:
Create a constructor function that computes the test statistic and p-value.
Call hypothesis_test() with appropriate superclasses.
The new test automatically inherits all generic methods.
Example:
my_test <- function(data, null_value) {
stat <- compute_statistic(data, null_value)
p.value <- compute_pvalue(stat)
hypothesis_test(
stat = stat, p.value = p.value, dof = length(data) - 1,
superclasses = "my_test",
data = data, null_value = null_value
)
}
lrt(), wald_test(), z_test() for specific test constructors;
pval(), test_stat(), dof(), is_significant_at() for accessors
# Direct construction (usually use specific constructors instead) test <- hypothesis_test(stat = 1.96, p.value = 0.05, dof = 1) test # Extract components using the API pval(test) test_stat(test) dof(test) is_significant_at(test, 0.05) # Create a custom test type custom <- hypothesis_test( stat = 2.5, p.value = 0.01, dof = 10, superclasses = "custom_test", method = "bootstrap", n_replicates = 1000 ) class(custom) # c("custom_test", "hypothesis_test") custom$method # "bootstrap"# Direct construction (usually use specific constructors instead) test <- hypothesis_test(stat = 1.96, p.value = 0.05, dof = 1) test # Extract components using the API pval(test) test_stat(test) dof(test) is_significant_at(test, 0.05) # Create a custom test type custom <- hypothesis_test( stat = 2.5, p.value = 0.01, dof = 10, superclasses = "custom_test", method = "bootstrap", n_replicates = 1000 ) class(custom) # c("custom_test", "hypothesis_test") custom$method # "bootstrap"
Combines hypothesis tests using the AND rule: rejects only when ALL component tests reject.
intersection_test(...)intersection_test(...)
... |
|
The p-value is –the intersection rejects
at level if and only if every component p-value is below
.
This is the intersection-union test (IUT; Berger, 1982). No multiplicity correction is needed –the max is inherently conservative.
A hypothesis_test of subclass intersection_test. The stat
and dof fields are NA (no natural test statistic for p-value
aggregation). Metadata fields: n_tests and component_pvals.
Bioequivalence requires showing a drug's effect is both "not too low" AND "not too high". This is naturally an intersection test.
Together with complement_test() (NOT) and union_test() (OR), this
forms a complete Boolean algebra. De Morgan's law holds by construction:
union_test(a, b) = complement_test(intersection_test(complement_test(a), complement_test(b)))
union_test(), complement_test(), fisher_combine()
# All must reject for intersection to reject intersection_test(0.01, 0.03, 0.04) # significant intersection_test(0.01, 0.80) # not significant# All must reject for intersection to reject intersection_test(0.01, 0.03, 0.04) # significant intersection_test(0.01, 0.80) # not significant
Takes a test constructor function and returns the confidence set: the set
of null values that are not rejected at level .
invert_test(test_fn, grid, alpha = 0.05)invert_test(test_fn, grid, alpha = 0.05)
test_fn |
A function that takes a single numeric argument (the
hypothesized null value) and returns a |
grid |
Numeric vector of candidate null values to test. |
alpha |
Numeric. Significance level (default 0.05). The confidence
level is |
Hypothesis tests and confidence sets are dual: a
confidence set contains exactly those parameter values
for which the test of would not reject at
level . This function makes that duality operational.
invert_test is the most general confidence set constructor in the
package. Any test –including user-defined tests –can be inverted. The
specialized confint() methods for wald_test and z_test give exact
analytical intervals; invert_test gives numerical intervals for
arbitrary tests at the cost of a grid search.
An S3 object of class confidence_set containing:
Numeric vector of non-rejected null values
The significance level used
The confidence level ()
The input test function
The input grid
This function takes a function as input (test_fn) and returns a
structured result. It demonstrates the power of the hypothesis_test
abstraction: because all tests implement the same interface (pval()),
invert_test can work with any test without knowing its internals.
confint.wald_test(), confint.z_test() for analytical CIs
# Invert a Wald test to get a confidence interval cs <- invert_test( test_fn = function(theta) wald_test(estimate = 2.5, se = 0.8, null_value = theta), grid = seq(0, 5, by = 0.01) ) cs lower(cs) upper(cs) # Compare with the analytical confint (should agree up to grid resolution) confint(wald_test(estimate = 2.5, se = 0.8)) # Invert ANY user-defined test --no special support needed my_test <- function(theta) { stat <- (5.0 - theta)^2 / 2 hypothesis_test(stat = stat, p.value = pchisq(stat, df = 1, lower.tail = FALSE), dof = 1) } invert_test(my_test, grid = seq(0, 10, by = 0.01))# Invert a Wald test to get a confidence interval cs <- invert_test( test_fn = function(theta) wald_test(estimate = 2.5, se = 0.8, null_value = theta), grid = seq(0, 5, by = 0.01) ) cs lower(cs) upper(cs) # Compare with the analytical confint (should agree up to grid resolution) confint(wald_test(estimate = 2.5, se = 0.8)) # Invert ANY user-defined test --no special support needed my_test <- function(theta) { stat <- (5.0 - theta)^2 / 2 hypothesis_test(stat = stat, p.value = pchisq(stat, df = 1, lower.tail = FALSE), dof = 1) } invert_test(my_test, grid = seq(0, 10, by = 0.01))
Check if a hypothesis test is significant at a given level
is_significant_at(x, alpha, ...) ## S3 method for class 'hypothesis_test' is_significant_at(x, alpha, ...)is_significant_at(x, alpha, ...) ## S3 method for class 'hypothesis_test' is_significant_at(x, alpha, ...)
x |
a hypothesis test object |
alpha |
significance level |
... |
additional arguments passed to methods |
Logical indicating whether the test is significant at level
alpha.
w <- wald_test(estimate = 2.5, se = 0.8) is_significant_at(w, 0.05)w <- wald_test(estimate = 2.5, se = 0.8) is_significant_at(w, 0.05)
Extract the lower bound of a confidence set
lower(x, ...) ## S3 method for class 'confidence_set' lower(x, ...)lower(x, ...) ## S3 method for class 'confidence_set' lower(x, ...)
x |
a confidence_set object |
... |
additional arguments (ignored) |
Named numeric scalar with the lower bound.
cs <- invert_test( function(theta) wald_test(estimate = 5, se = 1.2, null_value = theta), grid = seq(0, 10, by = 0.1) ) lower(cs)cs <- invert_test( function(theta) wald_test(estimate = 5, se = 1.2, null_value = theta), grid = seq(0, 10, by = 0.1) ) lower(cs)
Computes the likelihood ratio test (LRT) statistic and p-value for comparing nested models.
lrt(null_loglik, alt_loglik, dof = NULL)lrt(null_loglik, alt_loglik, dof = NULL)
null_loglik |
The log-likelihood under the null (simpler) model.
Either a numeric scalar or a |
alt_loglik |
The log-likelihood under the alternative (more complex)
model. Either a numeric scalar or a |
dof |
Positive integer. Degrees of freedom, typically the difference
in the number of free parameters between models. When both
|
The likelihood ratio test is a fundamental method for comparing nested
statistical models. Given a null model (simpler, fewer parameters)
nested within an alternative model (more complex), the LRT tests
whether the additional complexity of is justified by the data.
The test statistic is:
where and are the maximized log-likelihoods under
the null and alternative models, respectively.
Under and regularity conditions, is asymptotically
chi-squared distributed with degrees of freedom equal to the difference in
the number of free parameters between models.
A hypothesis_test object of subclass likelihood_ratio_test
containing:
The LRT statistic
P-value from chi-squared distribution with dof degrees
of freedom
The degrees of freedom
The input null model log-likelihood (numeric)
The input alternative model log-likelihood (numeric)
The null model must be nested within the alternative model (i.e., obtainable by constraining parameters of the alternative).
Both likelihoods must be computed from the same dataset.
Standard regularity conditions for asymptotic chi-squared distribution must hold (true parameter not on boundary, etc.).
The LRT is one of the "holy trinity" of likelihood-based tests, alongside
the Wald test (wald_test()) and the score (Lagrange multiplier) test.
All three are asymptotically equivalent under , but the LRT is
often preferred because it is invariant to reparameterization.
wald_test() for testing individual parameters,
stats::logLik() for extracting log-likelihoods from fitted models
# Comparing nested regression models with raw log-likelihoods # Null model: y ~ x1 (log-likelihood = -150) # Alt model: y ~ x1 + x2 + x3 (log-likelihood = -140) # Difference: 3 additional parameters test <- lrt(null_loglik = -150, alt_loglik = -140, dof = 3) test # Is the more complex model significantly better? is_significant_at(test, 0.05) # Extract the test statistic (should be 20) test_stat(test) # Using logLik objects (dof computed automatically) ll_null <- structure(-150, df = 2, nobs = 100, class = "logLik") ll_alt <- structure(-140, df = 5, nobs = 100, class = "logLik") lrt(ll_null, ll_alt) # dof = 5 - 2 = 3 # With real models (any model supporting stats::logLik) set.seed(42) x <- 1:50 y <- 2 + 3 * x + rnorm(50) m0 <- lm(y ~ 1) m1 <- lm(y ~ x) lrt(logLik(m0), logLik(m1))# Comparing nested regression models with raw log-likelihoods # Null model: y ~ x1 (log-likelihood = -150) # Alt model: y ~ x1 + x2 + x3 (log-likelihood = -140) # Difference: 3 additional parameters test <- lrt(null_loglik = -150, alt_loglik = -140, dof = 3) test # Is the more complex model significantly better? is_significant_at(test, 0.05) # Extract the test statistic (should be 20) test_stat(test) # Using logLik objects (dof computed automatically) ll_null <- structure(-150, df = 2, nobs = 100, class = "logLik") ll_alt <- structure(-140, df = 5, nobs = 100, class = "logLik") lrt(ll_null, ll_alt) # dof = 5 - 2 = 3 # With real models (any model supporting stats::logLik) set.seed(42) x <- 1:50 y <- 2 + 3 * x + rnorm(50) m0 <- lm(y ~ 1) m1 <- lm(y ~ x) lrt(logLik(m0), logLik(m1))
Print method for confidence sets
## S3 method for class 'confidence_set' print(x, ...)## S3 method for class 'confidence_set' print(x, ...)
x |
a confidence_set object |
... |
additional arguments (ignored) |
Returns x invisibly.
cs <- invert_test( function(theta) wald_test(estimate = 5, se = 1.2, null_value = theta), grid = seq(0, 10, by = 0.1) ) print(cs)cs <- invert_test( function(theta) wald_test(estimate = 5, se = 1.2, null_value = theta), grid = seq(0, 10, by = 0.1) ) print(cs)
Print method for hypothesis tests
## S3 method for class 'hypothesis_test' print(x, ...)## S3 method for class 'hypothesis_test' print(x, ...)
x |
a hypothesis test |
... |
additional arguments |
Returns x invisibly.
w <- wald_test(estimate = 2.5, se = 0.8) print(w)w <- wald_test(estimate = 2.5, se = 0.8) print(w)
Extract the p-value from a hypothesis test
pval(x, ...) ## S3 method for class 'hypothesis_test' pval(x, ...)pval(x, ...) ## S3 method for class 'hypothesis_test' pval(x, ...)
x |
a hypothesis test object |
... |
additional arguments to pass into the method |
Numeric p-value.
w <- wald_test(estimate = 2.5, se = 0.8) pval(w)w <- wald_test(estimate = 2.5, se = 0.8) pval(w)
Computes the score test statistic and p-value for testing whether a parameter equals a hypothesized value, using the score function and Fisher information evaluated at the null.
score_test(score, fisher_info, null_value = NULL)score_test(score, fisher_info, null_value = NULL)
score |
Numeric scalar or vector. The score function
|
fisher_info |
Numeric scalar or matrix. The Fisher information
|
null_value |
Optional. The null hypothesis value, stored for reference but not used in computation. |
The score test is one of the "holy trinity" of likelihood-based tests,
alongside the Wald test (wald_test()) and the likelihood ratio test
(lrt()). All three are asymptotically equivalent under , but
they differ in what they require:
Wald test: Needs the MLE and its standard error – requires fitting the alternative model.
LRT: Needs maximized log-likelihoods under both models – requires fitting both.
Score test: Needs only the score and information at
– requires fitting only the null model.
This makes the score test computationally attractive when the null model is simple but the alternative is expensive to fit.
For the univariate case:
For the multivariate case with parameters:
The function detects scalar vs. vector input and dispatches accordingly.
A hypothesis_test object of subclass score_test containing:
The score statistic
P-value from chi-squared distribution
Degrees of freedom (1 for univariate, for multivariate)
The input score value(s)
The input Fisher information
The input null hypothesis value (if provided)
wald_test(), lrt() for the other members of the trinity
# Univariate score test score_test(score = 2, fisher_info = 2) # Compare the trinity on the same problem score_test(score = 2, fisher_info = 2) wald_test(estimate = 6, se = sqrt(6/10), null_value = 5) # Multivariate score_test(score = c(1, 2), fisher_info = diag(c(1, 1)))# Univariate score test score_test(score = 2, fisher_info = 2) # Compare the trinity on the same problem score_test(score = 2, fisher_info = 2) wald_test(estimate = 6, se = sqrt(6/10), null_value = 5) # Multivariate score_test(score = c(1, 2), fisher_info = diag(c(1, 1)))
Extract the test statistic from a hypothesis test
test_stat(x, ...) ## S3 method for class 'hypothesis_test' test_stat(x, ...)test_stat(x, ...) ## S3 method for class 'hypothesis_test' test_stat(x, ...)
x |
a hypothesis test object |
... |
additional arguments to pass into the method |
Numeric test statistic.
w <- wald_test(estimate = 2.5, se = 0.8) test_stat(w)w <- wald_test(estimate = 2.5, se = 0.8) test_stat(w)
Combines hypothesis tests using the OR rule: rejects when ANY component test rejects.
union_test(...)union_test(...)
... |
|
The union test implements the OR operation in the Boolean algebra of hypothesis tests. It is defined via De Morgan's law:
This is not an approximation — it is the definition. The implementation
is literally the De Morgan law applied to complement_test() and
intersection_test().
The resulting p-value is .
A hypothesis_test object of subclass union_test. The stat
and dof fields are NA (no natural test statistic for p-value
aggregation). Metadata fields: n_tests and component_pvals.
The uncorrected is anti-conservative when testing multiple
hypotheses. If you need to control the family-wise error rate, apply
adjust_pval() to the component tests before combining, or use
fisher_combine() which pools evidence differently.
The raw union test is appropriate when you genuinely want to reject a global null if any sub-hypothesis is false, without multiplicity correction — for example, in screening or exploratory analysis.
Together with intersection_test() (AND) and complement_test() (NOT),
this forms a complete Boolean algebra over hypothesis tests:
AND: intersection_test() — reject when all reject
OR: union_test() — reject when any rejects
NOT: complement_test() — reject when original fails to reject
De Morgan's laws hold by construction:
union(a, b) = NOT(AND(NOT(a), NOT(b)))
intersection(a, b) = NOT(OR(NOT(a), NOT(b)))
intersection_test() for AND, complement_test() for NOT,
fisher_combine() for evidence pooling
# Screen three biomarkers: reject if ANY is significant t1 <- wald_test(estimate = 0.5, se = 0.3) t2 <- wald_test(estimate = 2.1, se = 0.8) t3 <- wald_test(estimate = 1.0, se = 0.4) union_test(t1, t2, t3) # De Morgan's law in action a <- wald_test(estimate = 2.0, se = 1.0) b <- wald_test(estimate = 1.5, se = 0.8) # These are equivalent: pval(union_test(a, b)) pval(complement_test(intersection_test(complement_test(a), complement_test(b))))# Screen three biomarkers: reject if ANY is significant t1 <- wald_test(estimate = 0.5, se = 0.3) t2 <- wald_test(estimate = 2.1, se = 0.8) t3 <- wald_test(estimate = 1.0, se = 0.4) union_test(t1, t2, t3) # De Morgan's law in action a <- wald_test(estimate = 2.0, se = 1.0) b <- wald_test(estimate = 1.5, se = 0.8) # These are equivalent: pval(union_test(a, b)) pval(complement_test(intersection_test(complement_test(a), complement_test(b))))
Extract the upper bound of a confidence set
upper(x, ...) ## S3 method for class 'confidence_set' upper(x, ...)upper(x, ...) ## S3 method for class 'confidence_set' upper(x, ...)
x |
a confidence_set object |
... |
additional arguments (ignored) |
Named numeric scalar with the upper bound.
cs <- invert_test( function(theta) wald_test(estimate = 5, se = 1.2, null_value = theta), grid = seq(0, 10, by = 0.1) ) upper(cs)cs <- invert_test( function(theta) wald_test(estimate = 5, se = 1.2, null_value = theta), grid = seq(0, 10, by = 0.1) ) upper(cs)
Computes the Wald test statistic and p-value for testing whether a parameter (or parameter vector) equals a hypothesized value.
wald_test(estimate, se = NULL, vcov = NULL, null_value = 0)wald_test(estimate, se = NULL, vcov = NULL, null_value = 0)
estimate |
Numeric. The estimated parameter value |
se |
Numeric. Standard error of the estimate for the univariate case.
Mutually exclusive with |
vcov |
Numeric matrix. Variance-covariance matrix for the multivariate
case. Mutually exclusive with |
null_value |
Numeric. The hypothesized value |
The Wald test is a fundamental tool in statistical inference, used to test
the null hypothesis against the alternative
.
Univariate case (when se is provided):
The test is based on the asymptotic normality of maximum likelihood
estimators. Under regularity conditions, if is the MLE
with standard error , then:
The Wald statistic is reported as , which follows
a chi-squared distribution with 1 degree of freedom under .
The z-score is stored in the returned object for reference.
Multivariate case (when vcov is provided):
For a -dimensional parameter vector with
variance-covariance matrix , the Wald statistic is:
The p-value is computed as .
A hypothesis_test object of subclass wald_test containing:
The Wald statistic
Two-sided p-value from chi-squared distribution
Degrees of freedom (1 for univariate, for multivariate)
The z-score (univariate case only)
The input estimate
The input standard error (univariate case only)
The input variance-covariance matrix (multivariate case only)
The input null hypothesis value
The Wald test is one of the "holy trinity" of likelihood-based tests,
alongside the likelihood ratio test (lrt()) and the score test.
For large samples, all three are asymptotically equivalent, but they
can differ substantially in finite samples.
lrt() for likelihood ratio tests, z_test() for testing means
# Univariate: test whether a regression coefficient differs from zero w <- wald_test(estimate = 2.5, se = 0.8, null_value = 0) w # Extract components test_stat(w) # Wald statistic (chi-squared) w$z # z-score pval(w) # p-value is_significant_at(w, 0.05) # Test against a non-zero null wald_test(estimate = 2.5, se = 0.8, null_value = 2) # Multivariate: test two parameters jointly est <- c(2.0, 3.0) V <- matrix(c(1.0, 0.3, 0.3, 1.0), 2, 2) w_mv <- wald_test(estimate = est, vcov = V, null_value = c(0, 0)) test_stat(w_mv) dof(w_mv) # 2 pval(w_mv)# Univariate: test whether a regression coefficient differs from zero w <- wald_test(estimate = 2.5, se = 0.8, null_value = 0) w # Extract components test_stat(w) # Wald statistic (chi-squared) w$z # z-score pval(w) # p-value is_significant_at(w, 0.05) # Test against a non-zero null wald_test(estimate = 2.5, se = 0.8, null_value = 2) # Multivariate: test two parameters jointly est <- c(2.0, 3.0) V <- matrix(c(1.0, 0.3, 0.3, 1.0), 2, 2) w_mv <- wald_test(estimate = est, vcov = V, null_value = c(0, 0)) test_stat(w_mv) dof(w_mv) # 2 pval(w_mv)
Tests whether a population mean equals a hypothesized value when the population standard deviation is known.
z_test(x, mu0 = 0, sigma, alternative = c("two.sided", "less", "greater"))z_test(x, mu0 = 0, sigma, alternative = c("two.sided", "less", "greater"))
x |
Numeric vector. The sample data. |
mu0 |
Numeric. The hypothesized population mean under |
sigma |
Numeric. The known population standard deviation. |
alternative |
Character. Type of alternative hypothesis:
|
The z-test is one of the simplest and most fundamental hypothesis tests.
It tests against various alternatives when the
population standard deviation is known.
Given a sample , the test statistic is:
Under , this follows a standard normal distribution. The p-value
depends on the alternative hypothesis:
Two-sided ():
Less ():
Greater ():
A hypothesis_test object of subclass z_test containing:
The z-statistic
The p-value for the specified alternative
Degrees of freedom (Inf for normal distribution)
The alternative hypothesis used
The hypothesized mean
The sample mean
The known population standard deviation
The sample size
The z-test requires knowing the population standard deviation, which is
rare in practice. When is unknown and estimated from data,
use a t-test instead. The z-test is primarily pedagogical, illustrating
the logic of hypothesis testing in its simplest form.
The z-test is a special case of the Wald test (wald_test()) where the
parameter is a mean and the standard error is .
The Wald test generalizes this to any asymptotically normal estimator.
wald_test() for the general case with estimated standard errors
# A light bulb manufacturer claims bulbs last 1000 hours on average. # We test 50 bulbs and know from historical data that sigma = 100 hours. lifetimes <- c(980, 1020, 950, 1010, 990, 1005, 970, 1030, 985, 995, 1000, 1015, 960, 1025, 975, 1008, 992, 1012, 988, 1002, 978, 1018, 965, 1022, 982, 1005, 995, 1010, 972, 1028, 990, 1000, 985, 1015, 968, 1020, 980, 1008, 992, 1012, 975, 1018, 962, 1025, 985, 1002, 988, 1010, 978, 1020) # Two-sided test: H0: mu = 1000 vs H1: mu != 1000 z_test(lifetimes, mu0 = 1000, sigma = 100) # One-sided test: are bulbs lasting less than claimed? z_test(lifetimes, mu0 = 1000, sigma = 100, alternative = "less")# A light bulb manufacturer claims bulbs last 1000 hours on average. # We test 50 bulbs and know from historical data that sigma = 100 hours. lifetimes <- c(980, 1020, 950, 1010, 990, 1005, 970, 1030, 985, 995, 1000, 1015, 960, 1025, 975, 1008, 992, 1012, 988, 1002, 978, 1018, 965, 1022, 982, 1005, 995, 1010, 972, 1028, 990, 1000, 985, 1015, 968, 1020, 980, 1008, 992, 1012, 975, 1018, 962, 1025, 985, 1002, 988, 1010, 978, 1020) # Two-sided test: H0: mu = 1000 vs H1: mu != 1000 z_test(lifetimes, mu0 = 1000, sigma = 100) # One-sided test: are bulbs lasting less than claimed? z_test(lifetimes, mu0 = 1000, sigma = 100, alternative = "less")