Appendix C — Combinations

This page holds the whole of what the grammar lets you combine, in four crossings. The first is what a mark lets you map, which is the channels. The second is what a mark lets you derive, which is the transforms. The third is which two transforms can be chained on one mark. The fourth is where a mark can be drawn, which is the coordinate spaces. Three of them show what a mark takes, what it does not, and the surprising pairings that are legal anyway. The third asks the same question of two transforms.

These grids are the orthogonality law made visible. Law 1 says every compatible atom combines with every other; a table is that promise enumerated, one cell at a time. It is the direct analog of the letter chart a Hangeul (한글) learner reads, with the consonants down one side and the vowels across the top. A syllable block is what those letters assemble into. This book has used that pairing from the start: a mark is a consonant and a position is a vowel, and neither draws alone. Every syllable the grammar can form appears somewhere in these grids.

Legal is not the same as useful. Law 8, Pronounceable ≠ Usable, guarantees only that the engine draws what a ✓︎ cell allows. Whether it should be drawn is your judgment. A cell the engine refuses teaches as much as one it draws. It records a real limit of the geometry, a ruling, or a part of the engine that is not built, and never an oversight.

Every crossing here is generated live from the engine’s own rule table, each time the book is built. That is the same table the engine consults to accept or refuse your plot. A grid written by hand can stop matching the code; a grid generated from it cannot. Add a mark, a channel, a transform or a space, and this page gains the row or the column by itself.

C.1 Marks and channels

Start with the channels. The marks run down the side and the channels across the top, and each cell says whether you may map a column to that channel on that mark.

Mark x y z color size shape pattern opacity group label play
point ● ● ✓︎ ✓︎ ✓︎ ✓︎ — ✓︎ ✓︎ — ✓︎
line ● ● — ✓︎ ○ — ✓︎ ○ ✓︎ — ✓︎
area ● ● — ✓︎ — — ✓︎ ○ ✓︎ — ✓︎
bar ● ● ✓︎ ✓︎ — — ✓︎ ✓︎ ✓︎ — ✓︎
step ● ● — ✓︎ ○ — ✓︎ ○ ✓︎ — ✓︎
interval ● ● ✓︎ ✓︎ ○ — ✓︎ ○ ✓︎ — ✓︎
box ● ● ✓︎ ✓︎ ○ — ✓︎ ○ ✓︎ — ✓︎
ribbon ● ● — ✓︎ — — ✓︎ ○ ✓︎ — ✓︎
text ● ● ✓︎ ✓︎ ✓︎ — — ✓︎ ✓︎ ● ✓︎
path ● ● ✓︎ ✓︎ ○ — ✓︎ ○ ✓︎ — ✓︎
rule ✓︎ ✓︎ ◌ ✓︎ ○ — ✓︎ ○ — — ✓︎
zone ✓︎ ✓︎ ◌ ✓︎ — — ✓︎ ○ ✓︎ — ✓︎
surface ● ● ● ✓︎ — — — ✓︎ ✓︎ — ✓︎
edge — — — ✓︎ ○ — ✓︎ ✓︎ — — ◌

Reading the five glyphs:

  • ● must: a required channel. The mark does not render without it. A point needs an x and a y; a text needs its label.
  • ✓︎ maps: bind a column of your table here and the engine draws it. This is the ordinary case: the channel shows the column. A column that a transform writes is checked the same way, and a few of those are not drawn yet (Other columns a transform writes).
  • ○ set: the mark has this feature, but not row by row. A line is one stroke, so its size and opacity are a single value for the whole layer, fixed with style() rather than mapped. Those constants have their own settings grid. Nine of them never appear on the channel grid at all: border_color, border_size, caps, center, nudge, angle, arrow, reach and gap are settings and nothing else. Two questions, not one shows in one table where each of five properties can be mapped and where it can only be set.
  • ◌ not drawn: the engine refuses this pairing and gives you a direction instead of a wrong picture. Three cells are marked this way. Two are z on rule and on zone. Their 3-D forms are a plane and a slab with no single depth to sort by (Space). Drawing one means working out what hides what, and the engine does not do that. The third is play on edge. An edge is always placed by layout, and the engine does not play a network yet. The message points to | facet(): every panel shares one layout, so each node keeps its place.
  • — none: the mark has no such feature, or the grammar does not allow the pairing. A bar has no glyph for shape to change, and a point has no line for group to divide. Either binding means nothing, and the engine says so. Decided: z on line, step, area and ribbon. These marks sort by x, and in a cube x can point into the page, so the order cannot be seen. The refusal points to path instead, which for an area or a ribbon draws only the edge.

