logrank_n_events()

Number of events needed for the log-rank test to reach a target power.

Usage

Source

logrank_n_events(
    hazard_ratio, *, power=0.8, alpha=0.05, allocation=0.5, sides=2
)

Computes the minimum number of events required for a log-rank test to achieve a specified power when detecting a given hazard ratio in a two-group survival study. This is the foundational calculation in trial planning.

Under the proportional hazards assumption (Schoenfeld, 1981), power depends only on the total number of events, not on follow-up duration, censoring distribution, or sample size per se. This makes this calculation practical as it tells you “how many events do you need?” and you plan your study to observe that many events through appropriate enrollment and follow-up.

Use this when you want to know the required event count; use logrank_sample_size to convert events to total enrollment given an expected event probability.

Parameters

hazard_ratio: float

The hazard ratio to detect (group 2 versus group 1). Can be < 1 (protective effect, better survival) or > 1 (harmful effect, worse survival). The result is symmetric: HR=0.5 and HR=2.0 require the same number of events. Typical ranges:

  • 0.5: 50% hazard reduction (strong effect)
  • 0.67: 33% hazard reduction (moderate effect)
  • 0.8: 20% hazard reduction (small effect)
power: float = 0.8

Target statistical power (default 0.8, i.e., 80%). The probability of detecting the effect if it truly exists. Standard choice is 0.8; use 0.9 or 0.95 for higher confidence (requires more events).

alpha: float = 0.05

Significance level or Type-I error rate (default 0.05). The probability of rejecting the null hypothesis if it’s true. Use 0.05 for the conventional two-sided test with p < 0.05 threshold.

allocation: float = 0.5

Fraction of subjects in one group (default 0.5, a balanced design). For example:

  • 0.5: Equal allocation (balanced, minimizes total events needed)
  • 0.33 or 0.67: 1:2 allocation (unbalanced)
  • 0.25 or 0.75: 1:3 allocation (more unbalanced)

Unbalanced allocation increases the events needed; balanced (0.5) is optimal for a given total sample size.

sides: int = 2
1 (one-sided) or 2 (two-sided, default). A two-sided test checks for differences in either direction (one-sided checks only one direction). One-sided tests require fewer events for the same power but are appropriate only when direction is known in advance.

Returns

int
Minimum number of events (rounded up), required across all groups combined.

Details

Schoenfeld’s formula: The exact number of events is

\[ d = \frac{(z_{1 - \alpha/\mathrm{sides}} + z_{\mathrm{power}})^2} {p \, (1 - p) \, (\ln \mathrm{HR})^2}, \]

where:

  • \(z_{1 - \alpha/\mathrm{sides}}\): critical value for significance level and sides
  • \(z_{\mathrm{power}}\): critical value for desired power
  • \(p\): allocation fraction (default 0.5)
  • \(\ln(\mathrm{HR})\): natural log of hazard ratio

Balanced allocation (\(p = 0.5\)) minimizes the denominator and thus minimizes events needed.

Practical use: Once you know the required event count, estimate sample size by dividing by the expected event rate: n_subjects = ceil(n_events / prob_event). Then design your study (enrollment, duration, follow-up) to observe that many events.

Examples

For a trial detecting a hazard ratio of 0.5 (50% hazard reduction) with conventional 80% power and two-sided significance level 0.05, how many events are needed?

import greenwood as gw

gw.logrank_n_events(hazard_ratio=0.5)
66

Repeat for higher power (90%) to see the increase:

gw.logrank_n_events(hazard_ratio=0.5, power=0.9)
88

Explore sensitivity to effect size. Smaller effects (HR closer to 1) require more events:

for hr in [0.5, 0.6, 0.7, 0.8]:
    events = gw.logrank_n_events(hazard_ratio=hr)
    print(f"HR {hr}: {events} events needed")
HR 0.5: 66 events needed
HR 0.6: 121 events needed
HR 0.7: 247 events needed
HR 0.8: 631 events needed

Use sides=1 for one-sided testing (higher power, fewer events, but assumes direction):

gw.logrank_n_events(hazard_ratio=0.5, sides=1)
52

Use allocation=0.33 for unbalanced assignment (e.g., 1 treated per 2 control):

gw.logrank_n_events(hazard_ratio=0.5, allocation=0.33)
74

After determining event count, convert to sample size using expected event probability. For instance, if 40% of subjects are expected to have events, divide by 0.4:

events = gw.logrank_n_events(hazard_ratio=0.5)
subjects = gw.logrank_sample_size(hazard_ratio=0.5, prob_event=0.4)
print(f"Events needed: {events}")
print(f"Subjects needed: {subjects}")
print(f"Ratio: {subjects / events:.1f}")
Events needed: 66
Subjects needed: 164
Ratio: 2.5