Skip to content

Commit e09856c

Browse files
committed
added fisher tests for data
1 parent 14c58b8 commit e09856c

File tree

85 files changed

+5795
-224
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+5795
-224
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
.Ruserdata
55
inst/doc
66
docs
7+
int/**/*.html

DESCRIPTION

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,7 @@ Imports:
3939
rlang,
4040
MASS,
4141
stringr,
42-
tidyselect
42+
tidyselect,
43+
rstatix,
44+
tibble
4345
Remotes: DoseResponse/bmd

NAMESPACE

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@ export(addECxCI)
1919
export(aggregate_from_individual_simple)
2020
export(aggregate_from_individual_tidy)
2121
export(backCalcSE)
22+
export(broom_dunnett)
23+
export(broom_williams)
2224
export(calcNW)
2325
export(calcSteepnessOverlap)
2426
export(calcTaronesTest)
27+
export(calculate_noec_rstatix)
28+
export(compare_to_control_fisher)
2529
export(contEndpoint)
2630
export(convert2Score)
2731
export(convert_fish_data)
32+
export(create_contingency_table)
2833
export(dose.p.glmmPQL)
2934
export(drcCompare)
3035
export(expand_to_individual_simple)
@@ -39,13 +44,14 @@ export(get_RS_adj_val)
3944
export(getwilliamRes)
4045
export(invlogxp)
4146
export(logxp)
47+
export(many_to_one_fisher_test)
4248
export(monotonicityTest)
4349
export(mselect.ED)
4450
export(mselect.ZG)
4551
export(mselect.plus)
4652
export(pavaMean)
47-
export(plot.edList)
4853
export(plot.modList)
54+
export(plot_edList)
4955
export(prelimPlot1)
5056
export(prelimPlot2)
5157
export(prelimPlot3)
@@ -59,6 +65,7 @@ export(run_all_threshold_tests)
5965
export(run_threshold_RSCA)
6066
export(simDRdata)
6167
export(simplifyTreatment)
68+
export(simulate_dose_response)
6269
export(stepDownRSCABS)
6370
export(stepKRSCABS)
6471
export(step_down_RSCABS)
@@ -69,8 +76,13 @@ export(williamsTest_JG)
6976
import(dplyr)
7077
import(ggplot2)
7178
import(scales)
79+
importFrom(DescTools,DunnettTest)
80+
importFrom(PMCMRplus,dunnettTest)
81+
importFrom(PMCMRplus,williamsTest)
7282
importFrom(bmd,bmd)
83+
importFrom(dplyr,"%>%")
7384
importFrom(dplyr,across)
85+
importFrom(dplyr,add_row)
7486
importFrom(dplyr,arrange)
7587
importFrom(dplyr,bind_rows)
7688
importFrom(dplyr,filter)
@@ -79,8 +91,10 @@ importFrom(dplyr,left_join)
7991
importFrom(dplyr,mutate)
8092
importFrom(dplyr,select)
8193
importFrom(dplyr,summarize)
94+
importFrom(dplyr,ungroup)
8295
importFrom(dplyr,where)
8396
importFrom(drc,ED)
97+
importFrom(drc,getMeanFunctions)
8498
importFrom(ggplot2,aes)
8599
importFrom(ggplot2,element_blank)
86100
importFrom(ggplot2,element_text)
@@ -96,24 +110,42 @@ importFrom(graphics,text)
96110
importFrom(isotone,gpava)
97111
importFrom(magrittr,"%>%")
98112
importFrom(metafor,rma.mh)
113+
importFrom(multcomp,glht)
114+
importFrom(multcomp,mcp)
115+
importFrom(purrr,map)
99116
importFrom(purrr,map2)
100117
importFrom(rlang,":=")
101118
importFrom(rlang,.data)
119+
importFrom(rlang,enquo)
120+
importFrom(rlang,quo_name)
121+
importFrom(rlang,sym)
122+
importFrom(rstatix,add_significance)
123+
importFrom(rstatix,adjust_pvalue)
124+
importFrom(rstatix,fisher_test)
125+
importFrom(rstatix,t_test)
126+
importFrom(rstatix,wilcox_test)
127+
importFrom(stats,"contrasts<-")
102128
importFrom(stats,AIC)
103129
importFrom(stats,aggregate)
104130
importFrom(stats,aov)
105131
importFrom(stats,approx)
132+
importFrom(stats,as.formula)
106133
importFrom(stats,coef)
134+
importFrom(stats,confint)
107135
importFrom(stats,fisher.test)
108136
importFrom(stats,lm)
109137
importFrom(stats,logLik)
138+
importFrom(stats,p.adjust)
110139
importFrom(stats,pnorm)
111140
importFrom(stats,predict)
112141
importFrom(stats,qnorm)
113142
importFrom(stats,qt)
114143
importFrom(stats,sd)
144+
importFrom(stats,setNames)
115145
importFrom(stats,summary.lm)
116146
importFrom(stats,update)
117147
importFrom(stats,var)
118148
importFrom(stats,vcov)
119149
importFrom(stats,xtabs)
150+
importFrom(tibble,tibble)
151+
importFrom(tidyselect,all_of)