With the glyphs read, the grid itself shows five patterns:

  • group splits a line, not a bar. Read down the group column: ✓︎ on every mark that builds one shape out of many rows. point, bar and text draw one shape or one label per row, and on them group splits only what a statistic or dodge acts on (and on a bar, stack). A bare group there has no line to divide, and is refused. rule and edge take no group at all.
  • label belongs to text alone. Its column is ● on text and — everywhere else. A labeled scatter is two layers, point + text, never a label given to the point.
  • Categories on x and on y. A category on x reaches every mark that has positions except surface, and on y it reaches fewer of them. Read the x and y columns against each other. Which types each accepts is in Channels, and the split divides the other marks with positions in two. The marks drawn at one place (point, text) and the marks drawn in a slot, which measure along the other axis (bar, interval, box), take either type on both positions. A point’s or a label’s two axes are the same kind, which is why it takes either type on both. That one rule is where a strip plot, a horizontal bar and a categorical box all come from. Three more marks do the same. A path sorts nothing, so it has no domain. A rule measures nothing on either axis. A zone measures nothing by length, and that is what makes a heatmap cell a zone rather than a second kind of bar. The stroke and region marks (line, step, area, ribbon) take either type on x alone. Their x is the domain the mark is read along, and their y is the quantity it traces. Those are two different kinds of axis. surface is outside the split, because both of its positions measure. A sheet is a height, and the two positions say where that height is read.
  • Three rows have no ● at all. rule and zone show ✓︎ on both positions and ● on neither, because no single cell can state what they need. A rule needs exactly one of the two, and the panel supplies the other (Rule). A zone has no fixed need: a categorical position, bounds, bin, density or a boundary on a map can each give it its sides (Zone). edge is the third, and it takes no position at all. Those obligations are stated once in the engine instead.
  • zone and surface take either type on color. Of the five filled regions, zone is the only one that takes a number: bar, box, area and ribbon take a category only. surface, a sheet rather than a region, takes a number as well, and both read the ramp.

C.2 Marks and transforms

The second crossing: not what a mark maps, but what it derives. A transform enters through * (bar * bin, interval * range, point * jitter), and which transform a mark takes is decided by the mark’s geometry against the transform’s kind. There are three kinds, and each has its own grid below.

Each cell holds one of three glyphs:

  • ✓︎ combines: the transform applies to this mark and renders.
  • ● required: on interval, ribbon and edge, the mark does not draw until one of its ● transforms is present, so that transform is part of its minimum syllable. The ● cells in a row are a choice, not a list: pick one. A zone is the exception. Each ● is one way to give it its sides, and a categorical position is another, so a zone can draw with no transform at all.
  • — none: the mark has no use for this kind of transform, and the engine refuses it with a direction toward the transform its geometry does want.

C.2.1 Statistics: a value per group

The first of the three kinds is also the largest. Most summarizing transforms reduce the data to one value at each x: a bin’s count, a group’s mean, a smoothed height. Any mark that draws a value at each x can draw that summary, so those marks are ✓︎ under every summary. That is why a distribution has several spellings rather than one histogram with a shape option: bar * bin is the histogram, line * bin is the frequency polygon, and step * bin is the staircase.

Mark bin smooth straight count density proportion sum mean median max min quantile cumulative partition flow layout cluster
point ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ — — ✓︎ —
line ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ — — — —
area ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ — — — —
bar ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ — — — —
step ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ — — — —
interval — — — — — — — — — — — — — — — — —
box — — — — — — — — — — — — — — — — —
ribbon — — — — ● — — — — — — — — — ● — —
text ✓︎ — — ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ —
path — — — — ✓︎ — — — — — — — — — — — ✓︎
rule — — — — — — — — — — — — — — — — —
zone ● — — ● ● ● ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ — ● ● — ✓︎
surface ✓︎ — — — ✓︎ — ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ — — — — —
edge — — — — — — — — — — — — — — — ● —

