| year | sales |
|---|---|
| 2019 | 120 |
| 2020 | 135 |
| 2021 | 128 |
| 2022 | 152 |
| 2023 | 168 |
11 Area
How large was the quantity at each date? area fills the region between the data and a baseline at zero. Its top edge is the boundary a line would draw. The fill below that edge is what makes the plot read as a quantity rather than as a stroke.
11.1 Basic usage
How large were sales in each of five years? Most examples in this chapter start from the smallest sentence that draws an area: a source, the mark, and its two positions.
data(actuals) + area + x(year) + y(sales) +
y_label("Sales") + title("Sales, 2019 to 2023")(data(actuals) + area + x(col.year) + y(col.sales) +
y_label("Sales") + title("Sales, 2019 to 2023"))data(actuals) + area + x(:year) + y(:sales) + y_label("Sales") +
title("Sales, 2019 to 2023")plot(data(actuals), area, x(col.year), y(col.sales), y_label("Sales"),
title("Sales, 2019 to 2023"))“Given the actuals: an area, x is year, y is sales.”
Notice that the fill reaches the left and right edges of the panel. With point, the axis ends a little past the data. A dot drawn at the last value would otherwise be cut in half by the panel edge. An area has no dots. Its edge is the data itself, so nothing can be cut off, and the axis ends exactly at the first and last year. Scales states the rule in full.
An area’s two axes are not interchangeable, and the rest of this chapter follows from that. x is the domain, the axis the area is read along. y is the measure, the quantity it fills to. A line has the same two axes. A bar draws horizontally when you swap its bindings. A line does not, because its measure is always y, as the profile section of the Line chapter explains. An area follows the same rule, so it has no horizontal form either. The one exception is density, whose curve runs along whichever axis is continuous; With transforms shows both of its forms.
11.2 The baseline has to be zero
A line says where the value was; an area says how much there was. The fill is the quantity, so it has to start from zero. An area drawn from any other baseline would show a height that measures nothing. gog does not stop you from starting the axis somewhere else. y(sales, limits = c(100, NA)) starts it at 100, the fill starts there too, and no warning is printed. That decision is yours to make: the fill then shows sales minus 100 rather than the sales, and the reader of your plot has to know it. Scales explains limits.
So the choice of mark is about the data, not the appearance. Sales have a true zero, where zero means none at all, so the first plot is correct: the fill is the sales. Life expectancy in years has a true zero too, and several plots below fill to it. A quantity whose zero is only a convention has no amount for a fill to show. A temperature in degrees Celsius is one, and so is a calendar year. Draw those with line.
Life expectancy has a true zero, so the data allows either mark, and the question you are asking decides between them. An area and a line draw the same top edge. Here is that edge drawn with line and split by color, as in the Line chapter. It shows where each country’s value was, and nothing about how much:
| country | continent | year | life | population | gdp |
|---|---|---|---|---|---|
| China | Asia | 1952 | 44.00000 | 556263527 | 400.4486 |
| China | Asia | 1957 | 50.54896 | 637408000 | 575.9870 |
| China | Asia | 1962 | 44.50136 | 665770000 | 487.6740 |
| China | Asia | 1967 | 58.38112 | 754550000 | 612.7057 |
| China | Asia | 1972 | 63.11888 | 862030000 | 676.9001 |
data(gapminder_asia) + line + x(year) + y(life) + color(country) +
title("Life expectancy as paths, not quantities")(data(gapminder_asia) + line + x(col.year) + y(col.life) + color(col.country) +
title("Life expectancy as paths, not quantities"))data(gapminder_asia) + line + x(:year) + y(:life) + color(:country) +
title("Life expectancy as paths, not quantities")plot(data(gapminder_asia), line, x(col.year), y(col.life),
color(col.country), title("Life expectancy as paths, not quantities"))“Given gapminder Asia: lines, x is year, y is life, color by country.”
11.3 Multiple series
The table holds five countries, so the sentence has to name the column that splits them. Without a split, the boundary runs through all sixty rows as one region. color splits the mark into one region per category and names each one in a legend. It is the same split that gives line one stroke per category. The sentence also sets a low opacity, so the regions drawn first show through the ones drawn over them:
data(gapminder_asia) + area + x(year) + y(life) + color(country) +
style(opacity = 0.45) +
title("One region per country")(data(gapminder_asia) + area + x(col.year) + y(col.life) + color(col.country) +
style(opacity = 0.45) +
title("One region per country"))data(gapminder_asia) + area + x(:year) + y(:life) + color(:country) +
style(opacity = 0.45) + title("One region per country")plot(data(gapminder_asia), area, x(col.year), y(col.life),
color(col.country), style({ opacity: 0.45 }),
title("One region per country"))“Given gapminder Asia: areas, x is year, y is life, color by country.”
Five lines can cross without hiding each other, because a line is a thin stroke. Five areas cannot: a fill covers whatever lies behind it, and at the default opacity only a little shows through. Write the same sentence with no opacity, and gog prints a warning before it draws:
data(gapminder_asia) + area + x(year) + y(life) + color(country) +
title("Five regions, no opacity set: each one covers the ones before it")(data(gapminder_asia) + area + x(col.year) + y(col.life) + color(col.country) +
title("Five regions, no opacity set: each one covers the ones before it"))data(gapminder_asia) + area + x(:year) + y(:life) + color(:country) +
title("Five regions, no opacity set: each one covers the ones before it")plot(data(gapminder_asia), area, x(col.year), y(col.life),
color(col.country),
title("Five regions, no opacity set: each one covers the ones before it"))gog: `area` split by `country` draws 5 regions on top of one another, and the last one drawn can hide the rest. Add `style(opacity = 0.5)` to see through them, or use `line` to compare shapes without filling.
Compare the legend with the plot. Every country is named there, but five regions cannot be distinguished. Each region covers the ones drawn before it, so you can read a region only where it rises above every region drawn after it. The warning is advice rather than a refusal: the sentence is legal, so gog draws it.
The other way to draw it is to stack the regions instead of overlaying them. That is written area * stack. stack is a collision modifier: it moves marks that would otherwise be drawn in the same place, rather than summarizing them. Its sibling dodge sets marks beside each other within a slot, and an area is one connected shape across the axis, not a mark in a slot. So of the two, only stack applies to an area. Each region starts at the top of the ones below it, so the five form one band and none is covered:
data(gapminder_asia) + area * stack + x(year) + y(population) + color(country) +
y_label("Population") +
title("Five Asian countries, population stacked")(data(gapminder_asia) + area * stack + x(col.year) + y(col.population) + color(col.country) +
y_label("Population") +
title("Five Asian countries, population stacked"))data(gapminder_asia) + area * stack + x(:year) + y(:population) +
color(:country) + y_label("Population") +
title("Five Asian countries, population stacked")plot(data(gapminder_asia), layer(area, stack), x(col.year),
y(col.population), color(col.country), y_label("Population"),
title("Five Asian countries, population stacked"))“Given gapminder Asia: areas derived by stack, x is year, y is population, color by country.”
Each country’s region is as thick as its population, and the band’s top edge is the five-country total. This plot measures population instead of life on purpose. You stack quantities that add: populations add up to a total that means something, and life expectancies do not. gog does not stop you from stacking them. Put y(life) in place of y(population) in the sentence above, and gog draws five regions to a top edge near 370 years, with no warning. That number means nothing. So use stack with care: you always get a plot, and only you know whether the sum of the parts means something.
Which country’s share of the five grew? The stacked plot cannot say, because every region’s baseline moves with the ones below it. stack(share = TRUE) divides each year’s pile by that year’s total. The band’s top edge is then flat at 1, and each region’s thickness is that country’s share:
data(gapminder_asia) + area * stack(share = TRUE) + x(year) + y(population) +
color(country) +
y_label("Share of population") +
title("Five Asian countries, population as shares")(data(gapminder_asia) + area * stack(share = True) + x(col.year) + y(col.population) +
color(col.country) +
y_label("Share of population") +
title("Five Asian countries, population as shares"))data(gapminder_asia) + area * stack(share = true) + x(:year) +
y(:population) + color(:country) + y_label("Share of population") +
title("Five Asian countries, population as shares")plot(data(gapminder_asia), layer(area, stack({ share: true })),
x(col.year), y(col.population), color(col.country),
y_label("Share of population"),
title("Five Asian countries, population as shares"))“Given gapminder Asia: areas derived by stack as shares, x is year, y is population, color by country.”
Between 1952 and 2007, India’s share rose from 0.33 to 0.39, and Japan’s fell from 0.08 to 0.05. The total is gone from this plot: every year reaches 1, so it shows which shares grew and no longer how large the total was. Draw both when you need both answers. The Transforms chapter covers stack in full, with a section on stacked areas.
Every stack so far was split by color. A stack can also be split by group. It splits the rows as color does, but every region gets the same color and there is no legend. Use it when the total matters more than telling the regions apart. Because the regions share one color, gog draws a thin line between them in the panel’s background color. A stacked bar separates its segments the same way. The channel_sales table holds each quarter’s revenue from two ways of selling. Its channel column names them, Retail and Online:
| quarter | channel | revenue |
|---|---|---|
| Q1 | Retail | 4 |
| Q1 | Online | 3 |
| Q2 | Retail | 5 |
| Q2 | Online | 6 |
| Q3 | Retail | 4 |
data(channel_sales) + area * stack + x(quarter) + y(revenue) + group(channel)data(channel_sales) + area * stack + x(col.quarter) + y(col.revenue) + group(col.channel)data(channel_sales) + area * stack + x(:quarter) + y(:revenue) +
group(:channel)plot(data(channel_sales), layer(area, stack), x(col.quarter),
y(col.revenue), group(col.channel))“Given the channel sales table: areas derived by stack, x is quarter, y is revenue, grouped by channel.”
quarter is a category, so each quarter gets a slot of its own, and the fill runs from the middle of the first slot to the middle of the last. A category on the domain describes this shape.
The top edge is each quarter’s total. Retail comes first in the table, and gog piles the groups in that order, so Retail is the lower region. The thin line marks where the retail region ends and the online region begins. No color or legend tells the two regions apart. When the reader needs to know which is which, another layer has to label them. This line is not a border: an area takes no border, as the next section explains. gog draws the line only when group alone splits the stack, and no setting removes it.
11.4 Giving an area an edge
A low opacity, like the 0.45 in Multiple series, leaves a faint top edge, and the top edge is where the data is. A stroke along it makes the boundary easy to read. An area takes no border of its own. The edge on top of a filled region is a line, so you layer one over the other:
data(actuals) + x(year) + y(sales) +
area + style(color = "steelblue", opacity = 0.25) +
line + style(color = "steelblue", size = 2.5) +
title("area + line: the fill and its edge, composed")(data(actuals) + x(col.year) + y(col.sales) +
area + style(color = "steelblue", opacity = 0.25) +
line + style(color = "steelblue", size = 2.5) +
title("area + line: the fill and its edge, composed"))data(actuals) + x(:year) + y(:sales) + area +
style(color = "steelblue", opacity = 0.25) + line +
style(color = "steelblue", size = 2.5) +
title("area + line: the fill and its edge, composed")plot(data(actuals), x(col.year), y(col.sales), area,
style({ color: "steelblue", opacity: 0.25 }), line,
style({ color: "steelblue", size: 2.5 }),
title("area + line: the fill and its edge, composed"))“Given the actuals: x is year, y is sales, an area and also a line.”
There is no border setting on area to use instead. A layered line gives the edge its own color, width, opacity and dash. A bar’s border settings, border_color and border_size, give only a color and a width.
11.5 One region, one fill
The plot above set one opacity for the whole area. Can you map a column to an area’s opacity instead? No, and the reason is the shape. An area is a single filled region, so opacity is a setting here and not a channel. Each row of the table is one vertex, a corner of the top edge, not a piece of the fill. No part of the region belongs to one row, so a column cannot make one row lighter or darker than another. opacity is a setting on line for the same reason.
size is refused both ways: area neither maps it nor sets it. A stroke has a width, so line lets you set one. An area’s extent comes from its x, its y and its baseline, so there is no size to choose. Both marks take style(color = ) and style(opacity = ). What it refuses shows the refusals for opacity and size.
11.6 With transforms
Apart from the stack, every plot so far filled to values already in the table. A transform computes new values first, and the area fills to what it computed. A distribution, for example, can be drawn as a filled shape. The question is the one a histogram answers: how many rows fall in each interval? bin, density and the aggregation family mean the same thing on an area as on a bar. bin counts the rows in each interval. The boundary then runs through the bin centers, and the region below it is filled. That shape is the frequency polygon line * bin draws, with its inside filled:
| 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) + area * bin + x(life) +
x_label("Life expectancy (years)") +
title("area * bin: a filled frequency polygon")(data(gapminder_2007) + area * bin + x(col.life) +
x_label("Life expectancy (years)") +
title("area * bin: a filled frequency polygon"))data(gapminder_2007) + area * bin + x(:life) +
x_label("Life expectancy (years)") +
title("area * bin: a filled frequency polygon")plot(data(gapminder_2007), layer(area, bin), x(col.life),
x_label("Life expectancy (years)"),
title("area * bin: a filled frequency polygon"))“Given gapminder 2007: an area derived by bin, x is life.”
bin(30) and bin(width = 5) set the bins here exactly as they do for a bar. Choosing the bin count shows both, and Choosing a width explains how to pick a width.
density replaces the counts with a smooth curve, and the area fills the region under it:
data(gapminder_2007) + area * density + x(life) +
x_label("Life expectancy (years)") +
title("area * density")(data(gapminder_2007) + area * density + x(col.life) +
x_label("Life expectancy (years)") +
title("area * density"))data(gapminder_2007) + area * density + x(:life) +
x_label("Life expectancy (years)") + title("area * density")plot(data(gapminder_2007), layer(area, density), x(col.life),
x_label("Life expectancy (years)"), title("area * density"))“Given gapminder 2007: an area derived by density, x is life.”
density is not the area’s own transform. Five other marks estimate the same curve, and only the shape changes:
Mark derived by density |
Shape |
|---|---|
line * density |
the curve as a stroke |
step * density |
the curve as a staircase |
point * density |
one dot at each point where the curve was estimated |
bar * density |
one thin bar at each of those points |
ribbon * density |
with a category, the curve mirrored: the violin |
area * density |
the curve filled to the baseline, the plot above |
Put line, step, point or bar in place of area in the sentence above, and each one draws. ribbon refuses it, because a ribbon spans between two boundaries and one curve gives only one. With a category, ribbon * density + x(<category>) + y(<number>) draws the violin.
Give the area * density sentence a category on x and a number on y, and the curve runs along y instead of x. One estimate is drawn across each category’s slot. This plot is the half violin:
data(gapminder_2007) + area * density + x(continent) + y(life) +
color(continent) +
y_label("Life expectancy") +
title("area * density + x(continent): the half violin")(data(gapminder_2007) + area * density + x(col.continent) + y(col.life) +
color(col.continent) +
y_label("Life expectancy") +
title("area * density + x(continent): the half violin"))data(gapminder_2007) + area * density + x(:continent) + y(:life) +
color(:continent) + y_label("Life expectancy") +
title("area * density + x(continent): the half violin")plot(data(gapminder_2007), layer(area, density), x(col.continent),
y(col.life), color(col.continent), y_label("Life expectancy"),
title("area * density + x(continent): the half violin"))“Given gapminder 2007: areas derived by density, x is continent, y is life, color by continent.”
The two sentences differ in three places: x moved from life to continent, y(life) was added, and so was color(continent). With only x(life), life runs along x and y holds no column, so y carries the density estimate, as it carried the count for bin. With a category on x and a number on y, both axes hold a column, so each estimate is drawn across its category’s slot, one distribution per continent. color does what it did in Multiple series: a categorical column gives each region its own fill and a legend. On a line the same channel colors the stroke; on an area it colors the fill. The slots already keep the continents apart, so here color splits nothing; it gives each slot its own fill and a legend entry.
Can the two bindings be swapped? For density, yes. Put life on x and the category on y, and each curve runs along life, rising from its own category’s place on the y axis. That is the ridgeline plot, and it is the half violin’s sentence with x and y exchanged:
data(gapminder_2007) + area * density + x(life) + y(continent) +
color(continent) +
x_label("Life expectancy") + title("area * density + x(life): the ridgeline")(data(gapminder_2007) + area * density + x(col.life) + y(col.continent) +
color(col.continent) +
x_label("Life expectancy") + title("area * density + x(life): the ridgeline"))data(gapminder_2007) + area * density + x(:life) + y(:continent) +
color(:continent) + x_label("Life expectancy") +
title("area * density + x(life): the ridgeline")plot(data(gapminder_2007), layer(area, density), x(col.life),
y(col.continent), color(col.continent), x_label("Life expectancy"),
title("area * density + x(life): the ridgeline"))“Given gapminder 2007: areas derived by density, x is life, y is continent, color by continent.”
Ribbon shows all four forms in one section: one side or two, and the category on either axis. Its violin section says what the width of a violin can mean.
The half violin and the ridgeline give each group a place of its own. To compare the groups in one panel instead, overlay their curves. Histograms of several groups drawn in one panel overlap, as the Bar chapter’s Overlaid histograms section showed with three species of iris. Smooth curves are easier to distinguish, because they cross at a point instead of sharing bin edges. In the iris flowers table, color(species) splits the rows, so density makes one estimate for each species, all in one panel.
As in Multiple series, a low opacity lets each region show through the others. As in Giving an area an edge, a line layered on top draws each region’s top edge. The line takes density too, so it traces the same curve the area fills. color is written before both marks, so it reaches both layers, as Encoding scope explains.
| 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 |
data(iris_flowers) + x(petal_length) + color(species) +
area * density + style(opacity = 0.25) +
line * density +
x_label("Petal length (cm)") + title("Petal length, one curve per species")(data(iris_flowers) + x(col.petal_length) + color(col.species) +
area * density + style(opacity = 0.25) +
line * density +
x_label("Petal length (cm)") + title("Petal length, one curve per species"))data(iris_flowers) + x(:petal_length) + color(:species) + area * density +
style(opacity = 0.25) + line * density + x_label("Petal length (cm)") +
title("Petal length, one curve per species")plot(data(iris_flowers), x(col.petal_length), color(col.species),
layer(area, density), style({ opacity: 0.25 }), layer(line, density),
x_label("Petal length (cm)"),
title("Petal length, one curve per species"))“Given the iris flowers: x is petal length, color by species, areas derived by density and also lines derived by density.”
The area under each density curve is 1, so the species whose petals vary least draws the tallest curve. Every setosa petal is between 1.0 and 1.9 cm long, so its curve is alone on the left and rises highest. Versicolor and virginica petals overlap from 4.5 to 5.1 cm, and around there each fill shows through the other. All three curves share one bandwidth, the width that decides how smooth each curve is. So a difference in their shapes comes from the data, not from the smoothing. Transforms explains how the shared bandwidth is chosen.
After bin and density, the third kind of transform is the aggregation family: sum, mean, median, max, min and quantile. Each leaves one value per year, and the area fills to those values, exactly as a bar would:
data(gapminder_asia) + area * mean + x(year) + y(life) +
y_label("Mean life expectancy") +
title("area * mean: five countries averaged per year")(data(gapminder_asia) + area * mean + x(col.year) + y(col.life) +
y_label("Mean life expectancy") +
title("area * mean: five countries averaged per year"))data(gapminder_asia) + area * mean + x(:year) + y(:life) +
y_label("Mean life expectancy") +
title("area * mean: five countries averaged per year")plot(data(gapminder_asia), layer(area, mean), x(col.year), y(col.life),
y_label("Mean life expectancy"),
title("area * mean: five countries averaged per year"))“Given gapminder Asia: an area derived by mean, x is year, y is life.”
A transform can also keep every year and add the years up. cumulative adds each year’s value to the years before it, and the area fills to the total so far. The actuals table holds one year of sales in each row:
data(actuals) + area * cumulative + x(year) + y(sales) +
title("area * cumulative: sales so far")(data(actuals) + area * cumulative + x(col.year) + y(col.sales) +
title("area * cumulative: sales so far"))data(actuals) + area * cumulative + x(:year) + y(:sales) +
title("area * cumulative: sales so far")plot(data(actuals), layer(area, cumulative), x(col.year), y(col.sales),
title("area * cumulative: sales so far"))“Given the actuals: an area derived by cumulative, x is year, y is sales.”
The region rises from 120 in 2019 to 703 in 2023, the sales of all five years. stack adds up the groups at one year, and cumulative adds up the years of one group. The two compose: area * cumulative * stack with a color split piles the running totals of the groups.
11.7 A category on the domain
The mean in the area * mean plot above was computed for each year, and year is a continuous domain. Put a category there, and one summary per continent is usually drawn as bars. An area draws the five continent means as one connected shape instead. Use it when the reader should see one overall shape rather than compare five heights. Each category gets one slot, the boundary runs from slot to slot in axis order, and the region still fills down to zero:
data(gapminder_2007) + area * mean + x(continent) + y(life) +
y_label("Mean life expectancy") +
title("area * mean + x(continent): the filled profile")(data(gapminder_2007) + area * mean + x(col.continent) + y(col.life) +
y_label("Mean life expectancy") +
title("area * mean + x(continent): the filled profile"))data(gapminder_2007) + area * mean + x(:continent) + y(:life) +
y_label("Mean life expectancy") +
title("area * mean + x(continent): the filled profile")plot(data(gapminder_2007), layer(area, mean), x(col.continent),
y(col.life), y_label("Mean life expectancy"),
title("area * mean + x(continent): the filled profile"))gog draws that sentence. The boundary runs from Asia to Oceania in axis order, and the fill reaches from each mean down to zero. The segments between the categories are where the plot can claim more than was measured. Nothing was measured between Africa and the Americas, and a straight segment is drawn there anyway. A line across that gap claims a change. A fill claims a quantity as well, at every point between the two categories. If you read only the vertices, the segments help you compare the five heights. If you read the segments themselves, they claim what was never measured. To compare the five means and nothing else, swap area for bar: one atom away, and no segments to read.
Keep the fill when the overall shape is what the reader should see. Continents have no order, but a straight axis still puts one of them first and another last. A circle has no first or last place. Add polar(), and the same sentence draws the filled radar chart:
data(gapminder_2007) + area * mean + x(continent) + y(life) + polar() +
title("area * mean + polar(): the filled radar")(data(gapminder_2007) + area * mean + x(col.continent) + y(col.life) + polar() +
title("area * mean + polar(): the filled radar"))data(gapminder_2007) + area * mean + x(:continent) + y(:life) + polar() +
title("area * mean + polar(): the filled radar")plot(data(gapminder_2007), layer(area, mean), x(col.continent),
y(col.life), polar(), title("area * mean + polar(): the filled radar"))“Given gapminder 2007: an area derived by mean, x is continent, y is life, in polar.”
11.8 What you can set
A setting changes how the region looks without reading a column, and each mark takes its own. These are an area’s, with the values each accepts:
| Setting | Value |
|---|---|
style(color = ) |
any CSS color name or hex |
style(opacity = ) |
0 to 1 |
style(pattern = ) |
solid, hatch, crosshatch, stripes, grid, dots |
That is the shortest settings list in the book, shared with ribbon. line has one more, size, and bar has two, border_color and border_size. Both are left out on purpose: One region, one fill explains size, and Giving an area an edge explains the border. The one setting this chapter has not shown yet is pattern, a texture on the fill. The plot below hatches the fill and draws the edge with a layered line, as before:
data(actuals) + x(year) + y(sales) +
area + style(color = "seagreen", pattern = "hatch", opacity = 0.5) +
line + style(color = "seagreen", size = 2) +
y_label("Sales") + title("A textured fill, and a line for the edge")(data(actuals) + x(col.year) + y(col.sales) +
area + style(color = "seagreen", pattern = "hatch", opacity = 0.5) +
line + style(color = "seagreen", size = 2) +
y_label("Sales") + title("A textured fill, and a line for the edge"))data(actuals) + x(:year) + y(:sales) + area +
style(color = "seagreen", pattern = "hatch", opacity = 0.5) + line +
style(color = "seagreen", size = 2) + y_label("Sales") +
title("A textured fill, and a line for the edge")plot(data(actuals), x(col.year), y(col.sales), area,
style({ color: "seagreen", pattern: "hatch", opacity: 0.5 }), line,
style({ color: "seagreen", size: 2 }), y_label("Sales"),
title("A textured fill, and a line for the edge"))“Given the actuals: x is year, y is sales, an area colored seagreen, with pattern hatch and opacity 0.5, and also a line colored seagreen, with size 2.”
That layered line traces only the top boundary. A border would also draw a line along the two sides and the baseline.
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.
11.9 What it refuses
The first two refusals come from an axis. The first comes from the measure. A region fills to a baseline at zero, and a category has no zero. So a categorical y is refused, and the message asks for the category on x instead:
data(gapminder_2007) + area * mean + x(life) + y(continent)data(gapminder_2007) + area * mean + x(col.life) + y(col.continent)data(gapminder_2007) + area * mean + x(:life) + y(:continent)plot(data(gapminder_2007), layer(area, mean), x(col.life),
y(col.continent))Error:
! gog: `y(continent)` maps a categorical (text) column, but `y` on `area` needs a continuous (numeric) column. On these marks `x` is the domain and `y` the measure, and a category is not a quantity to measure: a mean of category names is not a number, and a region has no categorical baseline to close on. Put the category on `x` instead — `line * mean + x(<category>) + y(<number>)` is the profile plot, and `area * mean` fills it. Unlike `bar`/`box`/`interval`, these marks do not read their orientation off the bindings, because their two axes do not have the same role.
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.
density is the exception. Its curve runs along the continuous axis, and each category’s place on y is the baseline its curve fills to. With y(continent) it draws the ridgeline in With transforms.
The second comes from the domain. bin cuts a continuous axis into intervals, and a category is one slot with no width to cut. The message names count, the transform that tallies the rows in each category:
data(gapminder_2007) + area * bin + x(continent)data(gapminder_2007) + area * bin + x(col.continent)data(gapminder_2007) + area * bin + x(:continent)plot(data(gapminder_2007), layer(area, bin), x(col.continent))Error:
! gog: `bin` cuts a continuous axis into intervals, and `x(continent)` is categorical — a category is one slot, with no width to cut. To tally rows per category, `count` is the transform that does it: `area * count`.
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.
density needs a continuous axis too, but either x or y can be that axis, so it refuses only when neither is continuous. Its message names proportion in the same way.
The next two come from the fill. A mapped opacity is the third. Each row is one vertex of the top edge, so no part of the region belongs to a single row, as One region, one fill explained.
data(gapminder_asia) + area + x(year) + y(life) + opacity(gdp)data(gapminder_asia) + area + x(col.year) + y(col.life) + opacity(col.gdp)data(gapminder_asia) + area + x(:year) + y(:life) + opacity(:gdp)plot(data(gapminder_asia), area, x(col.year), y(col.life),
opacity(col.gdp))Error:
! gog: `opacity` cannot be bound to `area` — an area takes one opacity for the whole layer, not one per row. Drop the `gdp` mapping and set it with `style(opacity = )`, or use a mark that maps `opacity`: `point`, `bar`, `text` or `surface`.
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.
A size is the fourth, and it is refused both ways. An area’s extent comes from its x, its y and its baseline, so there is no size to map and none to set:
data(actuals) + area + x(year) + y(sales) + style(size = 4)data(actuals) + area + x(col.year) + y(col.sales) + style(size = 4)data(actuals) + area + x(:year) + y(:sales) + style(size = 4)plot(data(actuals), area, x(col.year), y(col.sales), style({ size: 4 }))Error:
! gog: `style(size = 4)` — an area has no size to set. Remove it, or use a mark that has one: `point`, `line`, `step`, `interval`, `box`, `text`, `path` or `rule`.
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.
A line accepts a size, because a stroke has a width to choose.
The fifth comes from the series. An area needs at least two positions in each series, as a line does. A plot in which every series holds one would draw nothing, so gog refuses it, as Line shows.
11.10 In short
An area measures on y, so it does not draw horizontally the way a bar does. The one exception is density, whose curve runs along whichever axis is continuous. An area’s interior belongs to no single row, so opacity can only be set. Its extent comes from its x, its y and its baseline, so size is refused both ways.
11.11 What comes next
An area slopes from one measurement to the next, and that slope claims the value changed steadily in between. Step claims instead that each value held until the next measurement, and then jumped.