Skip to content

Commit 7a36b76

Browse files
committed
MVP?
1 parent 43a1b38 commit 7a36b76

File tree

6 files changed

+166
-89
lines changed

6 files changed

+166
-89
lines changed

DESCRIPTION

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ License: MIT + file LICENSE
1111
Encoding: UTF-8
1212
Roxygen: list(markdown = TRUE)
1313
RoxygenNote: 7.3.2
14+
Suggests:
15+
testthat (>= 3.0.0)
16+
Config/testthat/edition: 3

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
# Generated by roxygen2: do not edit by hand
22

3+
export(vfisher.test)
4+
import(data.table)

R/vfisher.R

Lines changed: 28 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,16 @@ pnhyper <- function(
2626
q, ncp = 1, upper.tail = FALSE, m, n, k, lo, hi, support, logdc
2727
) {
2828

29-
lims <- which(ncp == 0 | is.infinite(ncp))
30-
ones <- which(ncp == 1)
31-
ncp1 <- which(ncp == 1)
32-
res <- numeric(length(q))
33-
34-
res[lims] <- ifelse(
35-
ncp == 0,
36-
as.numeric(if (upper.tail) q <= lo else q >= lo),
37-
as.numeric(if (upper.tail) q <= hi else q >= hi)
38-
)
39-
40-
res[ones] <- phyper(
41-
x[ones] - upper.tail, m[ones], n[ones], k[ones],
42-
lower.tail = !upper.tail
43-
)
44-
45-
res[-c(ones, lims)] <- mapply(
46-
function(q, ncp, logdc, support) {
47-
sum(dnhyper(ncp, logdc, support)[if (upper.tail) support >= q else support <= q])
48-
},
49-
q = q[-c(ones, lims)], ncp = ncp[-c(ones, lims)], logdc = logdc[-c(ones, lims)],
50-
support = support[-c(ones, lims)]
51-
)
29+
if (ncp == 0) {
30+
return(as.numeric(if (upper.tail) q <= lo else q >= lo))
31+
} else if (is.infinite(ncp)) {
32+
return(as.numeric(if (upper.tail) q <= hi else q >= hi))
33+
} else if (ncp == 1) {
34+
return(phyper(q - upper.tail, m, n, k, lower.tail = !upper.tail))
35+
} else {
36+
return(sum(dnhyper(ncp, logdc, support)[if (upper.tail) q <= support else support <= q]))
37+
}
5238

53-
return(res)
5439
}
5540

5641
# ncp.U == ncp_ci(..., lower = FALSE)
@@ -126,6 +111,8 @@ vfisher.test <- function(
126111
or <- rep(or, length.out = length(a))
127112
}
128113

114+
alternative <- match.arg(alternative)
115+
129116
# matrix =
130117
# a, b
131118
# c, d
@@ -151,6 +138,20 @@ vfisher.test <- function(
151138
logdc := .(list(dhyper(support[[1]], m, n, k, log = TRUE))), by = rowid
152139
]
153140

141+
if (alternative %in% c("less", "greater")) {
142+
result_dt[, p.value := pnhyper(
143+
a, or, upper.tail = alternative == "greater", m, n, k, lo, hi, support[[1]], logdc[[1]]
144+
), by = rowid]
145+
} else {
146+
result_dt[or == 0, p.value := as.numeric(a == lo)]
147+
result_dt[is.infinite(or), p.value := as.numeric(a == hi)]
148+
result_dt[!(or == 0 | is.infinite(or)), p.value := {
149+
relErr <- 1 + 10^(-7)
150+
d <- dnhyper(or, logdc[[1]], support[[1]])
151+
sum(d[d <= d[a - lo + 1] * relErr])
152+
}, by = rowid]
153+
}
154+
154155
# x == a
155156