Five marks (point, line, area, bar, step) take the whole family of summaries. text takes all of them except smooth, so it can print the number each one writes as a label (Number labels). smooth fits a curve at points of its own, which hold no value of your table to print. interval, box and rule take none, and each has its own reason. An interval needs a pair, not a single value. A box carries its own summary. Its five numbers come from the mark, not from a transform you compose, so no statistic applies to it. A rule takes its length from the panel, so there is no measure to compute.

Four more transforms build a shape rather than a summary. edge takes only layout, and text takes partition, flow and layout. partition reads a hierarchy (zone, text), flow reads a path through stages (ribbon, zone, text), layout places a network (edge, point, text), and cluster orders the categories of an axis by similarity (path, zone). flow and cluster each have a chapter, Flow and Cluster. layout is taught in Network, and partition in Transforms.

C.2.2 Ranges and bands

The second kind answers with two numbers instead of one. Six transforms write a pair of values, a low boundary and a high one. Most give one pair per group; smooth_band and straight_band give one at each x position along a fitted curve or line. range, confidence, deviation, smooth_band and straight_band compute the pair from your data; bounds reshapes a pair you computed already. Four marks draw any such pair: interval shows it as a whisker, ribbon fills it, and line and step trace its two boundaries. A fifth, zone, reads a bounds pair as two sides.

Mark range confidence deviation smooth_band straight_band bounds
point — — — — — —
line ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎
area — — — — — —
bar — — — — — —
step ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎
interval ● ● ● ● ● ●
box — — — — — —
ribbon ● ● ● ● ● ●
text — — — — — —
path — — — — — —
rule — — — — — —
zone — — — — — ●
surface — — — — — —
edge — — — — — —

The ● cells on interval and ribbon are a requirement, not a permission. An interval has no baseline to start from, so it does not render until a pair transform gives it a low and a high. A bare interval is refused, and interval * range with an x and a y is the whole minimum syllable. A ribbon needs a pair too, or one of the two statistics that draw it as a band, density and flow. On a zone, the ● under bounds is one of the ways it gets its sides. A line or a step already draws on its own, so the same transform is optional there, a second way to use the mark. Everywhere else there is nothing for the pair to draw. On a point or a bar, gog refuses it and names the two span marks instead, interval and ribbon. On an area it names ribbon, which is the area with two boundaries instead of a baseline.

C.2.3 Collision modifiers: an offset, not a statistic

When marks collide at one position, four offsets separate them, and which one a mark takes is decided by its geometry and by the axis the collision is on. On the marks that have a width (bar, box, interval), dodge subdivides it. A point has no width, so dodge moves each point sideways within its category’s slot, only as far as it must to touch no other point: the beeswarm. stack piles marks along the measure axis. A bar or an area piles as lengths. A point has no length of its own, so it piles as dots, which is the dot plot. jitter also moves a point along a categorical axis, by a small amount that varies from point to point, and it serves point alone. Which marks take repel is decided by a different question: not what geometry a mark has, but what it is made of. A label is as wide as the word it draws, so two labels overlap where their points do not, and text is the only mark made of words.

Mark dodge stack jitter repel
point ✓︎ ✓︎ ✓︎ —
line — — — —
area — ✓︎ — —
bar ✓︎ ✓︎ — —
step — — — —
interval ✓︎ — — —
box ✓︎ — — —
ribbon — — — —
text ✓︎ — — ✓︎
path — — — —
rule — — — —
zone — — — —
surface — — — —
edge — — — —

Six marks take an offset. bar takes two, point takes three, and the other four take one. bar has both a width and a measure axis, so it takes dodge and stack. For a point, stack answers an overlap along the measure axis, which is a pile of dots you can count. jitter and dodge answer the same overlap by moving points along a categorical axis, which carries no magnitude, so no value changes. jitter moves each point a small amount that varies from point to point; dodge moves each only as far as it must to touch no other point. Each of the six takes a known offset, so when you ask one of them for an offset it does not take, the engine can name one it does: area * dodge names stack. A box shows most clearly that an offset is not a statistic. It takes no statistic, because its own is built in, yet it still takes dodge. Setting grouped boxes side by side is a question about position, and that is orthogonal to the summary each box already carries.

