Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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,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
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export(facet_circle)
export(facet_rect)
export(g2)
export(g2Output)
export(gg)
export(interact)
export(labels_)
export(legend_)
Expand Down
28 changes: 28 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ...)`.
82 changes: 75 additions & 7 deletions R/gglite.R
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -52,6 +52,24 @@ 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.
#' @return A `g2` object (S3 class).
#' @import stats utils
#' @export
Expand All @@ -66,16 +84,49 @@ 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')
#'
#' # 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')
g2 = function(
data = NULL, ..., width = 640, height = 480,
padding = NULL, margin = NULL, inset = NULL
padding = NULL, margin = NULL, inset = NULL,
main = NULL, sub = NULL, by = NULL, facet = NULL, alpha = NULL,
palette = 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
Expand All @@ -99,15 +150,22 @@ g2 = function(
theme = NULL,
axes = list(),
legends = list(),
chart_title = NULL,
facet = facet_from_formula,
chart_title = if (!is.null(main)) {
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),
process_layout('inset', inset)
)
), 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
}

Expand All @@ -128,4 +186,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

12 changes: 12 additions & 0 deletions R/render.R
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ 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
Expand Down
17 changes: 4 additions & 13 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
}

Expand All @@ -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)
}
55 changes: 52 additions & 3 deletions man/g2.Rd

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

70 changes: 70 additions & 0 deletions man/gg.Rd

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

Loading
Loading