Skip to content
Closed
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
10 changes: 3 additions & 7 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,9 @@ pkgdown
^cran-comments\.md$
^\.covrignore$
^\.idea$
^lint_results.txt$

# output
^\.vscode$
^air\.toml$
^output$
^junco_tlg_template_scripts$
^[^/]*\.R$
^\.vscode$
^[.]?air[.]toml$
^CHANGELOG\.md$
^air\.toml$
^dev$
83 changes: 83 additions & 0 deletions .github/workflows/hotfix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Hotfix Tests

on:
push:
branches:
- 'feature/hotfix-*'
pull_request:
branches: [main, CRAN]
workflow_dispatch:

jobs:
test-hotfix:
runs-on: ${{ matrix.config.os }}

name: ${{ matrix.config.os }} (${{ matrix.config.r }})

strategy:
fail-fast: false
matrix:
config:
- {os: ubuntu-latest, r: '4.5.0'}

env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
R_KEEP_PKG_SOURCE: yes

steps:
- uses: actions/checkout@v4

- uses: r-lib/actions/setup-pandoc@v2

- uses: r-lib/actions/setup-r@v2
with:
r-version: ${{ matrix.config.r }}
http-user-agent: ${{ matrix.config.http-user-agent }}
use-public-rspm: true

- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: |
any::devtools
any::testthat

- name: Install Local Junco
shell: Rscript {0}
run: |
devtools::install(".", upgrade = FALSE, quick = TRUE)

- name: Execute tests with hotfixes
shell: Rscript {0}
run: |
project_root <- getwd()
hotfix_file <- file.path(project_root, "dev", "junco_hotfix.R")

devtools::load_all(project_root)

# Inject the Hotfix
if (file.exists(hotfix_file)) {
message(">>> Injecting Hotfix: ", hotfix_file)
source(hotfix_file, local = FALSE)
} else {
stop("Hotfix file not found at ", hotfix_file)
}

# Run tests in the current environment
library(testthat)
options(rgl.useNULL = TRUE)
options(testthat.progress.max_reports = 10)
testthat::set_max_fails(Inf)

reporter <- testthat::ListReporter$new()
try({
testthat::test_dir("tests/testthat", reporter = reporter)
}, silent = FALSE)
results <- reporter$get_results()

# Exit with error code if any tests failed
if (!testthat:::all_passed(results)) {
stop("Tests failed with the current hotfix applied.")
}



15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.4] - Unreleased
## [0.1.4.9] - Unreleased

### Added and Removed
- Add `geom_boxplot_j()` to be able to draw boxplots whose statistics follow SAS quantile(type =2)


## [0.1.4] - 2026-02-02