C.3 Transforms and transforms

The third crossing is the only one where a mark does not appear. Two transforms can follow one mark: bar * bin * mean cuts the axis into bands, then averages inside each. Which pairs can do that is decided by the transforms alone, and by one rule.

A transform has a job. There are four of them, and every transform in gog fills at least one, except repel:

job the question it answers the transforms that answer it
extent where are the cells? bin, density, smooth, straight, smooth_band, straight_band, partition, and bounds on a zone
measure what is in them? count, sum, mean, median, max, min, quantile, range, confidence, deviation, bounds
scale what scale is the answer read on? proportion, cumulative, and stack(share = TRUE)
position where are the marks drawn? dodge, stack, jitter
all four at once what is the whole picture? flow, layout, cluster

cumulative fills the scale job as well: it keeps the measurement, and reads it as a total so far. So it contradicts proportion and stack(share = TRUE).

repel fills no job at all. It moves a label after every job has run, so it contradicts nothing. Its row in the grid below is the only one with no refused pair, not even under the other three offsets. flow, layout and cluster fill all four jobs at once. Each computes the whole picture, so repel is the only transform that composes with one. No mark takes cluster and repel together, though.

Two transforms compose when they do different jobs, and contradict when they do the same one. Each cell a transform computes (a bin, a group, a point on a curve) holds one extent, one measure, one scale and one position. So a second transform doing a job the first already did leaves the engine no reading that keeps both, and it refuses rather than drawing one and discarding the other.

One transform is the exception, and it is the exception that makes chaining useful. bin does two jobs: it cuts the axis, and it counts what lands in each band. The count is a by-product of the cut, so bin does not have to keep it. It hands the measure job to a transform that reduces a column you named, such as mean. The other extent transforms do two jobs as well, and none of them can hand one over. A density or a smooth cell is a point on an estimated curve, which is between the rows rather than holding any of them, so there is nothing inside it to reduce. That is why bar * bin * mean composes: bin supplies the cells and mean supplies their value. And it is why bar * bin * count does not, since count was never handed a column to reduce.

bin smooth straight count density proportion sum mean median max min quantile cumulative range confidence deviation smooth_band straight_band bounds partition flow layout cluster dodge stack jitter repel
bin · — — — — ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ — — — — — — — ✓︎ ✓︎ — ✓︎
smooth — · — — — — — — — — — — — — — — — — — — — — — — — — ∅
straight — — · — — — — — — — — — — — — — — — — — — — — — — — ∅
count — — — · — ✓︎ — — — — — — ✓︎ — — — — — — — — — — ✓︎ ✓︎ ✓︎ ✓︎
density — — — — · — — — — — — — — — — — — — — — — — — — — — ✓︎
proportion ✓︎ — — ✓︎ — · ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ — — — — — — — ✓︎ — — — ✓︎ ✓︎ ✓︎ ✓︎
sum ✓︎ — — — — ✓︎ · — — — — — ✓︎ — — — — — — — — — — ✓︎ ✓︎ ✓︎ ✓︎
mean ✓︎ — — — — ✓︎ — · — — — — ✓︎ — — — — — — — — — — ✓︎ ✓︎ ✓︎ ✓︎
median ✓︎ — — — — ✓︎ — — · — — — ✓︎ — — — — — — — — — — ✓︎ ✓︎ ✓︎ ✓︎
max ✓︎ — — — — ✓︎ — — — · — — ✓︎ — — — — — — — — — — ✓︎ ✓︎ ✓︎ ✓︎
min ✓︎ — — — — ✓︎ — — — — · — ✓︎ — — — — — — — — — — ✓︎ ✓︎ ✓︎ ✓︎
quantile ✓︎ — — — — ✓︎ — — — — — · ✓︎ — — — — — — — — — — ✓︎ ✓︎ ✓︎ ✓︎
cumulative ✓︎ — — ✓︎ — — ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ · — — — — — — — — — — ✓︎ ✓︎ ✓︎ ✓︎
range ✓︎ — — — — — — — — — — — — · — — — — — — — — — ✓︎ ∅ ∅ ∅
confidence ✓︎ — — — — — — — — — — — — — · — — — — — — — — ✓︎ ∅ ∅ ∅
deviation ✓︎ — — — — — — — — — — — — — — · — — — — — — — ✓︎ ∅ ∅ ∅
smooth_band — — — — — — — — — — — — — — — — · — — — — — — — — — ∅
straight_band — — — — — — — — — — — — — — — — — · — — — — — — — — ∅
bounds — — — — — — — — — — — — — — — — — — · — — — — ✓︎ ∅ ∅ ∅
partition — — — — — ✓︎ — — — — — — — — — — — — — · — — — ✓︎ ∅ ∅ ✓︎
flow — — — — — — — — — — — — — — — — — — — — · — — — — — ✓︎
layout — — — — — — — — — — — — — — — — — — — — — · — — — — ✓︎
cluster — — — — — — — — — — — — — — — — — — — — — — · — — — ∅
dodge ✓︎ — — ✓︎ — ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ — — ✓︎ ✓︎ — — — · — — ✓︎
stack ✓︎ — — ✓︎ — ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ∅ ∅ ∅ — — ∅ ∅ — — — — · — ∅
jitter — — — ✓︎ — ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ∅ ∅ ∅ — — ∅ ∅ — — — — — · ∅
repel ✓︎ ∅ ∅ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ∅ ∅ ∅ ∅ ∅ ∅ ✓︎ ✓︎ ✓︎ ∅ ✓︎ ∅ ∅ ·

