Skip to content

Alpha Spending Function

online_fdr.spending.alpha_spending.AlphaSpending

Bases: AbstractSequentialTest

Alpha Spending Function for sequential hypothesis testing with flexible interim analyses.

The alpha spending function approach, developed by Lan and DeMets (1983), provides flexible group sequential boundaries that control Type I error rate while allowing the number and timing of interim analyses to be determined adaptively during the trial.

This method overcomes key limitations of traditional group sequential methods by not requiring the total number of analyses or their exact timing to be specified in advance. It's particularly valuable in clinical trials where interim analyses may be needed at unplanned times for ethical or scientific reasons.

The approach works by "spending" portions of the overall alpha budget at each interim analysis according to a pre-specified spending function, ensuring that the cumulative Type I error rate never exceeds the target level.

Parameters:

Name Type Description Default
alpha float

Target Type I error rate (e.g., 0.05 for 5% error rate). Must be in (0, 1).

required
spend_func AbstractSpendFunc

Alpha spending function that determines how to allocate alpha across interim analyses. Must inherit from AbstractSpendFunc.

required

Attributes:

Name Type Description
alpha0 float

Original target Type I error rate.

rule AbstractSpendFunc

The spending function used to determine alpha allocation.

num_test int

Number of tests/analyses conducted so far.

alpha float

Current alpha level for the next test.

Examples:

>>> from online_fdr.spending.functions.bonferroni import BonferroniSpendFunc
>>> # Create Bonferroni spending function for 5 planned analyses
>>> spend_func = BonferroniSpendFunc(max_analyses=5)
>>> alpha_spending = AlphaSpending(alpha=0.05, spend_func=spend_func)
>>> # Test p-values sequentially
>>> decision1 = alpha_spending.test_one(0.01)  # First interim analysis
>>> decision2 = alpha_spending.test_one(0.03)  # Second interim analysis
References

Lan, K. K. Gordon, and D. L. DeMets (1983). "Discrete Sequential Boundaries for Clinical Trials." Biometrika, 70(3):659-663.

DeMets, D. L., and K. K. Gordon Lan (1994). "Interim Analysis: The Alpha Spending Function Approach." Statistics in Medicine, 13(13-14):1341-1352.

Jennison, C., and B. W. Turnbull (1999). "Group Sequential Methods with Applications to Clinical Trials." Chapman and Hall/CRC.

Source code in online_fdr/spending/alpha_spending.py
class AlphaSpending(AbstractSequentialTest):
    """Alpha Spending Function for sequential hypothesis testing with flexible interim analyses.

    The alpha spending function approach, developed by Lan and DeMets (1983), provides
    flexible group sequential boundaries that control Type I error rate while allowing
    the number and timing of interim analyses to be determined adaptively during the trial.

    This method overcomes key limitations of traditional group sequential methods by not
    requiring the total number of analyses or their exact timing to be specified in advance.
    It's particularly valuable in clinical trials where interim analyses may be needed
    at unplanned times for ethical or scientific reasons.

    The approach works by "spending" portions of the overall alpha budget at each interim
    analysis according to a pre-specified spending function, ensuring that the cumulative
    Type I error rate never exceeds the target level.

    Args:
        alpha: Target Type I error rate (e.g., 0.05 for 5% error rate). Must be in (0, 1).
        spend_func: Alpha spending function that determines how to allocate alpha
                   across interim analyses. Must inherit from AbstractSpendFunc.

    Attributes:
        alpha0: Original target Type I error rate.
        rule: The spending function used to determine alpha allocation.
        num_test: Number of tests/analyses conducted so far.
        alpha: Current alpha level for the next test.

    Examples:
        >>> from online_fdr.spending.functions.bonferroni import BonferroniSpendFunc
        >>> # Create Bonferroni spending function for 5 planned analyses
        >>> spend_func = BonferroniSpendFunc(max_analyses=5)
        >>> alpha_spending = AlphaSpending(alpha=0.05, spend_func=spend_func)
        >>> # Test p-values sequentially
        >>> decision1 = alpha_spending.test_one(0.01)  # First interim analysis
        >>> decision2 = alpha_spending.test_one(0.03)  # Second interim analysis

    References:
        Lan, K. K. Gordon, and D. L. DeMets (1983). "Discrete Sequential Boundaries
        for Clinical Trials." Biometrika, 70(3):659-663.

        DeMets, D. L., and K. K. Gordon Lan (1994). "Interim Analysis: The Alpha
        Spending Function Approach." Statistics in Medicine, 13(13-14):1341-1352.

        Jennison, C., and B. W. Turnbull (1999). "Group Sequential Methods with
        Applications to Clinical Trials." Chapman and Hall/CRC.
    """

    def __init__(
        self,
        alpha: float,
        spend_func: AbstractSpendFunc,
    ):
        super().__init__(alpha)
        self.alpha0: float = alpha
        self.rule: AbstractSpendFunc = spend_func

    def test_one(self, p_val: float) -> bool:
        """Test a single p-value using the alpha spending approach.

        Determines the alpha level for the current analysis based on the spending function
        and the analysis number, then tests whether the p-value meets the significance
        threshold.

        Args:
            p_val: P-value to test. Must be in [0, 1].

        Returns:
            True if the null hypothesis is rejected (p_val < alpha), False otherwise.

        Raises:
            ValueError: If p_val is not in [0, 1].

        Examples:
            >>> alpha_spending = AlphaSpending(alpha=0.05, spend_func=my_spend_func)
            >>> alpha_spending.test_one(0.01)  # First analysis
            True
            >>> alpha_spending.test_one(0.04)  # Second analysis, stricter threshold
            False
        """
        validity.check_p_val(p_val)

        self.alpha = self.rule.spend(index=self.num_test, alpha=self.alpha0)
        self.num_test += 1
        return p_val < self.alpha

