Skip to content

Commit

Permalink
adds discussion of linewidth aesthetic (#354)
Browse files Browse the repository at this point in the history
* replaces "size" with "linewidth" where needed

* adds minimal scale linewidth section

* loads grid in its own chunk

* adds binned linewidth example and discussion of linear scaling

* cleans binned scale

* narrower legend key

* drops plot, simplifies linewidth discussion
  • Loading branch information
djnavarro authored Mar 19, 2023
1 parent 6862b6d commit 062121c
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 43 deletions.
2 changes: 1 addition & 1 deletion annotations.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ mod_coef <- coef(lm(log10(price) ~ log10(carat), data = diamonds))
ggplot(diamonds, aes(log10(carat), log10(price))) +
geom_bin2d() +
geom_abline(intercept = mod_coef[1], slope = mod_coef[2],
colour = "white", size = 1) +
colour = "white", linewidth = 1) +
facet_wrap(vars(cut), nrow = 1)
```

Expand Down
8 changes: 4 additions & 4 deletions collective-geoms.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Instead of setting the grouping aesthetic in `ggplot()`, where it will apply to
```{r layer19}
ggplot(Oxboys, aes(age, height)) +
geom_line(aes(group = Subject)) +
geom_smooth(method = "lm", size = 2, se = FALSE)
geom_smooth(method = "lm", linewidth = 2, se = FALSE)
```

## Overriding the default grouping
Expand Down Expand Up @@ -93,11 +93,11 @@ In ggplot2, this is handled differently for different collective geoms. Lines an
df <- data.frame(x = 1:3, y = 1:3, colour = c(1, 3, 5))
ggplot(df, aes(x, y, colour = factor(colour))) +
geom_line(aes(group = 1), size = 2) +
geom_line(aes(group = 1), linewidth = 2) +
geom_point(size = 5)
ggplot(df, aes(x, y, colour = colour)) +
geom_line(aes(group = 1), size = 2) +
geom_line(aes(group = 1), linewidth = 2) +
geom_point(size = 5)
```

Expand All @@ -112,7 +112,7 @@ interp <- data.frame(
colour = approx(df$x, df$colour, xout = xgrid)$y
)
ggplot(interp, aes(x, y, colour = colour)) +
geom_line(size = 2) +
geom_line(linewidth = 2) +
geom_point(data = df, size = 5)
```

Expand Down
23 changes: 13 additions & 10 deletions ext-springs.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ Because we've written a new stat, we get a number of features, like scaling and
ggplot(some_data) +
geom_spring(
aes(x, y, xend = xend, yend = yend, colour = class),
size = 1
linewidth = 1
) +
facet_wrap(~ class)
```
Expand Down Expand Up @@ -514,7 +514,7 @@ GeomSpring <- ggproto("GeomSpring", Geom,
required_aes = c("x", "y", "xend", "yend"),
default_aes = aes(
colour = "black",
size = 0.5,
linewidth = 0.5,
linetype = 1L,
alpha = NA,
diameter = 1,
Expand Down Expand Up @@ -637,12 +637,15 @@ For example `unit(0.5, "npc") + unit(1, "cm")` defines a point one centimeter to

#### Example

Given this very cursory introduction, let's now look at an example grob.
The code below will create a grob that appears as a square if bigger than 5 cm and a circle if smaller::
Given this very cursory introduction, let's now look at an example grob. First, let's load the grid package:

```{r}
library(grid)
```

The code below will create a grob that appears as a square if bigger than 5 cm and a circle if smaller:

```{r}
surpriseGrob <- function(x, y, size,
default.units = "npc",
name = NULL,
Expand Down Expand Up @@ -802,7 +805,7 @@ GeomSpring <- ggproto("GeomSpring", Geom,
draw_panel = function(data, panel_params, coord, n = 50, lineend = "butt",
na.rm = FALSE) {
data <- remove_missing(data, na.rm = na.rm,
c("x", "y", "xend", "yend", "linetype", "size"),
c("x", "y", "xend", "yend", "linetype", "linewidth"),
name = "geom_spring")
if (is.null(data) || nrow(data) == 0) return(zeroGrob())
if (!coord$is_linear()) {
Expand All @@ -814,7 +817,7 @@ GeomSpring <- ggproto("GeomSpring", Geom,
tension = coord$tension, n = n,
gp = gpar(
col = alpha(coord$colour, coord$alpha),
lwd = coord$size * .pt,
lwd = coord$linewidth * .pt,
lty = coord$linetype,
lineend = lineend
)
Expand All @@ -823,7 +826,7 @@ GeomSpring <- ggproto("GeomSpring", Geom,
required_aes = c("x", "y", "xend", "yend"),
default_aes = aes(
colour = "black",
size = 0.5,
linewidth = 0.5,
linetype = 1L,
alpha = NA,
diameter = 0.35,
Expand Down Expand Up @@ -960,7 +963,7 @@ With our scales defined let us have a look:
ggplot(some_data) +
geom_spring(aes(x = x, y = y, xend = xend, yend = yend, tension = tension,
diameter = diameter)) +
scale_tension(range = c(0.1, 5))
scale_tension(range = c(0.1, 5))
```

The code above shows us that both the default scale (we didn't add an explicit scale for diameter) and the custom scales (`scale_tension()`) work.
Expand Down Expand Up @@ -994,7 +997,7 @@ draw_key_spring <- function(data, params, size) {
),
vp = viewport(clip = "on")
)
}
}
```

We add a little flourish here that is not necessary for the point key constructor, which is that we define a clipping viewport for our grob.
Expand Down Expand Up @@ -1022,7 +1025,7 @@ The default key size is a bit cramped for our key, but that has to be modified b
ggplot(some_data) +
geom_spring(aes(x = x, y = y, xend = xend, yend = yend, tension = tension,
diameter = diameter)) +
scale_tension(range = c(0.1, 5)) +
scale_tension(range = c(0.1, 5)) +
theme(legend.key.size = unit(1, "cm"))
```

