| year | sales |
|---|---|
| 2019 | 120 |
| 2020 | 135 |
| 2021 | 128 |
| 2022 | 152 |
| 2023 | 168 |
43 Over time
How did it change over time? Time is not a chart type in this grammar; it is a column, and you choose which channel carries it. On x it makes the familiar time series, and every recipe below puts it there.
43.1 How did it change?
The actuals table holds one sales figure per year. The question is whether sales rose or fell, and a line with year on x answers it:
data(actuals) + line + x(year) + y(sales)data(actuals) + line + x(col.year) + y(col.sales)data(actuals) + line + x(:year) + y(:sales)plot(data(actuals), line, x(col.year), y(col.sales))“Given the actuals: a line, x is year, y is sales.” A line connects the rows in x order; with years on x, that order is time.
One atom away. If the amount matters more than the rise and fall, swap line for area: it fills to zero, so the filled area is the amount.
43.2 How did several things change?
The gapminder_asia table holds five countries across the same years. One line over all of them would join the five into a single stroke. A categorical color splits the rows into one line per category and colors them at the same time:
| 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)data(gapminder_asia) + line + x(col.year) + y(col.life) + color(col.country)data(gapminder_asia) + line + x(:year) + y(:life) + color(:country)plot(data(gapminder_asia), line, x(col.year), y(col.life),
color(col.country))“Given gapminder Asia: lines, x is year, y is life, color by country.”
One atom away. Too many categories for a legend to stay readable? group splits without coloring; see Many groups.
43.3 What about a date column?
The six_weeks table records orders by day, and its day column holds calendar dates. The year above is a plain number. A column of calendar dates is temporal, so you get a calendar axis without asking for one. Ticks land on calendar boundaries, and weekly steps land on Mondays:
| day | orders | weekday | week |
|---|---|---|---|
| 2024-03-01 | 20 | Fri | Week 1 |
| 2024-03-02 | 23 | Sat | Week 1 |
| 2024-03-03 | 25 | Sun | Week 1 |
| 2024-03-04 | 28 | Mon | Week 1 |
| 2024-03-05 | 30 | Tue | Week 1 |
data(six_weeks) + line + x(day) + y(orders)data(six_weeks) + line + x(col.day) + y(col.orders)data(six_weeks) + line + x(:day) + y(:orders)plot(data(six_weeks), line, x(col.day), y(col.orders))“Given the six weeks table: a line, x is day, y is orders.”
You never have to write scale = "time": the column’s type already says it. Scales owns the calendar’s rules: three-month steps that read as quarters, and a midnight tick labeled with the day’s name.
43.4 What happened, and what comes next?
The actuals table ends in 2023, and the forecast table holds the three years after it. The two belong on one plot, with the actuals and the forecast drawn as different marks. The first data() is the plot’s table; a later one applies to the mark written after it.
| year | sales |
|---|---|
| 2024 | 180 |
| 2025 | 195 |
| 2026 | 210 |
data(actuals) + x(year) + y(sales) +
line +
data(forecast) + point(data(actuals) + x(col.year) + y(col.sales) +
line +
data(forecast) + point)data(actuals) + x(:year) + y(:sales) + line + data(forecast) + pointplot(data(actuals), x(col.year), y(col.sales), line, data(forecast),
point)“Given the actuals: x is year, y is sales, a line; then given the forecast: points.” The line ends at 2023, the dots start at 2024, and each layer reads its own rows. Data owns multi-table plots.
43.5 What this section refuses
You may want a scatter of wealth against life expectancy for each year, to compare the years side by side. One panel per year is faceting, but a facet variable names its panels, and year here is a number. The refusal says what to do with a year you mean as a label:
data(gapminder_asia) + point + x(gdp) + y(life) | facet(year)data(gapminder_asia) + point + x(col.gdp) + y(col.life) | facet(col.year)data(gapminder_asia) + point + x(:gdp) + y(:life) | facet(:year)plot(data(gapminder_asia), point, x(col.gdp), y(col.life),
across(col.year))Error:
! gog: `facet(year)` splits on a number column, but a facet variable names the panels, so it must be a category column. Make `year` text — in R, `factor(year)` — or cut it into named groups first.
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.