Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow p_significance() to accept non symetric threshold range #671

Merged
merged 1 commit into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Type: Package
Package: bayestestR
Title: Understand and Describe Bayesian Models and Posterior Distributions
Version: 0.14.0.3
Version: 0.14.0.4
Authors@R:
c(person(given = "Dominique",
family = "Makowski",
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
- Besides the existing `as.numeric()` method, `p_direction()` now also has an
`as.vector()` method.

* `p_significance()` now accepts non-symmetric ranges for the `threshold` argument.

* `p_to_pd()` now also works with data frames returned by `p_direction()`. If
a data frame contains a `pd`, `p_direction` or `PD` column name, this is assumed
to be the pd-values, which are then converted to p-values.
Expand Down
63 changes: 42 additions & 21 deletions R/p_significance.R
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
#' Practical Significance (ps)
#'
#' Compute the probability of **Practical Significance** (***ps***), which can be conceptualized as a unidirectional equivalence test. It returns the probability that effect is above a given threshold corresponding to a negligible effect in the median's direction. Mathematically, it is defined as the proportion of the posterior distribution of the median sign above the threshold.
#' Compute the probability of **Practical Significance** (***ps***), which can
#' be conceptualized as a unidirectional equivalence test. It returns the
#' probability that effect is above a given threshold corresponding to a
#' negligible effect in the median's direction. Mathematically, it is defined as
#' the proportion of the posterior distribution of the median sign above the
#' threshold.
#'
#' @param threshold The threshold value that separates significant from negligible effect. If `"default"`, the range is set to `0.1` if input is a vector, and based on [`rope_range()`][rope_range] if a Bayesian model is provided.
#' @param threshold The threshold value that separates significant from
#' negligible effect, which can have following possible values:
#' - `"default"`, in which case the range is set to `0.1` if input is a vector,
#' and based on [`rope_range()`] if a Bayesian model is provided.
#' - a single numeric value (e.g., 0.1), which is used as range around zero
#' (i.e. the threshold range is set to -0.1 and 0.1)
#' - a numeric vector of length two (e.g., `c(-0.2, 0.1)`).
#' @inheritParams rope
#' @inheritParams hdi
#'
Expand All @@ -18,7 +29,7 @@
#' range of the probability distribution `x`, `p_significance()`
#' will be less than 0.5, which indicates no clear practical significance.
#'
#' @note There is also a [`plot()`-method](https://easystats.github.io/see/articles/bayestestR.html) implemented in the \href{https://easystats.github.io/see/}{\pkg{see}-package}.

Check warning on line 32 in R/p_significance.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/p_significance.R,line=32,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 179 characters.
#'
#' @examplesIf require("rstanarm")
#' library(bayestestR)
Expand Down Expand Up @@ -67,7 +78,11 @@

#' @rdname p_significance
#' @export
p_significance.get_predicted <- function(x, threshold = "default", use_iterations = FALSE, verbose = TRUE, ...) {
p_significance.get_predicted <- function(x,
threshold = "default",
use_iterations = FALSE,
verbose = TRUE,
...) {
if (isTRUE(use_iterations)) {
if ("iterations" %in% names(attributes(x))) {
out <- p_significance(
Expand Down Expand Up @@ -214,7 +229,7 @@
p_significance.stanreg <- function(x,
threshold = "default",
effects = c("fixed", "random", "all"),
component = c("location", "all", "conditional", "smooth_terms", "sigma", "distributional", "auxiliary"),

Check warning on line 232 in R/p_significance.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/p_significance.R,line=232,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 139 characters.
parameters = NULL,
verbose = TRUE,
...) {
Expand All @@ -222,7 +237,7 @@
component <- match.arg(component)
threshold <- .select_threshold_ps(model = x, threshold = threshold, verbose = verbose)

data <- p_significance(

Check warning on line 240 in R/p_significance.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/p_significance.R,line=240,col=3,[object_overwrite_linter] 'data' is an exported object from package 'utils'. Avoid re-using such symbols.
insight::get_parameters(x, effects = effects, component = component, parameters = parameters),
threshold = threshold
)
Expand Down Expand Up @@ -258,7 +273,7 @@
component <- match.arg(component)
threshold <- .select_threshold_ps(model = x, threshold = threshold, verbose = verbose)

data <- p_significance(

Check warning on line 276 in R/p_significance.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/p_significance.R,line=276,col=3,[object_overwrite_linter] 'data' is an exported object from package 'utils'. Avoid re-using such symbols.
insight::get_parameters(x, effects = effects, component = component, parameters = parameters),
threshold = threshold
)
Expand All @@ -275,12 +290,21 @@
}

.p_significance <- function(x, threshold, ...) {
psig <- max(
c(
length(x[x > abs(threshold)]) / length(x), # ps positive
length(x[x < -abs(threshold)]) / length(x) # ps negative
if (length(threshold) == 1) {
psig <- max(
c(
length(x[x > abs(threshold)]) / length(x), # ps positive
length(x[x < -abs(threshold)]) / length(x) # ps negative
)
)
)
} else {
psig <- max(
c(
length(x[x > threshold[2]]) / length(x), # ps positive
length(x[x < threshold[1]]) / length(x) # ps negative
)
)
}

psig
}
Expand All @@ -291,9 +315,9 @@
#' @export
as.numeric.p_significance <- function(x, ...) {
if (inherits(x, "data.frame")) {
return(as.numeric(as.vector(x$ps)))
as.numeric(as.vector(x$ps))
} else {
return(as.vector(x))
as.vector(x)
}
}

Expand All @@ -308,24 +332,21 @@

#' @keywords internal
.select_threshold_ps <- function(model = NULL, threshold = "default", verbose = TRUE) {
# If a range is passed
if (length(threshold) > 1) {
if (length(unique(abs(threshold))) == 1) {
# If symmetric range
threshold <- abs(threshold[2])
} else {
insight::format_error("`threshold` should be 'default' or a numeric value (e.g., 0.1).")
}
}
# If default
if (all(threshold == "default")) {
if (is.null(model)) {
threshold <- 0.1
} else {
threshold <- rope_range(model, verbose = verbose)[2]
}
} else if (!all(is.numeric(threshold))) {
insight::format_error("`threshold` should be 'default' or a numeric value (e.g., 0.1).")
} else if (all(is.numeric(threshold)) && length(threshold) == 2) {
} else if (!all(is.numeric(threshold)) || length(threshold) > 2) {
insight::format_error(
"`threshold` should be one of the following values:",
"- \"default\", in which case the threshold is based on `rope_range()`",
"- a single numeric value (e.g., 0.1), which is used as range around zero (i.e. the threshold range is set to -0.1 and 0.1)",

Check warning on line 347 in R/p_significance.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/p_significance.R,line=347,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 131 characters.
"- a numeric vector of length two (e.g., `c(-0.2, 0.1)`)"
)
}
threshold
}
17 changes: 15 additions & 2 deletions man/p_significance.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions tests/testthat/test-p_significance.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,20 @@ test_that("p_significance", {
)
)

# non-symmetric intervals
ps <- p_significance(x, threshold = c(0.05, 0.2))
expect_equal(as.numeric(ps), 0.7881, tolerance = 0.1)
# should be identical, both ranges have same distance to the mean 1
ps <- p_significance(x, threshold = c(1.8, 1.95))
expect_equal(as.numeric(ps), 0.7881, tolerance = 0.1)

set.seed(333)
x <- data.frame(replicate(4, rnorm(100)))
pd <- p_significance(x)
expect_identical(dim(pd), c(4L, 2L))

# error:
expect_error(p_significance(x, threshold = 1:3))
})

test_that("stanreg", {
Expand Down
Loading