From 6e2fa522fd71cf06edea512519d5acaeb743cea5 Mon Sep 17 00:00:00 2001 From: Sebastian Henz <sebastian.henz@ufz.de> Date: Thu, 27 Feb 2020 13:41:19 +0100 Subject: [PATCH] * `ecxsys()` gains a new argument `curves_concentration_max` which sets the maximum concentration for the curves. * `ec()` raises an error if the requested curve is too short to calculate the result. * Improve merge request template. * Update readme example. --- .../merge_into_master.md | 9 +++--- DESCRIPTION | 4 +-- NEWS.md | 5 +++ R/ec.R | 26 +++++++++++---- R/ecxsys.R | 32 ++++++++++++------- README.md | 8 ++--- man/ec.Rd | 7 +++- man/ecxsys.Rd | 5 +++ tests/testthat/test-ec.R | 14 ++++++++ tests/testthat/test-ecxsys.R | 19 +++++++++-- 10 files changed, 97 insertions(+), 32 deletions(-) diff --git a/.gitlab/merge_request_templates/merge_into_master.md b/.gitlab/merge_request_templates/merge_into_master.md index 670d525..a8b3a26 100644 --- a/.gitlab/merge_request_templates/merge_into_master.md +++ b/.gitlab/merge_request_templates/merge_into_master.md @@ -18,14 +18,13 @@ Comment on any notes that come up during devtools::check(). ### checklist <!-- Please check `[x]` the applicable boxes. --> -* [ ] This merge includes bug fixes or changes in the functionality of the package (whether they are user-visible or not). - * [ ] update documentation including examples (if necessary) - * [ ] update and polish NEWS.md - * [ ] update version in DESCRIPTION - * [ ] update date in DESCRIPTION * [ ] This merge introduces new features. * [ ] new features are documented * [ ] new features have tests +* [ ] update documentation including examples (if necessary) +* [ ] update and polish NEWS.md +* [ ] update version in DESCRIPTION +* [ ] update date in DESCRIPTION * [ ] no remaining TODO, FIXME, or debug prints anywhere in the source files * [ ] `devtools::document()` * [ ] `devtools::check()` without errors or warnings diff --git a/DESCRIPTION b/DESCRIPTION index e96c337..ae78b57 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: stressaddition Type: Package Title: Modeling Tri-Phasic Concentration-Response Relationships -Version: 2.1.1 -Date: 2020-02-26 +Version: 2.2.0 +Date: 2020-02-27 Authors@R: c(person("Sebastian", "Henz", role = c("aut", "cre"), diff --git a/NEWS.md b/NEWS.md index 53a54a4..1379a56 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,8 @@ +# stressaddition 2.2.0 + +* `ec()` raises an error if the curve does not cross the desired response level. +* `ecxsys()` gains a new argument `curves_concentration_max` which allows setting the maximum concentration of the predicted curves. + # stressaddition 2.1.1 * Restore the default behaviour of `plot_effect()` to also show `effect_tox` and `effect_tox_env`. diff --git a/R/ec.R b/R/ec.R index 66d2592..5fb444b 100644 --- a/R/ec.R +++ b/R/ec.R @@ -7,6 +7,11 @@ #' happen for low values of \code{response_level}, then the occurrence with the #' smallest concentration is returned. #' +#' This function only makes sense for curves which generally go down with +#' increasing concentration, i.e. all \code{effect_*} curves and also +#' \code{sys_tox} and \code{sys_tox_env}. Others are untested and may give +#' unexpected results, if any. +#' #' @param model This can be one of three types of objects: Either the output of #' \code{\link{ecxsys}} or the output of \code{\link{predict_ecxsys}} or a #' data frame with a "concentration" column and a \code{response_name} column. @@ -15,7 +20,7 @@ #' calculate the EC. Must be one of \code{colnames(model$curves)}. #' @param response_level The desired response level as a percentage between 0 #' and 100. For example with the value 10 the function will return the EC10, -#' i.e. the concentration where the response falls below 90 \% of the maximum +#' i.e. the concentration where the response falls below 90 \% of the control #' response. #' #' @return A list containing the response concentration and the corresponding @@ -77,18 +82,25 @@ ec <- function(model, response_name, response_level) { reference <- response[1] if (reference == 0) { - stop("Reference value is zero, calculation of EC not possible.") + stop("Reference value is zero, calculation of EC is impossible.") } - response_level <- (1 - response_level / 100) * reference - output <- list(response_value = response_level) + response_value <- (1 - response_level / 100) * reference + output <- list(response_value = response_value) # Get the index of where the response changes from above to below - # response_level: - below <- which(response < response_level)[1] + # response_value: + below <- which(response < response_value)[1] + if (is.na(below)) { + stop("The curve '", response_name, "' does not fall below ", + 100 - response_level, "% of the control, which makes ", + "determining the EC", response_level, " impossible.\n You could ", + "try using predict_ecxsys() to predict more values in a wider ", + "concentration range.") + } above <- below - 1 # linear interpolation - dist <- (response_level - response[below]) / (response[above] - response[below]) + dist <- (response_value - response[below]) / (response[above] - response[below]) output$concentration <- dist * (concentration[above] - concentration[below]) + concentration[below] output diff --git a/R/ecxsys.R b/R/ecxsys.R index 95e090d..ccab533 100644 --- a/R/ecxsys.R +++ b/R/ecxsys.R @@ -1,9 +1,10 @@ # Note about the setting and resetting of options: # The drc package changes some of the options which some users may have # modified. Of particular interest is options("warn") because it is important -# that the ecxsys function can generate some warnings. So every time drc::drm is -# called this option is cached beforehand and reset afterwards. Additionally, -# all options are cached at the beginning of ecxsys and reset on exit. +# that the user is able to control the visibility of warnings generated by +# ecxsys. Therefore every time drc::drm is called this option is cached +# beforehand and reset immediately afterwards. Additionally, all options are +# cached at the beginning of ecxsys and reset on exit. #' ECx-SyS @@ -31,6 +32,9 @@ #' environmental stress. Must be between 0 and \code{effect_max}. #' @param effect_max The maximum value the effect could possibly reach. For #' survival data in percent this should be 100 (the default). +#' @param curves_concentration_max The maximum concentration of the predicted +#' curves. This might be useful if for example your highest observed +#' concentration is 30 but you would like to know the predicted values at 100. #' @param p,q The shape parameters of the beta distribution. Default is 3.2. #' #' @return A list (of class ecxsys) containing many different objects of which @@ -86,6 +90,7 @@ ecxsys <- function(concentration, effect_tox_observed, effect_tox_env_observed = NULL, effect_max = 100, + curves_concentration_max = NULL, p = 3.2, q = 3.2) { output <- list(args = as.list(environment())) @@ -268,23 +273,28 @@ ecxsys <- function(concentration, } - # smooth curves ------------------------------------------------------- + # curves ------------------------------------------------------- # In order to generate a broken x-axis the concentration vector must # also be broken in two. The left part of the axis is supposed to be at # 0 but because it's a log axis I have to make the values just really # small. The concentrations in the gap won't be used for plotting later. - n_smooth <- 1000 # number of points to approximate the curves + len_curves <- 1000 # number of points to approximate the curves conc_adjust_factor <- 10^-5 output$conc_adjust_factor <- conc_adjust_factor - concentration_smooth <- 10 ^ seq( + if (is.null(curves_concentration_max)) { + curves_concentration_max <- max(concentration) + } else if (curves_concentration_max < min(concentration[concentration > 0])) { + stop("'curves_concentration_max' is too low.") + } + curves_concentration <- 10 ^ seq( log10(min_conc * conc_adjust_factor), - log10(max(concentration)), - length.out = n_smooth + log10(curves_concentration_max), + length.out = len_curves ) - output$curves <- predict_ecxsys(output, concentration_smooth) + output$curves <- predict_ecxsys(output, curves_concentration) output$curves$use_for_plotting <- - concentration_smooth < min_conc * conc_adjust_factor * 1.5 | - concentration_smooth > min_conc * 1.5 + curves_concentration < min_conc * conc_adjust_factor * 1.5 | + curves_concentration > min_conc * 1.5 output } diff --git a/README.md b/README.md index 6522297..7ffe064 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,10 @@ In the paper we use the model in the context of survival experiments. However, i ```r library(stressaddition) model <- ecxsys( - concentration = c(0, 0.03, 0.3, 3, 10), - hormesis_concentration = 0.3, - effect_tox_observed = c(85, 76, 94, 35, 0), - effect_tox_env_observed = c(24, 23, 32, 0, 0) + concentration = c(0, 0.05, 0.5, 5, 30), + hormesis_concentration = 0.5, + effect_tox_observed = c(90, 81, 92, 28, 0), + effect_tox_env_observed = c(29, 27, 33, 5, 0) ) # Plot the effect and the system stress: diff --git a/man/ec.Rd b/man/ec.Rd index dea6581..0cacd6a 100644 --- a/man/ec.Rd +++ b/man/ec.Rd @@ -17,7 +17,7 @@ calculate the EC. Must be one of \code{colnames(model$curves)}.} \item{response_level}{The desired response level as a percentage between 0 and 100. For example with the value 10 the function will return the EC10, -i.e. the concentration where the response falls below 90 \% of the maximum +i.e. the concentration where the response falls below 90 \% of the control response.} } \value{ @@ -32,6 +32,11 @@ to the control. If the response level occurs multiple times because of hormesis, which may happen for low values of \code{response_level}, then the occurrence with the smallest concentration is returned. + +This function only makes sense for curves which generally go down with +increasing concentration, i.e. all \code{effect_*} curves and also +\code{sys_tox} and \code{sys_tox_env}. Others are untested and may give +unexpected results, if any. } \examples{ # Calculate the EC_10, the concentration where the effect falls diff --git a/man/ecxsys.Rd b/man/ecxsys.Rd index 620adb3..4d5bed1 100644 --- a/man/ecxsys.Rd +++ b/man/ecxsys.Rd @@ -10,6 +10,7 @@ ecxsys( effect_tox_observed, effect_tox_env_observed = NULL, effect_max = 100, + curves_concentration_max = NULL, p = 3.2, q = 3.2 ) @@ -31,6 +32,10 @@ environmental stress. Must be between 0 and \code{effect_max}.} \item{effect_max}{The maximum value the effect could possibly reach. For survival data in percent this should be 100 (the default).} +\item{curves_concentration_max}{The maximum concentration of the predicted +curves. This might be useful if for example your highest observed +concentration is 30 but you would like to know the predicted values at 100.} + \item{p, q}{The shape parameters of the beta distribution. Default is 3.2.} } \value{ diff --git a/tests/testthat/test-ec.R b/tests/testthat/test-ec.R index a4cb740..138fbce 100644 --- a/tests/testthat/test-ec.R +++ b/tests/testthat/test-ec.R @@ -74,3 +74,17 @@ test_that("ec values have not changed", { tolerance = 1e-4 ) }) + + +test_that("ec fails when response_level is outside the range of the curve", { + model <- ecxsys( + concentration = c(0, 0.05, 0.5, 5), + hormesis_concentration = 0.5, + effect_tox_observed = c(90, 81, 92, 28) + ) + expect_error( + ec(model, "effect_tox_sys", 90), + "You could try using predict_ecxsys() to predict more values", + fixed = TRUE + ) +}) diff --git a/tests/testthat/test-ecxsys.R b/tests/testthat/test-ecxsys.R index 0120163..9911f1d 100644 --- a/tests/testthat/test-ecxsys.R +++ b/tests/testthat/test-ecxsys.R @@ -164,6 +164,7 @@ test_that("function arguments are returned unchanged", { effect_tox_observed = c(90, 81, 92, 28, 0), effect_tox_env_observed = c(29, 27, 33, 5, 0), effect_max = 100, + curves_concentration_max = NULL, p = 3.2, q = 3.2 ) @@ -232,9 +233,9 @@ test_that("sys model not converging produces a warning, not an error", { expect_warning( ecxsys( concentration = c(0, 0.1, 0.5, 1, 10, 33), + hormesis_concentration = 0.5, effect_tox_observed = c(90, 81, 92, 50, 18, 0), - effect_tox_env_observed = c(75, 89, 54, 7, 0, 0), - hormesis_concentration = 0.5 + effect_tox_env_observed = c(75, 89, 54, 7, 0, 0) ), paste( "Using a horizontal linear model for sys_tox_env_mod because the", @@ -243,3 +244,17 @@ test_that("sys model not converging produces a warning, not an error", { fixed = TRUE ) }) + + +test_that("error when curves_concentration_max is too low", { + expect_error( + ecxsys( + concentration = c(0, 0.05, 0.5, 5, 30), + hormesis_concentration = 0.5, + effect_tox_observed = c(90, 81, 92, 28, 0), + curves_concentration_max = 0.01 + ), + "'curves_concentration_max' is too low.", + fixed = TRUE + ) +}) -- GitLab