Skip to content

Commit

Permalink
Point-range plots with type="pr" (#35)
Browse files Browse the repository at this point in the history
* pointrange

* news

* Skip density plot tests in R dev (#36)

- To help with failing CI tests on development run.
- See https://bugs.r-project.org/show_bug.cgi?id=18337

* PR review

* readme update
  • Loading branch information
vincentarelbundock authored Jun 19, 2023
1 parent 17c6029 commit 8ed8375
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 12 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: plot2
Type: Package
Title: Lightweight extension of base R plot
Version: 0.0.2.9006
Version: 0.0.2.9007
Authors@R:
c(
person(
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ New features:
- Both the `pch` and `lty` arguments now accept a "by" convenience keyword for
automatically adjusting plot characters and line types by groups (#28,
@grantmcdermott).
- Point-range plots with `type="pointrange"` (#35 @vincentarelbundock)

Bug fixes:

Expand Down
2 changes: 1 addition & 1 deletion R/by_aesthetics.R
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ by_col = function(ngrps = 1L, col = NULL, palette = NULL) {
by_pch = function(ngrps, type, pch=NULL) {

no_pch = FALSE
if (!type %in% c("p", "b", "o")) {
if (!type %in% c("p", "b", "o", "pointrange")) {
no_pch = TRUE
pch = NULL

Expand Down
41 changes: 33 additions & 8 deletions R/plot2.R
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
#' following values are possible, for details, see plot: "p" for points, "l"
#' for lines, "b" for both points and lines, "c" for empty points joined by
#' lines, "o" for overplotted points and lines, "s" and "S" for stair steps
#' and "h" for histogram-like vertical lines. Finally, "n" does not produce
#' any points or lines.
#' and "h" for histogram-like vertical lines. "n" does not produce
#' any points or lines. "pointrange" draws point-range plots.
#' @param xlim the x limits (x1, x2) of the plot. Note that x1 > x2 is allowed
#' and leads to a ‘reversed axis’. The default value, NULL, indicates that
#' the range of the `finite` values to be plotted should be used.
Expand Down Expand Up @@ -94,6 +94,7 @@
#' be calling `dev.off()` to reset all `par` settings to their defaults.)
#' @param subset,na.action,drop.unused.levels arguments passed to `model.frame`
#' when extracting the data from `formula` and `data`.
#' @param ymin,ymax minimum and maximum coordinates of the point range. Only used when `type="pointrange"`.
#' @param ... other `graphical` parameters (see `par` and also the "Details"
#' section of `plot`).
#'
Expand Down Expand Up @@ -241,6 +242,8 @@ plot2.default = function(
col = NULL,
lty = NULL,
par_restore = FALSE,
ymin = NULL,
ymax = NULL,
...) {

if (is.null(y)) {
Expand All @@ -255,12 +258,18 @@ plot2.default = function(

if (is.null(xlim)) xlim = range(x, na.rm = TRUE)
if (is.null(ylim)) ylim = range(y, na.rm = TRUE)


if (!is.null(ymin)) ylim[1] = min(c(ylim, ymin))
if (!is.null(ymax)) ylim[2] = max(c(ylim, ymax))

if (!is.null(by)) {
split_data = lapply(list(x=x, y=y), split, by)
l = list(x=x, y=y)
l[["ymin"]] = ymin
l[["ymax"]] = ymax
split_data = lapply(l, split, by)
split_data = do.call(function(...) Map("list", ...), split_data)
} else {
split_data = list(list(x=x, y=y))
split_data = list(list(x=x, y=y, ymin = ymin, ymax = ymax))
}

ngrps = length(split_data)
Expand Down Expand Up @@ -399,7 +408,22 @@ plot2.default = function(
if (!is.null(grid)) grid

# draw the points/lines
if (type == "p") {
if (type %in% "pointrange") { # segments before point
invisible(
lapply(
seq_along(split_data),
function(i) {
graphics::segments(
x0 = seq_along(split_data[[i]]$x),
y0 = split_data[[i]]$ymin,
x1 = seq_along(split_data[[i]]$x),
y1 = split_data[[i]]$ymax
)
}
)
)
}
if (type %in% c("p", "pointrange")) {
invisible(
lapply(
seq_along(split_data),
Expand All @@ -415,8 +439,7 @@ plot2.default = function(
}
)
)
}
if (type %in% c("l", "o", "b", "c", "h", "s", "S")) {
} else if (type %in% c("l", "o", "b", "c", "h", "s", "S")) {
invisible(
lapply(
seq_along(split_data),
Expand All @@ -432,6 +455,8 @@ plot2.default = function(
}
)
)
} else {
stop("`type` argument not supported.", call. = FALSE)
}

title(
Expand Down
31 changes: 31 additions & 0 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ Let's load the package then walk through some examples.
library(plot2)
```

### Similarity to `plot()`

As far as possible, `plot2` tries to be a drop-in replacement for regular `plot`
calls.

Expand All @@ -103,6 +105,8 @@ plot2(Temp ~ Day, data = airquality, main = "plot2 (formula)")
dev.off() # reset to default (single) plot window
```

### Grouped data

So far, so good. But where `plot2` starts to diverge from its base counterpart is
with respect to grouped data. In particular, `plot2` allows you to characterize
groups using the `by` argument.^[At this point, experienced base plot users
Expand Down Expand Up @@ -168,6 +172,8 @@ plot2(
)
```

### Colors

On the subject of group colours, these are easily customized via the `palette`
argument. The default group colours are inherited from either the "R4" or
"Viridis" palettes, depending on the number of groups. However, all of the many
Expand All @@ -190,6 +196,8 @@ Beyond these convenience strings, users can also supply a valid
palette-generating function for finer control over transparency, colour order,
and so forth. We'll see a demonstration of this further below.

### Legend

In all of the preceding plots, you will have noticed that we get an automatic
legend. The legend position and look can be customized using appropriate
arguments. You can change (or turn off) the legend title and bounding box,
Expand Down Expand Up @@ -219,6 +227,27 @@ with(airquality, plot2(
))
```

### Point-range

`plot2` adds a new `type="pointrange"` option to draw point-ranges plots:

```{r pointrange, warning = FALSE}
mod = lm(mpg ~ hp + factor(cyl), mtcars)
coefs = data.frame(names(coef(mod)), coef(mod), confint(mod))
coefs = setNames(coefs, c("x", "y", "ymin", "ymax"))
with(coefs,
plot2(pch = 17,
x = 1:4,
y = y,
ymin = ymin,
ymax = ymax,
type = "pointrange"
)
)
```

### Customization

Customizing your plots further is straightforward, whether that is done by
changing global parameters or invoking `plot2` arguments. Here's a quick
penultimate example, where we change our point character and font family
Expand Down Expand Up @@ -266,6 +295,8 @@ basetheme(NULL) # back to default theme
dev.off()
```

## Conclusion

In summary, consider the **plot2** package if you are looking for base R `plot`
functionality with some added convenience features. You can use pretty much the
same syntax and all of your theming elements should carry over too. It has no
Expand Down
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ Let’s load the package then walk through some examples.
library(plot2)
```

### Similarity to `plot()`

As far as possible, `plot2` tries to be a drop-in replacement for
regular `plot` calls.

Expand Down Expand Up @@ -105,6 +107,8 @@ dev.off() # reset to default (single) plot window
#> 1
```

### Grouped data

So far, so good. But where `plot2` starts to diverge from its base
counterpart is with respect to grouped data. In particular, `plot2`
allows you to characterize groups using the `by` argument.[^1]
Expand Down Expand Up @@ -170,6 +174,8 @@ plot2(

<img src="man/figures/README-by_lty-1.png" width="100%" />

### Colors

On the subject of group colours, these are easily customized via the
`palette` argument. The default group colours are inherited from either
the “R4” or “Viridis” palettes, depending on the number of groups.
Expand All @@ -191,6 +197,8 @@ Beyond these convenience strings, users can also supply a valid
palette-generating function for finer control over transparency, colour
order, and so forth. We’ll see a demonstration of this further below.

### Legend

In all of the preceding plots, you will have noticed that we get an
automatic legend. The legend position and look can be customized using
appropriate arguments. You can change (or turn off) the legend title and
Expand Down Expand Up @@ -226,6 +234,30 @@ with(airquality, plot2(

<img src="man/figures/README-desnity_topright-1.png" width="100%" />

### Point-range

`plot2` adds a new `type="pointrange"` option to draw point-ranges
plots:

``` r
mod = lm(mpg ~ hp + factor(cyl), mtcars)
coefs = data.frame(names(coef(mod)), coef(mod), confint(mod))
coefs = setNames(coefs, c("x", "y", "ymin", "ymax"))
with(coefs,
plot2(pch = 17,
x = 1:4,
y = y,
ymin = ymin,
ymax = ymax,
type = "pointrange"
)
)
```

<img src="man/figures/README-pointrange-1.png" width="100%" />

### Customization

Customizing your plots further is straightforward, whether that is done
by changing global parameters or invoking `plot2` arguments. Here’s a
quick penultimate example, where we change our point character and font
Expand Down Expand Up @@ -283,6 +315,8 @@ dev.off()
#> 1
```

## Conclusion

In summary, consider the **plot2** package if you are looking for base R
`plot` functionality with some added convenience features. You can use
pretty much the same syntax and all of your theming elements should
Expand Down
71 changes: 71 additions & 0 deletions inst/tinytest/_tinysnapshot/pointrange_triangle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions inst/tinytest/test-pointrange.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
source("helpers.R")
using("tinysnapshot")

mod = lm(mpg ~ hp + factor(cyl), mtcars)
coefs = data.frame(names(coef(mod)), coef(mod), confint(mod))
coefs = setNames(coefs, c("x", "y", "ymin", "ymax"))

fun = function() {
with(
coefs,
plot2(
pch = 17,
x = 1:4,
y = y,
ymin = ymin,
ymax = ymax,
type = "pointrange"))
}
expect_snapshot_plot(fun, label = "pointrange_triangle")
Binary file added man/figures/README-pointrange-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 6 additions & 2 deletions man/plot2.Rd

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

0 comments on commit 8ed8375

Please sign in to comment.