Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
11 changes: 11 additions & 0 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,14 @@ jobs:

- name: Test
run: ./run.sh run_tests

- name: Upload snapshot review (on failure)
if: failure()
uses: actions/upload-artifact@v4
with:
name: tinysnapshot-review-${{ matrix.name }}
path: |
*.Rcheck/**/_tinysnapshot_review/**
*.Rcheck/00check.log
if-no-files-found: ignore
retention-days: 7
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ S3method(tinyplot,data.frame)
S3method(tinyplot,default)
S3method(tinyplot,density)
S3method(tinyplot,formula)
S3method(tinyplot,ts)
export(draw_legend)
export(get_saved_par)
export(plt)
Expand Down Expand Up @@ -135,6 +136,7 @@ importFrom(stats,reformulate)
importFrom(stats,setNames)
importFrom(stats,spline)
importFrom(stats,terms)
importFrom(stats,time)
importFrom(stats,weighted.mean)
importFrom(tools,file_ext)
importFrom(utils,globalVariables)
Expand Down
83 changes: 51 additions & 32 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ Theme fixes:
subtitle, and legend stack correctly above the plot region with proper
spacing. (#605)

### New plot types
### New features

#### New plot types

- `type_chull()` (equivalently, `type = "chull"`) for drawing convex hulls
around grouped points. Uses `grDevices::chull()` under the hood and delegates
Expand All @@ -158,10 +160,10 @@ Theme fixes:
ellipses around grouped points. Like `type_chull`, works well as a filled
layer, e.g. `plt_add(type = "ellipse", fill = 0.2)`. (#610 @grantmcdermott)

### Other new features
#### New `tinyplot.*` methods

- A new top-level `tinypairs()` function, together with a dedicated
`tinyplot.data.frame()` method now supports direct plotting of data frames,
- A top-level `tinypairs()` function, together with a dedicated
`tinyplot.data.frame()` method, now supports direct plotting of data frames,
with or without a formula. Combining with a formula is mostly useful insofar
as it facilitates piping, e.g.

Expand All @@ -174,7 +176,20 @@ Theme fixes:
variables will yield a `pairs()`-style grid of all variable combinations.
Thanks to @mthulin for the suggestion and original implementation idea.
(#613, #640 @zeileis @grantmcdermott)
- New top-level `tinyplot()`/`plt()` arguments:
- New dedicated `tinyplot.ts()` method for `ts` time series, e.g.

```r
plt(EuStockMarkets)
```

Produces a line plot by default, although users can override by passing an
explicit `type` argument. Similarly, multivariate series are faceted by
default, but users can also override to obtain, say, a single frame with
direct labels. (#558 @zeileis @grantmcdermott)

#### Other new features

- New and updated top-level `tinyplot()`/`plt()` arguments:
- `cap = <string>` for adding a caption to your plots. Captions are drawn at
the bottom of the plot and are best paired with dynamic themes (since
separation from `sub` is guaranteed). Appearance is customizable via
Expand Down Expand Up @@ -211,33 +226,37 @@ Theme fixes:
```
The `labels` arg is silently ignored for non-text types.
(#639 @grantmcdermott)
- The `grid` argument (and `tpar("grid")`) now accepts character strings to
control axis-specific grids at different resolutions. Uppercase letters
(`"X"`, `"Y"`, `"XY"`) draw grid lines at the standard tick positions, while
lowercase letters (`"x"`, `"y"`, `"xy"`) draw a finer grid with additional
lines at the midpoints between ticks. Thanks to @zeileis for the suggestion.
(#578 @grantmcdermott)
- Facet formulas now support `1` as a convenience syntax for single row or
column arrangements. (#562 @zeileis)
- `plt(..., facet = z ~ 1)` <-> `plt(..., facet = ~z, facet.args = list(ncol = 1))`
- `plt(..., facet = 1 ~ z)` <-> `plt(..., facet = ~z, facet.args = list(nrow = 1))`.
- `type_barplot()` gains an `offset` argument for shifting bar baselines away
from zero. (#611, #615 @grantmcdermott @zeileis)
- If the offset is an unnamed scalar or numeric vector, it shifts the bars
positionally by the given values. Useful for creating waterfall plots and
floating bars.
- If the offset is a character or named numeric vector, it instead "sets
aside" the named level(s) of the `by` group, pulling them out of the stack
and drawing them as standalone bars. This is useful for Likert plots, where
you want to show a neutral categories (e.g., "Unsure") apart from the
diverging stack. Thanks to @strengejacke for the suggestion.
- `type_text()` gains two new arguments:
- a `labeller` argument that is passed to `tinylabel()` for formatting the
text labels. (#620 @grantmcdermott)
- a `repel` argument that automatically nudges overlapping text labels apart.
One limitation is that the repulsion logic operates with groups. So there
may still be some overlapping text for for grouped data.
(#621 @grantmcdermott)
- The `grid` argument (and `tpar("grid")`) now accepts character strings to
control axis-specific grids at different resolutions. Uppercase letters
(`"X"`, `"Y"`, `"XY"`) draw grid lines at the standard tick positions, while
lowercase letters (`"x"`, `"y"`, `"xy"`) draw a finer grid with additional
lines at the midpoints between ticks. Thanks to @zeileis for the suggestion.
(#578 @grantmcdermott)
- Facet formulas now support `1` as a convenience syntax for single row or
column arrangements. (#562 @zeileis)
- `plt(..., facet = z ~ 1)` <-> `plt(..., facet = ~z, facet.args = list(ncol = 1))`
- `plt(..., facet = 1 ~ z)` <-> `plt(..., facet = ~z, facet.args = list(nrow = 1))`.
- Type-specific updates:
- `type_barplot()` gains an `offset` argument for shifting bar baselines away
from zero. (#611, #615 @grantmcdermott @zeileis)
- If the offset is an unnamed scalar or numeric vector, it shifts the bars
positionally by the given values. Useful for creating waterfall plots and
floating bars.
- If the offset is a character or named numeric vector, it instead "sets
aside" the named level(s) of the `by` group, pulling them out of the stack
and drawing them as standalone bars. This is useful for Likert plots,
where you want to show a neutral categories (e.g., "Unsure") apart from
the diverging stack. Thanks to @strengejacke for the suggestion.
- `type_text()` gains two new arguments:
- a `labeller` argument that is passed to `tinylabel()` for formatting the
text labels. (#620 @grantmcdermott)
- a `repel` argument that automatically nudges overlapping text labels
apart. One limitation is that the repulsion logic operates with groups. So
there may still be some overlapping text for for grouped data.
(#621 @grantmcdermott)
- Model-fit and various distribution types gain a `weights` argument; although
this is best provided from the top-level `tinyplot()`/`plt()` call. See
above.

### Bug fixes

Expand Down
2 changes: 2 additions & 0 deletions R/tinyplot.data.frame.R
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#' to disambiguate from `frame.plot`.
#' @param ... further arguments passed to `tinyplot`.
#'
#' @returns No return value, called for the side effect of producing a plot.
#'
#' @examples
#' ## using tinyplot() with data frames
#' tinyplot(cars)
Expand Down
119 changes: 119 additions & 0 deletions R/tinyplot.ts.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#' tinyplot Method for Plotting ts Objects (Time Series)
#'
#' @description Convenience interface for visualizing \code{\link[stats]{ts}}
#' (time series) objects with tinyplot.
#'
#' @details Internally the time series object is converted to a long
#' data frame with columns `Time` (time index), `Value` (observations),
#' and `Series` (factor with column labels). Depending on the settings
#' of `facet` this data frame is visualized either with the formula
#' `Value ~ Time` or `Value ~ Time | Series`. See the `facet` argument
#' description for more details and the Examples below for some illustrations.
#'
#' An exception is made if the user explicitly supplies a distribution `type`
#' argument (i.e, one of `"histogram"`, `"density"`, `"boxplot"`, `"violin"`,
#' `"qq"`, `"ridge"`, or `"rug"`). These summarize the series values and
#' ignore the time index, so they are visualized with the one-sided
#' formula `~ Value` (or `~ Value | Series` for multivariate series). This
#' preserves base-R-compatible behaviour such as
#' `tinyplot(Nile, type = "histogram")`.
#'
#' @param x an object of class `"ts"`.
#' @param facet specification of `facet` for `tinyplot.formula`. The
#' default in the `tinyplot` method is to use `facet = NULL` for univariate
#' series and `facet = ~ Series` (equivalent to `facet = "by"`) for
#' multivariate series.
#' @param type,facet.args,xlab,ylab,... further arguments passed to `tinyplot`.
#'
#' @returns No return value, called for the side effect of producing a plot.
#'
#' @examples
#' ## univariate series
#' tinyplot(Nile)
#'
#' # exception: expicitly passing a distribution type on a univariate series
#' # still triggers the corresponding transformation
#' tinyplot(Nile, type = "histogram")
#'
#' ## multivariate series (generally, these also look better with a theme)
#' tinytheme("clean2")
#' tinyplot(EuStockMarkets) ## faceted, free scales, same color
#' tinyplot(EuStockMarkets, facet.args = NULL) ## faceted, same scale, same color
#' tinyplot(EuStockMarkets, facet = "by") ## faceted, free scales, diff colors
#' tinyplot(EuStockMarkets, facet = NULL) ## single frame, diff colors
#'
#' ## further variations
#' tinyplot(EuStockMarkets, facet = NULL, legend = list("direct", repel = TRUE))
#' tinyplot(EuStockMarkets, facet = "by", facet.args = NULL)
#' tinyplot(EuStockMarkets, facet.args = list(free = TRUE, ncol = 1))
#'
#' ## pass additional tinyplot args through `...` for further customization
#' tinyplot(EuStockMarkets,
#' facet.args = NULL,
#' type = "area", xlab = NA, yaxl = ",",
#' main = "European stock indices")
#'
#' tinytheme() ## reset
#'
#' @importFrom stats time
#' @export
tinyplot.ts = function(x, facet, type = "l", facet.args = list(free = TRUE), xlab = NULL, ylab = NA, ...) {
## basic object properties
n = NROW(x)
k = NCOL(x)
lab = deparse(substitute(x))
if (k > 1L) lab = paste(lab, 1L:k, sep = ".")
if (!is.null(colnames(x))) lab = colnames(x)

single = k == 1L

## convert to long data.frame
df = data.frame(
Time = rep.int(as.numeric(time(x)), k),
Value = as.numeric(x),
Series = factor(rep(1L:k, each = n), labels = lab)
)

## Distribution types summarize the series *values* and ignore the time index,
## so they dispatch with a one-sided formula (`~ Value`, or `~ Value | Series`
## when multivariate) rather than `Value ~ Time`, and skip the time-series
## `ylab` default so tinyplot's own label (e.g. "Frequency") shows. This
## preserves base-R-compatible behaviour like `tinyplot(Nile, type = "histogram")`.
dist_types = c("histogram", "hist", "density", "boxplot", "box",
"violin", "qq", "ridge", "rug")
## normalize to a type name to catch both the string ("histogram") and the
## type object (`type_histogram()`, whose `$name` is the canonical "histogram")
type_name = if (inherits(type, "tinyplot_type")) {
type[["name"]]
} else if (is.character(type) && length(type) == 1L) {
type
} else {
NA_character_
}
is_dist = !is.na(type_name) && type_name %in% dist_types

## default for facet
if(missing(facet)) {
auto = TRUE
facet = if(single || is_dist) NULL else ~ Series
} else {
auto = FALSE
}
if (is.null(facet)) facet.args = NULL

## dispatch formula and axis labels
if (is_dist) {
fml = if (single) ~ Value else ~ Value | Series
if (single && is.null(xlab)) xlab = lab
## use tinyplot's own ylab default (e.g. "Frequency") unless caller set one
if (missing(ylab)) ylab = NULL
} else if (single || (!is.null(facet) && auto)) {
fml = Value ~ Time
} else {
fml = Value ~ Time | Series
}

## call tinyplot
tinyplot(fml, data = df, type = type, facet = facet, facet.args = facet.args,
xlab = xlab, ylab = ylab, ...)
}
2 changes: 1 addition & 1 deletion altdoc/pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ altdoc: 0.7.2
pandoc: '3.10'
pkgdown: 2.1.3
pkgdown_sha: ~
last_built: 2026-06-22T18:44:13+0000
last_built: 2026-06-23T18:52:00+0000
urls:
reference: https://grantmcdermott.com/tinyplot/man
article: https://grantmcdermott.com/tinyplot/vignettes
8 changes: 2 additions & 6 deletions altdoc/quarto_website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,6 @@ website:
file: man/tinyplot.qmd
- text: tinyplot_add
file: man/tinyplot_add.qmd
- text: tinypairs
file: man/tinyplot.data.frame.qmd
# - section: "Methods"
# contents:
# - text: tinyplot.data.frame
# file: man/tinyplot.data.frame.qmd
- section: "Types"
contents:
- section: Shapes
Expand Down Expand Up @@ -166,6 +160,8 @@ website:
contents:
- text: tinyplot.data.frame
file: man/tinyplot.data.frame.qmd
- text: tinyplot.ts
file: man/tinyplot.ts.qmd

format:
html:
Expand Down
Loading