From a20693241fd8b01ec0693cba77052fd2c33c0662 Mon Sep 17 00:00:00 2001 From: Ian Hussey Date: Mon, 25 Aug 2025 16:10:17 +0200 Subject: [PATCH 1/4] Update format_p.R to remove leading zeros consistently Previously, leading zeros were only removed when the value was truncated. `format_p(.02)` would return "p = 0.02" but `format_p(.00002)` would return "p < .001". APA style suggests always dropping leading zeros from p-values. This commit adds a new argument, leading_zeros, which defaults to FALSE. When FALSE, a `gsub()` call removes the leading zero before the decimal_seperator. --- R/format_p.R | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/R/format_p.R b/R/format_p.R index 32803db772..f3e80a74b7 100644 --- a/R/format_p.R +++ b/R/format_p.R @@ -17,6 +17,9 @@ #' If `"scientific"`, control the number of digits by adding the value as #' a suffix, e.g.m `digits = "scientific4"` to have scientific notation #' with 4 decimal places. +#' @param leading_zero Logical, if `FALSE` (default), removes leading zero +#' before decimal seperator. Otherwise, retain leading zero except when +#' p value is truncated #' @param ... Arguments from other methods. #' @inheritParams format_value #' @@ -40,6 +43,7 @@ format_p <- function(p, missing = "", decimal_separator = NULL, digits = 3, + leading_zero = FALSE ...) { # only convert p if it's a valid numeric, or at least coercible to # valid numeric values... @@ -148,5 +152,14 @@ format_p <- function(p, p_text <- gsub(".", decimal_separator, p_text, fixed = TRUE) } + # remove leading zero + if(!leading_zero){ + # build a regex that matches "0" only when preceded by start, space, or an operator (= < >) + sep_esc <- if (is.null(decimal_separator)) "\\." else gsub("([\\^$.|?*+(){}\\[\\]\\\\])", "\\\\\\1", decimal_separator) + pattern <- paste0("(?:(?<=^)|(?<=\\s)|(?<==)|(?<=<)|(?<=>))0(", sep_esc, ")") + # replace with just the separator (i.e., ".123" or ",123") + p_text <- gsub(pattern, "\\1", p_text, perl = TRUE) + } + p_text } From e417401861a489f9073d56c9445a769fc2193555 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 27 Aug 2025 13:45:33 +0200 Subject: [PATCH 2/4] rename arg, add tests --- DESCRIPTION | 2 +- R/format_p.R | 42 ++++++++++++++++++------------------ man/format_p.Rd | 5 +++++ tests/testthat/test-format.R | 7 ++++++ 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index b84da67191..38067faedd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: insight Title: Easy Access to Model Information for Various Model Objects -Version: 1.4.0.13 +Version: 1.4.0.14 Authors@R: c(person(given = "Daniel", family = "Lüdecke", diff --git a/R/format_p.R b/R/format_p.R index f3e80a74b7..57995b09fe 100644 --- a/R/format_p.R +++ b/R/format_p.R @@ -17,8 +17,8 @@ #' If `"scientific"`, control the number of digits by adding the value as #' a suffix, e.g.m `digits = "scientific4"` to have scientific notation #' with 4 decimal places. -#' @param leading_zero Logical, if `FALSE` (default), removes leading zero -#' before decimal seperator. Otherwise, retain leading zero except when +#' @param lead_zero Logical, if `FALSE` (default), removes leading zero +#' before decimal separator. Otherwise, retain leading zero except when #' p value is truncated #' @param ... Arguments from other methods. #' @inheritParams format_value @@ -43,7 +43,7 @@ format_p <- function(p, missing = "", decimal_separator = NULL, digits = 3, - leading_zero = FALSE + lead_zero = FALSE, ...) { # only convert p if it's a valid numeric, or at least coercible to # valid numeric values... @@ -63,6 +63,15 @@ format_p <- function(p, digits <- 3 } + # hard-coded thresholds for small p-values, including or excluding leading zero + if (lead_zero) { + low_p <- "< 0.001***" + high_p <- "> 0.999" + } else { + low_p <- "< .001***" + high_p <- "> .999" + } + if (is.character(digits) && grepl("scientific", digits, fixed = TRUE)) { digits <- tryCatch(as.numeric(gsub("scientific", "", digits, fixed = TRUE)), error = function(e) NA @@ -83,11 +92,11 @@ format_p <- function(p, ) } else if (digits <= 3) { p_text <- ifelse(is.na(p), NA, - ifelse(p < 0.001, "< .001***", # nolint - ifelse(p < 0.01, paste0("= ", format_value(p, digits), "**"), # nolint - ifelse(p < 0.05, paste0("= ", format_value(p, digits), "*"), # nolint - ifelse(p > 0.999, "> .999", # nolint - paste0("= ", format_value(p, digits)) + ifelse(p < 0.001, low_p, # nolint + ifelse(p < 0.01, paste0("= ", format_value(p, digits, lead_zero = lead_zero), "**"), # nolint + ifelse(p < 0.05, paste0("= ", format_value(p, digits, lead_zero = lead_zero), "*"), # nolint + ifelse(p > 0.999, high_p, # nolint + paste0("= ", format_value(p, digits, lead_zero = lead_zero)) ) ) ) @@ -95,10 +104,10 @@ format_p <- function(p, ) } else { p_text <- ifelse(is.na(p), NA, - ifelse(p < 0.001, paste0("= ", format_value(p, digits), "***"), # nolint - ifelse(p < 0.01, paste0("= ", format_value(p, digits), "**"), # nolint - ifelse(p < 0.05, paste0("= ", format_value(p, digits), "*"), # nolint - paste0("= ", format_value(p, digits)) + ifelse(p < 0.001, paste0("= ", format_value(p, digits, lead_zero = lead_zero), "***"), # nolint + ifelse(p < 0.01, paste0("= ", format_value(p, digits, lead_zero = lead_zero), "**"), # nolint + ifelse(p < 0.05, paste0("= ", format_value(p, digits, lead_zero = lead_zero), "*"), # nolint + paste0("= ", format_value(p, digits, lead_zero = lead_zero)) ) ) ) @@ -152,14 +161,5 @@ format_p <- function(p, p_text <- gsub(".", decimal_separator, p_text, fixed = TRUE) } - # remove leading zero - if(!leading_zero){ - # build a regex that matches "0" only when preceded by start, space, or an operator (= < >) - sep_esc <- if (is.null(decimal_separator)) "\\." else gsub("([\\^$.|?*+(){}\\[\\]\\\\])", "\\\\\\1", decimal_separator) - pattern <- paste0("(?:(?<=^)|(?<=\\s)|(?<==)|(?<=<)|(?<=>))0(", sep_esc, ")") - # replace with just the separator (i.e., ".123" or ",123") - p_text <- gsub(pattern, "\\1", p_text, perl = TRUE) - } - p_text } diff --git a/man/format_p.Rd b/man/format_p.Rd index 25e030e9e7..6412043daf 100644 --- a/man/format_p.Rd +++ b/man/format_p.Rd @@ -13,6 +13,7 @@ format_p( missing = "", decimal_separator = NULL, digits = 3, + lead_zero = FALSE, ... ) } @@ -42,6 +43,10 @@ If \code{"scientific"}, control the number of digits by adding the value as a suffix, e.g.m \code{digits = "scientific4"} to have scientific notation with 4 decimal places.} +\item{lead_zero}{Logical, if \code{FALSE} (default), removes leading zero +before decimal separator. Otherwise, retain leading zero except when +p value is truncated} + \item{...}{Arguments from other methods.} } \value{ diff --git a/tests/testthat/test-format.R b/tests/testthat/test-format.R index ab6c6b7174..b2e199dcfe 100644 --- a/tests/testthat/test-format.R +++ b/tests/testthat/test-format.R @@ -143,6 +143,13 @@ test_that("format_p", { expect_identical(nchar(format_p(0.02)), 9L) expect_identical(nchar(format_p(0.02, stars = TRUE)), 10L) expect_identical(nchar(format_p(0.02, stars_only = TRUE)), 1L) + + expect_identical(format_p(0.02), "p = .020") + expect_identical(format_p(0.02, lead_zero = TRUE), "p = 0.020") + expect_identical(format_p(0.02, stars = TRUE), "p = .020*") + expect_identical(format_p(0.02, stars = TRUE, lead_zero = TRUE), "p = 0.020*") + expect_identical(format_p(0.0214568, digits = "apa"), "p = .021") + expect_identical(format_p(0.0214568, digits = "apa", lead_zero = TRUE), "p = 0.021") }) test_that("format_table, other CI columns", { From 01942b6490c0fc3de3fcee2dab96cc1e5735a69a Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 27 Aug 2025 13:46:06 +0200 Subject: [PATCH 3/4] news --- NEWS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS.md b/NEWS.md index 6d9376ec34..0e86a1bdc0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,9 @@ `component` argument gains the `"full"` option, to return the full variance-covariance matrix, including the random effects (theta parameters). +* `format_p()` now also gets a `lead_zero` argument, to keep or drop the leading + zero of a formatted p-value. + * Changes to prepare for *marginaleffects* 0.29.0. * `format_table()` now also formats ROPE columns for superiority and inferiority. From f9ee6cc8f7401fb66bb189aa1b6888fdd505bee5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 27 Aug 2025 23:52:39 +0200 Subject: [PATCH 4/4] add tests --- tests/testthat/test-format.R | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/testthat/test-format.R b/tests/testthat/test-format.R index b2e199dcfe..e727355ce3 100644 --- a/tests/testthat/test-format.R +++ b/tests/testthat/test-format.R @@ -150,6 +150,10 @@ test_that("format_p", { expect_identical(format_p(0.02, stars = TRUE, lead_zero = TRUE), "p = 0.020*") expect_identical(format_p(0.0214568, digits = "apa"), "p = .021") expect_identical(format_p(0.0214568, digits = "apa", lead_zero = TRUE), "p = 0.021") + expect_identical(format_p(0.00003, digits = "apa"), "p < .001") + expect_identical(format_p(0.00003, digits = "apa", lead_zero = TRUE), "p < 0.001") + expect_identical(format_p(0.00003, digits = 5), "p = .00003") + expect_identical(format_p(0.00003, digits = 5, lead_zero = TRUE), "p = 0.00003") }) test_that("format_table, other CI columns", {