| sepal_length | sepal_width | petal_length | species |
|---|---|---|---|
| 5.1 | 3.5 | 1.4 | setosa |
| 4.9 | 3.0 | 1.4 | setosa |
| 4.7 | 3.2 | 1.3 | setosa |
| 4.6 | 3.1 | 1.5 | setosa |
| 5.0 | 3.6 | 1.4 | setosa |
13 Interval
How far does each group reach, from its lowest value to its highest? interval draws a span from a low value to a high one at each position: a whisker, capped at both ends. It is the mark behind error bars and min–max ranges. A bar measures from a baseline up to a value. An interval spans a low value and a high value instead: it has no baseline, and it needs both before it can draw. Whiskers stand up by default and lie down when the categories are on y; see Whiskers on their side.
Almost everyone calls this an error bar, and the name hides a choice no drawing can show. Three different quantities get drawn as the same picture: a standard deviation, a standard error, and a confidence interval. On the same data, one can be several times longer than another. Nothing in the drawing says which one it is, so a reader without the caption is guessing at a number’s meaning.
gog makes the sentence name the statistic instead. interval * range spans the observed extremes, interval * confidence(0.95) computes the interval and names its level, and interval * bounds(lower, upper) takes two columns you computed yourself. The mark is the same whisker in all three; what differs is the transform, and the transform is written down. That is the law of Explicit Over Implicit, shown in one mark: the name error bar can hide the choice, and a written sentence cannot.
13.1 A range per group
How far do the petal lengths of each species reach, from the shortest to the longest? One whisker per species answers it, and a whisker needs a low value and a high value to span. The low value and the high value come from a transform, not from two extra channels. range reduces y to its minimum and maximum within each x group, and interval spans between them:
data(iris_flowers) + interval * range + x(species) + y(petal_length) +
x_label("Species") + y_label("Petal length (cm)") +
title("Range of petal length per species")(data(iris_flowers) + interval * range + x(col.species) + y(col.petal_length) +
x_label("Species") + y_label("Petal length (cm)") +
title("Range of petal length per species"))data(iris_flowers) + interval * range + x(:species) + y(:petal_length) +
x_label("Species") + y_label("Petal length (cm)") +
title("Range of petal length per species")plot(data(iris_flowers), layer(interval, range), x(col.species),
y(col.petal_length), x_label("Species"), y_label("Petal length (cm)"),
title("Range of petal length per species"))“Given the iris flowers: intervals derived by range, x is species, y is petal length.”
Read it exactly like bar * mean. Here the transform gives a range, while mean gives one number. You name the one variable whose spread you are asking about; the transform invents the two extents and the mark spans them.
13.2 The middle half of each group
A minimum and a maximum each come from a single row, so one unusual flower can set the whole whisker. To show the middle of a group instead of its extremes, give range two quantiles:
data(iris_flowers) + interval * range(0.25, 0.75) + x(species) + y(petal_length) +
x_label("Species") + y_label("Petal length (cm)") +
title("Middle half of petal length per species")(data(iris_flowers) + interval * range(0.25, 0.75) + x(col.species) + y(col.petal_length) +
x_label("Species") + y_label("Petal length (cm)") +
title("Middle half of petal length per species"))data(iris_flowers) + interval * range(0.25, 0.75) + x(:species) +
y(:petal_length) + x_label("Species") + y_label("Petal length (cm)") +
title("Middle half of petal length per species")plot(data(iris_flowers), layer(interval, range(0.25, 0.75)),
x(col.species), y(col.petal_length), x_label("Species"),
y_label("Petal length (cm)"),
title("Middle half of petal length per species"))“Given the iris flowers: intervals derived by range from 0.25 to 0.75, x is species, y is petal length.”
This spans the first quartile to the third, which is the interquartile range. range(0.1, 0.9) keeps the middle 80%, and any other pair works the same way. Bare range is range(0, 1), so the plain sentence above is unchanged. Transforms gives the full account of the two arguments.
13.3 Three layers instead of one mark
A box draws five numbers in one mark, and you take all five or none. Draw the band and the full range as two layers, and the median as a third. You can restyle any of the three layers, drop one, or replace one:
data(iris_flowers) +
interval * range + style(color = "lightgray") +
interval * range(0.25, 0.75) + style(color = "steelblue", size = 8) +
point * median + style(color = "white", size = 3) +
x(species) + y(petal_length) +
x_label("Species") + y_label("Petal length (cm)") +
title("Petal length per species, drawn in three layers")(data(iris_flowers) +
interval * range + style(color = "lightgray") +
interval * range(0.25, 0.75) + style(color = "steelblue", size = 8) +
point * median + style(color = "white", size = 3) +
x(col.species) + y(col.petal_length) +
x_label("Species") + y_label("Petal length (cm)") +
title("Petal length per species, drawn in three layers"))data(iris_flowers) + interval * range + style(color = "lightgray") +
interval * range(0.25, 0.75) + style(color = "steelblue", size = 8) +
point * median + style(color = "white", size = 3) + x(:species) +
y(:petal_length) + x_label("Species") + y_label("Petal length (cm)") +
title("Petal length per species, drawn in three layers")plot(data(iris_flowers), layer(interval, range),
style({ color: "lightgray" }), layer(interval, range(0.25, 0.75)),
style({ color: "steelblue", size: 8 }), layer(point, median),
style({ color: "white", size: 3 }), x(col.species), y(col.petal_length),
x_label("Species"), y_label("Petal length (cm)"),
title("Petal length per species, drawn in three layers"))“Given the iris flowers: intervals derived by range, and also intervals derived by range from 0.25 to 0.75, and also points derived by median, x is species, y is petal length.”
The gray whisker is the full range, the thick blue band is the middle half, and the white dot is the median. The three layers name the three statistics. A box plot draws the same three and names none of them.
This is not a box redrawn, and the difference is at the ends. A box stops its whiskers at a cutoff computed from the quartiles, and draws every point beyond that cutoff as an outlier. Drawn as a box, setosa shows four outlier dots that are absent here, because these whiskers run to the true minimum and maximum. What the layers give you instead is a plot you can change. Widen the band to range(0.1, 0.9) and the plot reports a different statistic, with the same marks. Drop the gray layer and the y axis covers the band rather than the extremes. Add a third layer with another pair of quantiles, and you have the fan chart drawn over categories instead of along a line. A box is still the shorter sentence when you want the five numbers and the outlier rule together.
13.4 A confidence interval, with a center
How well is the average petal length of each species known? That is a different question from how far the flowers spread, and it needs a different statistic. range shows the full spread. For the uncertainty of the mean, use confidence. It computes a t-interval for each group’s mean (a t-interval widens when the group is small) and keeps that mean as the center. The interval then draws a dot at that center: a pointrange.
data(iris_flowers) + interval * confidence(0.95) + x(species) + y(petal_length) +
x_label("Species") + y_label("Petal length (cm)") +
title("95% CI of mean petal length")(data(iris_flowers) + interval * confidence(0.95) + x(col.species) + y(col.petal_length) +
x_label("Species") + y_label("Petal length (cm)") +
title("95% CI of mean petal length"))data(iris_flowers) + interval * confidence(0.95) + x(:species) +
y(:petal_length) + x_label("Species") + y_label("Petal length (cm)") +
title("95% CI of mean petal length")plot(data(iris_flowers), layer(interval, confidence(0.95)),
x(col.species), y(col.petal_length), x_label("Species"),
y_label("Petal length (cm)"), title("95% CI of mean petal length"))“Given the iris flowers: intervals derived by confidence at 95%, x is species, y is petal length.”
The level is a parameter, so confidence(0.99) draws wider than confidence(0.95).
13.5 The spread of the data
How far is a typical flower from the average of its species? That is a question about the spread of the data, not about how well the average is known. The opening of this chapter named a standard deviation, a standard error and a confidence interval. confidence draws the third. The first is deviation:
data(iris_flowers) + interval * deviation + x(species) + y(petal_length) +
x_label("Species") + y_label("Petal length (cm)") +
title("Mean petal length, with one standard deviation")(data(iris_flowers) + interval * deviation + x(col.species) + y(col.petal_length) +
x_label("Species") + y_label("Petal length (cm)") +
title("Mean petal length, with one standard deviation"))data(iris_flowers) + interval * deviation + x(:species) +
y(:petal_length) + x_label("Species") + y_label("Petal length (cm)") +
title("Mean petal length, with one standard deviation")plot(data(iris_flowers), layer(interval, deviation), x(col.species),
y(col.petal_length), x_label("Species"), y_label("Petal length (cm)"),
title("Mean petal length, with one standard deviation"))“Given the iris flowers: intervals derived by deviation, x is species, y is petal length.”
deviation(2) reaches two standard deviations on each side of the mean, so that band is four standard deviations wide. The default is one each side. Like confidence it carries a center, so it draws a pointrange rather than a bare whisker.
The standard error has no transform of its own, and that is deliberate. confidence computes it internally and reports the interval, which is what a reader needs from it.
deviation and confidence look alike and answer different questions, so here they are on the same fifty flowers per species, sharing one y scale:
(data(iris_flowers) + interval * deviation + x(species) + y(petal_length) +
y_label("Petal length (cm)") + title("Spread of the data")) |
(data(iris_flowers) + interval * confidence + x(species) + y(petal_length) +
y_label("") + title("Uncertainty of the mean"))((data(iris_flowers) + interval * deviation + x(col.species) + y(col.petal_length) +
y_label("Petal length (cm)") + title("Spread of the data")) |
(data(iris_flowers) + interval * confidence + x(col.species) + y(col.petal_length) +
y_label("") + title("Uncertainty of the mean")))(data(iris_flowers) + interval * deviation + x(:species) +
y(:petal_length) + y_label("Petal length (cm)") +
title("Spread of the data")) |
(data(iris_flowers) + interval * confidence + x(:species) +
y(:petal_length) + y_label("") + title("Uncertainty of the mean"))beside(plot(data(iris_flowers), layer(interval, deviation),
x(col.species), y(col.petal_length), y_label("Petal length (cm)"),
title("Spread of the data")),
plot(data(iris_flowers), layer(interval, confidence), x(col.species),
y(col.petal_length), y_label(""), title("Uncertainty of the mean")))“Given the iris flowers: intervals derived by deviation, x is species, y is petal length, beside intervals derived by confidence, x is species, y is petal length.”
The bands on the right are less than a third as long, on identical data, and the ratio is the same in all three species. The two numbers do not contradict each other. deviation describes how far the flowers are from their average, and measuring more of them does not make that band shorter. confidence describes how precisely the average itself is known. More flowers make it shorter: four times as many flowers make it half as long. Fifty flowers per species is what sets that ratio here.
So choose by the sentence you would write under the plot. “A versicolor petal is usually within half a centimeter of its species average” describes the left panel. “The average versicolor petal is 4.26 cm, and the interval reaches 0.13 cm on either side” describes the right one. Reporting one and meaning the other is the mistake the name error bar allows, and the reason gog asks you to name the statistic.
Which band is wider is not fixed, either. confidence narrows as the group gets larger. On fifty flowers it is under a third of the spread; on three flowers it is the wider of the two.
Whether a center exists is decided by the statistic, not by a setting. confidence and deviation have a mean and range does not, so the statistic chooses the geometry: a bare error bar or a pointrange. A setting can hide an existing center (below), but no setting adds one to range. See Transforms for the whole range family.
13.6 Why interval needs a transform
bar needs no transform, bar + x + y, because a bar measures from a baseline the grammar already supplies: zero. An interval has no such default. A low and a high are two numbers, and a plain y() supplies one. So the shortest interval that draws, its minimum syllable, includes a transform that produces a range. Ask for an interval without one and the engine says so, rather than drawing an empty panel. What it refuses shows that refusal.
This is the design choice that keeps the channel vocabulary small. The obvious alternative would add ymin and ymax as two more channels meaningful to a single mark, and a box plot would need five channels. Instead a transform invents the extents, exactly as bin invents a count, so the channel set never grows. The same low and high rows are what box and ribbon draw from. When you already have the extents, from a fitted model or a published standard error, bounds(lower, upper) supplies them without computing anything: interval * bounds(lower, upper) draws one whisker per row.
13.7 Whiskers on their side
Whiskers lie down for the reasons bars do: the names are long, or there are many of them. A list of rows is then easier to scan than a row of columns. An interval reads its orientation off the bindings, the way a bar and a box do. Put the categories on y and the measured column on x, and the whiskers lie down:
data(iris_flowers) + interval * confidence(0.95) + x(petal_length) + y(species) +
x_label("Petal length (cm)") + y_label("Species") +
title("The same 95% CI, lying down")(data(iris_flowers) + interval * confidence(0.95) + x(col.petal_length) + y(col.species) +
x_label("Petal length (cm)") + y_label("Species") +
title("The same 95% CI, lying down"))data(iris_flowers) + interval * confidence(0.95) + x(:petal_length) +
y(:species) + x_label("Petal length (cm)") + y_label("Species") +
title("The same 95% CI, lying down")plot(data(iris_flowers), layer(interval, confidence(0.95)),
x(col.petal_length), y(col.species), x_label("Petal length (cm)"),
y_label("Species"), title("The same 95% CI, lying down"))The caps turn with the whisker, since a cap always crosses the span at its end, and the center dot stays at the center. Everything else in this chapter is unchanged: pattern, color and dodge behave the same whichever axis carries the categories, and so do the two settings the later sections introduce.
This is the usual shape for a coefficient plot, where each row is a named estimate and the reader scans down a column of intervals. Estimates like these normally arrive already computed, from a model rather than from raw observations. That is what bounds is for: it reads two columns you already have as the low and the high, and computes nothing. With the terms on y, the measured axis is the one bounds invents, so there is no x() to write:
| term | lo | hi |
|---|---|---|
| Age | 0.02 | 0.18 |
| Education | 0.31 | 0.55 |
| Experience | 0.11 | 0.29 |
| Region: North | -0.24 | 0.06 |
| Region: South | -0.05 | 0.21 |
data(coefs) + interval * bounds(lo, hi) + y(term) +
x_label("Effect on log wage") +
title("A coefficient plot, from bounds you already have")(data(coefs) + interval * bounds(col.lo, col.hi) + y(col.term) +
x_label("Effect on log wage") +
title("A coefficient plot, from bounds you already have"))data(coefs) + interval * bounds(:lo, :hi) + y(:term) +
x_label("Effect on log wage") +
title("A coefficient plot, from bounds you already have")plot(data(coefs), layer(interval, bounds(col.lo, col.hi)), y(col.term),
x_label("Effect on log wage"),
title("A coefficient plot, from bounds you already have"))“Given the coefs table: intervals from lo to hi, y is term.”
The two intervals that cross zero cover positive and negative values alike, so the data does not settle whether those two effects are positive or negative.
An interval needs a number to span, so two categorical axes are refused, as they are on a bar and a box. What it refuses shows that refusal.
13.8 Splitting by color
A color says which whisker is which without reading the axis. It is also the only way to put a second category on the same axis. color splits an interval into one whisker per group, the same categorical split line and bar make. The statistic is computed inside each group, so each gets its own interval, its own hue, and a legend:
data(iris_flowers) + interval * confidence(0.95) + x(species) + y(petal_length) +
color(species) +
x_label("Species") + y_label("Petal length (cm)") +
title("95% CI of mean petal length, by species")(data(iris_flowers) + interval * confidence(0.95) + x(col.species) + y(col.petal_length) +
color(col.species) +
x_label("Species") + y_label("Petal length (cm)") +
title("95% CI of mean petal length, by species"))data(iris_flowers) + interval * confidence(0.95) + x(:species) +
y(:petal_length) + color(:species) + x_label("Species") +
y_label("Petal length (cm)") +
title("95% CI of mean petal length, by species")plot(data(iris_flowers), layer(interval, confidence(0.95)),
x(col.species), y(col.petal_length), color(col.species),
x_label("Species"), y_label("Petal length (cm)"),
title("95% CI of mean petal length, by species"))“Given the iris flowers: intervals derived by confidence at 95%, x is species, y is petal length, color by species.”
When the split is a second category, so several whiskers share one x, they overlap, and dodge sets them side by side, the same collision modifier that separates grouped bars and boxes:
| country | continent | year | life | population | gdp | era |
|---|---|---|---|---|---|---|
| Afghanistan | Asia | 1957 | 30.332 | 9240934 | 820.8530 | 1957 |
| Afghanistan | Asia | 2007 | 43.828 | 31889923 | 974.5803 | 2007 |
| Albania | Europe | 1957 | 59.280 | 1476505 | 1942.2842 | 1957 |
| Albania | Europe | 2007 | 76.423 | 3600523 | 5937.0295 | 2007 |
| Algeria | Africa | 1957 | 45.685 | 10270856 | 3013.9760 | 1957 |
data(gm_eras) + interval * range * dodge + x(continent) + y(life) + color(era) +
y_label("Life expectancy range") +
title("Life expectancy range per continent, 1957 vs 2007")(data(gm_eras) + interval * range * dodge + x(col.continent) + y(col.life) + color(col.era) +
y_label("Life expectancy range") +
title("Life expectancy range per continent, 1957 vs 2007"))data(gm_eras) + interval * range * dodge + x(:continent) + y(:life) +
color(:era) + y_label("Life expectancy range") +
title("Life expectancy range per continent, 1957 vs 2007")plot(data(gm_eras), layer(interval, range, dodge), x(col.continent),
y(col.life), color(col.era), y_label("Life expectancy range"),
title("Life expectancy range per continent, 1957 vs 2007"))“Given the gapminder eras: intervals derived by range and dodge, x is continent, y is life, color by era.”
Width and opacity stay settings. One whisker is a single stroke, so there is no row for size or opacity to vary over, and style() sets them for the layer:
data(iris_flowers) + interval * range + x(species) + y(petal_length) +
style(color = "tomato", size = 2.5) +
x_label("Species") + y_label("Petal length (cm)") +
title("A heavier, one-color range")(data(iris_flowers) + interval * range + x(col.species) + y(col.petal_length) +
style(color = "tomato", size = 2.5) +
x_label("Species") + y_label("Petal length (cm)") +
title("A heavier, one-color range"))data(iris_flowers) + interval * range + x(:species) + y(:petal_length) +
style(color = "tomato", size = 2.5) + x_label("Species") +
y_label("Petal length (cm)") + title("A heavier, one-color range")plot(data(iris_flowers), layer(interval, range), x(col.species),
y(col.petal_length), style({ color: "tomato", size: 2.5 }),
x_label("Species"), y_label("Petal length (cm)"),
title("A heavier, one-color range"))“Given the iris flowers: intervals derived by range, x is species, y is petal length, colored tomato, with size 2.5.”
13.9 Caps, or a bare linerange
Whether a whisker ends in a crossbar is a matter of style, and a crowded plot often reads more cleanly without them. The end caps, the short crossbars, are decoration, not data; they only emphasize where a whisker stops. So whether to draw them is a setting: style(caps = FALSE) drops them for a bare linerange, and caps = TRUE (the default) keeps the capped error bar.
data(iris_flowers) + interval * confidence(0.95) + x(species) + y(petal_length) +
color(species) + style(caps = FALSE) +
x_label("Species") + y_label("Petal length (cm)") +
title("The same CI, drawn as a linerange")(data(iris_flowers) + interval * confidence(0.95) + x(col.species) + y(col.petal_length) +
color(col.species) + style(caps = False) +
x_label("Species") + y_label("Petal length (cm)") +
title("The same CI, drawn as a linerange"))data(iris_flowers) + interval * confidence(0.95) + x(:species) +
y(:petal_length) + color(:species) + style(caps = false) +
x_label("Species") + y_label("Petal length (cm)") +
title("The same CI, drawn as a linerange")plot(data(iris_flowers), layer(interval, confidence(0.95)),
x(col.species), y(col.petal_length), color(col.species),
style({ caps: false }), x_label("Species"),
y_label("Petal length (cm)"),
title("The same CI, drawn as a linerange"))“Given the iris flowers: intervals derived by confidence at 95%, x is species, y is petal length, color by species, with caps off.”
Because caps are geometry only interval has, style(caps = ) on any other mark is refused with direction, the same way a bar’s border_size is refused on a line. caps combines with range, confidence and deviation, and with the color split. So error bar, linerange, pointrange and their colored versions are one mark, one transform and one setting.
13.10 Hiding the center dot
A pointrange’s dot is the mean; sometimes you want the confidence whiskers without it, a plain error bar. style(center = FALSE) drops the dot, and center = TRUE (the default) keeps it. Like caps, it only hides: it cannot add a center to range, which has none to draw.
data(iris_flowers) + interval * confidence(0.95) + x(species) + y(petal_length) +
color(species) + style(center = FALSE) +
x_label("Species") + y_label("Petal length (cm)") +
title("The same CI, without its center dots")(data(iris_flowers) + interval * confidence(0.95) + x(col.species) + y(col.petal_length) +
color(col.species) + style(center = False) +
x_label("Species") + y_label("Petal length (cm)") +
title("The same CI, without its center dots"))data(iris_flowers) + interval * confidence(0.95) + x(:species) +
y(:petal_length) + color(:species) + style(center = false) +
x_label("Species") + y_label("Petal length (cm)") +
title("The same CI, without its center dots")plot(data(iris_flowers), layer(interval, confidence(0.95)),
x(col.species), y(col.petal_length), color(col.species),
style({ center: false }), x_label("Species"),
y_label("Petal length (cm)"),
title("The same CI, without its center dots"))“Given the iris flowers: intervals derived by confidence at 95%, x is species, y is petal length, color by species, with center off.”
center and caps are the interval’s two settings, one for its middle and one for its ends, so a confidence interval draws four ways. A capped pointrange is the default. A capped bare error bar is center = FALSE. A linerange with a dot is caps = FALSE, and a plain linerange is both FALSE. Like style(caps = ), style(center = ) belongs to interval alone; on any other mark it is refused with direction.
13.11 A dashed whisker
A dash separates one layer from another without a second color, and it survives a print in black and white. A whisker is a stroke, so it takes style(pattern = ) the same way a line does. This is the settable rule: a dash patterns every path-stroke mark (line, step, interval, path, rule). The dash is drawn on the whisker span only. The caps and the center dot stay solid, since a dashed crossbar is hard to read as a cap:
data(iris_flowers) + interval * confidence(0.95) + x(species) + y(petal_length) +
color(species) + style(pattern = "dashed") +
x_label("Species") + y_label("Petal length (cm)") +
title("A dashed 95% CI")(data(iris_flowers) + interval * confidence(0.95) + x(col.species) + y(col.petal_length) +
color(col.species) + style(pattern = "dashed") +
x_label("Species") + y_label("Petal length (cm)") +
title("A dashed 95% CI"))data(iris_flowers) + interval * confidence(0.95) + x(:species) +
y(:petal_length) + color(:species) + style(pattern = "dashed") +
x_label("Species") + y_label("Petal length (cm)") +
title("A dashed 95% CI")plot(data(iris_flowers), layer(interval, confidence(0.95)),
x(col.species), y(col.petal_length), color(col.species),
style({ pattern: "dashed" }), x_label("Species"),
y_label("Petal length (cm)"), title("A dashed 95% CI"))“Given the iris flowers: intervals derived by confidence at 95%, x is species, y is petal length, color by species, with pattern dashed.”
The five-number summary has its own mark, box, and the same low and high pair drawn as a continuous fill is the ribbon band. See Transforms for where range, confidence and deviation sit among the others.
13.12 What you can set
A setting changes how every whisker looks without reading a column, and each mark takes its own. These are an interval’s, with the values each accepts:
| Setting | Value |
|---|---|
style(color = ) |
any CSS color name or hex |
style(opacity = ) |
0 to 1 |
style(size = ) |
pixels |
style(pattern = ) |
solid, dashed, dotted |
style(caps = ) |
TRUE, FALSE |
style(center = ) |
TRUE, FALSE |
caps and center are the interval’s own settings, and both only ever hide. caps turns the end crossbars off, leaving a bare linerange; center hides the dot that confidence supplies. Neither can add geometry the statistic did not produce, so style(center = TRUE) on interval * range changes nothing: a range has no center. The plot below reads gm_continents, three continents across every year from 1952 to 2007, so each interval pools twelve years of countries.
| country | continent | year | life | population | gdp |
|---|---|---|---|---|---|
| Afghanistan | Asia | 1952 | 28.801 | 8425333 | 779.4453 |
| Afghanistan | Asia | 1957 | 30.332 | 9240934 | 820.8530 |
| Afghanistan | Asia | 1962 | 31.997 | 10267083 | 853.1007 |
| Afghanistan | Asia | 1967 | 34.020 | 11537966 | 836.1971 |
| Afghanistan | Asia | 1972 | 36.088 | 13079460 | 739.9811 |
data(gm_continents) + interval * confidence(0.99) + x(continent) + y(life) +
style(caps = FALSE, size = 3, color = "darkslateblue", pattern = "dotted") +
y_label("Life expectancy") + title("A bare, thick, dotted 99% interval")(data(gm_continents) + interval * confidence(0.99) + x(col.continent) + y(col.life) +
style(caps = False, size = 3, color = "darkslateblue", pattern = "dotted") +
y_label("Life expectancy") + title("A bare, thick, dotted 99% interval"))data(gm_continents) + interval * confidence(0.99) + x(:continent) +
y(:life) +
style(caps = false, size = 3, color = "darkslateblue", pattern = "dotted") +
y_label("Life expectancy") + title("A bare, thick, dotted 99% interval")plot(data(gm_continents), layer(interval, confidence(0.99)),
x(col.continent), y(col.life),
style({ caps: false, size: 3, color: "darkslateblue",
pattern: "dotted" }), y_label("Life expectancy"),
title("A bare, thick, dotted 99% interval"))One number is missing from that table, and the transform carries it: confidence(0.99) widens the interval from the default 0.95. That belongs to the transform rather than to style(), because it changes what is computed, not how it is drawn. The test is whether the numbers change: they do here, so it is not a setting.
The grid of every mark and every setting shows which other marks share this list. What a mark maps rather than sets is its row on the companion grid.
13.13 What it refuses
An interval is a stroke, however much it looks like a bar, so its pattern is a dash and a hatch is refused:
| country | continent | year | life | population | gdp |
|---|---|---|---|---|---|
| Afghanistan | Asia | 2007 | 43.828 | 31889923 | 974.5803 |
| Albania | Europe | 2007 | 76.423 | 3600523 | 5937.0295 |
| Algeria | Africa | 2007 | 72.301 | 33333216 | 6223.3675 |
| Angola | Africa | 2007 | 42.731 | 12420476 | 4797.2313 |
| Argentina | Americas | 2007 | 75.320 | 40301927 | 12779.3796 |
data(gapminder_2007) + interval * range + x(continent) + y(life) +
style(pattern = "hatch")(data(gapminder_2007) + interval * range + x(col.continent) + y(col.life) +
style(pattern = "hatch"))data(gapminder_2007) + interval * range + x(:continent) + y(:life) +
style(pattern = "hatch")plot(data(gapminder_2007), layer(interval, range), x(col.continent),
y(col.life), style({ pattern: "hatch" }))Error:
! gog: `style(pattern = "hatch")` is not a stroke pattern. Use "solid" (the default), "dashed", or "dotted".
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.
That refusal is worth explaining, because a capped range looks like a shape with an inside. The engine draws it as a stroke, which is why the whisker takes a dash and not a hatch.
A bare interval is the second refusal. A plain y() supplies one number, and a span needs two, as Why interval needs a transform explained. The engine says so rather than drawing an empty panel:
data(iris_flowers) + interval + x(species) + y(petal_length)data(iris_flowers) + interval + x(col.species) + y(col.petal_length)data(iris_flowers) + interval + x(:species) + y(:petal_length)plot(data(iris_flowers), interval, x(col.species), y(col.petal_length))Error:
! gog: `interval` draws a span from a low value to a high one, but nothing here produces those extents. Add a range transform — `interval * range + x(group) + y(value)` draws the min–max range per group, or `interval * bounds(lo, hi)` a pre-computed one.
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.
The message lists the transforms that produce a range. Two categorical axes are the third refusal. An interval reads its orientation off the bindings, so one axis has to be continuous:
data(gapminder_2007) + interval * range + x(continent) + y(continent)data(gapminder_2007) + interval * range + x(col.continent) + y(col.continent)data(gapminder_2007) + interval * range + x(:continent) + y(:continent)plot(data(gapminder_2007), layer(interval, range), x(col.continent),
y(col.continent))Error:
! gog: `interval` has categorical columns on both axes — `x(continent)` and `y(continent)` — so there is nothing for it to span. One axis must be a number: that is the column the low and high extents come from.
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.
That continuous column is where the low and the high come from. Two categories leave the mark nothing to span.