A ∅ marks a pair that composes but that no mark takes together, so the engine refuses every sentence that writes both. range is drawn by a line, a step, an interval or a ribbon. stack piles a bar, an area or a point. So on a bar the engine refuses range, and on a line it refuses stack.

The grid is symmetric, and the rule is the reason. Whether two transforms contradict is a fact about their jobs, and it has nothing to do with which one you typed first. So the order is yours to choose. Each job has its own step in the engine, extent first and position last. Write the transforms in that order, and the sentence reads the way it runs. The · on the diagonal pairs a transform with itself. Written twice, a transform repeats its own job, and the engine refuses it like any other repeat. repel has no job to repeat, so it is the one transform the engine lets you write twice.

Eleven transforms measure, so no two of them combine: read their rows against each other, and every cell between two of them is —. That is why chains such as bar * count * mean, bar * mean * sum and interval * range * confidence are refused. Now compare bin’s row with smooth’s, straight’s and density’s. Those three chain with no measure transform at all. bin chains with nine of the eleven. The two it refuses are count and bounds. count was never handed a column. bounds reads two columns but reduces neither: it draws each row’s own low and high, so a bin would hold many pairs and no one value. That is the exception above, written out in the grid.

Three more rules refuse a pair whose jobs differ. A share and a running total each add up one number per cell. range computes two, a low and a high, and the points of a curve such as density are where it was computed, not parts of a whole. So proportion and cumulative refuse both. stack and dodge move marks that share one position. density, smooth, straight, smooth_band and straight_band compute each group’s curve or line at points along that group’s own range, and the groups’ points do not line up. That is why stack and dodge refuse all five. And jitter spreads points within a category’s slot. bin, density, smooth, straight, smooth_band and straight_band each compute a value along a continuous axis, so there is no slot to spread in.

One contradiction does not fit the grid, because it depends on something outside the pair. stack(share = TRUE) divides each pile by its own total, which is a scale. proportion divides again, and a number divided twice is not a share of anything. The engine refuses it, and the grid shows the common case: stack without share.

A chain fills at most four jobs, and the number was not chosen. There are four jobs and each takes at most one transform, so a fifth transform that fills a job has to repeat one. Repeating one is the contradiction this whole section is about. repel fills no job, so it can still follow a chain of four: text * bin * mean * proportion * dodge * repel draws.

C.3.1 The shapes a long chain can take

