Appendix D — Chart names, translated
gog has no chart-type atoms (a “histogram” is a sentence, not a word), but chart names are how the world indexes its questions, so this appendix is the dictionary. It is the one place in the book organized by name; the cookbook organizes the same territory by question, which is usually the better door in.
D.1 Drawn today
| The name you know | The sentence |
|---|---|
| Scatter plot | point + x(a) + y(b) |
| Bubble chart | point + x(a) + y(b) + size(c) |
| Strip plot | point + x(category) + y(measure), or swap the axes to lay it down |
| Bar chart | bar + x(category) + y(measure) |
| Horizontal bar chart | bar + x(measure) + y(category) |
| Hatched (print-safe) bars | bar + x(category) + y(measure) + style(pattern = "hatch"), also "crosshatch"/"grid"/"dots" |
| Count (frequency) chart | bar * count + x(category) |
| Proportion chart | bar * proportion + x(category) |
| Histogram | bar * bin + x(measure), bin(30) or bin(width = 5) to tune |
| Frequency polygon | line * bin + x(measure) |
| Outline (step) histogram | step * bin + x(measure) |
| Overlaid histograms | bar * bin + x(measure) + color(group) |
| Density plot | line * density + x(measure) |
| Line chart / time series | line + x(time) + y(measure) |
| Multi-series lines by dash (no color) | line + x(time) + y(measure) + pattern(series), add color(series) to double-encode |
| Step chart | step + x(a) + y(b) |
| Empirical CDF | step over cumulative shares, worked example in Step |
| Area chart | area + x(a) + y(b) |
| Spread / range band | ribbon * range + x(a) + y(b), split by group with color(group) |
| Confidence band (computed) | ribbon * confidence(0.95) + x(a) + y(b), often under a line * mean trend |
| Confidence band (pre-computed) | ribbon * bounds(lower, upper) + x(a), bounds you already have (a model SE, an SEM) |
| Unfilled (dashed) error band | line * bounds(lower, upper) + style(pattern = "dashed"), the two ± edges, no fill |
| Min–max envelope, as two lines | line * range + x(a) + y(b), the unfilled counterpart to ribbon * range |
| Dashed / dotted line | any line/step + style(pattern = "dashed") (or "dotted") |
| Trend line / smoother / LOESS | line * smooth + x(a) + y(b) |
| Summary bars (mean per group) | bar * mean + x(category) + y(measure), also sum, median, max, min |
| Grouped bars (side by side) | bar * mean * dodge + x(category) + y(measure) + color(group), * dodge separates the split |
| Colorblind-safe grouped bars | add + pattern(group) to the grouped bars: hue and hatch, one merged legend |
| Ranked bars | add order(measure, desc = TRUE) |
| Small multiples / trellis / conditioning plot / facets | plot \| facet(category), plot / facet(category), or both for a grid |
| Small multiples of 3-D plots | any cube sentence \| facet(category): one projected box per panel |
| 3-D scatter | point + x(a) + y(b) + z(c), view angle via space(turn = , tilt = ) |
| 3-D trajectory / space curve | path + x(a) + y(b) + z(c), the rows joined in table order, in the cube |
Surface plot / wireframe / persp |
surface + x(a) + y(b) + z(c) over a table with one row per (x, y) crossing (expand.grid()’s shape); style(border_color = "white") draws the mesh lines, color(c) ramps it by height |
| Terrain / elevation / bathymetry | the same sentence: a height over a plane is a surface, whatever the height means |
| Response surface | surface + x(factor1) + y(factor2) + z(response) over the design grid; from a fitted model, predict onto a grid first and hand over the result |
| 3-D density surface | surface * density + x(a) + y(b) + space(), the estimate raised as height, the third geometry of the same field zone * density paints and path * density traces |
| Profile plot (categories joined) | line * mean + x(category) + y(measure), one summary per category with the path across them; step holds instead of sloping |
| Filled profile | area * mean + x(category) + y(measure), the same boundary filled to the baseline |
| Connected scatterplot | path + x(a) + y(b), the rows joined in the table’s order rather than sorted by x; style(arrow = "end") says which end is last |
| Trajectory / route / hysteresis loop | the same sentence: any reading whose order is the data’s is path, not line |
| Annotation arrow / callout | data(two_rows) + path + style(arrow = "end"), layered over the plot; the note itself is data(one_row) + text + label(what) |
| Rose / coxcomb / wind rose | bar * count + x(direction) + polar(), the bar chart read in a circle |
| Radar / spider plot | line * mean + x(category) + y(measure) + polar(), the profile bent into a circle; area fills it |
| Pie chart | bar * count * stack + color(category) + polar(), a stacked bar in polar coordinates; the category is a split, not a position |
| Sunburst | zone * partition(<a>, <b>, <c>) + x(<measure>) + polar(), worked example in Polar. One ring per level of a tree, each arc as wide as its share; the hierarchy is columns, outermost first, and a blank level ends that branch early, which is what gives the rim its ragged edge. y(depth, limits = c(0, n)) puts the hole in the middle, and text * partition(…) + label(name) names the nodes, reading the center the same computation published |
| Icicle / partition chart | the sunburst’s sentence with polar() taken off, which is the whole difference: depth up the y axis, share across the x. The two names are one coordinate space apart, exactly as a rose and a bar chart are, or a pie and a stacked bar |
| Treemap | bar * sum + y(value) + color(category) + nest(), the same shares carried by area rather than by angle (Nest) |
| Nested treemap | the same, plus x(group): one region per group, its rows packed inside |
| Labeled treemap | the same, plus a text layer: + text + label(name) writes each region’s name at its center, which is what a packing needs once the split is too wide for a legend to read. A name wider than the region it belongs to is left out, and the plot reports how many (Nest) |
| Pie of a measure | bar * sum * stack + y(amount) + color(category) + polar(), also proportion for shares on the axis |
| Share-of-total column | the same sentence without polar(), one segmented bar |
| Polar histogram | bar * bin + x(bearing) + polar(), bins as wedges of a measured angle |
| Circular (cyclical) line | line + x(hour) + y(measure) + polar(), both ends of the cycle in the data |
| Polar scatter | point + x(bearing) + y(measure) + polar() |
| Contour plot / density contours | path * density + x(a) + y(b), the iso-lines of a density estimated over the plane; density(levels = ) says how many, group(category) estimates one field per group |
| 2-D density heatmap | zone * density + x(a) + y(b), the same field painted as cells instead of traced as lines |
| Filled contour / filled density | zone * density(levels = 8) + x(a) + y(b), the bands between eight contours; the same levels a path traces |
| Binned heatmap | zone * bin + x(a) + y(b), cells colored by how many rows fell in each |
| Hexbin | zone * bin(tiling = "hex") + x(a) + y(b), the staggered mesh that keeps the eye from reading the lattice as data |
| Heat strip / binned distribution per group | zone * bin + x(<number>) + y(<category>), the mixed mesh: the continuous axis cut into cells, one row of them per category, every row cut on the same edges |
| Candlestick / OHLC chart | two layers over one table, worked example in Zone: interval * bounds(low, high) + color(dir) is the wick and zone * bounds(body_lo, body_hi, start = left, end = right) + color(dir) + style(opacity = 1) the body, over x(session). Four numbers a session, and no new channels for them: the pair a band mark reads is the pair a rectangle’s sides are. The opacity and the named sides both overrule a zone default that assumes a highlight: here the rectangle is the data |
| Waterfall / bridge chart | zone * bounds(base, top, start = left, end = right) + x(step) + color(dir) + style(opacity = 1), worked example in Zone. The running total is host arithmetic (cumsum), because stack accumulates within a position and a waterfall accumulates across positions; the subtotal bars that stand on the floor are rows whose base is 0, and left/right are the slot center ± a little, which is what puts air between the bars |
| Funnel chart | zone * bounds(lo, hi) + y(stage) + style(opacity = 1), worked example in Zone, with text + label(n) + x(mid) for the numbers inside. The columns lo and hi hold -n/2 and n/2, computed in the host, which is the same arithmetic the waterfall uses for its running total. A funnel is a bar chart whose blocks are centered on zero, and centering is two columns rather than a chart type. The measure lands on x because y(stage) is a category; nothing had to ask for it. The sloped connectors between stages are deliberately absent. Each one spans the gap between two categories, where the axis means nothing, so its size is read off no scale |
D.2 Asked for, and where each one stands
These names need more than one line to answer. Nearly all of them draw, and the entry gives the sentence. Where one does not, it says what is missing rather than drawing something else in its place.
| The name | Where it stands |
|---|---|
| Box plot | ✅ drawn: box + x(group) + y(value), the five-number summary per group |
| Violin plot | ✅ drawn: ribbon * density + x(group) + y(value), the shape the box plot’s five numbers summarize. density(compare = ) says whether the widths carry each group’s row count (the default) or draw every violin to the same area |
| Half violin | ✅ drawn: area * density + x(group) + y(value): the same estimate closed on the slot’s center line instead of on its own reflection, which is the difference between those two marks everywhere else |
| Ridgeline plot / joyplot | ✅ drawn: area * density(reach = 2.5) + x(value) + y(group), the half violin with its axes exchanged, one density per category, each on its own line, sharing one measure axis. reach is in slots, so past 0.5 the ridges run into the row above, which is the joyplot’s look. Its black edge is a layer, line * density reading the same slot reading and tracing what the fill fills |
| Jittered strip plot | ✅ drawn: point * jitter + x(group) + y(value), the jitter collision modifier spreads coincident points along the category axis |
| Stacked bars / areas | ✅ drawn: bar * sum * stack / area * sum * stack with a color split, the stack collision modifier (grouped bars are its sibling dodge) |
| 100% stacked bar / filled area | ✅ drawn: bar * count * stack(share = TRUE) + x(<a>) + color(<b>), every pile divided by its own slot’s total so all of them reach 1 and only the composition is left. Any measurement fills the same way, sum included |
| Streamgraph | ✅ drawn: area * stack(baseline = "wiggle") + x(<t>) + y(<v>) + color(<g>), worked through in Transforms. The foot of each pile is chosen to make the bands as flat as it can, so a band’s shape is its own rather than the sum of everything below it. share and baseline are the pile’s two free choices and compose |
| ThemeRiver | ✅ drawn: area * stack(baseline = "center"), the same sentence with the symmetric layout instead (Havre et al., 2000). Often confused with the streamgraph and not the same plot: this one centers every pile, that one minimizes movement, and readers compare values more accurately on the streamgraph (Byron & Wattenberg, 2008) |
| Relative-frequency histogram | ✅ drawn: bar * bin * proportion + x(<number>), the histogram’s own counts read as fractions of n, on the mesh bar * bin cuts |
| Heatmap | ✅ drawn on either kind of axis, and on one of each. Which transform you want depends on which: a continuous axis needs cutting and a categorical one arrives cut, so zone * bin counts the rows per cell wherever there is something to cut (both axes for the classic heatmap, one for the mixed mesh), zone * density estimates a value at each cell of a plane, and on two categorical axes zone * count tallies into the slots while zone + color(<value>) draws a table you already have |
| Summary heatmap / matrix of means | ✅ drawn: zone * mean + x(<a>) + y(<b>) + color(<column>) reduces the named column within every cell two categories cross, and sum/median/max/min read the same mesh. The column is named on color because that is the channel a zone measures with, and a summary reduces in place, the same binding that both names and receives it on bar * mean + x + y |
| Tile plot / calendar heatmap | ✅ drawn: zone + x(<a>) + y(<b>) + color(<value>), one cell per pair of categories, filling the slot each one owns |
| Confusion matrix | ✅ drawn: zone * count + x(actual) + y(predicted) tallies the raw rows; zone * proportion reads them as shares of the whole. Normalizing per true class (a share within each row of the grid) is still not drawn: stack(share = ) answered the 100% stacked bar’s half of the margin question, but a grid has no pile to fill |
| Correlation matrix / corrplot | ✅ drawn as a tile plot, once the correlations are a table: compute cor() where your data lives, reshape it long, and zone + x(var1) + y(var2) + color(r) draws it. gog computes no correlations; that is the host’s job, as x(sin(t)) is |
| Contour plot | ✅ drawn both ways: path * density traces the iso-lines, zone * density(levels = ) fills the bands between them. A crater (a ring-shaped cluster with a hollow middle) is the one field the filled reading draws wrongly, since a band round a hollow is a shape with a hole and gog has no mark for one, so the traced form shows it correctly |
| Donut chart | ✅ drawn: zone * partition(<category>) + x(<measure>) + y(depth, limits = c(0, 2)) + polar(), worked example in Polar. A donut is a sunburst of a tree one level deep, and the hole is not a round thing at all; it is a stretch of the radial axis with nothing standing on it, which is what the stated limits leave empty. The plain pie (bar * count * stack) is the one reading that cannot say it, having spent its measurement on the angle and left its radius a constant with no domain to widen; that is a fact about the reading rather than a gap, so there is no polar(hole = ) and no plan for one |
| Radar / spider plot | ✅ drawn: line * mean + x(category) + y(measure) + polar(), filled with area; the path closes because a categorical angle wraps |
| Choropleth / geographic map | half drawn, and the half that is missing is not the space. map() projects the earth and takes any mark that draws on a plane, so a quantity at a place is point + size(<value>) + map() (Map). The shaded region is what is owed: a choropleth needs a filled area whose boundary comes from the data, and none of the four ways zone finds its sides is a coastline |
| Animated (time-lapse) chart | ✅ drawn: any sentence + play(year): faceting read in time |
| Marginal / joint plot (scatter with histograms in its margins) | ✅ drawn: top / (main | right): three ordinary plots and the two composition operators. No chart type, no ggMarginal(): the marginals align and share their scale because both name the same column on the same axis, which is the one rule composition adds (Composition). Any mark can be the marginal: a histogram, a box, a violin, a dot plot |
| 3-D surface | ✅ drawn: surface + x + y + z over a grid table, or surface * density + space() to raise an estimated field. A scatter cannot be one, and is refused toward point rather than drawn as an empty sheet: a surface’s faces span the gaps between its samples, so it needs samples on a grid |
| Terraced / stepped surface (one value per cell) | ✅ drawn: surface * bin * mean + x(a) + y(b) + z(v) cuts the floor into cells and lays a flat plateau on each, with a riser at every step. The sentence for a design that measures one number per condition, where an interpolated sheet would assert a gradient nobody observed (Surface) |
If the name you came with is in neither table, it is usually a styling of one that is; ask what question the chart answers, and enter through the cookbook. If you came from ggplot2 and the name was one of its function names, Coverage is the same territory indexed that way, and says which of its features are refused, which are owed, and which nobody has decided about yet.