tests/testthat/test-power.roc.test.R

library(pROC)
data(aSAH)

context("power.roc.test")

# define variables shared among multiple tests here

test_that("power.roc.test basic function", {
  res <- power.roc.test(r.s100b)
  expect_equal(as.numeric(res$auc), as.numeric(r.s100b$auc))
  expect_equal(res$ncases, length(r.s100b$cases))
  expect_equal(res$ncontrols, length(r.s100b$controls))
  expect_equal(res$sig.level, 0.05)
  expect_equal(res$power, 0.9904833, tolerance = 0.000001)
})

test_that("power.roc.test with percent works", {
	res <- power.roc.test(r.s100b.percent)
	expect_equal(as.numeric(res$auc), as.numeric(r.s100b$auc))
	expect_equal(res$ncases, length(r.s100b$cases))
	expect_equal(res$ncontrols, length(r.s100b$controls))
	expect_equal(res$sig.level, 0.05)
	expect_equal(res$power, 0.9904833, tolerance = 0.000001)
})

test_that("power.roc.test with given auc function", {
  res <- power.roc.test(ncases=41, ncontrols=72, auc=0.73, sig.level=0.05)
  expect_equal(as.numeric(res$auc), 0.73)
  expect_equal(res$ncases, 41)
  expect_equal(res$ncontrols, 72)
  expect_equal(res$sig.level, 0.05)
  expect_equal(res$power, 0.9897453, tolerance = 0.000001)
})


test_that("power.roc.test sig.level can be omitted", {
  res <- power.roc.test(ncases=41, ncontrols=72, auc=0.73)
  expect_equal(res$sig.level, 0.05)
  expect_equal(res$power, 0.9897453, tolerance = 0.000001)
})

test_that("power.roc.test can determine ncases & ncontrols", {
  res <- power.roc.test(auc=r.s100b$auc, sig.level=0.05, power=0.95, kappa=1.7)
  expect_equal(as.numeric(res$auc), as.numeric(r.s100b$auc))
  expect_equal(res$sig.level, 0.05)
  expect_equal(res$power, 0.95)
  expect_equal(res$ncases, 29.29764, tolerance = 0.000001)
  expect_equal(res$ncontrols, 49.806, tolerance = 0.000001)
})
  
test_that("power.roc.test can determine sig.level", {
  res <- power.roc.test(ncases=41, ncontrols=72, auc=0.73, power=0.95, sig.level=NULL)
  expect_equal(as.numeric(res$auc), 0.73)
  expect_equal(res$ncases, 41)
  expect_equal(res$ncontrols, 72)
  expect_equal(res$power, 0.95)
  expect_equal(res$sig.level, 0.009238584, tolerance = 0.000001)
})

test_that("power.roc.test can determine AUC", {
  res <- power.roc.test(ncases=41, ncontrols=72, sig.level=0.05, power=0.95)
  expect_equal(res$ncases, 41)
  expect_equal(res$ncontrols, 72)
  expect_equal(res$power, 0.95)
  expect_equal(res$sig.level, 0.05)
  expect_equal(as.numeric(res$auc), 0.6961054, tolerance = 0.000001)
})

test_that("power.roc.test can take 2 ROC curves with DeLong variance", {
  res <- power.roc.test(r.ndka, r.wfns)
  expect_equal(res$ncases, 41)
  expect_equal(res$ncontrols, 72)
  expect_equal(as.numeric(res$auc1), as.numeric(r.ndka$auc))
  expect_equal(as.numeric(res$auc2), as.numeric(r.wfns$auc))
  expect_equal(res$power, 0.7131594, tolerance = 0.000001)
  expect_equal(res$sig.level, 0.05)
  expect_equal(res$alternative, "two.sided")
})


test_that("power.roc.test can take 2 percent ROC curves with DeLong variance", {
	res <- power.roc.test(r.ndka.percent, r.wfns.percent)
	expect_equal(res$ncases, 41)
	expect_equal(res$ncontrols, 72)
	expect_equal(as.numeric(res$auc1), as.numeric(r.ndka$auc))
	expect_equal(as.numeric(res$auc2), as.numeric(r.wfns$auc))
	expect_equal(res$power, 0.7131594, tolerance = 0.000001)
	expect_equal(res$sig.level, 0.05)
	expect_equal(res$alternative, "two.sided")
})

test_that("power.roc.test can take 2 ROC curves with Obuchowski variance", {
  res <- power.roc.test(r.ndka, r.wfns, method="obuchowski")
  expect_equal(res$ncases, 41)
  expect_equal(res$ncontrols, 72)
  expect_equal(as.numeric(res$auc1), as.numeric(r.ndka$auc))
  expect_equal(as.numeric(res$auc2), as.numeric(r.wfns$auc))
  expect_equal(res$power, 0.8061004, tolerance = 0.000001)
  expect_equal(res$sig.level, 0.05)
  expect_equal(res$alternative, "two.sided")
})