156157
result_dt[a == lo, estimate := 0]
@@ -164,16 +165,12 @@ vfisher.test <- function(
164165

165166
if (conf.int) {
166167

167-
alternative <- match.arg(alternative)
168-
169168
if (!((length(conf.level) == 1L) && is.finite(conf.level) &&
170169
(conf.level > 0) && (conf.level < 1)))
171170
stop("'conf.level' must be a single number between 0 and 1")
172171

173172
sdcols <- c(sdcols, c("ci.lo", "ci.hi"))
174173

175-
setattr(result_dt, "conf.level", conf.level)
176-
177174
if (alternative == "less") {
178175
result_dt[, ci.lo := 0]
179176
result_dt[, ci.hi := ncp_ci(a, 1 - conf.level, m, n, k, lo, hi, support[[1]], logdc[[1]], lower = FALSE), by = rowid]
@@ -188,68 +185,10 @@ vfisher.test <- function(
188185

189186
}
190187

191-
result_dt[, .SD, .SDcols = sdcols]
192-
193-
}
188+
res <- result_dt[, .SD, .SDcols = sdcols]
194189

190+
if (conf.int) setattr(result_dt, "conf.level", conf.level)
195191

196-
function (x, y = NULL, workspace = 2e+05, hybrid = FALSE, hybridPars = c(expect = 5,
197-
percent = 80, Emin = 1), control = list(), or = 1, alternative = "two.sided",
198-
conf.int = TRUE, conf.level = 0.95, simulate.p.value = FALSE,
199-
B = 2000)
200-
{
201-
202-
PVAL <- NULL
203-
else {
204-
PVAL <- switch(alternative, less = pnhyper(x, or),
205-
greater = pnhyper(x, or, upper.tail = TRUE),
206-
two.sided = {
207-
if (or == 0) as.numeric(x == lo) else if (or ==
208-
Inf) as.numeric(x == hi) else {
209-
relErr <- 1 + 10^(-7)
210-
d <- dnhyper(or)
211-
sum(d[d <= d[x - lo + 1] * relErr])
212-
}
213-
})
214-
}
192+
return(res)
215193

216-
ESTIMATE <- c(`odds ratio` = mle(x))
217-
if (conf.int) {
218-
ncp.U <- function(x, alpha) {
219-
if (x == hi)
220-
return(Inf)
221-
p <- pnhyper(x, 1)
222-
if (p < alpha)
223-
uniroot(function(t) pnhyper(x, t) - alpha,
224-
c(0, 1))$root
225-
else if (p > alpha)
226-
1/uniroot(function(t) pnhyper(x, 1/t) - alpha,
227-
c(.Machine$double.eps, 1))$root
228-
else 1
229-
}
230-
ncp.L <- function(x, alpha) {
231-
if (x == lo)
232-
return(0)
233-
p <- pnhyper(x, 1, upper.tail = TRUE)
234-
if (p > alpha)
235-
uniroot(function(t) pnhyper(x, t, upper.tail = TRUE) -
236-
alpha, c(0, 1))$root
237-
else if (p < alpha)
238-
1/uniroot(function(t) pnhyper(x, 1/t, upper.tail = TRUE) -
239-
alpha, c(.Machine$double.eps, 1))$root
240-
else 1
241-
}
242-
CINT <- switch(alternative, less = c(0, ncp.U(x,
243-
1 - conf.level)), greater = c(ncp.L(x, 1 - conf.level),
244-
Inf), two.sided = {
245-
alpha <- (1 - conf.level)/2
246-
c(ncp.L(x, alpha), ncp.U(x, alpha))
247-
})
248-
attr(CINT, "conf.level") <- conf.level
249-
}
250-
RVAL <- c(RVAL, list(conf.int = if (conf.int) CINT, estimate = ESTIMATE,
251-
null.value = NVAL))
252-
}
253-
structure(c(RVAL, alternative = alternative, method = METHOD,
254-
data.name = DNAME), class = "htest")
255194
}

man/vfisher.test.Rd

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat.R

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# This file is part of the standard setup for testthat.
2+
# It is recommended that you do not modify it.
3+
#
4+
# Where should you do additional test configuration?
5+
# Learn more about the roles of various files in:
6+
# * https://r-pkgs.org/testing-design.html#sec-tests-files-overview
7+
# * https://testthat.r-lib.org/articles/special-files.html
8+
9+
library(testthat)
10+
library(vfisher)
11+
12+
test_check("vfisher")

tests/testthat/test_vfisher.R

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
2+
test_that("vfisher gives same results as fisher.test example 1", {
3+
TeaTasting <- matrix(
4+
c(3, 1, 1, 3), nrow = 2, dimnames = list(
5+
Guess = c("Milk", "Tea"),
6+
Truth = c("Milk", "Tea")
7+
))
8+
fref <- fisher.test(TeaTasting, alternative = "greater")
9+
vres <- vfisher.test(
10+
TeaTasting[1,1], TeaTasting[1, 2], TeaTasting[2, 1], TeaTasting[2, 2], alternative = "greater"
11+
)
12+
expect_equal(fref$p.value, vres[, p.value])
13+
expect_equal(unname(fref$estimate), vres[, estimate])
14+
expect_equal(fref$conf.int[1], vres[, ci.lo])
15+
expect_equal(fref$conf.int[2], vres[, ci.hi])
16+
})
17+
18+
test_that("vfisher gives same results as fisher.test example 2", {
19+
Convictions <- matrix(c(2, 10, 15, 3), nrow = 2,
20+
dimnames =
21+
list(c("Dizygotic", "Monozygotic"),
22+
c("Convicted", "Not convicted")))
23+
fref <- fisher.test(Convictions, alternative = "less")
24+
vres <- vfisher.test(
25+
Convictions[1,1], Convictions[1, 2], Convictions[2, 1], Convictions[2, 2], alternative = "less"
26+
)
27+
expect_equal(fref$p.value, vres[, p.value])
28+
expect_equal(unname(fref$estimate), vres[, estimate])
29+
expect_equal(fref$conf.int[1], vres[, ci.lo])
30+
expect_equal(fref$conf.int[2], vres[, ci.hi])
31+
})
32+
33+
test_that("vfisher gives same results as fisher.test example 1 & 2", {
34+
TeaTasting <- matrix(
35+
c(3, 1, 1, 3), nrow = 2, dimnames = list(
36+
Guess = c("Milk", "Tea"),
37+
Truth = c("Milk", "Tea")
38+
))
39+
Convictions <- matrix(c(2, 10, 15, 3), nrow = 2,
40+
dimnames =
41+
list(c("Dizygotic", "Monozygotic"),
42+
c("Convicted", "Not convicted")))
43+
fref1 <- fisher.test(TeaTasting)
44+
fref2 <- fisher.test(Convictions)
45+
46+
vres <- vfisher.test(
47+
c(TeaTasting[1,1], Convictions[1,1]),
48+
c(TeaTasting[1,2], Convictions[1,2]),
49+
c(TeaTasting[2,1], Convictions[2,1]),
50+
c(TeaTasting[2,2], Convictions[2,2])
51+
)
52+
53+
expect_equal(fref1$p.value, vres[1, p.value])
54+
expect_equal(fref2$p.value, vres[2, p.value])
55+
expect_equal(unname(fref1$estimate), vres[1, estimate])
56+
expect_equal(unname(fref2$estimate), vres[2, estimate])
57+
expect_equal(as.numeric(fref1$conf.int), vres[1, c(ci.lo, ci.hi)])
58+
expect_equal(as.numeric(fref2$conf.int), vres[2, c(ci.lo, ci.hi)])
59+
})

0 commit comments

Comments
 (0)