The rule above allows chains of three and four, so the next question is how many there are. The answer is a short list. Once each job is filled at most once, and the marks are asked which transforms they take, every chain of three or four takes one of the shapes below. Two slots stand for a family rather than a transform: <aggregation> is any of sum, mean, median, max, min, quantile, and <collision modifier> is dodge, stack, jitter or repel, whichever the mark takes.

how many the shape the jobs it fills
3 <aggregation> * <collision modifier> measure, position
3 <aggregation> * cumulative * <collision modifier> measure, scale, position
3 <aggregation> * proportion * <collision modifier> measure, scale, position
3 bin * <aggregation> * <collision modifier> extent, measure, position
3 bin * <aggregation> * cumulative extent, measure, scale
3 bin * <aggregation> * proportion extent, measure, scale
3 bin * <collision modifier> extent, position
3 bin * cumulative * <collision modifier> extent, scale, position
3 bin * proportion * <collision modifier> extent, scale, position
3 count * <collision modifier> measure, position
3 count * cumulative * <collision modifier> measure, scale, position
3 count * proportion * <collision modifier> measure, scale, position
3 cumulative * <collision modifier> scale, position
3 partition * <collision modifier> extent, position
3 partition * proportion * <collision modifier> extent, scale, position
3 proportion * <collision modifier> scale, position
4 bin * <aggregation> * cumulative * <collision modifier> extent, measure, scale, position
4 bin * <aggregation> * proportion * <collision modifier> extent, measure, scale, position

Read the two four-transform rows first. Each fills every job exactly once. bin always takes the extent, because only it hands its measure job to another. The scale is proportion or cumulative. That leaves the aggregation, which is free among six, and the offset, which the mark narrows to one or two. So chains of four exist, but few of them.

The shapes say what the transforms allow. A mark can still refuse a chain whose every pair is ✓︎, for a reason about its own geometry, and no grid shows it. point * proportion * stack is the clearest case. A dot plot piles one whole dot per observation, and a share is less than one dot. So the chain is refused on point and drawn on bar.

C.4 Marks and spaces

The fourth crossing is not about the mark itself. It is the coordinate space the whole plot is drawn in, and it asks a different question of every mark. Once the plane is bent, or extended into a cube, can this geometry still be drawn? A polar() bar is a wedge, a polar() line runs around the center, and a scatter with a z is drawn in a cube, all from the same sentences that draw them flat.

Two glyphs here, not five. Either the engine draws this mark in this space, or it does not and says so:

  • ✓︎ drawn: the mark renders in this space.
  • — not drawn: refused, with a message that says what to write instead, never half-drawn. Apart from the edge row, a — in the space column has one of two reasons, blocked or decided. On the channel grid a blocked cell shows as ◌ and a decided one as —. Every other blank is simpler: the mark needs what the space lacks, as a surface needs the cube. The paragraphs under the table say why each column’s blanks are blank.
Mark flat space polar nest globe map network
point ✓︎ ✓︎ ✓︎ — ✓︎ ✓︎ ✓︎
line ✓︎ — ✓︎ — — — —
area ✓︎ — ✓︎ — — — —
bar ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ — —
step ✓︎ — ✓︎ — — — —
interval ✓︎ ✓︎ ✓︎ — — — —
box ✓︎ ✓︎ ✓︎ — — — —
ribbon ✓︎ — ✓︎ — — — —
text ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎ ✓︎
path ✓︎ ✓︎ ✓︎ — ✓︎ ✓︎ —
rule ✓︎ — ✓︎ — ✓︎ ✓︎ —
zone ✓︎ — ✓︎ — ✓︎ ✓︎ —
surface — ✓︎ — — — — —
edge — — — — — — ✓︎

flat holds every mark except surface and edge. Read surface first: it does not draw in the plane at all. A surface is a sheet through three positions, and there is no such thing without the third, so its minimum syllable includes the cube. It is the only mark that draws in the cube and not in the plane (Surface). Six of the marks that draw in the plane also draw in the cube. An edge is the other kind of blank, and it is blank in every column but the last. It takes no position at all, so it draws only in network(), the space that draws the places layout computes. In the columns that follow, ignore the edge row: every blank in it has this one reason.