Expand Down
2 changes: 1 addition & 1 deletion layers.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ We've generated these datasets because it's common to enhance the display of raw
```{r}
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
geom_line(data = grid, colour = "blue", size = 1.5) +
geom_line(data = grid, colour = "blue", linewidth = 1.5) +
geom_text(data = outlier, aes(label = model))
```

Expand Down
14 changes: 7 additions & 7 deletions programming.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ Each component of a ggplot plot is an object. Most of the time you create the co
bestfit <- geom_smooth(
method = "lm",
se = FALSE,
colour = alpha("steelblue", 0.5),
size = 2
colour = alpha("steelblue", 0.5),
linewidth = 2
)
ggplot(mpg, aes(cty, hwy)) +
geom_point() +
Expand All @@ -40,20 +40,20 @@ ggplot(mpg, aes(displ, hwy)) +
bestfit
```

That's a great way to reduce simple types of duplication (it's much better than copying-and-pasting!), but requires that the component be exactly the same each time. If you need more flexibility, you can wrap these reusable snippets in a function. For example, we could extend our `bestfit` object to a more general function for adding lines of best fit to a plot. The following code creates a `geom_lm()` with three parameters: the model `formula`, the line `colour` and the line `size`:
That's a great way to reduce simple types of duplication (it's much better than copying-and-pasting!), but requires that the component be exactly the same each time. If you need more flexibility, you can wrap these reusable snippets in a function. For example, we could extend our `bestfit` object to a more general function for adding lines of best fit to a plot. The following code creates a `geom_lm()` with three parameters: the model `formula`, the line `colour` and the `linewidth`:

```{r geom-lm}
geom_lm <- function(formula = y ~ x, colour = alpha("steelblue", 0.5),
size = 2, ...) {
linewidth = 2, ...) {
geom_smooth(formula = formula, se = FALSE, method = "lm", colour = colour,
size = size, ...)
linewidth = linewidth, ...)
}
ggplot(mpg, aes(displ, 1 / hwy)) +
geom_point() +
geom_lm()
ggplot(mpg, aes(displ, 1 / hwy)) +
geom_point() +
geom_lm(y ~ poly(x, 2), size = 1, colour = "red")
geom_lm(y ~ poly(x, 2), linewidth = 1, colour = "red")
```

Pay close attention to the use of "`...`". When included in the function definition "`...`" allows a function to accept arbitrary additional arguments. Inside the function, you can then use "`...`" to pass those arguments on to another function. Here we pass "`...`" onto `geom_smooth()` so the user can still modify all the other arguments we haven't explicitly overridden. When you write your own component functions, it's a good idea to always use "`...`" in this way. \indexc{...}
Expand Down Expand Up @@ -175,7 +175,7 @@ geom_mean <- function(..., bar.params = list(), errorbar.params = list()) {
ggplot(mpg, aes(class, cty)) +
geom_mean(
colour = "steelblue",
errorbar.params = list(width = 0.5, size = 1)
errorbar.params = list(width = 0.5, linewidth = 1)
)
ggplot(mpg, aes(class, cty)) +
geom_mean(
Expand Down
2 changes: 1 addition & 1 deletion scales-colour.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ All continuous colour scales have an `na.value` parameter that controls what col
```{r}
df <- data.frame(x = 1, y = 1:5, z = c(1, 3, 2, NA, 5))
base <- ggplot(df, aes(x, y)) +
geom_tile(aes(fill = z), size = 5) +
geom_tile(aes(fill = z), linewidth = 5) +
labs(x = NULL, y = NULL) +
scale_x_continuous(labels = NULL)
Expand Down
37 changes: 36 additions & 1 deletion scales-other.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ columns(1, 2 / 3)