test_that("power.roc.test ncases/ncontrols can take 2 ROC curves with DeLong variance", {
  res <- power.roc.test(r.ndka, r.wfns, power=0.9)
  expect_equal(res$ncases, 64.77777, tolerance = 0.000001)
  expect_equal(res$ncontrols, 113.7561, tolerance = 0.000001)
  expect_equal(as.numeric(res$auc1), as.numeric(r.ndka$auc))
  expect_equal(as.numeric(res$auc2), as.numeric(r.wfns$auc))
  expect_equal(res$power, 0.9)
  expect_equal(res$sig.level, 0.05)
  expect_equal(res$alternative, "two.sided")
})

test_that("power.roc.test ncases/ncontrols can take 2 ROC curves with Obuchowski variance", {
  res <- power.roc.test(r.ndka, r.wfns, power=0.9, method="obuchowski")
  expect_equal(res$ncases, 53.23685, tolerance = 0.000001)
  expect_equal(res$ncontrols, 93.48911, tolerance = 0.000001)
  expect_equal(as.numeric(res$auc1), as.numeric(r.ndka$auc))
  expect_equal(as.numeric(res$auc2), as.numeric(r.wfns$auc))
  expect_equal(res$power, 0.9)
  expect_equal(res$sig.level, 0.05)
  expect_equal(res$alternative, "two.sided")
})

test_that("power.roc.test sig.level can take 2 ROC curves with DeLong variance", {
  res <- power.roc.test(r.ndka, r.wfns, power=0.9, sig.level=NULL)
  expect_equal(res$ncases, 41)
  expect_equal(res$ncontrols, 72)
  expect_equal(as.numeric(res$auc1), as.numeric(r.ndka$auc))
  expect_equal(as.numeric(res$auc2), as.numeric(r.wfns$auc))
  expect_equal(res$power, 0.9)
  expect_equal(res$sig.level, 0.1836639, tolerance = 0.000001)
  expect_equal(res$alternative, "two.sided")
})

test_that("power.roc.test sig.level can take 2 ROC curves with Obuchowski variance", {
  res <- power.roc.test(r.ndka, r.wfns, power=0.9, sig.level=NULL, method="obuchowski")
  expect_equal(res$ncases, 41)
  expect_equal(res$ncontrols, 72)
  expect_equal(as.numeric(res$auc1), as.numeric(r.ndka$auc))
  expect_equal(as.numeric(res$auc2), as.numeric(r.wfns$auc))
  expect_equal(res$power, 0.9)
  expect_equal(res$sig.level, 0.1150686, tolerance = 0.000001)
  expect_equal(res$alternative, "two.sided")
})

test_that("power.roc.test works with partial AUC", {
  skip_slow()
  skip("Bootstrap cannot be tested yet")
  r.wfns.partial <- roc(aSAH$outcome, aSAH$wfns, quiet = TRUE, partial.auc=c(1, 0.9))
  r.ndka.partial <- roc(aSAH$outcome, aSAH$ndka, quiet = TRUE, partial.auc=c(1, 0.9))
  power.roc.test(r.wfns.partial, r.ndka.partial, power=0.9)
})


test_that("power.roc.test works with partial AUC", {
  r.wfns.partial <- roc(aSAH$outcome, aSAH$wfns, quiet = TRUE, partial.auc=c(1, 0.9))
  r.ndka.partial <- roc(aSAH$outcome, aSAH$ndka, quiet = TRUE, partial.auc=c(1, 0.9))
  res <- power.roc.test(r.wfns.partial, r.ndka.partial, power=0.9, method="obuchowski")
  
  expect_equal(res$ncases, 0.5061498, tolerance = 0.000001)
  expect_equal(res$ncontrols, 0.8888484, tolerance = 0.000001)
  expect_equal(as.numeric(res$auc1), as.numeric(r.wfns.partial$auc))
  expect_equal(as.numeric(res$auc2), as.numeric(r.ndka.partial$auc))
  expect_equal(res$power, 0.9)
  expect_equal(res$sig.level, 0.05)
  expect_equal(res$alternative, "two.sided")
})