R/Endpoints.R

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pavaMean <- function(x,g,alternative = "greater"){
7373
#' Summary Williams Test results.
7474
#'
7575
#' @param object William test result object
76+
#' @param verbose whether to print intermediate results.
7677
#' @param ... additional parameters to be passed into the function, placeholder
7778
#'
7879
#' @return William test results
@@ -169,7 +170,7 @@ summaryZG <- function (object, verbose=F,...)
169170
#' res <- PMCMRplus::williamsTest(x ~ g)
170171
#' getwilliamRes(res)
171172
getwilliamRes <- function(william,n=NULL){
172-
if(inherits(william,"class")=="try-error"){
173+
if(inherits(william,"try-error")){
173174
if(is.null(n)) stop("when the test does not return a valid results, you need to specify
174175
the number of hypotheses. ")
175176
return(rep(NA,n=n))
@@ -290,7 +291,7 @@ contEndpoint <- function(paov,pks,pnormal,phomogeneity,monotonicity,william,dunn
290291
}else{ ## monotonic
291292
if(pnormal>0.05 & phomogeneity>0.05){
292293
## check if PAVA Problem!
293-
if(class(william)!="try-error"){
294+
if(!inherits(william,"try-error")){
294295
# pvals <- as.character(summaryZG(william)$decision)
295296
#pvals <- as.numeric(plyr::mapvalues(pvals,from=c("accept","reject"),to=c(0.2,0.01)))
296297
pvals <- william
@@ -312,7 +313,4 @@ contEndpoint <- function(paov,pks,pnormal,phomogeneity,monotonicity,william,dunn
312313
return(NOEC)
313314
}
314315

315-
## find / -type f -exec grep -H 'text-to-find-here' {} \;
316-
## grep -rnw '/path/to/somewhere/' -e 'contEndpoint'
317316

318-
## find . -name "*.Rmd" | xargs -d '\n' grep -i "contEndpoint"

R/NOEC.R

Lines changed: 163 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,176 @@
11
## Function to calculate NOECs using multiple methods and broomed together
2+
## Unit Testing in # FILE: tests/testthat/test_quantal_categorical.R
23

34

45

5-
## Unit Testing in # FILE: tests/testthat/test_quantal_categorical.R
6-
7-
#' Tarone's Z Test
6+
#' Calculate NOEC Using Many-to-one Pairwise Tests
87
#'
9-
#' Tests the goodness of fit of the binomial distribution.
8+
#' This function calculates the No Observed Effect Concentration (NOEC) from dose response data
9+
#' using pairwise comparison tests from the rstatix package.
1010
#'
11-
#' @param M Counts
12-
#' @param N Trials
11+
#' @param data A data frame containing the dose response data
12+
#' @param response The name of the response variable (unquoted)
13+
#' @param dose The name of the dose variable (unquoted)
14+
#' @param control The level of the dose variable to be used as control
15+
#' @param test The statistical test to use: "t.test" or "wilcox.test"
16+
#' @param p_adjust_method Method for p-value adjustment for multiple comparisons
17+
#' @param alternative Direction of the alternative hypothesis: "two.sided", "greater", or "less"
18+
#' @param alpha Significance level (default: 0.05)
1319
#'
14-
#' @return a \code{htest} object
15-
#'
16-
#' @author \href{https://stats.stackexchange.com/users/173082/reinstate-monica}{Ben O'Neill}
17-
#' @references \url{https://stats.stackexchange.com/a/410376/6378} and
18-
#' R. E. TARONE, Testing the goodness of fit of the binomial distribution, Biometrika, Volume 66, Issue 3, December 1979, Pages 585–590, \url{https://doi.org/10.1093/biomet/66.3.585}
19-
#' @importFrom stats pnorm
20+
#' @return A list containing the NOEC value and the full test results
2021
#' @export
21-
#' @examples
22-
#' #Generate example data
23-
#' N <- c(30, 32, 40, 28, 29, 35, 30, 34, 31, 39)
24-
#' M <- c( 9, 10, 22, 15, 8, 19, 16, 19, 15, 10)
25-
#' Tarone.test(N, M)
26-
Tarone.test <- function(N, M) {
27-
28-
#Check validity of inputs
29-
if(!(all(N == as.integer(N)))) { stop("Error: Number of trials should be integers"); }
30-
if(min(N) < 1) { stop("Error: Number of trials should be positive"); }
31-
if(!(all(M == as.integer(M)))) { stop("Error: Count values should be integers"); }
32-
if(min(M) < 0) { stop("Error: Count values cannot be negative"); }
33-
if(any(M > N)) { stop("Error: Observed count value exceeds number of trials"); }
34-
35-
#Set description of test and data
36-
method <- "Tarone's Z test";
37-
data.name <- paste0(deparse(substitute(M)), " successes from ",
38-
deparse(substitute(N)), " trials");
39-
40-
#Set null and alternative hypotheses
41-
null.value <- 0;
42-
attr(null.value, "names") <- "dispersion parameter";
43-
alternative <- "greater";
44-
45-
#Calculate test statistics
46-
estimate <- sum(M)/sum(N);
47-
attr(estimate, "names") <- "proportion parameter";
48-
S <- ifelse(estimate == 1, sum(N),
49-
sum((M - N*estimate)^2/(estimate*(1 - estimate))));
50-
statistic <- (S - sum(N))/sqrt(2*sum(N*(N-1)));
51-
attr(statistic, "names") <- "z";
52-
53-
#Calculate p-value
54-
p.value <- 2*pnorm(-abs(statistic), 0, 1);
55-
attr(p.value, "names") <- NULL;
56-
57-
#Create htest object
58-
TEST <- list(method = method, data.name = data.name,
59-
null.value = null.value, alternative = alternative,
60-
estimate = estimate, statistic = statistic, p.value = p.value);
61-
class(TEST) <- "htest";
62-
TEST;
22+
#'
23+
#' @importFrom dplyr mutate filter arrange %>%
24+
#' @importFrom rlang enquo quo_name .data
25+
#' @importFrom rstatix t_test wilcox_test
26+
#' @importFrom stats as.formula setNames
27+
calculate_noec_rstatix <- function(data, response, dose, control = "0",
28+
test = c("t.test", "wilcox.test"),
29+
p_adjust_method = "holm",
30+
alternative = "two.sided",
31+
alpha = 0.05) {
32+
33+
# Match test argument
34+
test <- match.arg(test)
35+
36+
# Capture variable names using rlang
37+
response_var <- rlang::enquo(response)
38+
dose_var <- rlang::enquo(dose)
39+
response_name <- rlang::quo_name(response_var)
40+
dose_name <- rlang::quo_name(dose_var)
41+
42+
# Ensure dose is a factor
43+
if (!is.factor(data[[dose_name]])) {
44+
data[[dose_name]] <- as.factor(data[[dose_name]])
45+
}
46+
47+
# Create formula
48+
formula_obj <- stats::as.formula(paste(response_name, "~", dose_name))
49+
50+
# Run the appropriate test
51+
if (test == "t.test") {
52+
test_results <- rstatix::t_test(
53+
data = data,
54+
formula = formula_obj,
55+
ref.group = control,
56+
p.adjust.method = p_adjust_method,
57+
alternative = alternative
58+
)
59+
} else if (test == "wilcox.test") {
60+
test_results <- rstatix::wilcox_test(
61+
data = data,
62+
formula = formula_obj,
63+
ref.group = control,
64+
p.adjust.method = p_adjust_method,
65+
alternative = alternative
66+
)
67+
}
68+
69+
# Extract dose levels and convert to numeric for sorting
70+
dose_levels <- levels(data[[dose_name]])
71+
numeric_doses <- suppressWarnings(as.numeric(dose_levels))
72+
73+
# If conversion to numeric was successful, reorder the test results
74+
if (!any(is.na(numeric_doses))) {
75+
# Create a mapping from dose level to numeric value
76+
dose_mapping <- stats::setNames(numeric_doses, dose_levels)
77+
78+
# Add numeric dose values to test results
79+
test_results <- test_results %>%
80+
dplyr::mutate(
81+
group1_numeric = dose_mapping[.data$group1],
82+
group2_numeric = dose_mapping[.data$group2]
83+
)
84+
85+
# Determine which column contains the non-control doses
86+
if (all(test_results$group1 == control)) {
87+
test_results <- test_results %>%
88+
dplyr::mutate(dose_numeric = .data$group2_numeric)
89+
} else if (all(test_results$group2 == control)) {
90+
test_results <- test_results %>%
91+
dplyr::mutate(dose_numeric = .data$group1_numeric)
92+
} else {
93+
# Mixed case - need to handle both possibilities
94+
test_results <- test_results %>%
95+
dplyr::mutate(
96+
dose_numeric = ifelse(.data$group1 == control,
97+
.data$group2_numeric,
98+
.data$group1_numeric)
99+
)
100+
}
101+
102+
# Sort by numeric dose
103+
test_results <- test_results %>%
104+
dplyr::arrange(.data$dose_numeric)
105+
}
106+
107+
# Find the NOEC (highest dose with p > alpha)
108+
significant_results <- test_results %>%
109+
dplyr::filter(.data$p.adj <= alpha)
110+
111+
if (nrow(significant_results) == 0) {
112+
# If no significant differences, NOEC is the highest tested dose
113+
all_doses <- as.numeric(as.character(unique(data[[dose_name]])))
114+
all_doses <- all_doses[!is.na(all_doses) & all_doses != as.numeric(control)]
115+
116+
if (length(all_doses) > 0) {
117+
noec_value <- max(all_doses, na.rm = TRUE)
118+
noec_message <- "No significant effects detected at any dose level"
119+
} else {
120+
noec_value <- NA
121+
noec_message <- "No valid dose levels found"
122+
}
123+
} else {
124+
# Get all doses with significant effects
125+
significant_doses <- c()
126+
127+
for (i in 1:nrow(significant_results)) {
128+
row <- significant_results[i, ]
129+
if (row$group1 == control) {
130+
significant_doses <- c(significant_doses, as.character(row$group2))
131+
} else {
132+
significant_doses <- c(significant_doses, as.character(row$group1))
133+
}
134+
}
135+
136+
# Convert to numeric for comparison
137+
significant_doses <- suppressWarnings(as.numeric(significant_doses))
138+
139+
# Get all tested doses
140+
all_doses <- suppressWarnings(as.numeric(as.character(unique(data[[dose_name]]))))
141+
all_doses <- all_doses[!is.na(all_doses) & all_doses != as.numeric(control)]
142+
all_doses <- sort(all_doses)
143+
144+
# Find the highest non-significant dose
145+
non_significant_doses <- all_doses[!all_doses %in% significant_doses]
146+
147+
if (length(non_significant_doses) == 0) {
148+
# If all doses show significant effects
149+
noec_value <- as.numeric(control)
150+
noec_message <- "All tested doses showed significant effects"
151+
} else {
152+
noec_value <- max(non_significant_doses)
153+
noec_message <- paste("NOEC determined as", noec_value)
154+
}
155+
}
156+
157+
# Return both the NOEC value and the full test results
158+
return(list(
159+
noec = noec_value,
160+
noec_message = noec_message,
161+
test_results = test_results
162+
))
63163
}
64164

65165

66166

67167

168+
169+
170+
171+
172+
173+
174+
175+
176+

0 commit comments

Comments
 (0)