Skip to content

Commit 0bce2f2

Browse files
authored
Scales 1.4.0 (#711)
1 parent e4c1f89 commit 0bce2f2

File tree

8 files changed

+452
-0
lines changed

8 files changed

+452
-0
lines changed
54.3 KB
Loading
73 KB
Loading
19 KB
Loading
14 KB
Loading
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
---
2+
output: hugodown::hugo_document
3+
4+
slug: scales-1-4-0
5+
title: scales 1.4.0
6+
date: 2025-04-23
7+
author: Teun van den Brand
8+
description: >
9+
The new 1.4.0 release of the scales package adds some colourful updates.
10+
Read about colour manipulation, palettes and new label functions.
11+
12+
photo:
13+
url: https://unsplash.com/photos/a-close-up-of-a-person-holding-a-paintbrush-Xrelr7cTYm4
14+
author: Jennie Razumnaya
15+
16+
# one of: "deep-dive", "learn", "package", "programming", "roundup", or "other"
17+
categories: [package]
18+
tags: [scales]
19+
---
20+
21+
<!--
22+
TODO:
23+
* [x] Look over / edit the post's title in the yaml
24+
* [x] Edit (or delete) the description; note this appears in the Twitter card
25+
* [x] Pick category and tags (see existing with `hugodown::tidy_show_meta()`)
26+
* [x] Find photo & update yaml metadata
27+
* [x] Create `thumbnail-sq.jpg`; height and width should be equal
28+
* [x] Create `thumbnail-wd.jpg`; width should be >5x height
29+
* [x] `hugodown::use_tidy_thumbnails()`
30+
* [x] Add intro sentence, e.g. the standard tagline for the package
31+
* [x] `usethis::use_tidy_thanks()`
32+
-->
33+
34+
We're stoked to announce the release of [scales]({https://scales.r-lib.org/}) 1.4.0.
35+
scales is a package that provides much of the scaling logic that is used in ggplot2 to a general framework, along with utility functions for e.g. formatting labels or creating colour palettes.
36+
37+
You can install it from CRAN with:
38+
39+
```{r, eval = FALSE}
40+
install.packages("scales")
41+
```
42+
43+
This blog post will give an overview of the 1.4.0 release, which has some nifty upgrades for working with colours and labels.
44+
45+
You can see a full list of changes in the [release notes](https://scales.r-lib.org/news/index.html)
46+
47+
```{r setup}
48+
library(scales)
49+
```
50+
51+
```{r ragg, include=FALSE}
52+
knitr::opts_chunk$set(dev = "ragg_png")
53+
```
54+
55+
## Colour manipulation
56+
57+
The `alpha()` and `muted()` functions have been part of scales for a long time.
58+
Back in the 1.1.0 release we swapped to [farver](https://farver.data-imaginist.com/) to power these functions.
59+
We felt it was appropriate to use this package for other common colour tasks, and so `col_shift()`, `col_lighter()`, `col_darker()`, `col_saturate()` and `col_mix()` were born.
60+
61+
```{r}
62+
my_colours <- c("red", "green", "blue")
63+
64+
m <- rbind(
65+
original = my_colours,
66+
shift = col_shift(my_colours, 90),
67+
lighter = col_lighter(my_colours, 20),
68+
darker = col_darker(my_colours, 20),
69+
duller = col_saturate(my_colours, -50),
70+
mixed = col_mix(my_colours, "orchid")
71+
)
72+
73+
show_col(t(m), ncol = ncol(m))
74+
text(x = ncol(m) + 0.25, y = -(1:nrow(m)) + 0.5, rownames(m), adj = 0)
75+
```
76+
77+
## Palettes
78+
79+
Palettes have also been reworked in this release to include some useful properties.
80+
Palettes now come in one of two classes: 'pal_discrete' or 'pal_continuous'.
81+
82+
```{r}
83+
my_palette <- manual_pal(c("palegreen", "deepskyblue", "magenta"))
84+
class(my_palette)
85+
```
86+
87+
Having palettes as a class rather than as plain functions, allows us to store useful metadata about the palette which can be used downstream.
88+
In addition, most colour palette functions also allow the aforementioned colour manipulation functions to work on the palette output.
89+
90+
```{r}
91+
palette_type(my_palette)
92+
93+
palette_nlevels(my_palette)
94+
95+
col_shift(my_palette, 180)(3)
96+
```
97+
98+
With the new setup it is now possible to expand discrete palettes to continuous palettes with `as_continuous_pal()` or vise versa to chop up continuous palettes into discrete palettes with `as_discrete_pal()`.
99+
100+
```{r}
101+
plot(as_continuous_pal(my_palette))
102+
```
103+
104+
Another quality of life improvement for palettes, is that the 'scales' package now keeps track of some named palettes.
105+
By default, the collection of 'known' palettes is pre-populated with colour palettes from the grDevices, RColorBrewer and viridisLite packages.
106+
107+
108+
```{r}
109+
as_discrete_pal("Okabe-Ito")(8)
110+
```
111+
112+
### Providing palettes as package
113+
114+
For those that are interested in developing R packages with palettes, there are a few recommendations we make in `?palette-recommendations` to smoothly interface with the scales package.
115+
116+
If your palettes are vectors of colour values, we recommend simply exporting the naked vector.
117+
118+
```{r}
119+
#' @export
120+
aurora <- c("palegreen", "deepskyblue", "magenta")
121+
```
122+
123+
That way, they can easily be accessed and used in `as_discrete_pal()` and `as_continuous_pal()`.
124+
125+
```{r, results='hide'}
126+
as_continuous_pal(aurora)
127+
as_discrete_pal(aurora)
128+
```
129+
130+
Alternatively, if you have functions that generate colours that is not predefined, we recommend wrapping the function in `new_discrete_palette()` and `new_continuous_palette()`. For predefined palettes, you can also use `pal_manual()` or `pal_gradient_n()`.
131+
132+
```{r}
133+
pal_random <- function() {
134+
fun <- function(n) {
135+
sample(colours(distinct = TRUE), size = n, replace = TRUE)
136+
}
137+
new_discrete_palette(fun, type = "colour", nlevels = length(colours()))
138+
}
139+
```
140+
141+
Populating the metadata in `new_discrete_palette()`/`new_continuous_palette()` helps to make converting between palette types less painful.
142+
143+
```{r, results='hide'}
144+
as_continuous_pal(pal_random())
145+
as_discrete_pal(pal_random())
146+
```
147+
148+
149+
150+
## Labels
151+
152+
This release also provides improvements to labelling in the form of two new labelling functions and two new convenience functions for labels.
153+
In contrast to most of scales' label functions, these label functions are great for discrete input.
154+
First up is `label_glue()`, which uses the string interpolation from the glue package to format your labels.
155+
156+
```{r}
157+
label_glue("The {x} penguin")(c("Gentoo", "Chinstrap", "Adelie"))
158+
```
159+
160+
The other labelling function, `label_dictionary()`, is convenient when some variable you use consists of short-codes or abbreviations.
161+
You can provide `label_dictionary()` with a named vector that translates the values to prettier labels.
162+
If one or more of your values doesn't exist in the dictionary, they stay as-is by default.
163+
164+
```{r}
165+
dict <- c(
166+
diy = "Do it yourself", eta = "Estimated time of arrival",
167+
asap = "As soon as possible", tldr = "Too long; didn't read"
168+
)
169+
label_dictionary(dict)(c("diy", "tldr", "bff"))
170+
```
171+
172+
`compose_label()` is a useful convenience function we've added which will help you to create custom labelling behaviour without needing to write a labelling function from scratch.
173+
Similar to `compose_trans()`, it allows you to chain together different labelling functions.
174+
175+
```{r}
176+
screaming_flowers <- compose_label(label_glue("The {x} flower"), toupper)
177+
screaming_flowers(c("daffodil", "orchid", "tulip"))
178+
```
179+
Lastly, we haven't completely forgotton about numeric labels either.
180+
We have introduced the `number_options()` functions to globally populate defaults for functions such as `label_number()` and `label_currency()`.
181+
This can be convenient if you produce statistical reports in non-English languages.
182+
183+
```{r}
184+
number_options(
185+
decimal.mark = ",",
186+
big.mark = ".",
187+
style_negative = "minus",
188+
currency.prefix = "",
189+
currency.suffix = "€",
190+
currency.decimal.mark = ",",
191+
currency.big.mark = " ",
192+
ordinal.rules = ordinal_french()
193+
)
194+
195+
label_currency(accuracy = 0.01)(c(0.1, 10, 1000000, -1000))
196+
197+
label_ordinal()(1:4)
198+
```
199+
200+
201+
202+
## Acknowledgements
203+
204+
We'd like to thank all people who have contributed in some way, whether it was filing issues, participating in discussion or contributing to code and documentation:
205+
206+
[&#x0040;Aariq](https://github.com/Aariq), [&#x0040;Aehmlo](https://github.com/Aehmlo), [&#x0040;Ali-Hudson](https://github.com/Ali-Hudson), [&#x0040;cb12991](https://github.com/cb12991), [&#x0040;colindouglas](https://github.com/colindouglas), [&#x0040;d-morrison](https://github.com/d-morrison), [&#x0040;davidhodge931](https://github.com/davidhodge931), [&#x0040;EricMarcon](https://github.com/EricMarcon), [&#x0040;kellijohnson-NOAA](https://github.com/kellijohnson-NOAA), [&#x0040;kmcd39](https://github.com/kmcd39), [&#x0040;lz1nwm](https://github.com/lz1nwm), [&#x0040;mine-cetinkaya-rundel](https://github.com/mine-cetinkaya-rundel), [&#x0040;mjskay](https://github.com/mjskay), [&#x0040;Moohan](https://github.com/Moohan), [&#x0040;muschellij2](https://github.com/muschellij2), [&#x0040;ppreshant](https://github.com/ppreshant), [&#x0040;rawktheuniversemon](https://github.com/rawktheuniversemon), [&#x0040;rogiersbart](https://github.com/rogiersbart), [&#x0040;SchmidtPaul](https://github.com/SchmidtPaul), [&#x0040;teunbrand](https://github.com/teunbrand), and [&#x0040;thomasp85](https://github.com/thomasp85).
207+
208+

0 commit comments

Comments
 (0)