test_that("power.roc.test works with binormal parameters", {
  ob.params <- list(A1=2.6, B1=1, A2=1.9, B2=1, rn=0.6, ra=0.6, FPR11=0,
                    FPR12=0.2, FPR21=0, FPR22=0.2, delta=0.037) 
  
  res1 <- power.roc.test(ob.params, power=0.8, sig.level=0.05)
  expect_equal(res1$ncases, 107.0238, tolerance = 0.000001)
  expect_equal(res1$ncontrols, 107.0238, tolerance = 0.000001)
  expect_equal(res1$power, 0.8)
  expect_equal(res1$sig.level, 0.05)
  
  res2 <- power.roc.test(ob.params, power=0.8, sig.level=NULL, ncases=107)
  expect_equal(res2$ncases, 107)
  expect_equal(res2$ncontrols, 107)
  expect_equal(res2$power, 0.8)
  expect_equal(res2$sig.level, 0.05004012, tolerance = 0.000001)
  
  res3 <- power.roc.test(ob.params, power=NULL, sig.level=0.05, ncases=107)
  expect_equal(res3$ncases, 107)
  expect_equal(res3$ncontrols, 107)
  expect_equal(res3$sig.level, 0.05)
  expect_equal(res3$power, 0.7999286, tolerance = 0.000001)
})

## With only binormal parameters given
# From example 2 of Obuchowski and McClish, 1997.


test_that("power.roc.test returns correct results from litterature", {
	context("Check results in Obuchowski 2004 Table 4")
	# Note: the table reports at least 10 in each cell, and adapts
	# the complement value to match kappa. So in 0.25/0.95 we have 10/40
	# although both values are < 10.
	# Note2: some values don't match exactly, specifically
	# expected.ncases[0.5, 0.6] and expected.ncontrols[4, 0.6]
	# are off by 1 (< 1%).
	kappas <- c(0.25, 0.5, 1, 2, 4)
	thetas <- c(0.6, 0.7, 0.8, 0.9, 0.95)
	expected.ncontrols <- matrix(c(
		84, 	20,	10,	10,	10,
		101,	25,	10,	10,	10,
		135,	33,	14,	10,	10,
		203,	50,	21,	20,	20,
		#339,	84,	40,	40,	40
		340,	84,	40,	40,	40 # Fixed
	), nrow = 5, byrow = TRUE,
	dimnames = list(kappas, thetas))
	expected.ncases <- matrix(c(
		334,	80,	40,	40,	40,
		#201,	49,	20,	20,	20,
		202,	49,	20,	20,	20, # Fixed
		135,	33,	14,	10,	10,
		102,	25,	11,	10,	10,
		85, 	21,	10,	10,	10
	), nrow = 5, byrow = TRUE, 
	dimnames = list(kappas, thetas))
	
	for (kappa in kappas) {
		for (theta in thetas) {
			context(sprintf("kappa: %s, theta: %s", kappa, theta))
			pr <- power.roc.test(auc=theta, sig.level=0.05, power=0.9, kappa=kappa, alternative="one.sided")
			expect_equal(max(10, ifelse(ceiling(pr$ncases) < 10, 10, 0) * kappa, ceiling(pr$ncontrols)), expected.ncontrols[as.character(kappa), as.character(theta)])
			expect_equal(max(10, ifelse(ceiling(pr$ncontrols) < 10, 10, 0) / kappa, ceiling(pr$ncases)), expected.ncases[as.character(kappa), as.character(theta)])
		}
	}
})


test_that("kappa works with a single ROC curve", {
	# kappa from data
	res <- power.roc.test(r.s100b, sig.level = 0.05, power = 0.9)
	expect_equal(res$ncases, 23.5598674)
	expect_equal(res$ncontrols, 41.3734257)
	expect_equal(res$ncases/res$ncontrols, length(r.s100b$cases)/length(r.s100b$controls))
	# set kappa
	res <- power.roc.test(r.s100b, sig.level = 0.05, power = 0.9, kappa = 1)
	expect_equal(res$ncases, 29.5697422)
	expect_equal(res$ncontrols, 29.5697422)
})


test_that("kappa works with two ROC curves", {
	# kappa from data
	res <- power.roc.test(r.s100b, r.ndka, sig.level = 0.05, power = 0.9)
	expect_equal(res$ncases, 213.117677)
	expect_equal(res$ncontrols, 374.255432)
	expect_equal(res$ncases/res$ncontrols, length(r.s100b$cases)/length(r.s100b$controls))
	# set kappa
	res <- power.roc.test(r.s100b, r.ndka, sig.level = 0.05, power = 0.9, kappa = 1)
	expect_equal(res$ncases, 213.117677)
	expect_equal(res$ncases, 213.117677)
	# ...
})

Try the pROC package in your browser

Any scripts or data that you put into this service are public.

pROC documentation built on Nov. 2, 2023, 6:05 p.m.