From c5cbe335664f9218f7689d8c3baa56d3437e8674 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 06:35:56 +0000 Subject: [PATCH 01/12] Add gg() shorthand alias for g2(), inspired by tinyplot's plt() Agent-Logs-Url: https://github.com/yihui/gglite/sessions/df53ceb6-3fa6-43d1-aba3-b50da99ad847 Co-authored-by: yihui-bot <264330240+yihui-bot@users.noreply.github.com> --- DESCRIPTION | 2 +- NAMESPACE | 1 + R/gglite.R | 10 ++++++++++ man/gg.Rd | 40 ++++++++++++++++++++++++++++++++++++++ tests/testit/test-gglite.R | 7 +++++++ 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 man/gg.Rd diff --git a/DESCRIPTION b/DESCRIPTION index a9990fc..05d3a06 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: gglite Title: Lightweight Data Visualization via the Grammar of Graphics -Version: 0.0.10 +Version: 0.0.11 Authors@R: person("Yihui", "Xie", role = c("aut", "cre"), email = "xie@yihui.name", comment = c(ORCID = "0000-0003-0645-5666")) Description: A lightweight R interface to the AntV G2 JavaScript visualization diff --git a/NAMESPACE b/NAMESPACE index a4c897f..305571a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -20,6 +20,7 @@ export(facet_circle) export(facet_rect) export(g2) export(g2Output) +export(gg) export(interact) export(labels_) export(legend_) diff --git a/R/gglite.R b/R/gglite.R index 81b96b8..e931e2a 100644 --- a/R/gglite.R +++ b/R/gglite.R @@ -128,4 +128,14 @@ encode = function(chart, ...) { chart } +#' Shorthand Alias for [g2()] +#' +#' `gg()` is a convenient shorthand for [g2()], inspired by tinyplot's `plt()` +#' alias. All arguments are passed through to [g2()]. +#' +#' @inheritParams g2 +#' @export +#' @examples +#' gg(mtcars, x = 'mpg', y = 'hp') +gg = g2 diff --git a/man/gg.Rd b/man/gg.Rd new file mode 100644 index 0000000..3f60b5d --- /dev/null +++ b/man/gg.Rd @@ -0,0 +1,40 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gglite.R +\name{gg} +\alias{gg} +\title{Shorthand Alias for \code{\link[=g2]{g2()}}} +\usage{ +gg( + data = NULL, + ..., + width = 640, + height = 480, + padding = NULL, + margin = NULL, + inset = NULL +) +} +\arguments{ +\item{data}{A data frame, a \code{ts}/\code{mts} time series object, or \code{NULL}. Time +series objects are automatically converted to data frames (with columns +\code{time} and \code{value} for univariate series, or \code{time}, \code{series}, and \code{value} +for multivariate series) and default aesthetic mappings are set +accordingly.} + +\item{...}{Aesthetic mappings as \code{name = 'column'} pairs (character strings), +or a formula followed by optional named aesthetics.} + +\item{width, height}{Width and height of the chart in pixels.} + +\item{padding, margin, inset}{Layout spacing in pixels. Each can be a scalar +(applied to all sides) or a length-4 vector \code{c(top, right, bottom, left)}; +use \code{NA} to skip individual sides. \code{NULL} (the default) leaves the value +unset.} +} +\description{ +\code{gg()} is a convenient shorthand for \code{\link[=g2]{g2()}}, inspired by tinyplot's \code{plt()} +alias. All arguments are passed through to \code{\link[=g2]{g2()}}. +} +\examples{ +gg(mtcars, x = 'mpg', y = 'hp') +} diff --git a/tests/testit/test-gglite.R b/tests/testit/test-gglite.R index 68d5df2..732ebcb 100644 --- a/tests/testit/test-gglite.R +++ b/tests/testit/test-gglite.R @@ -103,3 +103,10 @@ assert('g2() formula ~ x1 + x2 + x3 sets position encoding', { c('Sepal.Length', 'Sepal.Width', 'Petal.Length')) (is.null(chart$aesthetics$x)) }) + +assert('gg() is a shorthand alias for g2()', { + chart = gg(mtcars, x = 'mpg', y = 'hp') + (inherits(chart, 'g2')) + (chart$aesthetics$x %==% 'mpg') + (chart$aesthetics$y %==% 'hp') +}) From 836b299f82817ee532e108b390ce830cf6eecfd1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 06:37:47 +0000 Subject: [PATCH 02/12] Add main/sub title arguments to g2(), inspired by tinyplot and base plot() Agent-Logs-Url: https://github.com/yihui/gglite/sessions/df53ceb6-3fa6-43d1-aba3-b50da99ad847 Co-authored-by: yihui-bot <264330240+yihui-bot@users.noreply.github.com> --- R/gglite.R | 13 +++++++++++-- man/g2.Rd | 12 +++++++++++- man/gg.Rd | 9 ++++++++- tests/testit/test-gglite.R | 11 +++++++++++ 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/R/gglite.R b/R/gglite.R index e931e2a..5645586 100644 --- a/R/gglite.R +++ b/R/gglite.R @@ -52,6 +52,9 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' (applied to all sides) or a length-4 vector `c(top, right, bottom, left)`; #' use `NA` to skip individual sides. `NULL` (the default) leaves the value #' unset. +#' @param main Chart title string, a convenient alternative to calling +#' [title_()] separately (inspired by base [plot()] and tinyplot). +#' @param sub Chart subtitle string. Only used when `main` is also provided. #' @return A `g2` object (S3 class). #' @import stats utils #' @export @@ -66,9 +69,13 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' # Time series #' g2(sunspot.year) #' g2(EuStockMarkets) +#' +#' # Title and subtitle (like base plot's main/sub) +#' g2(mtcars, hp ~ mpg, main = 'Motor Trend Cars', sub = 'mpg vs hp') g2 = function( data = NULL, ..., width = 640, height = 480, - padding = NULL, margin = NULL, inset = NULL + padding = NULL, margin = NULL, inset = NULL, + main = NULL, sub = NULL ) { dots = list(...) has_formula = length(dots) && inherits(dots[[1]], 'formula') @@ -99,7 +106,9 @@ g2 = function( theme = NULL, axes = list(), legends = list(), - chart_title = NULL, + chart_title = if (!is.null(main)) { + if (!is.null(sub)) list(title = main, subtitle = sub) else main + }, facet = facet_from_formula, layout = c( process_layout('padding', padding), diff --git a/man/g2.Rd b/man/g2.Rd index 01ceb78..44afdb9 100644 --- a/man/g2.Rd +++ b/man/g2.Rd @@ -11,7 +11,9 @@ g2( height = 480, padding = NULL, margin = NULL, - inset = NULL + inset = NULL, + main = NULL, + sub = NULL ) } \arguments{ @@ -30,6 +32,11 @@ or a formula followed by optional named aesthetics.} (applied to all sides) or a length-4 vector \code{c(top, right, bottom, left)}; use \code{NA} to skip individual sides. \code{NULL} (the default) leaves the value unset.} + +\item{main}{Chart title string, a convenient alternative to calling +\code{\link[=title_]{title_()}} separately (inspired by base \code{\link[=plot]{plot()}} and tinyplot).} + +\item{sub}{Chart subtitle string. Only used when \code{main} is also provided.} } \value{ A \code{g2} object (S3 class). @@ -66,4 +73,7 @@ g2(mtcars, ~ mpg) # Time series g2(sunspot.year) g2(EuStockMarkets) + +# Title and subtitle (like base plot's main/sub) +g2(mtcars, hp ~ mpg, main = 'Motor Trend Cars', sub = 'mpg vs hp') } diff --git a/man/gg.Rd b/man/gg.Rd index 3f60b5d..c2b4fb7 100644 --- a/man/gg.Rd +++ b/man/gg.Rd @@ -11,7 +11,9 @@ gg( height = 480, padding = NULL, margin = NULL, - inset = NULL + inset = NULL, + main = NULL, + sub = NULL ) } \arguments{ @@ -30,6 +32,11 @@ or a formula followed by optional named aesthetics.} (applied to all sides) or a length-4 vector \code{c(top, right, bottom, left)}; use \code{NA} to skip individual sides. \code{NULL} (the default) leaves the value unset.} + +\item{main}{Chart title string, a convenient alternative to calling +\code{\link[=title_]{title_()}} separately (inspired by base \code{\link[=plot]{plot()}} and tinyplot).} + +\item{sub}{Chart subtitle string. Only used when \code{main} is also provided.} } \description{ \code{gg()} is a convenient shorthand for \code{\link[=g2]{g2()}}, inspired by tinyplot's \code{plt()} diff --git a/tests/testit/test-gglite.R b/tests/testit/test-gglite.R index 732ebcb..7aa7fed 100644 --- a/tests/testit/test-gglite.R +++ b/tests/testit/test-gglite.R @@ -110,3 +110,14 @@ assert('gg() is a shorthand alias for g2()', { (chart$aesthetics$x %==% 'mpg') (chart$aesthetics$y %==% 'hp') }) + +assert('g2() main argument sets chart title', { + chart = g2(mtcars, x = 'mpg', y = 'hp', main = 'My Title') + (chart$chart_title %==% 'My Title') +}) + +assert('g2() main and sub arguments set title and subtitle', { + chart = g2(mtcars, x = 'mpg', y = 'hp', main = 'Title', sub = 'Subtitle') + (chart$chart_title$title %==% 'Title') + (chart$chart_title$subtitle %==% 'Subtitle') +}) From 75d5d4b32511cb594da18dfedfb6e5c0dc2b136f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 06:39:05 +0000 Subject: [PATCH 03/12] Add by argument for color grouping in g2(), inspired by tinyplot's by Agent-Logs-Url: https://github.com/yihui/gglite/sessions/df53ceb6-3fa6-43d1-aba3-b50da99ad847 Co-authored-by: yihui-bot <264330240+yihui-bot@users.noreply.github.com> --- R/gglite.R | 9 ++++++++- man/g2.Rd | 10 +++++++++- man/gg.Rd | 7 ++++++- tests/testit/test-gglite.R | 5 +++++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/R/gglite.R b/R/gglite.R index 5645586..b9c3ee5 100644 --- a/R/gglite.R +++ b/R/gglite.R @@ -55,6 +55,9 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' @param main Chart title string, a convenient alternative to calling #' [title_()] separately (inspired by base [plot()] and tinyplot). #' @param sub Chart subtitle string. Only used when `main` is also provided. +#' @param by Column name (character string) to group the data by color, +#' inspired by tinyplot's `by` argument. Equivalent to passing +#' `color = 'column'`. #' @return A `g2` object (S3 class). #' @import stats utils #' @export @@ -72,10 +75,13 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' #' # Title and subtitle (like base plot's main/sub) #' g2(mtcars, hp ~ mpg, main = 'Motor Trend Cars', sub = 'mpg vs hp') +#' +#' # Grouping by color (like tinyplot's by argument) +#' g2(iris, Sepal.Length ~ Sepal.Width, by = 'Species') g2 = function( data = NULL, ..., width = 640, height = 480, padding = NULL, margin = NULL, inset = NULL, - main = NULL, sub = NULL + main = NULL, sub = NULL, by = NULL ) { dots = list(...) has_formula = length(dots) && inherits(dots[[1]], 'formula') @@ -117,6 +123,7 @@ g2 = function( ) ), class = 'g2') if (length(dots)) chart$aesthetics = modifyList(chart$aesthetics, dots) + if (!is.null(by)) chart$aesthetics$color = by chart } diff --git a/man/g2.Rd b/man/g2.Rd index 44afdb9..e8d1a68 100644 --- a/man/g2.Rd +++ b/man/g2.Rd @@ -13,7 +13,8 @@ g2( margin = NULL, inset = NULL, main = NULL, - sub = NULL + sub = NULL, + by = NULL ) } \arguments{ @@ -37,6 +38,10 @@ unset.} \code{\link[=title_]{title_()}} separately (inspired by base \code{\link[=plot]{plot()}} and tinyplot).} \item{sub}{Chart subtitle string. Only used when \code{main} is also provided.} + +\item{by}{Column name (character string) to group the data by color, +inspired by tinyplot's \code{by} argument. Equivalent to passing +\code{color = 'column'}.} } \value{ A \code{g2} object (S3 class). @@ -76,4 +81,7 @@ g2(EuStockMarkets) # Title and subtitle (like base plot's main/sub) g2(mtcars, hp ~ mpg, main = 'Motor Trend Cars', sub = 'mpg vs hp') + +# Grouping by color (like tinyplot's by argument) +g2(iris, Sepal.Length ~ Sepal.Width, by = 'Species') } diff --git a/man/gg.Rd b/man/gg.Rd index c2b4fb7..50e0d1a 100644 --- a/man/gg.Rd +++ b/man/gg.Rd @@ -13,7 +13,8 @@ gg( margin = NULL, inset = NULL, main = NULL, - sub = NULL + sub = NULL, + by = NULL ) } \arguments{ @@ -37,6 +38,10 @@ unset.} \code{\link[=title_]{title_()}} separately (inspired by base \code{\link[=plot]{plot()}} and tinyplot).} \item{sub}{Chart subtitle string. Only used when \code{main} is also provided.} + +\item{by}{Column name (character string) to group the data by color, +inspired by tinyplot's \code{by} argument. Equivalent to passing +\code{color = 'column'}.} } \description{ \code{gg()} is a convenient shorthand for \code{\link[=g2]{g2()}}, inspired by tinyplot's \code{plt()} diff --git a/tests/testit/test-gglite.R b/tests/testit/test-gglite.R index 7aa7fed..33b4826 100644 --- a/tests/testit/test-gglite.R +++ b/tests/testit/test-gglite.R @@ -121,3 +121,8 @@ assert('g2() main and sub arguments set title and subtitle', { (chart$chart_title$title %==% 'Title') (chart$chart_title$subtitle %==% 'Subtitle') }) + +assert('g2() by argument sets color aesthetic', { + chart = g2(iris, x = 'Sepal.Width', y = 'Sepal.Length', by = 'Species') + (chart$aesthetics$color %==% 'Species') +}) From 43b1356b54dd5b6a0b3b344749d19dbacffa5ff5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 06:42:28 +0000 Subject: [PATCH 04/12] Change formula | to color grouping, add facet param to g2() (tinyplot convention) Agent-Logs-Url: https://github.com/yihui/gglite/sessions/df53ceb6-3fa6-43d1-aba3-b50da99ad847 Co-authored-by: yihui-bot <264330240+yihui-bot@users.noreply.github.com> --- R/gglite.R | 37 +++++++++++++++++++++++++++++++------ R/utils.R | 17 ++++------------- man/g2.Rd | 19 ++++++++++++++++--- man/gg.Rd | 8 +++++++- tests/testit/test-gglite.R | 24 ++++++++++++++++++------ tests/testit/test-utils.R | 12 +++++------- vignettes/gglite.Rmd | 8 +++++++- 7 files changed, 88 insertions(+), 37 deletions(-) diff --git a/R/gglite.R b/R/gglite.R index b9c3ee5..d7ea243 100644 --- a/R/gglite.R +++ b/R/gglite.R @@ -34,8 +34,8 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' \item{`~ x`}{Maps only `x` (e.g., for histograms or bar counts).} #' \item{`~ x1 + x2 + x3`}{Creates a `position` encoding with multiple #' fields (for parallel coordinates).} -#' \item{`y ~ x | z`}{Facets the chart by `z` (column direction).} -#' \item{`y ~ x | z1 + z2`}{Facets by `z1` (columns) and `z2` (rows).} +#' \item{`y ~ x | z`}{Groups by `z` (maps to color), inspired by +#' tinyplot's formula convention.} #' } #' Additional aesthetics (e.g., `color`, `size`) can still be passed as named #' arguments alongside the formula. @@ -58,6 +58,10 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' @param by Column name (character string) to group the data by color, #' inspired by tinyplot's `by` argument. Equivalent to passing #' `color = 'column'`. +#' @param facet Faceting specification: a one-sided formula (`~ z` for +#' column faceting, `y ~ x` for grid faceting), or a character string +#' naming the faceting variable. Inspired by tinyplot's `facet` argument. +#' For more control, use [facet_rect()] or [facet_circle()] instead. #' @return A `g2` object (S3 class). #' @import stats utils #' @export @@ -78,17 +82,36 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' #' # Grouping by color (like tinyplot's by argument) #' g2(iris, Sepal.Length ~ Sepal.Width, by = 'Species') +#' +#' # Formula | for color grouping (tinyplot convention) +#' g2(iris, Sepal.Length ~ Sepal.Width | Species) +#' +#' # Faceting via the facet argument (like tinyplot) +#' g2(iris, Sepal.Length ~ Sepal.Width, facet = ~Species) +#' g2(mtcars, hp ~ mpg, facet = gear ~ cyl) g2 = function( data = NULL, ..., width = 640, height = 480, padding = NULL, margin = NULL, inset = NULL, - main = NULL, sub = NULL, by = NULL + main = NULL, sub = NULL, by = NULL, facet = NULL ) { dots = list(...) + by_from_formula = NULL has_formula = length(dots) && inherits(dots[[1]], 'formula') - facet_from_formula = if (has_formula) { + if (has_formula) { parsed = parse_formula(dots[[1]]) dots = c(parsed$aesthetics, dots[-1]) - parsed$facet + by_from_formula = parsed$by + } + # Build facet from the facet argument (formula or character) + facet_config = if (inherits(facet, 'formula')) { + fterms = if (length(facet) == 3) { + list(y = deparse(facet[[2]]), x = deparse(facet[[3]])) + } else { + list(x = deparse(facet[[2]])) + } + list(type = 'facetRect', encode = fterms) + } else if (is.character(facet)) { + list(type = 'facetRect', encode = list(x = facet)) } # Convert time series to data frame with default aesthetics ts_aes = NULL @@ -115,7 +138,7 @@ g2 = function( chart_title = if (!is.null(main)) { if (!is.null(sub)) list(title = main, subtitle = sub) else main }, - facet = facet_from_formula, + facet = facet_config, layout = c( process_layout('padding', padding), process_layout('margin', margin), @@ -123,6 +146,8 @@ g2 = function( ) ), class = 'g2') if (length(dots)) chart$aesthetics = modifyList(chart$aesthetics, dots) + # by argument (explicit or from formula |) sets color grouping + if (is.null(by) && !is.null(by_from_formula)) by = by_from_formula if (!is.null(by)) chart$aesthetics$color = by chart } diff --git a/R/utils.R b/R/utils.R index 14289f8..bce5778 100644 --- a/R/utils.R +++ b/R/utils.R @@ -129,10 +129,10 @@ parse_formula = function(f) { lhs = if (length(f) == 3) f[[2]] rhs = if (length(f) == 3) f[[3]] else f[[2]] - # Extract conditioning (facet) variables from | - facet_terms = NULL + # Extract conditioning (by/color) variable from | + by_term = NULL if (is.call(rhs) && identical(rhs[[1]], as.name('|'))) { - facet_terms = extract_terms(rhs[[3]]) + by_term = deparse(rhs[[3]]) rhs = rhs[[2]] } @@ -147,14 +147,5 @@ parse_formula = function(f) { aesthetics$position = rhs_terms } - # Build facet - facet = NULL - if (length(facet_terms)) { - enc = list() - if (length(facet_terms) >= 1) enc$x = facet_terms[1] - if (length(facet_terms) >= 2) enc$y = facet_terms[2] - facet = list(type = 'facetRect', encode = enc) - } - - list(aesthetics = aesthetics, facet = facet) + list(aesthetics = aesthetics, by = by_term) } diff --git a/man/g2.Rd b/man/g2.Rd index e8d1a68..4ad8b5c 100644 --- a/man/g2.Rd +++ b/man/g2.Rd @@ -14,7 +14,8 @@ g2( inset = NULL, main = NULL, sub = NULL, - by = NULL + by = NULL, + facet = NULL ) } \arguments{ @@ -42,6 +43,11 @@ unset.} \item{by}{Column name (character string) to group the data by color, inspired by tinyplot's \code{by} argument. Equivalent to passing \code{color = 'column'}.} + +\item{facet}{Faceting specification: a one-sided formula (\code{~ z} for +column faceting, \code{y ~ x} for grid faceting), or a character string +naming the faceting variable. Inspired by tinyplot's \code{facet} argument. +For more control, use \code{\link[=facet_rect]{facet_rect()}} or \code{\link[=facet_circle]{facet_circle()}} instead.} } \value{ A \code{g2} object (S3 class). @@ -60,8 +66,8 @@ argument after \code{data}: \item{\code{~ x}}{Maps only \code{x} (e.g., for histograms or bar counts).} \item{\code{~ x1 + x2 + x3}}{Creates a \code{position} encoding with multiple fields (for parallel coordinates).} -\item{\code{y ~ x | z}}{Facets the chart by \code{z} (column direction).} -\item{\code{y ~ x | z1 + z2}}{Facets by \code{z1} (columns) and \code{z2} (rows).} +\item{\code{y ~ x | z}}{Groups by \code{z} (maps to color), inspired by +tinyplot's formula convention.} } Additional aesthetics (e.g., \code{color}, \code{size}) can still be passed as named arguments alongside the formula. @@ -84,4 +90,11 @@ g2(mtcars, hp ~ mpg, main = 'Motor Trend Cars', sub = 'mpg vs hp') # Grouping by color (like tinyplot's by argument) g2(iris, Sepal.Length ~ Sepal.Width, by = 'Species') + +# Formula | for color grouping (tinyplot convention) +g2(iris, Sepal.Length ~ Sepal.Width | Species) + +# Faceting via the facet argument (like tinyplot) +g2(iris, Sepal.Length ~ Sepal.Width, facet = ~Species) +g2(mtcars, hp ~ mpg, facet = gear ~ cyl) } diff --git a/man/gg.Rd b/man/gg.Rd index 50e0d1a..6f993b0 100644 --- a/man/gg.Rd +++ b/man/gg.Rd @@ -14,7 +14,8 @@ gg( inset = NULL, main = NULL, sub = NULL, - by = NULL + by = NULL, + facet = NULL ) } \arguments{ @@ -42,6 +43,11 @@ unset.} \item{by}{Column name (character string) to group the data by color, inspired by tinyplot's \code{by} argument. Equivalent to passing \code{color = 'column'}.} + +\item{facet}{Faceting specification: a one-sided formula (\code{~ z} for +column faceting, \code{y ~ x} for grid faceting), or a character string +naming the faceting variable. Inspired by tinyplot's \code{facet} argument. +For more control, use \code{\link[=facet_rect]{facet_rect()}} or \code{\link[=facet_circle]{facet_circle()}} instead.} } \description{ \code{gg()} is a convenient shorthand for \code{\link[=g2]{g2()}}, inspired by tinyplot's \code{plt()} diff --git a/tests/testit/test-gglite.R b/tests/testit/test-gglite.R index 33b4826..a4c70d8 100644 --- a/tests/testit/test-gglite.R +++ b/tests/testit/test-gglite.R @@ -82,19 +82,31 @@ assert('g2() formula with extra aesthetics', { (chart$aesthetics$color %==% 'Species') }) -assert('g2() formula with faceting y ~ x | z', { +assert('g2() formula with | sets color grouping (tinyplot convention)', { chart = g2(iris, Sepal.Length ~ Sepal.Width | Species) (chart$aesthetics$x %==% 'Sepal.Width') (chart$aesthetics$y %==% 'Sepal.Length') + (chart$aesthetics$color %==% 'Species') + (is.null(chart$facet)) +}) + +assert('g2() facet argument with one-sided formula', { + chart = g2(iris, x = 'Sepal.Width', y = 'Sepal.Length', facet = ~Species) (chart$facet$type %==% 'facetRect') (chart$facet$encode$x %==% 'Species') }) -assert('g2() formula with two facet variables', { - df = data.frame(x = 1, y = 2, a = 'A', b = 'B') - chart = g2(df, y ~ x | a + b) - (chart$facet$encode$x %==% 'a') - (chart$facet$encode$y %==% 'b') +assert('g2() facet argument with two-sided formula', { + chart = g2(mtcars, x = 'mpg', y = 'hp', facet = gear ~ cyl) + (chart$facet$type %==% 'facetRect') + (chart$facet$encode$x %==% 'cyl') + (chart$facet$encode$y %==% 'gear') +}) + +assert('g2() facet argument with character string', { + chart = g2(iris, x = 'Sepal.Width', y = 'Sepal.Length', facet = 'Species') + (chart$facet$type %==% 'facetRect') + (chart$facet$encode$x %==% 'Species') }) assert('g2() formula ~ x1 + x2 + x3 sets position encoding', { diff --git a/tests/testit/test-utils.R b/tests/testit/test-utils.R index 55428e1..da0f4b4 100644 --- a/tests/testit/test-utils.R +++ b/tests/testit/test-utils.R @@ -54,18 +54,16 @@ assert('parse_formula: ~ x1 + x2 + x3', { (is.null(res$aesthetics$x)) }) -assert('parse_formula: y ~ x | z', { +assert('parse_formula: y ~ x | z sets color grouping (not facet)', { res = parse_formula(hp ~ mpg | cyl) (res$aesthetics$x %==% 'mpg') (res$aesthetics$y %==% 'hp') - (res$facet$type %==% 'facetRect') - (res$facet$encode$x %==% 'cyl') + (res$by %==% 'cyl') }) -assert('parse_formula: y ~ x | z1 + z2', { - res = parse_formula(y ~ x | a + b) - (res$facet$encode$x %==% 'a') - (res$facet$encode$y %==% 'b') +assert('parse_formula: no | returns NULL by', { + res = parse_formula(y ~ x) + (is.null(res$by)) }) assert('ts_to_df converts univariate ts', { diff --git a/vignettes/gglite.Rmd b/vignettes/gglite.Rmd index 8011024..0197bfe 100644 --- a/vignettes/gglite.Rmd +++ b/vignettes/gglite.Rmd @@ -67,12 +67,18 @@ Additional aesthetics like `color` can be passed alongside the formula: g2(iris, Sepal.Length ~ Sepal.Width, color = 'Species') ``` -Use `|` for faceting: +Use `|` for color grouping (inspired by tinyplot): ```{r} g2(iris, Sepal.Length ~ Sepal.Width | Species) ``` +Use the `facet` argument for faceting (also inspired by tinyplot): + +```{r} +g2(iris, Sepal.Length ~ Sepal.Width, facet = ~Species) +``` + A one-sided formula maps only `x` (useful for histograms or counts): ```{r} From 5f944a5259be3b73bdbddb7b7d2dd61ae4d15244 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 06:44:47 +0000 Subject: [PATCH 05/12] Add alpha convenience parameter for opacity, inspired by tinyplot Agent-Logs-Url: https://github.com/yihui/gglite/sessions/df53ceb6-3fa6-43d1-aba3-b50da99ad847 Co-authored-by: yihui-bot <264330240+yihui-bot@users.noreply.github.com> --- R/gglite.R | 10 +++++++++- R/render.R | 11 +++++++++++ man/g2.Rd | 11 ++++++++++- man/gg.Rd | 8 +++++++- tests/testit/test-render.R | 15 +++++++++++++++ 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/R/gglite.R b/R/gglite.R index d7ea243..dcd4b53 100644 --- a/R/gglite.R +++ b/R/gglite.R @@ -62,6 +62,10 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' column faceting, `y ~ x` for grid faceting), or a character string #' naming the faceting variable. Inspired by tinyplot's `facet` argument. #' For more control, use [facet_rect()] or [facet_circle()] instead. +#' @param alpha Numeric in `[0, 1]` controlling the fill and stroke opacity +#' of marks. A convenient alternative to calling +#' `style_mark(fillOpacity = ..., strokeOpacity = ...)`. Inspired by +#' tinyplot's `alpha` argument. #' @return A `g2` object (S3 class). #' @import stats utils #' @export @@ -89,10 +93,13 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' # Faceting via the facet argument (like tinyplot) #' g2(iris, Sepal.Length ~ Sepal.Width, facet = ~Species) #' g2(mtcars, hp ~ mpg, facet = gear ~ cyl) +#' +#' # Adjust opacity (like tinyplot's alpha) +#' g2(iris, Sepal.Length ~ Sepal.Width | Species, alpha = 0.5) g2 = function( data = NULL, ..., width = 640, height = 480, padding = NULL, margin = NULL, inset = NULL, - main = NULL, sub = NULL, by = NULL, facet = NULL + main = NULL, sub = NULL, by = NULL, facet = NULL, alpha = NULL ) { dots = list(...) by_from_formula = NULL @@ -139,6 +146,7 @@ g2 = function( if (!is.null(sub)) list(title = main, subtitle = sub) else main }, facet = facet_config, + alpha = alpha, layout = c( process_layout('padding', padding), process_layout('margin', margin), diff --git a/R/render.R b/R/render.R index 87aad54..91c5dc5 100644 --- a/R/render.R +++ b/R/render.R @@ -97,6 +97,17 @@ build_config = function(chart) { if (length(marks)) config$children = marks + # Apply chart-level alpha to all marks + if (!is.null(chart$alpha)) { + a = chart$alpha + for (i in seq_along(config$children)) { + s = config$children[[i]]$style + if (is.null(s$fillOpacity)) s$fillOpacity = a + if (is.null(s$strokeOpacity)) s$strokeOpacity = a + config$children[[i]]$style = s + } + } + # Chart-wide config if (length(chart$scales)) config$scale = chart$scales if (!is.null(chart$coords)) config$coordinate = chart$coords diff --git a/man/g2.Rd b/man/g2.Rd index 4ad8b5c..2255902 100644 --- a/man/g2.Rd +++ b/man/g2.Rd @@ -15,7 +15,8 @@ g2( main = NULL, sub = NULL, by = NULL, - facet = NULL + facet = NULL, + alpha = NULL ) } \arguments{ @@ -48,6 +49,11 @@ inspired by tinyplot's \code{by} argument. Equivalent to passing column faceting, \code{y ~ x} for grid faceting), or a character string naming the faceting variable. Inspired by tinyplot's \code{facet} argument. For more control, use \code{\link[=facet_rect]{facet_rect()}} or \code{\link[=facet_circle]{facet_circle()}} instead.} + +\item{alpha}{Numeric in \verb{[0, 1]} controlling the fill and stroke opacity +of marks. A convenient alternative to calling +\code{style_mark(fillOpacity = ..., strokeOpacity = ...)}. Inspired by +tinyplot's \code{alpha} argument.} } \value{ A \code{g2} object (S3 class). @@ -97,4 +103,7 @@ g2(iris, Sepal.Length ~ Sepal.Width | Species) # Faceting via the facet argument (like tinyplot) g2(iris, Sepal.Length ~ Sepal.Width, facet = ~Species) g2(mtcars, hp ~ mpg, facet = gear ~ cyl) + +# Adjust opacity (like tinyplot's alpha) +g2(iris, Sepal.Length ~ Sepal.Width | Species, alpha = 0.5) } diff --git a/man/gg.Rd b/man/gg.Rd index 6f993b0..6d00c0c 100644 --- a/man/gg.Rd +++ b/man/gg.Rd @@ -15,7 +15,8 @@ gg( main = NULL, sub = NULL, by = NULL, - facet = NULL + facet = NULL, + alpha = NULL ) } \arguments{ @@ -48,6 +49,11 @@ inspired by tinyplot's \code{by} argument. Equivalent to passing column faceting, \code{y ~ x} for grid faceting), or a character string naming the faceting variable. Inspired by tinyplot's \code{facet} argument. For more control, use \code{\link[=facet_rect]{facet_rect()}} or \code{\link[=facet_circle]{facet_circle()}} instead.} + +\item{alpha}{Numeric in \verb{[0, 1]} controlling the fill and stroke opacity +of marks. A convenient alternative to calling +\code{style_mark(fillOpacity = ..., strokeOpacity = ...)}. Inspired by +tinyplot's \code{alpha} argument.} } \description{ \code{gg()} is a convenient shorthand for \code{\link[=g2]{g2()}}, inspired by tinyplot's \code{plt()} diff --git a/tests/testit/test-render.R b/tests/testit/test-render.R index d437869..e1c44b1 100644 --- a/tests/testit/test-render.R +++ b/tests/testit/test-render.R @@ -215,3 +215,18 @@ assert('build_config ts y-axis title does not override user title', { config = build_config(chart) (config$axis$y$title %==% 'My Title') }) + +assert('build_config applies alpha to mark styles', { + chart = g2(mtcars, x = 'mpg', y = 'hp', alpha = 0.3) |> mark_point() + config = build_config(chart) + (config$children[[1]]$style$fillOpacity %==% 0.3) + (config$children[[1]]$style$strokeOpacity %==% 0.3) +}) + +assert('build_config alpha does not override explicit mark style opacity', { + chart = g2(mtcars, x = 'mpg', y = 'hp', alpha = 0.3) |> + mark_point() |> style_mark(fillOpacity = 0.8) + config = build_config(chart) + (config$children[[1]]$style$fillOpacity %==% 0.8) + (config$children[[1]]$style$strokeOpacity %==% 0.3) +}) From f5f066a09cb1623c9238b4403b2ea84dfe42617e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 06:46:13 +0000 Subject: [PATCH 06/12] Add palette convenience parameter for color palettes, inspired by tinyplot Agent-Logs-Url: https://github.com/yihui/gglite/sessions/df53ceb6-3fa6-43d1-aba3-b50da99ad847 Co-authored-by: yihui-bot <264330240+yihui-bot@users.noreply.github.com> --- R/gglite.R | 11 ++++++++++- man/g2.Rd | 11 ++++++++++- man/gg.Rd | 8 +++++++- tests/testit/test-gglite.R | 5 +++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/R/gglite.R b/R/gglite.R index dcd4b53..4b5e4d6 100644 --- a/R/gglite.R +++ b/R/gglite.R @@ -66,6 +66,10 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' of marks. A convenient alternative to calling #' `style_mark(fillOpacity = ..., strokeOpacity = ...)`. Inspired by #' tinyplot's `alpha` argument. +#' @param palette Character string naming a color palette (e.g., +#' `'category10'`, `'tableau10'`, `'set2'`). A convenient alternative to +#' calling `scale_color(palette = ...)`. Inspired by tinyplot's `palette` +#' argument. #' @return A `g2` object (S3 class). #' @import stats utils #' @export @@ -96,10 +100,14 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' #' # Adjust opacity (like tinyplot's alpha) #' g2(iris, Sepal.Length ~ Sepal.Width | Species, alpha = 0.5) +#' +#' # Set color palette (like tinyplot's palette) +#' g2(iris, Sepal.Length ~ Sepal.Width | Species, palette = 'set2') g2 = function( data = NULL, ..., width = 640, height = 480, padding = NULL, margin = NULL, inset = NULL, - main = NULL, sub = NULL, by = NULL, facet = NULL, alpha = NULL + main = NULL, sub = NULL, by = NULL, facet = NULL, alpha = NULL, + palette = NULL ) { dots = list(...) by_from_formula = NULL @@ -157,6 +165,7 @@ g2 = function( # by argument (explicit or from formula |) sets color grouping if (is.null(by) && !is.null(by_from_formula)) by = by_from_formula if (!is.null(by)) chart$aesthetics$color = by + if (!is.null(palette)) chart$scales$color = list(palette = palette) chart } diff --git a/man/g2.Rd b/man/g2.Rd index 2255902..37aa04c 100644 --- a/man/g2.Rd +++ b/man/g2.Rd @@ -16,7 +16,8 @@ g2( sub = NULL, by = NULL, facet = NULL, - alpha = NULL + alpha = NULL, + palette = NULL ) } \arguments{ @@ -54,6 +55,11 @@ For more control, use \code{\link[=facet_rect]{facet_rect()}} or \code{\link[=fa of marks. A convenient alternative to calling \code{style_mark(fillOpacity = ..., strokeOpacity = ...)}. Inspired by tinyplot's \code{alpha} argument.} + +\item{palette}{Character string naming a color palette (e.g., +\code{'category10'}, \code{'tableau10'}, \code{'set2'}). A convenient alternative to +calling \code{scale_color(palette = ...)}. Inspired by tinyplot's \code{palette} +argument.} } \value{ A \code{g2} object (S3 class). @@ -106,4 +112,7 @@ g2(mtcars, hp ~ mpg, facet = gear ~ cyl) # Adjust opacity (like tinyplot's alpha) g2(iris, Sepal.Length ~ Sepal.Width | Species, alpha = 0.5) + +# Set color palette (like tinyplot's palette) +g2(iris, Sepal.Length ~ Sepal.Width | Species, palette = 'set2') } diff --git a/man/gg.Rd b/man/gg.Rd index 6d00c0c..728b3a9 100644 --- a/man/gg.Rd +++ b/man/gg.Rd @@ -16,7 +16,8 @@ gg( sub = NULL, by = NULL, facet = NULL, - alpha = NULL + alpha = NULL, + palette = NULL ) } \arguments{ @@ -54,6 +55,11 @@ For more control, use \code{\link[=facet_rect]{facet_rect()}} or \code{\link[=fa of marks. A convenient alternative to calling \code{style_mark(fillOpacity = ..., strokeOpacity = ...)}. Inspired by tinyplot's \code{alpha} argument.} + +\item{palette}{Character string naming a color palette (e.g., +\code{'category10'}, \code{'tableau10'}, \code{'set2'}). A convenient alternative to +calling \code{scale_color(palette = ...)}. Inspired by tinyplot's \code{palette} +argument.} } \description{ \code{gg()} is a convenient shorthand for \code{\link[=g2]{g2()}}, inspired by tinyplot's \code{plt()} diff --git a/tests/testit/test-gglite.R b/tests/testit/test-gglite.R index a4c70d8..f29db06 100644 --- a/tests/testit/test-gglite.R +++ b/tests/testit/test-gglite.R @@ -138,3 +138,8 @@ assert('g2() by argument sets color aesthetic', { chart = g2(iris, x = 'Sepal.Width', y = 'Sepal.Length', by = 'Species') (chart$aesthetics$color %==% 'Species') }) + +assert('g2() palette argument sets color scale palette', { + chart = g2(iris, x = 'Sepal.Width', y = 'Sepal.Length', palette = 'set2') + (chart$scales$color$palette %==% 'set2') +}) From c67c1886780c617fe3c44e4fb0f7072038842bdd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 06:47:07 +0000 Subject: [PATCH 07/12] Update NEWS.md with tinyplot-inspired features Agent-Logs-Url: https://github.com/yihui/gglite/sessions/df53ceb6-3fa6-43d1-aba3-b50da99ad847 Co-authored-by: yihui-bot <264330240+yihui-bot@users.noreply.github.com> --- NEWS.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/NEWS.md b/NEWS.md index 0624123..2d0801f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,3 +7,31 @@ with support for 35+ geometry types (marks), scales, coordinates, themes, transforms, facets, animations, and chart components (axes, legends, titles, tooltips, labels, sliders, and scrollbars). Renders in R Markdown, litedown, Shiny, and standalone HTML previews. + +## Ergonomic improvements inspired by [tinyplot](https://grantmcdermott.com/tinyplot/) + +- Added `gg()` as a shorthand alias for `g2()`, similar to tinyplot's `plt()` + alias for `tinyplot()`. + +- Added `main` and `sub` arguments to `g2()` for setting the chart title and + subtitle directly, like base `plot()` and tinyplot. + +- Added `by` argument to `g2()` for color grouping, inspired by tinyplot's `by` + argument. Equivalent to `color = 'column'`. + +- **Breaking change:** The formula `|` operator now sets color grouping (e.g., + `y ~ x | Species` maps `Species` to color) instead of faceting. This aligns + with tinyplot's formula convention, where `|` means "group by." Faceting is + now done via the new `facet` argument. + +- Added `facet` argument to `g2()` accepting a formula (`~ z` for columns, + `y ~ x` for grid) or a character string. Inspired by tinyplot's `facet` + argument. + +- Added `alpha` argument to `g2()` for controlling mark opacity, inspired by + tinyplot's `alpha` argument. Sets `fillOpacity` and `strokeOpacity` on all + marks without needing `style_mark()`. + +- Added `palette` argument to `g2()` for convenient color palette selection + (e.g., `palette = 'set2'`), inspired by tinyplot's `palette` argument. + Equivalent to `scale_color(palette = ...)`. From f206bf9d2037592a5339c5d99678dfb5a0d12bd7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 06:48:42 +0000 Subject: [PATCH 08/12] Address code review: fix alpha null style, simplify by logic, enhance gg() test Agent-Logs-Url: https://github.com/yihui/gglite/sessions/df53ceb6-3fa6-43d1-aba3-b50da99ad847 Co-authored-by: yihui-bot <264330240+yihui-bot@users.noreply.github.com> --- R/gglite.R | 2 +- R/render.R | 3 ++- tests/testit/test-gglite.R | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/R/gglite.R b/R/gglite.R index 4b5e4d6..86625bf 100644 --- a/R/gglite.R +++ b/R/gglite.R @@ -163,7 +163,7 @@ g2 = function( ), class = 'g2') if (length(dots)) chart$aesthetics = modifyList(chart$aesthetics, dots) # by argument (explicit or from formula |) sets color grouping - if (is.null(by) && !is.null(by_from_formula)) by = by_from_formula + if (is.null(by)) by = by_from_formula if (!is.null(by)) chart$aesthetics$color = by if (!is.null(palette)) chart$scales$color = list(palette = palette) chart diff --git a/R/render.R b/R/render.R index 91c5dc5..9965ecb 100644 --- a/R/render.R +++ b/R/render.R @@ -101,7 +101,8 @@ build_config = function(chart) { if (!is.null(chart$alpha)) { a = chart$alpha for (i in seq_along(config$children)) { - s = config$children[[i]]$style + s = if (is.null(config$children[[i]]$style)) list() else + config$children[[i]]$style if (is.null(s$fillOpacity)) s$fillOpacity = a if (is.null(s$strokeOpacity)) s$strokeOpacity = a config$children[[i]]$style = s diff --git a/tests/testit/test-gglite.R b/tests/testit/test-gglite.R index f29db06..7c65c1f 100644 --- a/tests/testit/test-gglite.R +++ b/tests/testit/test-gglite.R @@ -121,6 +121,12 @@ assert('gg() is a shorthand alias for g2()', { (inherits(chart, 'g2')) (chart$aesthetics$x %==% 'mpg') (chart$aesthetics$y %==% 'hp') + # gg() passes through all parameters + chart2 = gg(iris, Sepal.Length ~ Sepal.Width, by = 'Species', + main = 'Test', alpha = 0.5) + (chart2$aesthetics$color %==% 'Species') + (chart2$chart_title %==% 'Test') + (chart2$alpha %==% 0.5) }) assert('g2() main argument sets chart title', { From 2ed6e9b20b2fe43f8311969eda2ff311a79c46d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 07:02:41 +0000 Subject: [PATCH 09/12] Keep only title/subtitle; drop gg(), by, |color, alpha, palette, facet per review Agent-Logs-Url: https://github.com/yihui/gglite/sessions/2d28c2bc-b9d1-4f63-b367-28a0353f740a Co-authored-by: yihui-bot <264330240+yihui-bot@users.noreply.github.com> --- NAMESPACE | 1 - NEWS.md | 29 +------------ R/gglite.R | 86 +++++++------------------------------- R/render.R | 12 ------ R/utils.R | 17 ++++++-- man/g2.Rd | 58 +++++-------------------- man/gg.Rd | 70 ------------------------------- tests/testit/test-gglite.R | 56 +++++-------------------- tests/testit/test-render.R | 15 ------- tests/testit/test-utils.R | 12 +++--- vignettes/gglite.Rmd | 8 +--- 11 files changed, 58 insertions(+), 306 deletions(-) delete mode 100644 man/gg.Rd diff --git a/NAMESPACE b/NAMESPACE index 305571a..a4c897f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -20,7 +20,6 @@ export(facet_circle) export(facet_rect) export(g2) export(g2Output) -export(gg) export(interact) export(labels_) export(legend_) diff --git a/NEWS.md b/NEWS.md index 2d0801f..662483e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,30 +8,5 @@ transforms, facets, animations, and chart components (axes, legends, titles, tooltips, labels, sliders, and scrollbars). Renders in R Markdown, litedown, Shiny, and standalone HTML previews. -## Ergonomic improvements inspired by [tinyplot](https://grantmcdermott.com/tinyplot/) - -- Added `gg()` as a shorthand alias for `g2()`, similar to tinyplot's `plt()` - alias for `tinyplot()`. - -- Added `main` and `sub` arguments to `g2()` for setting the chart title and - subtitle directly, like base `plot()` and tinyplot. - -- Added `by` argument to `g2()` for color grouping, inspired by tinyplot's `by` - argument. Equivalent to `color = 'column'`. - -- **Breaking change:** The formula `|` operator now sets color grouping (e.g., - `y ~ x | Species` maps `Species` to color) instead of faceting. This aligns - with tinyplot's formula convention, where `|` means "group by." Faceting is - now done via the new `facet` argument. - -- Added `facet` argument to `g2()` accepting a formula (`~ z` for columns, - `y ~ x` for grid) or a character string. Inspired by tinyplot's `facet` - argument. - -- Added `alpha` argument to `g2()` for controlling mark opacity, inspired by - tinyplot's `alpha` argument. Sets `fillOpacity` and `strokeOpacity` on all - marks without needing `style_mark()`. - -- Added `palette` argument to `g2()` for convenient color palette selection - (e.g., `palette = 'set2'`), inspired by tinyplot's `palette` argument. - Equivalent to `scale_color(palette = ...)`. +- Added `title` and `subtitle` arguments to `g2()` for setting the chart title + and subtitle directly, as a convenient alternative to piping into `title_()`. diff --git a/R/gglite.R b/R/gglite.R index 86625bf..3e6a229 100644 --- a/R/gglite.R +++ b/R/gglite.R @@ -34,8 +34,8 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' \item{`~ x`}{Maps only `x` (e.g., for histograms or bar counts).} #' \item{`~ x1 + x2 + x3`}{Creates a `position` encoding with multiple #' fields (for parallel coordinates).} -#' \item{`y ~ x | z`}{Groups by `z` (maps to color), inspired by -#' tinyplot's formula convention.} +#' \item{`y ~ x | z`}{Facets the chart by `z` (column direction).} +#' \item{`y ~ x | z1 + z2`}{Facets by `z1` (columns) and `z2` (rows).} #' } #' Additional aesthetics (e.g., `color`, `size`) can still be passed as named #' arguments alongside the formula. @@ -52,24 +52,10 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' (applied to all sides) or a length-4 vector `c(top, right, bottom, left)`; #' use `NA` to skip individual sides. `NULL` (the default) leaves the value #' unset. -#' @param main Chart title string, a convenient alternative to calling -#' [title_()] separately (inspired by base [plot()] and tinyplot). -#' @param sub Chart subtitle string. Only used when `main` is also provided. -#' @param by Column name (character string) to group the data by color, -#' inspired by tinyplot's `by` argument. Equivalent to passing -#' `color = 'column'`. -#' @param facet Faceting specification: a one-sided formula (`~ z` for -#' column faceting, `y ~ x` for grid faceting), or a character string -#' naming the faceting variable. Inspired by tinyplot's `facet` argument. -#' For more control, use [facet_rect()] or [facet_circle()] instead. -#' @param alpha Numeric in `[0, 1]` controlling the fill and stroke opacity -#' of marks. A convenient alternative to calling -#' `style_mark(fillOpacity = ..., strokeOpacity = ...)`. Inspired by -#' tinyplot's `alpha` argument. -#' @param palette Character string naming a color palette (e.g., -#' `'category10'`, `'tableau10'`, `'set2'`). A convenient alternative to -#' calling `scale_color(palette = ...)`. Inspired by tinyplot's `palette` -#' argument. +#' @param title Chart title string, a convenient alternative to piping into +#' [title_()] separately. +#' @param subtitle Chart subtitle string. Only used when `title` is also +#' provided. #' @return A `g2` object (S3 class). #' @import stats utils #' @export @@ -85,48 +71,19 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' g2(sunspot.year) #' g2(EuStockMarkets) #' -#' # Title and subtitle (like base plot's main/sub) -#' g2(mtcars, hp ~ mpg, main = 'Motor Trend Cars', sub = 'mpg vs hp') -#' -#' # Grouping by color (like tinyplot's by argument) -#' g2(iris, Sepal.Length ~ Sepal.Width, by = 'Species') -#' -#' # Formula | for color grouping (tinyplot convention) -#' g2(iris, Sepal.Length ~ Sepal.Width | Species) -#' -#' # Faceting via the facet argument (like tinyplot) -#' g2(iris, Sepal.Length ~ Sepal.Width, facet = ~Species) -#' g2(mtcars, hp ~ mpg, facet = gear ~ cyl) -#' -#' # Adjust opacity (like tinyplot's alpha) -#' g2(iris, Sepal.Length ~ Sepal.Width | Species, alpha = 0.5) -#' -#' # Set color palette (like tinyplot's palette) -#' g2(iris, Sepal.Length ~ Sepal.Width | Species, palette = 'set2') +#' # Title and subtitle +#' g2(mtcars, hp ~ mpg, title = 'Motor Trend Cars', subtitle = 'mpg vs hp') g2 = function( data = NULL, ..., width = 640, height = 480, padding = NULL, margin = NULL, inset = NULL, - main = NULL, sub = NULL, by = NULL, facet = NULL, alpha = NULL, - palette = NULL + title = NULL, subtitle = NULL ) { dots = list(...) - by_from_formula = NULL has_formula = length(dots) && inherits(dots[[1]], 'formula') - if (has_formula) { + facet_from_formula = if (has_formula) { parsed = parse_formula(dots[[1]]) dots = c(parsed$aesthetics, dots[-1]) - by_from_formula = parsed$by - } - # Build facet from the facet argument (formula or character) - facet_config = if (inherits(facet, 'formula')) { - fterms = if (length(facet) == 3) { - list(y = deparse(facet[[2]]), x = deparse(facet[[3]])) - } else { - list(x = deparse(facet[[2]])) - } - list(type = 'facetRect', encode = fterms) - } else if (is.character(facet)) { - list(type = 'facetRect', encode = list(x = facet)) + parsed$facet } # Convert time series to data frame with default aesthetics ts_aes = NULL @@ -150,11 +107,10 @@ g2 = function( theme = NULL, axes = list(), legends = list(), - chart_title = if (!is.null(main)) { - if (!is.null(sub)) list(title = main, subtitle = sub) else main + chart_title = if (!is.null(title)) { + if (!is.null(subtitle)) list(title = title, subtitle = subtitle) else title }, - facet = facet_config, - alpha = alpha, + facet = facet_from_formula, layout = c( process_layout('padding', padding), process_layout('margin', margin), @@ -162,10 +118,6 @@ g2 = function( ) ), class = 'g2') if (length(dots)) chart$aesthetics = modifyList(chart$aesthetics, dots) - # by argument (explicit or from formula |) sets color grouping - if (is.null(by)) by = by_from_formula - if (!is.null(by)) chart$aesthetics$color = by - if (!is.null(palette)) chart$scales$color = list(palette = palette) chart } @@ -186,14 +138,4 @@ encode = function(chart, ...) { chart } -#' Shorthand Alias for [g2()] -#' -#' `gg()` is a convenient shorthand for [g2()], inspired by tinyplot's `plt()` -#' alias. All arguments are passed through to [g2()]. -#' -#' @inheritParams g2 -#' @export -#' @examples -#' gg(mtcars, x = 'mpg', y = 'hp') -gg = g2 diff --git a/R/render.R b/R/render.R index 9965ecb..87aad54 100644 --- a/R/render.R +++ b/R/render.R @@ -97,18 +97,6 @@ build_config = function(chart) { if (length(marks)) config$children = marks - # Apply chart-level alpha to all marks - if (!is.null(chart$alpha)) { - a = chart$alpha - for (i in seq_along(config$children)) { - s = if (is.null(config$children[[i]]$style)) list() else - config$children[[i]]$style - if (is.null(s$fillOpacity)) s$fillOpacity = a - if (is.null(s$strokeOpacity)) s$strokeOpacity = a - config$children[[i]]$style = s - } - } - # Chart-wide config if (length(chart$scales)) config$scale = chart$scales if (!is.null(chart$coords)) config$coordinate = chart$coords diff --git a/R/utils.R b/R/utils.R index bce5778..14289f8 100644 --- a/R/utils.R +++ b/R/utils.R @@ -129,10 +129,10 @@ parse_formula = function(f) { lhs = if (length(f) == 3) f[[2]] rhs = if (length(f) == 3) f[[3]] else f[[2]] - # Extract conditioning (by/color) variable from | - by_term = NULL + # Extract conditioning (facet) variables from | + facet_terms = NULL if (is.call(rhs) && identical(rhs[[1]], as.name('|'))) { - by_term = deparse(rhs[[3]]) + facet_terms = extract_terms(rhs[[3]]) rhs = rhs[[2]] } @@ -147,5 +147,14 @@ parse_formula = function(f) { aesthetics$position = rhs_terms } - list(aesthetics = aesthetics, by = by_term) + # Build facet + facet = NULL + if (length(facet_terms)) { + enc = list() + if (length(facet_terms) >= 1) enc$x = facet_terms[1] + if (length(facet_terms) >= 2) enc$y = facet_terms[2] + facet = list(type = 'facetRect', encode = enc) + } + + list(aesthetics = aesthetics, facet = facet) } diff --git a/man/g2.Rd b/man/g2.Rd index 37aa04c..cec2db1 100644 --- a/man/g2.Rd +++ b/man/g2.Rd @@ -12,12 +12,8 @@ g2( padding = NULL, margin = NULL, inset = NULL, - main = NULL, - sub = NULL, - by = NULL, - facet = NULL, - alpha = NULL, - palette = NULL + title = NULL, + subtitle = NULL ) } \arguments{ @@ -37,29 +33,11 @@ or a formula followed by optional named aesthetics.} use \code{NA} to skip individual sides. \code{NULL} (the default) leaves the value unset.} -\item{main}{Chart title string, a convenient alternative to calling -\code{\link[=title_]{title_()}} separately (inspired by base \code{\link[=plot]{plot()}} and tinyplot).} +\item{title}{Chart title string, a convenient alternative to piping into +\code{\link[=title_]{title_()}} separately.} -\item{sub}{Chart subtitle string. Only used when \code{main} is also provided.} - -\item{by}{Column name (character string) to group the data by color, -inspired by tinyplot's \code{by} argument. Equivalent to passing -\code{color = 'column'}.} - -\item{facet}{Faceting specification: a one-sided formula (\code{~ z} for -column faceting, \code{y ~ x} for grid faceting), or a character string -naming the faceting variable. Inspired by tinyplot's \code{facet} argument. -For more control, use \code{\link[=facet_rect]{facet_rect()}} or \code{\link[=facet_circle]{facet_circle()}} instead.} - -\item{alpha}{Numeric in \verb{[0, 1]} controlling the fill and stroke opacity -of marks. A convenient alternative to calling -\code{style_mark(fillOpacity = ..., strokeOpacity = ...)}. Inspired by -tinyplot's \code{alpha} argument.} - -\item{palette}{Character string naming a color palette (e.g., -\code{'category10'}, \code{'tableau10'}, \code{'set2'}). A convenient alternative to -calling \code{scale_color(palette = ...)}. Inspired by tinyplot's \code{palette} -argument.} +\item{subtitle}{Chart subtitle string. Only used when \code{title} is also +provided.} } \value{ A \code{g2} object (S3 class). @@ -78,8 +56,8 @@ argument after \code{data}: \item{\code{~ x}}{Maps only \code{x} (e.g., for histograms or bar counts).} \item{\code{~ x1 + x2 + x3}}{Creates a \code{position} encoding with multiple fields (for parallel coordinates).} -\item{\code{y ~ x | z}}{Groups by \code{z} (maps to color), inspired by -tinyplot's formula convention.} +\item{\code{y ~ x | z}}{Facets the chart by \code{z} (column direction).} +\item{\code{y ~ x | z1 + z2}}{Facets by \code{z1} (columns) and \code{z2} (rows).} } Additional aesthetics (e.g., \code{color}, \code{size}) can still be passed as named arguments alongside the formula. @@ -97,22 +75,6 @@ g2(mtcars, ~ mpg) g2(sunspot.year) g2(EuStockMarkets) -# Title and subtitle (like base plot's main/sub) -g2(mtcars, hp ~ mpg, main = 'Motor Trend Cars', sub = 'mpg vs hp') - -# Grouping by color (like tinyplot's by argument) -g2(iris, Sepal.Length ~ Sepal.Width, by = 'Species') - -# Formula | for color grouping (tinyplot convention) -g2(iris, Sepal.Length ~ Sepal.Width | Species) - -# Faceting via the facet argument (like tinyplot) -g2(iris, Sepal.Length ~ Sepal.Width, facet = ~Species) -g2(mtcars, hp ~ mpg, facet = gear ~ cyl) - -# Adjust opacity (like tinyplot's alpha) -g2(iris, Sepal.Length ~ Sepal.Width | Species, alpha = 0.5) - -# Set color palette (like tinyplot's palette) -g2(iris, Sepal.Length ~ Sepal.Width | Species, palette = 'set2') +# Title and subtitle +g2(mtcars, hp ~ mpg, title = 'Motor Trend Cars', subtitle = 'mpg vs hp') } diff --git a/man/gg.Rd b/man/gg.Rd deleted file mode 100644 index 728b3a9..0000000 --- a/man/gg.Rd +++ /dev/null @@ -1,70 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/gglite.R -\name{gg} -\alias{gg} -\title{Shorthand Alias for \code{\link[=g2]{g2()}}} -\usage{ -gg( - data = NULL, - ..., - width = 640, - height = 480, - padding = NULL, - margin = NULL, - inset = NULL, - main = NULL, - sub = NULL, - by = NULL, - facet = NULL, - alpha = NULL, - palette = NULL -) -} -\arguments{ -\item{data}{A data frame, a \code{ts}/\code{mts} time series object, or \code{NULL}. Time -series objects are automatically converted to data frames (with columns -\code{time} and \code{value} for univariate series, or \code{time}, \code{series}, and \code{value} -for multivariate series) and default aesthetic mappings are set -accordingly.} - -\item{...}{Aesthetic mappings as \code{name = 'column'} pairs (character strings), -or a formula followed by optional named aesthetics.} - -\item{width, height}{Width and height of the chart in pixels.} - -\item{padding, margin, inset}{Layout spacing in pixels. Each can be a scalar -(applied to all sides) or a length-4 vector \code{c(top, right, bottom, left)}; -use \code{NA} to skip individual sides. \code{NULL} (the default) leaves the value -unset.} - -\item{main}{Chart title string, a convenient alternative to calling -\code{\link[=title_]{title_()}} separately (inspired by base \code{\link[=plot]{plot()}} and tinyplot).} - -\item{sub}{Chart subtitle string. Only used when \code{main} is also provided.} - -\item{by}{Column name (character string) to group the data by color, -inspired by tinyplot's \code{by} argument. Equivalent to passing -\code{color = 'column'}.} - -\item{facet}{Faceting specification: a one-sided formula (\code{~ z} for -column faceting, \code{y ~ x} for grid faceting), or a character string -naming the faceting variable. Inspired by tinyplot's \code{facet} argument. -For more control, use \code{\link[=facet_rect]{facet_rect()}} or \code{\link[=facet_circle]{facet_circle()}} instead.} - -\item{alpha}{Numeric in \verb{[0, 1]} controlling the fill and stroke opacity -of marks. A convenient alternative to calling -\code{style_mark(fillOpacity = ..., strokeOpacity = ...)}. Inspired by -tinyplot's \code{alpha} argument.} - -\item{palette}{Character string naming a color palette (e.g., -\code{'category10'}, \code{'tableau10'}, \code{'set2'}). A convenient alternative to -calling \code{scale_color(palette = ...)}. Inspired by tinyplot's \code{palette} -argument.} -} -\description{ -\code{gg()} is a convenient shorthand for \code{\link[=g2]{g2()}}, inspired by tinyplot's \code{plt()} -alias. All arguments are passed through to \code{\link[=g2]{g2()}}. -} -\examples{ -gg(mtcars, x = 'mpg', y = 'hp') -} diff --git a/tests/testit/test-gglite.R b/tests/testit/test-gglite.R index 7c65c1f..cd84008 100644 --- a/tests/testit/test-gglite.R +++ b/tests/testit/test-gglite.R @@ -82,31 +82,19 @@ assert('g2() formula with extra aesthetics', { (chart$aesthetics$color %==% 'Species') }) -assert('g2() formula with | sets color grouping (tinyplot convention)', { +assert('g2() formula with faceting y ~ x | z', { chart = g2(iris, Sepal.Length ~ Sepal.Width | Species) (chart$aesthetics$x %==% 'Sepal.Width') (chart$aesthetics$y %==% 'Sepal.Length') - (chart$aesthetics$color %==% 'Species') - (is.null(chart$facet)) -}) - -assert('g2() facet argument with one-sided formula', { - chart = g2(iris, x = 'Sepal.Width', y = 'Sepal.Length', facet = ~Species) (chart$facet$type %==% 'facetRect') (chart$facet$encode$x %==% 'Species') }) -assert('g2() facet argument with two-sided formula', { - chart = g2(mtcars, x = 'mpg', y = 'hp', facet = gear ~ cyl) - (chart$facet$type %==% 'facetRect') - (chart$facet$encode$x %==% 'cyl') - (chart$facet$encode$y %==% 'gear') -}) - -assert('g2() facet argument with character string', { - chart = g2(iris, x = 'Sepal.Width', y = 'Sepal.Length', facet = 'Species') - (chart$facet$type %==% 'facetRect') - (chart$facet$encode$x %==% 'Species') +assert('g2() formula with two facet variables', { + df = data.frame(x = 1, y = 2, a = 'A', b = 'B') + chart = g2(df, y ~ x | a + b) + (chart$facet$encode$x %==% 'a') + (chart$facet$encode$y %==% 'b') }) assert('g2() formula ~ x1 + x2 + x3 sets position encoding', { @@ -116,36 +104,14 @@ assert('g2() formula ~ x1 + x2 + x3 sets position encoding', { (is.null(chart$aesthetics$x)) }) -assert('gg() is a shorthand alias for g2()', { - chart = gg(mtcars, x = 'mpg', y = 'hp') - (inherits(chart, 'g2')) - (chart$aesthetics$x %==% 'mpg') - (chart$aesthetics$y %==% 'hp') - # gg() passes through all parameters - chart2 = gg(iris, Sepal.Length ~ Sepal.Width, by = 'Species', - main = 'Test', alpha = 0.5) - (chart2$aesthetics$color %==% 'Species') - (chart2$chart_title %==% 'Test') - (chart2$alpha %==% 0.5) -}) - -assert('g2() main argument sets chart title', { - chart = g2(mtcars, x = 'mpg', y = 'hp', main = 'My Title') +assert('g2() title argument sets chart title', { + chart = g2(mtcars, x = 'mpg', y = 'hp', title = 'My Title') (chart$chart_title %==% 'My Title') }) -assert('g2() main and sub arguments set title and subtitle', { - chart = g2(mtcars, x = 'mpg', y = 'hp', main = 'Title', sub = 'Subtitle') +assert('g2() title and subtitle arguments set title and subtitle', { + chart = g2(mtcars, x = 'mpg', y = 'hp', + title = 'Title', subtitle = 'Subtitle') (chart$chart_title$title %==% 'Title') (chart$chart_title$subtitle %==% 'Subtitle') }) - -assert('g2() by argument sets color aesthetic', { - chart = g2(iris, x = 'Sepal.Width', y = 'Sepal.Length', by = 'Species') - (chart$aesthetics$color %==% 'Species') -}) - -assert('g2() palette argument sets color scale palette', { - chart = g2(iris, x = 'Sepal.Width', y = 'Sepal.Length', palette = 'set2') - (chart$scales$color$palette %==% 'set2') -}) diff --git a/tests/testit/test-render.R b/tests/testit/test-render.R index e1c44b1..d437869 100644 --- a/tests/testit/test-render.R +++ b/tests/testit/test-render.R @@ -215,18 +215,3 @@ assert('build_config ts y-axis title does not override user title', { config = build_config(chart) (config$axis$y$title %==% 'My Title') }) - -assert('build_config applies alpha to mark styles', { - chart = g2(mtcars, x = 'mpg', y = 'hp', alpha = 0.3) |> mark_point() - config = build_config(chart) - (config$children[[1]]$style$fillOpacity %==% 0.3) - (config$children[[1]]$style$strokeOpacity %==% 0.3) -}) - -assert('build_config alpha does not override explicit mark style opacity', { - chart = g2(mtcars, x = 'mpg', y = 'hp', alpha = 0.3) |> - mark_point() |> style_mark(fillOpacity = 0.8) - config = build_config(chart) - (config$children[[1]]$style$fillOpacity %==% 0.8) - (config$children[[1]]$style$strokeOpacity %==% 0.3) -}) diff --git a/tests/testit/test-utils.R b/tests/testit/test-utils.R index da0f4b4..55428e1 100644 --- a/tests/testit/test-utils.R +++ b/tests/testit/test-utils.R @@ -54,16 +54,18 @@ assert('parse_formula: ~ x1 + x2 + x3', { (is.null(res$aesthetics$x)) }) -assert('parse_formula: y ~ x | z sets color grouping (not facet)', { +assert('parse_formula: y ~ x | z', { res = parse_formula(hp ~ mpg | cyl) (res$aesthetics$x %==% 'mpg') (res$aesthetics$y %==% 'hp') - (res$by %==% 'cyl') + (res$facet$type %==% 'facetRect') + (res$facet$encode$x %==% 'cyl') }) -assert('parse_formula: no | returns NULL by', { - res = parse_formula(y ~ x) - (is.null(res$by)) +assert('parse_formula: y ~ x | z1 + z2', { + res = parse_formula(y ~ x | a + b) + (res$facet$encode$x %==% 'a') + (res$facet$encode$y %==% 'b') }) assert('ts_to_df converts univariate ts', { diff --git a/vignettes/gglite.Rmd b/vignettes/gglite.Rmd index 0197bfe..8011024 100644 --- a/vignettes/gglite.Rmd +++ b/vignettes/gglite.Rmd @@ -67,18 +67,12 @@ Additional aesthetics like `color` can be passed alongside the formula: g2(iris, Sepal.Length ~ Sepal.Width, color = 'Species') ``` -Use `|` for color grouping (inspired by tinyplot): +Use `|` for faceting: ```{r} g2(iris, Sepal.Length ~ Sepal.Width | Species) ``` -Use the `facet` argument for faceting (also inspired by tinyplot): - -```{r} -g2(iris, Sepal.Length ~ Sepal.Width, facet = ~Species) -``` - A one-sided formula maps only `x` (useful for histograms or counts): ```{r} From 1c4c63a502d36878bab30239167b31d481c5f37b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 07:21:20 +0000 Subject: [PATCH 10/12] Simplify chart_title, remove is.null() checks in build_config, drop NEWS for v0.1 Agent-Logs-Url: https://github.com/yihui/gglite/sessions/1f1ea7ca-8cdb-4cc9-9f6f-5bfbe65344d2 Co-authored-by: yihui-bot <264330240+yihui-bot@users.noreply.github.com> --- .github/copilot-instructions.md | 17 ++++++++++------- NEWS.md | 3 --- R/gglite.R | 7 ++----- R/render.R | 8 ++++---- man/g2.Rd | 3 +-- tests/testit/test-gglite.R | 2 +- 6 files changed, 18 insertions(+), 22 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index cdcc6a6..7ae5734 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -155,7 +155,9 @@ Before submitting changes: 2. Run `R CMD check gglite_*.tar.gz --no-manual` to validate 3. Ensure all tests pass: `Rscript tests/test-all.R` 4. Check GitHub Actions status for multi-platform validation -5. Update `NEWS.md` to document your changes (except for v0.1) +5. Update `NEWS.md` to document your changes (except for v0.1). **Do NOT add + NEWS entries while the package is still at v0.1** — the initial release + description is sufficient. ## Important Conventions @@ -220,12 +222,13 @@ as character strings, e.g., `g2(mtcars, x = 'mpg', y = 'hp')`. especially automatically generated ones. 8. **Testing**: Use testit assertions with proper error handling 9. **Update NEWS.md**: When making changes, make sure to update `NEWS.md` - accordingly to document what changed. The first heading in NEWS.md always - represents the dev version and must be of the form `# PKG x.y` where PKG - is the package name and x.y is the next version to be released to CRAN - (note: x.y, not x.y.0). Usually y is bumped from the current minor - version, e.g., if the current dev version is 1.8.3, the next CRAN release - is expected to be 1.9. + accordingly to document what changed — **except for v0.1** (do NOT add + individual change entries for the initial release). The first heading in + NEWS.md always represents the dev version and must be of the form + `# PKG x.y` where PKG is the package name and x.y is the next version to + be released to CRAN (note: x.y, not x.y.0). Usually y is bumped from the + current minor version, e.g., if the current dev version is 1.8.3, the next + CRAN release is expected to be 1.9. ## Package API diff --git a/NEWS.md b/NEWS.md index 662483e..0624123 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,3 @@ with support for 35+ geometry types (marks), scales, coordinates, themes, transforms, facets, animations, and chart components (axes, legends, titles, tooltips, labels, sliders, and scrollbars). Renders in R Markdown, litedown, Shiny, and standalone HTML previews. - -- Added `title` and `subtitle` arguments to `g2()` for setting the chart title - and subtitle directly, as a convenient alternative to piping into `title_()`. diff --git a/R/gglite.R b/R/gglite.R index 3e6a229..6ea3c79 100644 --- a/R/gglite.R +++ b/R/gglite.R @@ -54,8 +54,7 @@ g2_patches_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils@v1.14.30/js/g2-patche #' unset. #' @param title Chart title string, a convenient alternative to piping into #' [title_()] separately. -#' @param subtitle Chart subtitle string. Only used when `title` is also -#' provided. +#' @param subtitle Chart subtitle string. #' @return A `g2` object (S3 class). #' @import stats utils #' @export @@ -107,9 +106,7 @@ g2 = function( theme = NULL, axes = list(), legends = list(), - chart_title = if (!is.null(title)) { - if (!is.null(subtitle)) list(title = title, subtitle = subtitle) else title - }, + chart_title = dropNulls(list(title = title, subtitle = subtitle)), facet = facet_from_formula, layout = c( process_layout('padding', padding), diff --git a/R/render.R b/R/render.R index 87aad54..7fcdfca 100644 --- a/R/render.R +++ b/R/render.R @@ -109,10 +109,10 @@ build_config = function(chart) { config$axis$y = modifyList(as.list(y_ax), list(title = chart$ts_name)) } if (length(chart$legends)) config$legend = chart$legends - if (!is.null(chart$chart_title)) config$title = chart$chart_title - if (!is.null(chart$tooltip_config)) config$tooltip = chart$tooltip_config - if (!is.null(chart$sliders)) config$slider = chart$sliders - if (!is.null(chart$scrollbars)) config$scrollbar = chart$scrollbars + if (length(chart$chart_title)) config$title = chart$chart_title + config$tooltip = chart$tooltip_config + config$slider = chart$sliders + config$scrollbar = chart$scrollbars if (length(chart$layout)) config = modifyList(config, chart$layout) # Theme: merge global option with per-chart theme diff --git a/man/g2.Rd b/man/g2.Rd index cec2db1..d1692e8 100644 --- a/man/g2.Rd +++ b/man/g2.Rd @@ -36,8 +36,7 @@ unset.} \item{title}{Chart title string, a convenient alternative to piping into \code{\link[=title_]{title_()}} separately.} -\item{subtitle}{Chart subtitle string. Only used when \code{title} is also -provided.} +\item{subtitle}{Chart subtitle string.} } \value{ A \code{g2} object (S3 class). diff --git a/tests/testit/test-gglite.R b/tests/testit/test-gglite.R index cd84008..0fe2564 100644 --- a/tests/testit/test-gglite.R +++ b/tests/testit/test-gglite.R @@ -106,7 +106,7 @@ assert('g2() formula ~ x1 + x2 + x3 sets position encoding', { assert('g2() title argument sets chart title', { chart = g2(mtcars, x = 'mpg', y = 'hp', title = 'My Title') - (chart$chart_title %==% 'My Title') + (chart$chart_title$title %==% 'My Title') }) assert('g2() title and subtitle arguments set title and subtitle', { From 42c2a3b0ae8c6eee5cf45659de93f57504774b6c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 07:35:32 +0000 Subject: [PATCH 11/12] Add title/subtitle g2() param examples to titles-tooltips.Rmd Agent-Logs-Url: https://github.com/yihui/gglite/sessions/c1c083a1-cad5-4057-a1c8-81d37f9f548b Co-authored-by: yihui-bot <264330240+yihui-bot@users.noreply.github.com> --- examples/titles-tooltips.Rmd | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/examples/titles-tooltips.Rmd b/examples/titles-tooltips.Rmd index 7204c80..1fe41cb 100644 --- a/examples/titles-tooltips.Rmd +++ b/examples/titles-tooltips.Rmd @@ -8,6 +8,19 @@ library(gglite) ## Chart Title +### Title via `g2()` arguments + +```{r} +g2(mtcars, x = 'mpg', y = 'hp', title = 'Motor Trend Cars') +``` + +### Title and subtitle via `g2()` arguments + +```{r} +g2(mtcars, x = 'mpg', y = 'hp', + title = 'Motor Trend Cars', subtitle = 'mpg vs horsepower') +``` + ### Simple title ```{r} From 05c16fe3bb4e8e05889d6c7fa549bf779f7bdf65 Mon Sep 17 00:00:00 2001 From: Yihui bot Date: Thu, 2 Apr 2026 02:37:04 -0500 Subject: [PATCH 12/12] Remove title example from titles-tooltips.Rmd --- examples/titles-tooltips.Rmd | 6 ------ 1 file changed, 6 deletions(-) diff --git a/examples/titles-tooltips.Rmd b/examples/titles-tooltips.Rmd index 1fe41cb..70a5230 100644 --- a/examples/titles-tooltips.Rmd +++ b/examples/titles-tooltips.Rmd @@ -8,12 +8,6 @@ library(gglite) ## Chart Title -### Title via `g2()` arguments - -```{r} -g2(mtcars, x = 'mpg', y = 'hp', title = 'Motor Trend Cars') -``` - ### Title and subtitle via `g2()` arguments ```{r}