# Other aesthetics {#scale-other}

In addition to position and colour, there are several other aesthetics that ggplot2 can use to represent data. In this chapter we'll look at size scales (Section \@ref(scale-size)), shape scales (Section \@ref(scale-shape)), and line type scales (Section \@ref(scale-linetype)), which use visual features other than location and colour to represent data values. Additionally, we'll talk about manual scales (Section \@ref(scale-manual)) and identity scales (Section \@ref(scale-identity)): these don't necessarily use different visual features, but they construct data mappings in an unusual way.
In addition to position and colour, there are several other aesthetics that ggplot2 can use to represent data. In this chapter we'll look at size scales (Section \@ref(scale-size)), shape scales (Section \@ref(scale-shape)), line width scales (Section \@ref(scale-linewidth)), and line type scales (Section \@ref(scale-linetype)), which use visual features other than location and colour to represent data values. Additionally, we'll talk about manual scales (Section \@ref(scale-manual)) and identity scales (Section \@ref(scale-identity)): these don't necessarily use different visual features, but they construct data mappings in an unusual way.

## Size {#scale-size}
\index{Size}
Expand Down Expand Up @@ -169,6 +169,41 @@ base +

For more information about manual scales see Section \@ref(scale-manual).

## Line width {#scale-linewidth}

The linewidth aesthetic, introduced in ggplot2 3.4.0, is used to control the width of lines. In earlier versions of ggplot2 the size aesthetic was used for this purpose, which caused some difficulty for complex geoms such as `geom_pointrange()` that contain both points and lines. For these geoms it's often important to be able to separately control the size of the points and the width of the lines. This is illustrated in the plots below. In the leftmost plot both the size and linewidth aesthetics are set at their default values. The middle plot increases the size of the points while leaving the linewidth unchanged, whereas the plot on the right increases the linewidth while leaving the point size unchanged.

`r columns(3, 2)`
```{r}
base <- ggplot(airquality, aes(x = factor(Month), y = Temp))
base + geom_pointrange(stat = "summary", fun.data = "median_hilow")
base + geom_pointrange(
stat = "summary",
fun.data = "median_hilow",
size = 2
)
base + geom_pointrange(
stat = "summary",
fun.data = "median_hilow",
linewidth = 2
)
```

In practice you're most likely to set linewidth as a fixed parameter, as shown in the previous example, but it is a true aesthetic and can be mapped onto data values:

`r columns(1, 1/2, 1)`
```{r}
ggplot(airquality, aes(Day, Temp, group = Month)) +
geom_line(aes(linewidth = Month)) +
scale_linewidth(range = c(0.5, 3))
```

Linewidth scales behave like size scales in most ways, but there are differences. As discussed earlier the default behaviour of a size scale is to increase linearly with the area of the plot marker (e.g., the diameter of a circular plot marker increases with the square root of the data value). In contrast, the linewidth increases linearly with the data value.

Binned linewidth scales can be added using `scale_linewidth_binned()`.


## Line type {#scale-linetype}