Functions

test_one(p_val)

Test a single p-value using the alpha spending approach.

Determines the alpha level for the current analysis based on the spending function and the analysis number, then tests whether the p-value meets the significance threshold.

Parameters:

Name Type Description Default
p_val float

P-value to test. Must be in [0, 1].

required

Returns:

Type Description
bool

True if the null hypothesis is rejected (p_val < alpha), False otherwise.

Raises:

Type Description
ValueError

If p_val is not in [0, 1].

Examples:

>>> alpha_spending = AlphaSpending(alpha=0.05, spend_func=my_spend_func)
>>> alpha_spending.test_one(0.01)  # First analysis
True
>>> alpha_spending.test_one(0.04)  # Second analysis, stricter threshold
False
Source code in online_fdr/spending/alpha_spending.py
def test_one(self, p_val: float) -> bool:
    """Test a single p-value using the alpha spending approach.

    Determines the alpha level for the current analysis based on the spending function
    and the analysis number, then tests whether the p-value meets the significance
    threshold.

    Args:
        p_val: P-value to test. Must be in [0, 1].

    Returns:
        True if the null hypothesis is rejected (p_val < alpha), False otherwise.

    Raises:
        ValueError: If p_val is not in [0, 1].

    Examples:
        >>> alpha_spending = AlphaSpending(alpha=0.05, spend_func=my_spend_func)
        >>> alpha_spending.test_one(0.01)  # First analysis
        True
        >>> alpha_spending.test_one(0.04)  # Second analysis, stricter threshold
        False
    """
    validity.check_p_val(p_val)

    self.alpha = self.rule.spend(index=self.num_test, alpha=self.alpha0)
    self.num_test += 1
    return p_val < self.alpha

Overview

The Alpha Spending Function approach provides a flexible framework for sequential hypothesis testing that allows the number and timing of interim analyses to be determined adaptively during the trial. Unlike traditional group sequential methods, it does not require advance specification of when analyses will be conducted.

Key Features

  • Adaptive timing: Interim analyses can be conducted at any time
  • Flexible boundaries: Spending function determines significance boundaries
  • Type I error control: Maintains strict control regardless of analysis timing
  • Clinical applicability: Designed for real-world trial scenarios

Mathematical Framework

The alpha spending function \(\alpha(t)\) satisfies:

  1. \(\alpha(0) = 0\) (no spending at the start)
  2. \(\alpha(1) = \alpha\) (complete budget spent at the end)
  3. \(\alpha(t)\) is non-decreasing in \(t\)

At analysis \(k\), the incremental alpha spent is: \(\(\Delta_k = \alpha(t_k) - \alpha(t_{k-1})\)\)

Implementation Details

The AlphaSpending class requires: - Target Type I error rate (\(\alpha\)) - A spending function implementing AbstractSpendFunc

Common spending functions include: - O'Brien-Fleming boundaries - Pocock boundaries
- Linear spending - Custom user-defined functions

Examples

Basic Usage

from online_fdr.spending import AlphaSpending
from online_fdr.spending.functions import LinearSpendFunc

# Create linear spending function
spend_func = LinearSpendFunc(max_analyses=5)

# Initialize alpha spending procedure
alpha_spending = AlphaSpending(alpha=0.05, spend_func=spend_func)

# Test p-values sequentially
p_values = [0.02, 0.15, 0.01, 0.8, 0.003]
decisions = []

for p_val in p_values:
    decision = alpha_spending.test_one(p_val)
    decisions.append(decision)
    print(f"P-value: {p_val}, Decision: {decision}")

Unplanned Interim Analysis

# Alpha spending allows unplanned analyses
# No need to pre-specify analysis times

analysis_times = [0.2, 0.35, 0.6, 0.9, 1.0]  # Can be determined adaptively
p_values = [0.03, 0.12, 0.008, 0.45, 0.002]

for time, p_val in zip(analysis_times, p_values):
    decision = alpha_spending.test_one(p_val)
    print(f"Analysis at t={time}: p={p_val}, reject={decision}")

Clinical Trial Application

Alpha spending is particularly useful in:

  • Adaptive trials: Where analysis timing depends on enrollment or events
  • Safety monitoring: Unscheduled safety analyses may be required
  • Futility assessment: Early stopping for lack of efficacy
  • Regulatory compliance: FDA guidance supports alpha spending approaches

Comparison with Group Sequential Methods

Aspect Alpha Spending Group Sequential
Analysis timing Flexible Fixed
Number of analyses Adaptive Pre-specified
Boundary calculation Via spending function Via recursive formula
Implementation More complex Simpler
Regulatory acceptance High High

Best Practices

  1. Choose spending function carefully based on trial objectives
  2. Monitor cumulative spending to avoid early exhaustion
  3. Document analysis timing for regulatory submissions
  4. Consider practical constraints in spending function selection
  5. Plan for emergency analyses that may be required

References

Lan, K. K. Gordon, and D. L. DeMets (1983). "Discrete Sequential Boundaries for Clinical Trials." Biometrika, 70(3):659-663.

DeMets, D. L., and K. K. Gordon Lan (1994). "Interim Analysis: The Alpha Spending Function Approach." Statistics in Medicine, 13(13-14):1341-1352.

Wassmer, G., and W. Brannath (2016). Group Sequential and Confirmatory Adaptive Designs in Clinical Trials. Springer.