space holds seven marks, and one rule explains all seven: each means the same thing in the cube as on the page. A point is a place, a text is a label at a place, a path is its rows in order, a surface is a sheet. bar, interval and box are drawn in a slot and measure along the other axis. A bar gives a length, an interval a span, a box a summary, and the grammar treats the three as one family.

Each blank in that column has one of two reasons, and each reason has its own refusal message. line, step, area and ribbon read a domain left to right, and a cube has no left to right. So they are decided: the grammar forbids them in a cube, and the refusal points to path instead. rule and zone are blocked, the same verdict their z cells carry. A plane and a slab have no single depth to sort by. Placing one among the data means working out what hides what, and the engine does not do that (Space).

polar matches flat: the same twelve marks draw, and the same two do not. Every mark that draws on the page also draws in the circle. Bending the plane changes only how a mark’s outline is drawn: a held value becomes an arc, and a line between two points becomes a straight chord. A ribbon’s boundaries are the same chords a line draws. The two blanks are surface and edge, and neither is a polar refusal: a sheet needs the cube, and an edge needs network() (Polar).

nest is a column of the opposite shape: two cells filled and the rest blank. Those blanks are not unfinished work. This space asks a different question from flat, space, polar, globe and map. In those five, a mark draws if its geometry survives what the space does to the plane. A nesting is not a transformation of the plane at all, so it can draw any mark that accepts a rectangle. A bar accepts one, because a bar is a measure. A measure has been a length on the page and a radius or an angle in the circle, and here it becomes the size of a rectangle. text can take one too, and it is the only mark here that names a region rather than filling one. The rectangle is already computed, so the label goes to its center. That is what a published treemap shows. A legend cannot name a hundred regions, so the label has to be written in the rectangle (Nest).

Apart from edge, the blanks share one reason: they are the marks placed by a position. point needs a place to be drawn. line, step, area, ribbon and path join places in order, and two touching regions here are not near each other in the data. rule and zone span an axis that is not there, and box, interval and surface measure along one. text is missing from that list because no position places it here. The region places it, and the region is what places the bar too.

map, the sixth column, fills five cells, and the shape of its column comes from one fact: longitude and latitude use both positions for the place. The marks that draw are the ones that place something. A point is a place, a path is a route, and a text is a name at a place. A rule at one position spans the other, which makes it a meridian or a parallel. A zone takes its sides from a boundary, which is the choropleth (Map). The blanks share one reason: each of them needs an axis of its own, either to measure along or to read a domain along, and a map has neither to give. The quantity moves to size or color instead, which is what cartography does with a measure once both positions are gone.

globe, the column before it, fills six cells. Five of them are map’s five, and the sixth is one a flat map cannot have. A globe and a map answer one question: where a place is. So the marks that place something draw on both, and only the reader’s view changes. The far half of the earth is hidden behind the sphere. A route bends along a great circle, and a rule draws the whole meridian or parallel it is on. The sixth cell is bar, because only the sphere leaves one direction free: the radius, which runs straight out from the surface at every place. A bar drawn along it is a spike, with z naming its measure (Globe). The blanks are map’s, minus that bar, and they are blank for the same reason. A sphere has no axis of its own to give them either.

network holds three cells, and one of its marks draws in no other space. edge draws nowhere else, because an edge is placed by neither position: layout computes where every node is placed, network() draws those places, and the edge joins two of them (Network). point and text join it there, as the nodes and their names. The blanks are the marks a layout does not place.

That is the last column. Three of the four crossings have a mark on every row, so one mark can also be read across all three at once. A path takes nearly every channel on Mark × Channel and almost nothing on Mark × Transform, because a path is its rows in order and every statistic replaces them. rule reads the same way on the transform grid, and for the reason the statistics grid already gave: it takes its length from the panel, so it has no measure to compute. And surface’s row on the statistics grid holds the transforms that cover a plane. density estimates a value at every point of a grid and bin cuts adjacent cells, while sum, mean, median, max and min reduce the rows in each cell to one height. path derives two things. With density it draws the contour of a field, the same field a surface lifts into a sheet. With cluster it draws the dendrogram, the tree that joins similar categories.

With these four crossings and the settings grid, the orthogonality law is stated in full: what each mark maps, what it sets, what it derives, which transforms can chain on it, and where it can be drawn.