blob: ebf66dc5cb937c7c1096707792ad4e90b3d6261d [file] [log] [blame]
# File src/library/stats/R/power.R
# Part of the R package, https://www.R-project.org
#
# Copyright (C) 1995-2019 The R Core Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License is available at
# https://www.R-project.org/Licenses/
power.t.test <-
function(n=NULL, delta=NULL, sd=1, sig.level=0.05, power=NULL,
type=c("two.sample", "one.sample", "paired"),
alternative=c("two.sided", "one.sided"), strict=FALSE,
tol = .Machine$double.eps^0.25)
{
if ( sum(sapply(list(n, delta, sd, power, sig.level), is.null)) != 1 )
stop("exactly one of 'n', 'delta', 'sd', 'power', and 'sig.level' must be NULL")
if(!is.null(sig.level) && !is.numeric(sig.level) ||
any(0 > sig.level | sig.level > 1))
stop("'sig.level' must be numeric in [0, 1]")
type <- match.arg(type)
alternative <- match.arg(alternative)
tsample <- switch(type, one.sample = 1, two.sample = 2, paired = 1)
force(tsample)# codetools
tside <- switch(alternative, one.sided = 1, two.sided = 2)
if (tside == 2 && !is.null(delta)) delta <- abs(delta)
p.body <-
if (strict && tside == 2) # count rejections in opposite tail
quote({
nu <- pmax(1e-7, n - 1) * tsample
qu <- qt(sig.level/tside, nu, lower.tail = FALSE)
pt( qu, nu, ncp = sqrt(n/tsample) * delta/sd, lower.tail = FALSE) +
pt(-qu, nu, ncp = sqrt(n/tsample) * delta/sd, lower.tail = TRUE)
})
else ## normal case:
quote({nu <- pmax(1e-7, n - 1) * tsample
pt(qt(sig.level/tside, nu, lower.tail = FALSE),
nu, ncp = sqrt(n/tsample) * delta/sd, lower.tail = FALSE)})
if (is.null(power))
power <- eval(p.body)
else if (is.null(n))
n <- uniroot(function(n) eval(p.body) - power,
c(2, 1e7), tol=tol, extendInt = "upX")$root
else if (is.null(sd))
sd <- uniroot(function(sd) eval(p.body) - power,
delta * c(1e-7, 1e+7), tol=tol, extendInt = "downX")$root
else if (is.null(delta))
delta <- uniroot(function(delta) eval(p.body) - power,
sd * c(1e-7, 1e+7), tol=tol, extendInt = "upX")$root
else if (is.null(sig.level))
sig.level <- uniroot(function(sig.level) eval(p.body) - power,
c(1e-10, 1-1e-10), tol=tol, extendInt = "yes")$root
else # Shouldn't happen
stop("internal error", domain = NA)
NOTE <- switch(type,
paired = "n is number of *pairs*, sd is std.dev. of *differences* within pairs",
two.sample = "n is number in *each* group", NULL)
METHOD <- paste(switch(type,
one.sample = "One-sample",
two.sample = "Two-sample",
paired = "Paired"),
"t test power calculation")
structure(list(n=n, delta=delta, sd=sd,
sig.level=sig.level, power=power,
alternative=alternative, note=NOTE, method=METHOD),
class="power.htest")
}
power.prop.test <-
function(n=NULL, p1=NULL, p2=NULL, sig.level=0.05, power=NULL,
alternative=c("two.sided", "one.sided"), strict=FALSE,
tol = .Machine$double.eps^0.25)
{
if ( sum(sapply(list(n, p1, p2, power, sig.level), is.null)) != 1 )
stop("exactly one of 'n', 'p1', 'p2', 'power', and 'sig.level' must be NULL")
if(!is.null(sig.level) && !is.numeric(sig.level) ||
any(0 > sig.level | sig.level > 1))
stop("'sig.level' must be numeric in [0, 1]")
alternative <- match.arg(alternative)
tside <- switch(alternative, one.sided = 1, two.sided = 2)
p.body <-
if (strict && tside == 2) # count rejections in opposite tail
quote({
qu <- qnorm(sig.level/tside, lower.tail = FALSE)
d <- abs(p1 - p2)
q1 <- 1 - p1
q2 <- 1 - p2
pbar <- (p1 + p2)/2
qbar <- 1 - pbar
v1 <- p1 * q1
v2 <- p2 * q2
vbar <- pbar * qbar
pnorm((sqrt(n)*d - qu * sqrt(2 * vbar) ) / sqrt(v1 + v2)) +
pnorm((sqrt(n)*d + qu * sqrt(2 * vbar) ) / sqrt(v1 + v2),
lower.tail=FALSE)
})
else ## normal case:
quote(pnorm((sqrt(n) * abs(p1 - p2)
- (qnorm(sig.level/tside, lower.tail = FALSE)
* sqrt((p1 + p2) * (1 - (p1 + p2)/2))))
/ sqrt(p1 * (1 - p1) + p2 * (1 - p2))))
if (is.null(power))
power <- eval(p.body)
else if (is.null(n))
n <- uniroot(function(n) eval(p.body) - power,
c(1,1e7), tol=tol, extendInt = "upX")$root
else if (is.null(p1)) {
p1 <- uniroot(function(p1) eval(p.body) - power,
c(0,p2), tol=tol, extendInt = "yes")$root
if(p1 < 0) warning("No p1 in [0, p2] can be found to achieve the desired power")
}
else if (is.null(p2)) {
p2 <- uniroot(function(p2) eval(p.body) - power,
c(p1,1), tol=tol, extendInt = "yes")$root
if(p2 > 1) warning("No p2 in [p1, 1] can be found to achieve the desired power")
}
else if (is.null(sig.level)) {
sig.level <- uniroot(function(sig.level) eval(p.body) - power,
c(1e-10, 1-1e-10), tol=tol, extendInt = "upX")$root
if(sig.level < 0 || sig.level > 1)
warning("No significance level [0, 1] can be found to achieve the desired power")
}
else # Shouldn't happen
stop("internal error", domain = NA)
structure(list(n=n, p1=p1, p2=p2,
sig.level=sig.level, power=power,
alternative=alternative,
note = "n is number in *each* group",
method = "Two-sample comparison of proportions power calculation"),
class="power.htest")
}
print.power.htest <- function(x, digits = getOption("digits"), ...)
{
cat("\n ", x$method, "\n\n")
note <- x$note
x[c("method", "note")] <- NULL
cat(paste(format(names(x), width = 15L, justify = "right"),
format(x, digits=digits), sep = " = "), sep = "\n")
if(!is.null(note)) cat("\n", "NOTE: ", note, "\n\n", sep = "") else cat("\n")
invisible(x)
}