Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 45 additions & 5 deletions R/by_aesthetics.R
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,27 @@ by_col = function(ngrps, col = NULL, palette = NULL, palette.args = NULL) {
}


by_pch = function(ngrps, type, pch) {
by_pch = function(ngrps, type, pch=NULL) {

no_pch = FALSE
# return NULL if not a valid point type
if (!type %in% c("p", "b", "o")) {
no_pch = TRUE
pch = NULL

# special "by" convenience keyword
} else if (!is.null(pch) && length(pch)==1 && pch=="by") {
no_pch = TRUE # skip checks below
pch = 1:ngrps + par("pch") - 1
# correctly recycle if over max pch type
pch_ceiling = 25 # see ?pch
if (max(pch)>pch_ceiling) {
pch_below = pch[pch<=pch_ceiling]
pch_above = pch[pch>pch_ceiling]
pch_above = rep_len(0:pch_ceiling, length(pch_above))
pch = c(pch_below, pch_above)
}

# return NULL if not a valid point type
} else if (is.null(pch)) {
pch = par("pch")
}
Expand All @@ -82,13 +96,39 @@ by_pch = function(ngrps, type, pch) {
}


by_lty = function(ngrps, type, lty) {
by_lty = function(ngrps, type, lty=NULL) {

# don't care about line type, return NULL
if (!type %in% c("l", "b", "o", "c", "h", "s", "S")) {
out = NULL

# NULL -> solid line

# special "by" convenience keyword
} else if (!is.null(lty) && length(lty)==1 && lty=="by") {

lty_dict = c("solid", "dashed", "dotted", "dotdash", "longdash", "twodash")
par_lty = par("lty")

if (!par_lty %in% lty_dict) {
warning(
"\nBesoke lty specifications (i.e., using string combinations) are not",
"currently supported alongside the lty='by' keyword argument.",
"Defaulting to 1 and looping from there.\n"
)
par_lty = 1
} else {
par_lty = which(par_lty==lty_dict)
}
out = 1:ngrps + par_lty - 1
# correctly recycle if over max pch type
lty_ceiling = 6 # see ?pch
if (max(out)>lty_ceiling) {
lty_below = out[out<=lty_ceiling]
lty_above = out[out>lty_ceiling]
lty_above = rep_len(1:lty_ceiling, length(lty_above))
out = c(lty_below, lty_above)
}

# NULL -> solid (or default) line
} else if (is.null(lty)) {
out = rep(par("lty"), ngrps)

Expand Down
11 changes: 9 additions & 2 deletions R/plot2.R
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,18 @@
#' the moment, only "bty", "horiz", "xpd", and "title" are supported.
#' @param pch plotting "character", i.e., symbol to use. Character, integer, or
#' vector of length equal to the number of categories in the `by` variable.
#' See `pch`.
#' See `pch`. In addition, users can supply a special `pch = "by"` convenience
#' argument, in which case the characters will automatically loop over the
#' number groups. This automatic looping will begin at the global character
#' value (i.e., `par("pch")`) and recycle as necessary.
#' @param col plotting color. Character, integer, or vector of length equal to
#' the number of categories in the `by` variable. See `col`.
#' @param lty line type. Character, integer, or vector of length equal to the
#' number of categories in the `by` variable. See `lty`.
#' number of categories in the `by` variable. See `lty`. In addition, users
#' can supply a special `lty = "by"` convenience argument, in which case the
#' line type will automatically loop over the number groups. This automatic
#' looping will begin at the global line type value (i.e., `par("lty")`) and
#' recycle as necessary.
#' @param par_restore a logical value indicating whether the `par` settings
#' prior to calling `plot2` should be restored on exit. Defaults to FALSE,
#' which makes it possible to add elements to the plot after it has been
Expand Down
43 changes: 30 additions & 13 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ library(plot2)
As far as possible, `plot2` tries to be a drop-in replacement for regular `plot`
calls.

```{r}
```{r base_1}
par(mfrow = c(1, 2))

plot(0:10, main = "plot")
Expand All @@ -92,7 +92,7 @@ plot2(0:10, main = "plot2")
Similarly, we can plot elements from a data frame using either the atomic or
formula methods.

```{r}
```{r base_2}
par(mfrow = c(2, 2))

plot(airquality$Day, airquality$Temp, main = "plot")
Expand All @@ -117,21 +117,21 @@ plots (i.e., type="l"). Instead, you have to transpose your data and use
[this](https://stackoverflow.com/questions/10519873/how-to-create-a-line-plot-with-groups-in-base-r-without-loops)
old StackOverflow thread for a longer discussion.]

```{r}
```{r by}
plot2(airquality$Day, airquality$Temp, by = airquality$Month)
```

An even more convenient approach is to use the equivalent formula syntax. Just
place the grouping variable after a vertical bar (i.e., `|`).

```{r}
```{r formula}
plot2(Temp ~ Day | Month, data = airquality)
```

You can use standard base plotting arguments to adjust features of your plot.
For example, change `pch` (plot character) to get filled points.

```{r}
```{r pch_16}
plot2(
Temp ~ Day | Month,
data = airquality,
Expand All @@ -142,22 +142,38 @@ plot2(
Similarly, converting to a grouped line plot is a simple matter of adjusting the
`type` argument.

```{r}
```{r type_l}
plot2(
Temp ~ Day | Month,
data = airquality,
type = "l"
)
```

Note that we can automatically adjust both `pch` and `lty` by groups using the
`"by"` convenience keyword. This can be used in conjunction with the default
group colouring. Or, as a replacement for group colouring---an option that may
be particularly useful for contexts where colour is expensive or prohibited
(e.g., certain academic journals).

```{r by_lty}
plot2(
Temp ~ Day | Month,
data = airquality,
type = "l",
col = "black", # override automatic group colours
lty = "by" # change line type by group instead
)
```

In all of the above cases, 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,
switch the direction of the legend text, etc. Below, we particularly draw your
attention to the trailing "!" in the `legend.position` argument. This tells
`plot2` to place the legend _outside_ the plot area.

```{r}
```{r legend_bottom}
plot2(
Temp ~ Day | Month,
data = airquality,
Expand All @@ -170,7 +186,7 @@ plot2(
Note that legend position keywords without the exclamation point (i.e., for
inside the plot area) should still as per normal. Grouped density plot example:

```{r}
```{r desnity_topright}
plot2(
density(airquality$Temp),
by = airquality$Month,
Expand All @@ -187,7 +203,7 @@ pages of those two functions for more details, or read the
[article](https://arxiv.org/pdf/2303.04918.pdf) by Achim Zeileis and Paul
Murrell.] Simply pass on an appropriate palette name as a string.

```{r}
```{r palette_tableau}
plot2(
Temp ~ Day | Month,
data = airquality,
Expand All @@ -202,7 +218,7 @@ penultimate example, where we change our point character and font family
globally, add some transparency to our colour palette, and use Tufte-style
floating axes with a background panel grid.

```{r}
```{r hershey_plus}
par(
pch = 16, # Filled points as default
family = "HersheySans" # Use a (built-in) Hershey font instead of Arial default
Expand All @@ -224,16 +240,17 @@ around base `plot`, any global elements that you have set for the latter should
carry over to the former. For nice out-of-the-box themes, we recommend the
**basetheme** package.

```{r}
par(family = "", pch = 1) # revert global changes from above
```{r basethme_royal}
par(family = "", pch = 15) # revert/change global changes from above

library(basetheme)
basetheme("royal") # or "clean", "dark", "ink", "brutal", etc.

plot2(
Temp ~ Day | Month,
data = airquality,
type = "b", pch = 15:19,
type = "b",
pch = "by",
palette = "Tropic",
main = "Daily temperatures by month"
)
Expand Down
45 changes: 32 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ plot(0:10, main = "plot")
plot2(0:10, main = "plot2")
```

<img src="man/figures/README-unnamed-chunk-3-1.png" width="100%" />
<img src="man/figures/README-base_1-1.png" width="100%" />

Similarly, we can plot elements from a data frame using either the
atomic or formula methods.
Expand All @@ -96,7 +96,7 @@ plot2(airquality$Day, airquality$Temp, main = "plot2")
plot2(Temp ~ Day, data = airquality, main = "plot2 (formula)")
```

<img src="man/figures/README-unnamed-chunk-4-1.png" width="100%" />
<img src="man/figures/README-base_2-1.png" width="100%" />

``` r

Expand All @@ -113,7 +113,7 @@ allows you to characterize groups using the `by` argument.[^1]
plot2(airquality$Day, airquality$Temp, by = airquality$Month)
```

<img src="man/figures/README-unnamed-chunk-5-1.png" width="100%" />
<img src="man/figures/README-by-1.png" width="100%" />

An even more convenient approach is to use the equivalent formula
syntax. Just place the grouping variable after a vertical bar (i.e.,
Expand All @@ -123,7 +123,7 @@ syntax. Just place the grouping variable after a vertical bar (i.e.,
plot2(Temp ~ Day | Month, data = airquality)
```

<img src="man/figures/README-unnamed-chunk-6-1.png" width="100%" />
<img src="man/figures/README-formula-1.png" width="100%" />

You can use standard base plotting arguments to adjust features of your
plot. For example, change `pch` (plot character) to get filled points.
Expand All @@ -136,7 +136,7 @@ plot2(
)
```

<img src="man/figures/README-unnamed-chunk-7-1.png" width="100%" />
<img src="man/figures/README-pch_16-1.png" width="100%" />

Similarly, converting to a grouped line plot is a simple matter of
adjusting the `type` argument.
Expand All @@ -149,7 +149,25 @@ plot2(
)
```

<img src="man/figures/README-unnamed-chunk-8-1.png" width="100%" />
<img src="man/figures/README-type_l-1.png" width="100%" />

Note that we can automatically adjust both `pch` and `lty` by groups
using the `"by"` convenience keyword. This can be used in conjunction
with the default group colouring. Or, as a replacement for group
colouring—an option that may be particularly useful for contexts where
colour is expensive or prohibited (e.g., certain academic journals).

``` r
plot2(
Temp ~ Day | Month,
data = airquality,
type = "l",
col = "black", # override automatic group colours
lty = "by" # change line type by group instead
)
```

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

In all of the above cases, you will have noticed that we get an
automatic legend. The legend position and look can be customized using
Expand All @@ -169,7 +187,7 @@ plot2(
)
```

<img src="man/figures/README-unnamed-chunk-9-1.png" width="100%" />
<img src="man/figures/README-legend_bottom-1.png" width="100%" />

Note that legend position keywords without the exclamation point (i.e.,
for inside the plot area) should still as per normal. Grouped density
Expand All @@ -184,7 +202,7 @@ plot2(
)
```

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

Colour palettes can be customized easily via the `palette` argument. The
default group colours are inherited from either the “Okabe-Ito” or
Expand All @@ -201,7 +219,7 @@ plot2(
)
```

<img src="man/figures/README-unnamed-chunk-11-1.png" width="100%" />
<img src="man/figures/README-palette_tableau-1.png" width="100%" />

Customizing your plots further is straightforward, whether that is done
by changing global parameters or invoking `plot2` arguments. Here’s a
Expand All @@ -225,7 +243,7 @@ plot2(
)
```

<img src="man/figures/README-unnamed-chunk-12-1.png" width="100%" />
<img src="man/figures/README-hershey_plus-1.png" width="100%" />

The use of `par` in the above example again underscores the
correspondence with the base graphics system. Because `plot2` is
Expand All @@ -235,21 +253,22 @@ former. For nice out-of-the-box themes, we recommend the **basetheme**
package.

``` r
par(family = "", pch = 1) # revert global changes from above
par(family = "", pch = 15) # revert/change global changes from above

library(basetheme)
basetheme("royal") # or "clean", "dark", "ink", "brutal", etc.

plot2(
Temp ~ Day | Month,
data = airquality,
type = "b", pch = 15:19,
type = "b",
pch = "by",
palette = "Tropic",
main = "Daily temperatures by month"
)
```

<img src="man/figures/README-unnamed-chunk-13-1.png" width="100%" />
<img src="man/figures/README-basethme_royal-1.png" width="100%" />

``` r

Expand Down
Loading