36  Two variables

You have two columns. Do they move together, or do they differ by group? The questions split by type: two measurements ask about relationship; a category and a measurement ask about comparison.

36.2 What is the trend through the cloud?

Layer a derived line over the points; + stacks layers in one panel, and * smooth fits where the cloud is heading:

data(gapminder_2007) + x(gdp) + y(life) +
  point + color(continent) +
  line * smooth
(data(gapminder_2007) + x(col.gdp) + y(col.life) +
  point + color(col.continent) +
  line * smooth)
data(gapminder_2007) + x(:gdp) + y(:life) + point + color(:continent) +
  line * smooth
plot(data(gapminder_2007), x(col.gdp), y(col.life), point,
  color(col.continent), layer(line, smooth))
0K 10K 20K 30K 40K 50K 40 50 60 70 80 Life Gdp Continent Asia Europe Africa Americas Oceania

The positions are written before any mark, so both layers share them; color is written after point, so only the points split. Encoding scope owns that rule.

36.3 How much was there, over the range?

A line says where the value went; an area says how much there was. It fills to zero so the quantity is the ink:

data(actuals) + area + x(year) + y(sales)
data(actuals) + area + x(col.year) + y(col.sales)
data(actuals) + area + x(:year) + y(:sales)
plot(data(actuals), area, x(col.year), y(col.sales))
2019 2020 2021 2022 2023 0 50 100 150 Sales Year

One atom away. If zero is not a meaningful floor for your variable, you wanted line, not area.

36.4 What is the typical value per group?

A category and a measurement, summarized. The aggregation family (mean, median, sum, max, min) all read a bound y and collapse each group to one number:

data(gapminder_2007) + bar * mean + x(continent) + y(life)
data(gapminder_2007) + bar * mean + x(col.continent) + y(col.life)
data(gapminder_2007) + bar * mean + x(:continent) + y(:life)
plot(data(gapminder_2007), layer(bar, mean), x(col.continent),
  y(col.life))
Asia Europe Africa Americas Oceania 0 20 40 60 80 Life Continent

One atom away. Robust to outliers? Swap the word:

data(gapminder_2007) + bar * median + x(continent) + y(life)
data(gapminder_2007) + bar * median + x(col.continent) + y(col.life)
data(gapminder_2007) + bar * median + x(:continent) + y(:life)
plot(data(gapminder_2007), layer(bar, median), x(col.continent),
  y(col.life))
Asia Europe Africa Americas Oceania 0 20 40 60 80 Life Continent

36.5 What does every value per group look like?

Summaries hide spread. The strip plot is the same sentence with the derivation removed. One dot per row, stacked over each category’s slot:

data(gapminder_2007) + point + x(continent) + y(life)
data(gapminder_2007) + point + x(col.continent) + y(col.life)
data(gapminder_2007) + point + x(:continent) + y(:life)
plot(data(gapminder_2007), point, x(col.continent), y(col.life))
Asia Europe Africa Americas Oceania 40 50 60 70 80 Life Continent

When the dots pile onto one line, * jitter spreads them sideways, along the category axis only, so every dot keeps its exact height:

data(gapminder_2007) + point * jitter + x(continent) + y(life) + style(opacity = 0.6)
data(gapminder_2007) + point * jitter + x(col.continent) + y(col.life) + style(opacity = 0.6)
data(gapminder_2007) + point * jitter + x(:continent) + y(:life) +
  style(opacity = 0.6)
plot(data(gapminder_2007), layer(point, jitter), x(col.continent),
  y(col.life), style({ opacity: 0.6 }))
Asia Europe Africa Americas Oceania 40 50 60 70 80 Life Continent

One atom away. Swap the positions and the strips lie down, useful when the category names are long. See Point and jitter.

36.6 When the numbers are already the answer

When the table already holds one value per category, no transform is involved; bar draws data as it arrives:

data(medals) + bar + x(country) + y(gold)
data(medals) + bar + x(col.country) + y(col.gold)
data(medals) + bar + x(:country) + y(:gold)
plot(data(medals), bar, x(col.country), y(col.gold))
USA China Great Britain Russia Germany 0 10 20 30 40 Gold Country

One atom away. Ranked, because comparison reads best sorted:

data(medals) + bar + x(country) + y(gold) + order(gold, desc = TRUE)
data(medals) + bar + x(col.country) + y(col.gold) + order(col.gold, desc = True)
data(medals) + bar + x(:country) + y(:gold) + order(:gold, desc = true)
plot(data(medals), bar, x(col.country), y(col.gold),
  order(col.gold, { desc: true }))
USA China Great Britain Russia Germany 0 10 20 30 40 Gold Country

There is no hidden counting here to surprise you: if you want a computation, you write its name. That is why this recipe and How many of each? are different sentences.

36.7 What this section refuses

A bar needs exactly one of its axes to be a number. Two categories give it nothing to measure, and the refusal points at the counting derivation:

data(gapminder_2007) + bar + x(country) + y(continent)
Error:
! gog: `bar` has categorical columns on both axes — `x(country)` and `y(continent)` — so there is nothing for it to measure. One axis must be a number: that is the length of the bar. To count rows per category instead, use `bar * count`.
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.