It is possible to map a variable onto the linetype aesthetic in ggplot2. This works best for discrete variables with a small number of categories, and `scale_linetype()` is an alias for `scale_linetype_discrete()`. Continuous variables cannot be mapped to line types unless `scale_linetype_binned()` is used: although there is a `scale_linetype_continuous()` function, all it does is produce an error. To see why the linetype aesthetic is suited only to cases with a few categories, consider this plot:
Expand Down
4 changes: 2 additions & 2 deletions statistical-summaries.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ For more complicated geoms which involve some statistical transformation, we spe
# Unweighted
ggplot(midwest, aes(percwhite, percbelowpoverty)) +
geom_point() +
geom_smooth(method = lm, size = 1)
geom_smooth(method = lm, linewidth = 1)
# Weighted by population
ggplot(midwest, aes(percwhite, percbelowpoverty)) +
geom_point(aes(size = poptotal / 1e6)) +
geom_smooth(aes(weight = poptotal), method = lm, size = 1) +
geom_smooth(aes(weight = poptotal), method = lm, linewidth = 1) +
scale_size_area(guide = "none")
```

Expand Down
40 changes: 24 additions & 16 deletions themes.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,15 @@ styled <- labelled +
theme_bw() +
theme(
plot.title = element_text(face = "bold", size = 12),
legend.background = element_rect(fill = "white", size = 4, colour = "white"),
legend.background = element_rect(
fill = "white",
linewidth = 4,
colour = "white"
),
legend.justification = c(0, 1),
legend.position = c(0, 1),
axis.ticks = element_line(colour = "grey70", size = 0.2),
panel.grid.major = element_line(colour = "grey70", size = 0.2),
axis.ticks = element_line(colour = "grey70", linewidth = 0.2),
panel.grid.major = element_line(colour = "grey70", linewidth = 0.2),
panel.grid.minor = element_blank()
)
styled
Expand Down Expand Up @@ -189,22 +193,22 @@ There are four basic types of built-in element functions: text, lines, rectangle
base_t + theme(axis.title.y = element_text(margin = margin(r = 10)))
```
* `element_line()` draws lines parameterised by `colour`, `size` and
* `element_line()` draws lines parameterised by `colour`, `linewidth` and
`linetype`: \indexf{element\_line} \index{Themes!lines}
```{r element_line}
base + theme(panel.grid.major = element_line(colour = "black"))
base + theme(panel.grid.major = element_line(size = 2))
base + theme(panel.grid.major = element_line(linewidth = 2))
base + theme(panel.grid.major = element_line(linetype = "dotted"))
```
* `element_rect()` draws rectangles, mostly used for backgrounds, parameterised
by `fill` colour and border `colour`, `size` and `linetype`.
by `fill` colour and border `colour`, `linewidth` and `linetype`.
\index{Background} \index{Themes!background} \indexf{theme\_rect}
```{r element_rect}
base + theme(plot.background = element_rect(fill = "grey80", colour = NA))
base + theme(plot.background = element_rect(colour = "red", size = 2))
base + theme(plot.background = element_rect(colour = "red", linewidth = 2))
base + theme(panel.background = element_rect(fill = "linen"))
```
Expand Down Expand Up @@ -268,9 +272,9 @@ plot.margin | `margin()` | margins around plot

`r columns(3, 3/4)`
```{r plot}
base + theme(plot.background = element_rect(colour = "grey50", size = 2))
base + theme(plot.background = element_rect(colour = "grey50", linewidth = 2))
base + theme(
plot.background = element_rect(colour = "grey50", size = 2),
plot.background = element_rect(colour = "grey50", linewidth = 2),
plot.margin = margin(2, 2, 2, 2)
)
base + theme(plot.background = element_rect(fill = "lightblue"))
Expand Down Expand Up @@ -300,7 +304,7 @@ df <- data.frame(x = 1:3, y = 1:3)
base <- ggplot(df, aes(x, y)) + geom_point()
# Accentuate the axes
base + theme(axis.line = element_line(colour = "grey50", size = 1))
base + theme(axis.line = element_line(colour = "grey50", linewidth = 1))
# Style both x and y axis labels
base + theme(axis.text = element_text(color = "blue", size = 12))
# Useful for long labels
Expand Down Expand Up @@ -352,7 +356,7 @@ base + theme(
legend.background = element_rect(
fill = "lemonchiffon",
colour = "grey50",
size = 1
linewidth = 1
)
)
base + theme(
Expand Down Expand Up @@ -394,11 +398,11 @@ base + theme(panel.background = element_rect(fill = "lightblue"))
# Tweak major grid lines
base + theme(
panel.grid.major = element_line(color = "gray60", size = 0.8)
panel.grid.major = element_line(color = "gray60", linewidth = 0.8)
)
# Just in one direction
base + theme(
panel.grid.major.x = element_line(color = "gray60", size = 0.8)
panel.grid.major.x = element_line(color = "gray60", linewidth = 0.8)
)
```

Expand All @@ -408,7 +412,7 @@ Note that aspect ratio controls the aspect ratio of the _panel_, not the overall
base2 <- base + theme(plot.background = element_rect(colour = "grey50"))
# Wide screen
base2 + theme(aspect.ratio = 9 / 16)
# Long and skiny
# Long and skinny
base2 + theme(aspect.ratio = 2 / 1)
# Square
base2 + theme(aspect.ratio = 1)
Expand Down Expand Up @@ -438,8 +442,12 @@ base_f <- ggplot(df, aes(x, y)) + geom_point() + facet_wrap(~z)
base_f
base_f + theme(panel.spacing = unit(0.5, "in"))
base_f + theme(
strip.background = element_rect(fill = "grey20", color = "grey80", size = 1),
strip.text = element_text(colour = "white")
strip.text = element_text(colour = "white"),
strip.background = element_rect(
fill = "grey20",
color = "grey80",
linewidth = 1
)
)
```

Expand Down

0 comments on commit 062121c

Please sign in to comment.