### Fixed
- "caption" paragraph style in the docx exporter is now handled by flextable (#182)
- Fixed `tt_to_tlgrtf()`, when exporting an empty listing do not lose Title and Footers
- Fixed `tt_to_tlgrtf()` argument `label_width_ins` which was not applying the change in the row label column width (#166).

### Changed
- Reinstate rbmi as dependency


## [0.1.3.9.1] - 2026-02-02

### Fixed
- Fixed `tt_to_tlgrtf()` argument `label_width_ins` which was not applying the change in the row label column width (#166).


## [0.1.3] - 2026-01-12

### Changed
Expand Down
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: junco
Title: Create Common Tables and Listings Used in Clinical Trials
Version: 0.1.4
Date: 2026-01-12
Version: 0.1.4.9
Date: 2026-03-11
Authors@R: c(
person("Gabriel", "Becker", , "gabembecker@gmail.com", role = c("cre", "aut"),
comment = "Original creator of the package, and author of included formatters functions"),
Expand Down
6 changes: 6 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export(find_missing_chg_after_avisit)
export(fit_ancova)
export(fit_mmrm_j)
export(format_stats)
export(geom_boxplot_j)
export(get_mmrm_lsmeans)
export(get_ref_info)
export(get_titles_from_file)
Expand Down Expand Up @@ -137,6 +138,11 @@ import(tidytlg)
importFrom(assertthat,assert_that)
importFrom(formatters,wrap_string_ttype)
importFrom(generics,tidy)
importFrom(ggplot2,GeomBoxplot)
importFrom(ggplot2,Stat)
importFrom(ggplot2,ggproto)
importFrom(ggplot2,layer)
importFrom(stats,quantile)
importFrom(stats,setNames)
importFrom(survival,Surv)
importFrom(survival,coxph)
Expand Down
9 changes: 1 addition & 8 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
# junco 0.1.4
# junco 0.1.4.9


### Fixed
- "caption" paragraph style in the docx exporter is now handled by flextable (#182)
- Fixed `tt_to_tlgrtf()`, when exporting an empty listing do not lose Title and Footers
- Fixed `tt_to_tlgrtf()` argument `label_width_ins` which was not applying the change in the row label column width (#166).

### Changed
- Reinstate rbmi as dependency


124 changes: 124 additions & 0 deletions R/geom_j.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#' Boxplot statistics with configurable quantile type
#'
#' `StatBoxplotQuantile` computes boxplot statistics using a configurable
#' quantile definition. By default, quartiles use `quantile(type = 2)` to follow
#' `PCTLDEF = 5` (SAS default). It computes quartiles, whiskers, and outliers.
#'
#' Required aesthetics: `x`, `y`.
#'
#' Computed variables (per group):
#' - `ymin`: lower whisker (min value within lower fence)
#' - `lower`: first quartile (Q1)
#' - `middle`: median (Q2)
#' - `upper`: third quartile (Q3)
#' - `ymax`: upper whisker (max value within upper fence)
#' - `outliers`: list-column of values outside whiskers
#' - `x`: x position (first value in group)
#' - `width`: box width used by `GeomBoxplot`
#'
#' @section Quantile details:
#' By default, quartiles are computed with `quantile(type = 2)` to follow
#' `PCTLDEF = 5` (SAS default). You can change this via the `quantile_type` argument. Fences
#' are defined as `Q1 - coef * IQR` and `Q3 + coef * IQR` where `IQR = Q3 - Q1` and `coef`
#' defaults to `1.5`.
#'
#' @keywords internal
#' @importFrom ggplot2 ggproto Stat
#' @importFrom stats quantile
StatBoxplotQuantile <- ggproto(
"StatBoxplotQuantile", Stat,
required_aes = c("x", "y"),
dropped_aes = c("x", "y", "weight"),
compute_group = function(
data,
scales,
width = NULL,
na.rm = FALSE,
coef = 1.5,
quantile_type = 2
) {
vec <- data$y
if (length(vec) == 0) {
return(data.frame())
}

# Quantiles controlled by quantile_type (default 2 = PCTLDEF=5)
qs <- quantile(vec, probs = c(0, 0.25, 0.5, 0.75, 1), type = quantile_type, na.rm = na.rm)
iqr <- qs[4] - qs[2]

lower_fence <- qs[2] - (coef * iqr)
upper_fence <- qs[4] + (coef * iqr)

ymin <- min(vec[vec >= lower_fence], na.rm = na.rm)
ymax <- max(vec[vec <= upper_fence], na.rm = na.rm)

outliers <- vec[vec < ymin | vec > ymax]

data.frame(
ymin = ymin,
lower = qs[2],
middle = qs[3],
upper = qs[4],
ymax = ymax,
outliers = I(list(outliers)),
x = data$x[1],
width = if (is.null(width)) 0.75 else width
)
}
)

#' Draws boxplots with configurable quantile type
#'
#' `geom_boxplot_j()` draws boxplots whose statistics can follow different
#' percentile definitions for quartiles. By default it uses
#' `quantile(type = 2)` to follow `PCTLDEF = 5` (SAS default). You can override
#' the quantile definition via `quantile_type`. It uses `StatBoxplotQuantile`
#' then renders with `GeomBoxplot`.
#'
#' @param mapping,data,position,... Standard ggplot2 layer arguments passed to
#' `layer()`/`GeomBoxplot`.
#' @param coef Numeric multiplier for the IQR to compute fences. Defaults to 1.5
#' (Tukey).
#' @param quantile_type Integer in 1:9 passed to `stats::quantile(type = ...)` to
#' compute quartiles. Defaults to 2 (follows PCTLDEF = 5).
#' @param na.rm Logical; if `TRUE`, silently removes `NA` values.
#' @param show.legend,inherit.aes See `ggplot2::layer()`.
#'
#' @return A ggplot2 layer that produces boxplots using the chosen quantile type.
#'
#' @examples
#' library(ggplot2)
#' library(pharmaverseadamjnj)
#' ggplot(advs, aes(AVISIT, AVAL, fill = TRT01A)) +
#' geom_boxplot_j(position = position_dodge2(preserve = "single"), na.rm = TRUE) +
#' theme(axis.text.x = element_text(angle = 45, hjust = 1))
#'
#' @export
#' @importFrom ggplot2 layer GeomBoxplot
geom_boxplot_j <- function(
mapping = NULL,
data = NULL,
position = "dodge2",
...,
coef = 1.5,
quantile_type = 2,
na.rm = FALSE,
show.legend = NA,
inherit.aes = TRUE
) {
layer(
data = data,
mapping = mapping,
stat = StatBoxplotQuantile,
geom = GeomBoxplot,
position = position,
show.legend = show.legend,
inherit.aes = inherit.aes,
params = list(
na.rm = na.rm,
coef = coef,
quantile_type = quantile_type,
...
)
)
}
15 changes: 10 additions & 5 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,16 @@ reference:
- title: junco Miscellaneous Functions
desc: The following functions are other miscellaneous junco functions used to create common table layouts.
contents:
- remove_col_count
- rm_levels
- get_mmrm_lsmeans
- get_ref_info
- get_visit_levels
- remove_col_count
- rm_levels
- get_mmrm_lsmeans
- get_ref_info
- get_visit_levels

- title: junco Geoms
desc: ggplot2 geoms provided by junco.
contents:
- geom_boxplot_j

- title: internal
desc: The following are additional junco functions.
Expand Down
Loading
Loading