CoRpower
The CoRpower
package
performs power calculations for correlates of risk, as described in
Gilbert, Janes, and Huang (2016). Power/Sample Size Calculations for
Assessing Correlates of Risk in Clinical Efficacy Trials. The power
calculations accommodate three types of biomarkers:
as well as two types of sampling design:
The vignette aims to illustrate distinct features of the functions in the package (with some mathematical background) by walking through a number of power calculation scenarios for different biomarker types, sampling designs, and input parameters.
The functions included in this package are:
computeN()
computePower()
plotPowerTri()
plotPowerCont()
plotRRgradVE()
plotVElatCont()
Assume a randomized vaccine vs. placebo/control vaccine efficacy trial
Participants are followed for the first occurrence of the primary clinical study endpoint, with follow-up through time τmax
T is the time from randomization (or enrollment) to the primary endpoint
Y = I(T ≤ τmax) is the binary outcome of interest
Δ is the indicator that Y is observed; i.e., Δ = 0 if the participants drops out before τmax and before experiencing the primary endpoint and Δ = 1 otherwise
N1 is the number of vaccine recipients observed or expected to be at risk at τ (typically, τ is the biomarker sampling timepoint)
ncases, 1 (ncontrols, 1) is the number of observed or expected cases (controls) in vaccine recipients between τ and τmax, where cases have ΔY = 1 and controls have Δ(1 − Y) = 1
ncases, 1S (ncontrols, 1S) is the number of observed cases (controls) in vaccine recipients between τ and τmax with measured biomarker response S(1) (or S*(1))
If calculations done at design stage, then N1, ncases, 1, ncontrols, 1, ncases, 1S, and ncontrols, 1S are expected counts
Typically, σobs2 = 1
Calculation of (Sens, Spec, FP0, FP1, FN1, FN2)
Assuming the classical measurement error model, where X* ∼ N(0, ρσobs2), solve P0lat = P(X* ≤ θ0) and P2lat = P(X* > θ2) for θ0 and θ2
Generate B realizations of X* and S* = X* + e, where e ∼ N(0, (1 − ρ)σobs2), and X* independent of e
B = 20, 000 by default
Using θ0 and θ2 from the first step, define $$ \begin{align} Spec(\phi_0) &= P(S^{\ast} \leq \phi_0 \mid X^{\ast} \leq \theta_0)\\ FN^1(\phi_0) &= P(S^{\ast} \leq \phi_0 \mid X^{\ast} \in (\theta_0,\theta_2])\\ FN^2(\phi_0) &= P(S^{\ast} \leq \phi_0 \mid X^{\ast} > \theta_2)\\ Sens(\phi_2) &= P(S^{\ast} > \phi_2 \mid X^{\ast} > \theta_2)\\ FP^1(\phi_2) &= P(S^{\ast} > \phi_2 \mid X^{\ast} \in (\theta_0,\theta_2])\\ FP^0(\phi_2) &= P(S^{\ast} > \phi_2 \mid X^{\ast} \leq \theta_0) \end{align} $$
Estimate Spec(ϕ0) by $$\widehat{Spec}(\phi_0) = \frac{\#\{S^{\ast}_b \leq \phi_0, X^{\ast}_b \leq \theta_0\}}{\#\{X^{\ast}_b \leq \theta_0\}}\,$$ etc.
Find ϕ0 = ϕ0* and ϕ2 = ϕ2* that numerically solve $$ \begin{align} P_0 &= \widehat{Spec}(\phi_0)P^{lat}_0 + \widehat{FN}^1(\phi_0)P^{lat}_1 + \widehat{FN}^2(\phi_0)P^{lat}_2\\ P_2 &= \widehat{Sens}(\phi_2)P^{lat}_2 + \widehat{FP}^1(\phi_2)P^{lat}_1 + \widehat{FP}^0(\phi_2)P^{lat}_0 \end{align} $$ and compute $$ Spec = \widehat{Spec}(\phi^{\ast}_0),\; Sens = \widehat{Sens}(\phi^{\ast}_2),\; \textrm{etc.} $$
In Approach 2, plot $$ RR_t \quad \textrm{vs.} \quad \frac{RR^{lat}_2}{RR^{lat}_0}, $$ where RRt is the CoR effect size defined as $$ RR_t := \frac{risk_1(2)}{risk_1(0)} = \frac{\sum_{x=0}^2 RR^{lat}_x P(X=x|S(1)=2)}{\sum_{x=0}^2 RR^{lat}_x P(X=x|S(1)=0)} $$
If ρ < 1, then RRt is closer to 1 than RR2lat/RR0lat
Note that, under the assumption of homogeneous risk in the placebo group, i.e., risk0lat(x) = risk0, x = 0, 1, 2, the relative risk ratio RR2lat/RR0lat = risk1lat(2)/risk1lat(0)
Consequently, risk1(2)/risk1(0) > risk1lat(2)/risk1lat(0) because, if ρ < 1, then risk1(2) > risk1lat(2) and risk1(0) < risk1lat(0)
tps
in R package osDesign
)
computeN()
$$ \begin{align} N_1 &= N_{rand}\, P(T>\tau, C>\tau \mid Z=1)\\ &= N_{rand}\, P(T>\tau \mid Z=1)\, P(C>\tau \mid Z=1)\\ &= N_{rand}\, \{1 - RR_{0-\tau}\, P(T \leq \tau \mid Z=0)\}\, P(C>\tau \mid Z=1)\\ &\approx 4,023 \end{align} $$
$$ \begin{align} n_{cases,1} &= N_1\, P(T\leq \tau_{\mathrm{max}}, T\leq C \mid T>\tau, C>\tau, Z=1)\\ &= N_1\, P(T\leq \min(C,\tau_{\mathrm{max}}) \mid T>\tau, C>\tau, Z=1)\\ &= N_1\, \frac{\int_{\tau}^{\infty}P(\tau < T \leq \min(c,\tau_{\mathrm{max}})\mid Z=1)f_C(c)\mathop{\mathrm{d}}\!c}{P(T>\tau, C>\tau \mid Z=1)}\\ &= N_1\, \frac{\bigg\{\int_{\tau}^{\tau_{\mathrm{max}}}P(\tau < T \leq c\mid Z=1)f_C(c)\mathop{\mathrm{d}}\!c + P(\tau < T \leq \tau_{\mathrm{max}}\mid Z=1) P(C>\tau_{\mathrm{max}})\bigg\}}{P(T>\tau, C>\tau \mid Z=1)}\\ &\approx 32 \end{align} $$
$$ \begin{align} n_{controls,1} &= N_1 \, P(T > \tau_{\mathrm{max}}, C > \tau_{\mathrm{max}} \mid T>\tau, C>\tau, Z=1)\\ &\approx 3,654 \end{align} $$
$$ \begin{align} n^S_{cases,1} &= n_{cases,1}\\ n^S_{controls,1} &= K n^S_{cases,1} \end{align} $$
computeN()
library(CoRpower)
computeN(Nrand = 4100, # participants randomized to vaccine arm
tau = 3.5, # biomarker sampling timepoint
taumax = 24, # end of follow-up
VEtauToTaumax = 0.75, # VE between 'tau' and 'taumax'
VE0toTau = 0.75/2, # VE between 0 and 'tau'
risk0 = 0.034, # placebo-group endpoint risk between 'tau' and 'taumax'
dropoutRisk = 0.1, # dropout risk between 0 and 'taumax'
propCasesWithS = 1) # proportion of observed cases with measured S(1)
## $N
## [1] 4023
##
## $nCases
## [1] 33
##
## $nControls
## [1] 3653
##
## $nCasesWithS
## [1] 33
CoRpower
for trichotomous S(1) | Without replacement
samplingApproach 1 (Sens, Spec, FP0, FN2 specified)
Approach 2 (σobs2 and ρ specified)
computePower()
pwr <- computePower(nCasesTx = 32,
nControlsTx = 3654,
nCasesTxWithS = 32,
controlCaseRatio = c(5, 3, 1), # n^S_controls : n^S_cases ratio
VEoverall = 0.75, # overall VE
risk0 = 0.034, # placebo-group endpoint risk from tau - taumax
VElat0 = seq(0, VEoverall, len=100), # grid of VE (V/P) among lower protected
VElat1 = rep(VEoverall, 100), # grid of VE (V/P) among medium protected
Plat0 = 0.2, # prevalence of lower protected
Plat2 = 0.6, # prevalence of higher protected
P0 = 0.2, # probability of low biomarker response
P2 = 0.6, # probability of high biomarker response
sens = 0.8, spec = 0.8, FP0 = 0, FN2 = 0,
M = 1000, # number of simulated clinical trials
alpha = 0.05, # two-sided Wald test Type 1 error rate
biomType = "trichotomous") # "continuous" by default
plotPowerTri()
Basic plotting functions are included in the package to aid with
visualizing results. plotPowerTri
plots the power curve
against the CoR relative risk, RRt,
for trichotomous or binary biomarkers.
Output from computePower()
can be saved as an object and
assigned to the outComputePower
input parameter.
plotPowerTri(outComputePower = pwr, # 'computePower' output list of lists
legendText = paste0("Control:Case = ", c("5:1", "3:1", "1:1")))
Alternatively, output from computePower()
can be saved
in RData files. In this case, the outComputePower
input
parameter should be the name(s) of the output file(s), and the
outDir
input parameter should be the name(s) of the file
location(s). For more information, visit the plotPowerTri()
help page.
computePower(..., saveDir = "myDir", saveFile = c("myFile1.RData", "myFile2.RData", "myFile3.RData"))
plotPowerTri(outComputePower = c("myFile1.RData", "myFile2.RData", "myFile3.RData"), # 'computePower' output files
outDir = rep("~/myDir", 3), # path to each myFilex.RData
legendText = paste0("Control:Case = ", c("5:1", "3:1", "1:1")))
pwr <- computePower(nCasesTx = 32,
nControlsTx = 3654,
nCasesTxWithS = 32,
controlCaseRatio = 5, # n^S_controls : n^S_cases ratio
VEoverall = 0.75, # overall VE
risk0 = 0.034, # placebo-group endpoint risk from tau - taumax
VElat0 = seq(0, VEoverall, len=100), # grid of VE (V/P) among lower protected
VElat1 = rep(VEoverall, 100), # grid of VE (V/P) among medium protected
Plat0 = 0.2, # prevalence of lower protected
Plat2 = 0.6, # prevalence of higher protected
P0 = 0.2, # probability of low biomarker response
P2 = 0.6, # probability of high biomarker response
sens = c(1, 0.9, 0.8, 0.7), spec = c(1, 0.9, 0.8, 0.7),
FP0 = c(0, 0, 0, 0), FN2 = c(0, 0, 0, 0),
M = 1000, # number of simulated clinical trials
alpha = 0.05, # two-sided Wald test Type 1 error rate
biomType = "trichotomous") # "continuous" by default
pwr <- computePower(nCasesTx = 32,
nControlsTx = 3654,
nCasesTxWithS = 32,
controlCaseRatio = 5, # n^S_controls : n^S_cases ratio
VEoverall = 0.75, # overall VE
risk0 = 0.034, # placebo-group endpoint risk from tau - taumax
VElat0 = seq(0, VEoverall, len=100), # grid of VE (V/P) among lower protected
VElat1 = rep(VEoverall, 100), # grid of VE (V/P) among medium protected
Plat0 = c(0.05, 0.1, 0.15, 0.2),
Plat2 = c(0.15, 0.3, 0.45, 0.6),
P0 = c(0.05, 0.1, 0.15, 0.2),
P2 = c(0.15, 0.3, 0.45, 0.6),
sens = 0.8, spec = 0.8, FP0 = 0, FN2 = 0,
M = 1000, # number of simulated clinical trials
alpha = 0.05, # two-sided Wald test Type 1 error rate
biomType = "trichotomous") # "continuous" by default
computePower()
pwr <- computePower(nCasesTx = 32,
nControlsTx = 3654,
nCasesTxWithS = 32,
controlCaseRatio = 5, # n^S_controls : n^S_cases ratio
VEoverall = 0.75, # overall VE
risk0 = 0.034, # placebo-group endpoint risk from tau - taumax
VElat0 = seq(0, VEoverall, len=100), # grid of VE (V/P) among lower protected
VElat1 = rep(VEoverall, 100), # grid of VE (V/P) among medium protected
Plat0 = 0.2, # prevalence of lower protected
Plat2 = 0.6, # prevalence of higher protected
P0 = 0.2, # probability of low biomarker response
P2 = 0.6, # probability of high biomarker response
sigma2obs = 1, # variance of observed biomarker S(1)
rho = c(1, 0.9, 0.7, 0.5), # protection-relevant fraction of variance of S(1)
M = 1000, # number of simulated clinical trials
alpha = 0.05, # two-sided Wald test Type 1 error rate
biomType = "trichotomous") # "continuous" by default
plotPowerTri()
plotRRgradVE()
plotRRgradVE()
plots the ratio of relative risks for the
higher and lower latent subgroups RR2lat/RR0lat
against the CoR relative risk effect size RRt = risk1(2)/risk1(0).
Output from computePower()
can be saved as an object and
assigned to the outComputePower
input parameter.
plotRRgradVE(outComputePower = pwr, # 'computePower' output list of lists
legendText = paste0("rho = ", c(1, 0.9, 0.7, 0.5)))
Alternatively, output from computePower()
can be saved
in RData files. In this case, the outComputePower
input
parameter should be the name(s) of the output file(s), and the
outDir
input parameter should be the name(s) of the file
location(s). For more information, visit the plotRRgradVE()
help page.
plotROCcurveTri()
plotROCcurveTri()
plots the receiver operating
characteristic (ROC) curve displaying sensitivity and specificity for a
range of values for P2
, P0
,
Plat2
, and rho
. For more information, visit
the plotROCcurveTri()
help page.
pwr <- computePower(nCasesTx = 32,
nControlsTx = 3654,
nCasesTxWithS = 32,
controlCaseRatio = 5, # n^S_controls : n^S_cases ratio
VEoverall = 0.75, # overall VE
risk0 = 0.034, # placebo-group endpoint risk from tau - taumax
VElat0 = seq(0, VEoverall, len=100), # grid of VE (V/P) among lower protected
VElat1 = rep(VEoverall, 100), # grid of VE (V/P) among medium protected
Plat0 = c(0.05, 0.1, 0.15, 0.2),
Plat2 = c(0.15, 0.3, 0.45, 0.6),
P0 = c(0.05, 0.1, 0.15, 0.2),
P2 = c(0.15, 0.3, 0.45, 0.6),
sigma2obs = 1, # variance of observed biomarker S(1)
rho = 0.9, # protection-relevant fraction of variance of S(1)
M = 1000, # number of simulated clinical trials
alpha = 0.05, # two-sided Wald test Type 1 error rate
biomType = "trichotomous") # "continuous" by default
pwr <- computePower(nCasesTx = c(25, 32, 35, 40),
nControlsTx = c(3661, 3654, 3651, 3646),
nCasesTxWithS = c(25, 32, 35, 40),
controlCaseRatio = 5, # n^S_controls : n^S_cases ratio
VEoverall = 0.75, # overall VE
risk0 = 0.034, # placebo-group endpoint risk fom tau - taumax
VElat0 = seq(0, VEoverall, len=100), # grid of VE (V/P) among lower protected
VElat1 = rep(VEoverall, 100), # grid of VE (V/P) among medium protected
Plat0 = 0.2, # prevalence of lower protected
Plat2 = 0.6, # prevalence of higher protected
P0 = 0.2, # probability of low biomarker response
P2 = 0.6, # probability of high biomarker response
sigma2obs = 1, # variance of observed biomarker S(1)
rho = 0.9, # protection-relevant fraction of variance of S(1)
M = 1000, # number of simulated clinical trials
alpha = 0.05, # two-sided Wald test Type 1 error rate
biomType = "trichotomous") # "continuous" by default
CoRpower
for binary S(1) | Without replacement
samplingAchieved by selecting P0lat, P2lat, P0, P2 such that $$ \begin{align} P_0^{lat} + P_2^{lat} &= 1\\ P_0 + P_2 &= 1 \end{align} $$
Approach 2 (σobs2 and ρ specified)
computePower()
pwr <- computePower(nCasesTx = c(25, 32, 35, 40),
nControlsTx = c(3661, 3654, 3651, 3646),
nCasesTxWithS = c(25, 32, 35, 40),
controlCaseRatio = 5, # n^S_controls : n^S_cases ratio
VEoverall = 0.75, # overall VE
risk0 = 0.034, # placebo-group endpoint risk from tau - taumax
VElat0 = seq(0, VEoverall, len=100), # grid of VE (V/P) among lower protected
VElat1 = rep(VEoverall, 100), # grid of VE (V/P) among medium protected
Plat0 = 0.2, # prevalence of lower protected
Plat2 = 0.8, # prevalence of higher protected
P0 = 0.2, # probability of low biomarker response
P2 = 0.8, # probability of high biomarker response
sigma2obs = 1, # variance of observed biomarker S(1)
rho = 0.9, # protection-relevant fraction of variance of S(1)
M = 1000, # number of simulated clinical trials
alpha = 0.05, # two-sided Wald test Type 1 error rate
biomType = "binary") # "continuous" by default
sample(
x̃*,
prob=
fX*(x̃*|Y = ⋅, Yτ = 0, Z = 1),
replace=TRUE)
tps
in R package osDesign
)
CoRpower
for continuous S*(1) | Without
replacement samplingcomputePower()
pwr <- computePower(nCasesTx = 32,
nControlsTx = 3654,
nCasesTxWithS = 32,
controlCaseRatio = 5, # n^S_controls : n^S_cases ratio
VEoverall = 0.75, # overall VE
risk0 = 0.034, # placebo-group endpoint risk from tau - taumax
PlatVElowest = 0.2, # prevalence of VE_lowest
VElowest = seq(0, VEoverall, len=100), # lowest VE for true biomarker X*<=nu
sigma2obs = 1, # variance of observed biomarker S
rho = c(1, 0.9, 0.7, 0.5) # protection-relevant fraction of variance of S
M = 1000, # number of simulated clinical trials
alpha = 0.05, # two-sided Wald test Type 1 error rate
biomType = "continuous") # "continuous" by default
plotPowerCont()
plotPowerCont()
plots the power curve against the CoR
relative risk, RRc,
for continuous biomarkers.
Output from computePower()
can be saved as an object and
assigned to the outComputePower
input parameter. In this
scenario, since computePower()
was run multiple times to
vary the controls:cases ratio, these multiple output objects can be read
into the function as a list.
plotPowerCont(outComputePower = pwr, # output list of lists from 'computePower'
legendText = paste0("rho = ", c(1, 0.9, 0.7, 0.5)))
Alternatively, output from computePower()
can be saved
in RData files. In this case, the outComputePower
input
parameter should be the name(s) of the output file(s), and the
outDir
input parameter should be the name(s) of the file
location(s). For more information, visit the
plotPowerCont()
help page.
plotVElatCont()
plotVElatCont()
plots the vaccine efficacy (VE) curve
for the true biomarker X=x for eight different values of the
true CoR relative risk, , in vaccine recipients and the lowest vaccine
efficacy level for the true biomarker, .
outComputePower
contains output from a single run of
computePower()
with no varying argument (i.e., no
vectorized input parameters other than VE0lat,
VE1lat,
and ). This output can be in the form of an assigned object, which is a
list of lists of length 1, or a
character string specifying the file containing the output. Note that
this is unlike plotPowerTri()
and
plotPowerCont()
, which can take in output from
computePower()
in the form of a list of lists of length
greater than 1 or a character vector.
For more information, visit the plotVElatCont()
help
page.
Using the function when computePower()
output is saved
as list object pwr
:
Using the function when computePower()
output is saved
in a file with name “myFile” and location “~/myDir”:
computePower()
pwr <- computePower(nCasesTx = 32,
nControlsTx = 3654,
nCasesTxWithS = 32,
controlCaseRatio = 5, # n^S_controls : n^S_cases ratio
VEoverall = 0.75, # overall VE
risk0 = 0.034, # placebo-group endpoint risk from tau - taumax
PlatVElowest = c(0.05, 0.1, 0.15, 0.2),
VElowest = seq(0, VEoverall, len=100), # lowest VE for true biomarker X*<=nu
sigma2obs = 1, # variance of observed biomarker S(1)
rho = 0.9 # protection-relevant fraction of variance of S(1)
M = 1000, # number of simulated clinical trials
alpha = 0.05, # two-sided Wald test Type 1 error rate
biomType = "continuous") # "continuous" by default
CoRpower
for trichotomous S(1) and continuous S*(1) | Bernoulli
samplingTrichotomous S(1) (Approach 1)
Continuous S*(1)
computePower()
pwr <- computePower(nCasesTx = 32,
nControlsTx = 3654,
nCasesTxWithS = 32,
cohort = TRUE, # FALSE by default
p = c(0.01, 0.02, 0.03, 0.05),
VEoverall = 0.75, # overall VE
risk0 = 0.034, # placebo-group endpoint risk from tau - taumax
VElat0 = seq(0, VEoverall, len=100), # grid of VE (V/P) among lower protected
VElat1 = rep(VEoverall, 100), # grid of VE (V/P) among medium protected
Plat0 = 0.2, # prevalence of lower protected
Plat2 = 0.6, # prevalence of higher protected
P0 = 0.2, # probability of low biomarker response
P2 = 0.6, # probability of high biomarker response
sens = 0.8, spec = 0.8, FP0 = 0, FN2 = 0,
M = 1000, # number of simulated clinical trials
alpha = 0.05, # two-sided Wald test Type 1 error rate
biomType = "trichotomous") # "continuous" by default
computePower()
pwr <- computePower(nCasesTx = 32,
nControlsTx = 3654,
nCasesTxWithS = 32,
cohort = TRUE, # FALSE by default
p = c(0.01, 0.02, 0.03, 0.05),
VEoverall = 0.75, # overall VE
risk0 = 0.034, # placebo-group endpoint risk from tau - taumax
PlatVElowest = 0.2, # prevalence of VE_lowest
VElowest = seq(0, VEoverall, len=100), # lowest VE for true biomarker X*<=nu
sigma2obs = 1, # variance of observed biomarker S(1)
rho = 0.9 # protection-relevant fraction of variance of S(1)
M = 1000, # number of simulated clinical trials
alpha = 0.05, # two-sided Wald test Type 1 error rate
biomType = "continuous") # "continuous" by default
computeN()
CoRpower
for trichotomous S(1) | Without replacement
samplingCoRpower
for binary S(1) | Without replacement
samplingCoRpower
for continuous S*(1) | Without
replacement samplingCoRpower
for trichotomous S(1) and continuous S*(1) | Bernoulli
sampling