Last modified: 2026-11-13 12:24:54 (PST)
Talks, posters, and figures carry the same research as a paper, but the reader sees them for less time and cannot reread them. This chapter gives practical rules for each.
When preparing slides for a lab meeting, conference, qualifying exam, or thesis defense, follow these guidelines:
A figure often shows a pattern, an outlier or a comparison faster and more reliably than a sentence or a table can describe it. So when a result can be drawn, draw it, and use the text to point the reader to what the figure shows.
Example 1 (Four datasets with the same summaries) Anscombe (1973) constructed four small datasets of \((x, y)\) pairs (Wikipedia: Anscombe’s quartet). All four have the same mean of \(x\) (9), the same mean of \(y\) (7.5), the same correlation (0.82 to two decimal places) and the same least-squares line, with slope 0.5. A table of these summaries cannot tell the datasets apart. A scatterplot of each can: one is roughly linear with scatter, one follows a smooth curve, one is an exact line with a single outlier, and one has all but one of its points at the same \(x\).
Include a figure wherever one helps a reader see a definition, an example, or a result. Draw a graph for a relationship between quantities, a diagram for a structure or a process, and a plot of the data for a fitted model or an estimate. Add figures to new work as you write it, and add them to existing chapters that lack them. On a website or slide deck, draw a graph as an interactive plotly figure where feasible, and as a 3D plotly figure where a third dimension shows the idea, such as a surface over two parameters. In static formats, such as PDF, Word, and PowerPoint, still render every 3D plot and other interactive figure, as a noninteractive version of it (Ezra, 2026-11-10). A note that sends readers to the HTML version does not replace the figure.
When a definition or example graphs a model whose parameters are chosen rather than fitted to data, such as a density with a given mean and covariance, make the graph interactive in HTML output, slide decks included (Ezra, 2026-11-10). Give each parameter a slider, a numeric entry box, or both, and redraw the graph as the reader changes them. Start each control at the value the text uses, so the prose about that value describes the graph as it first appears, and in static formats draw the graph at those starting values. A graph of a model fitted to data stays fixed, because its parameters come from the data. In Quarto, build the graph with Observable JS cells, which run in both the website and the slides:
Inputs.range, and a numeric box alone with Inputs.number..content-visible when-format="html" div.Give every figure alt text, a numbered caption, and a code chunk that comments what the code does and why.
Open each caption with a sentence that says what the figure or table shows, so a reader who sees only the figure or table learns its point without following a link. A cross-reference to the example or section that the figure or table illustrates may support that sentence, either after the sentence or in parentheses within the sentence, but the cross-reference never replaces the sentence. Two captions for the same figure show the difference:
@exm-x.” The reader learns nothing until following the link.@exm-x).” The reader learns the point at once.A caption does not rely on another figure’s caption (Ezra, 2026-11-03): do not describe a figure as “the surface of @fig-x” or “the same paths as @fig-x”. State what this figure shows, as if it were the only figure on the page. A figure that adds a view of another, such as a surface for a contour plot, gets a caption that names its own quantities and its own point.
Lead with a well-labeled figure or table instead of a long paragraph that describes a pipeline or compares groups. Use a diagram for a conceptual model, such as a directed acyclic graph (DAG), and for a process, such as a participant flow (CONSORT) diagram or a workflow schematic. Let the text interpret what the figure shows instead of listing every number the reader can already see in it. Give the key estimates in the text and point to the figure for the rest.
A clear figure lets a reader check a result without help from the text. Before you share a figure:
transform_pseudo_log() function of the R package scales maps a value \(x\) to \(\operatorname{asinh}\paren{x / (2 \sigma)}\) divided by the natural logarithm of its base, and the argument \(\sigma\) (sigma) sets the width of the linear part around zero. Pass it to a ggplot2 scale, as in scale_y_continuous(transform = scales::transform_pseudo_log(sigma = 0.1)). Say in the axis title or the caption that the axis is pseudo-log.#fig- div with a caption, and set alt text that reads out the structure with fig-alt. The Morrison-Lab/gha check check-tree-lists flags a pull request that adds a definition, example, or other labeled div that mentions a tree, holds a list nested three or more levels deep, and has no figure; a <!-- no-tree-figure: reason --> comment inside the div exempts it.For more:
A figure or table with several panels can be put together in two places:
#fig- div nested in the parent figure’s div, and a subtable is a #tbl- div nested in the parent table’s div. The parent div arranges the panels with layout-ncol, layout-nrow, or layout.subplot(), matplotlib’s subplots(), or one table with grouped columns.Use Quarto layout by default. Quarto gives each panel its own label, subcaption, and alt text, and those keep working in every output format:
fig-alt describes that panel alone, so a screen reader user hears the panels one at a time.Combine the panels in code instead when they share something that Quarto layout cannot give them:
guides = "collect", saves repeating it.subplot(shareX = TRUE).A panel combined in code has no label of its own for a cross-reference. Tag the panels in the code, in the same style as Quarto’s subcaptions, with plot_annotation(tag_levels = "a", tag_prefix = "(", tag_suffix = ")") in patchwork, and refer to them in the text as “panel (b) of @fig-x”. Write one alt text for the whole figure that describes each panel in turn, and give the panels in the caption by their tags. A combined interactive figure, such as one plotly widget with two 3D scenes, needs a static version that shows every panel too.
Keep panels in separate subfigures when they share nothing. Two 3D surfaces from two models, each with its own rotation, are two subfigures, not one widget with two scenes.
Put a language tabset (group="language") inside each subfigure, not the subfigures inside one tabset, when each panel has R and Python versions:
::: {#fig-surfaces layout-ncol=2}
::: {#fig-surfaces-additive}
::: {.panel-tabset group="language"}
## R
```r
# draw the additive model's surface
```
## Python
```python
# draw the additive model's surface
```
:::
The additive model.
:::
::: {#fig-surfaces-interaction}
<!-- the same tabset for the interaction model -->
The model with an interaction term.
:::
Fitted surfaces of the two models for fuel economy over weight and model year.
:::Each r or python block stands for an executed chunk, whose fence is {r} or {python} in a real page. Quarto then labels the panels (a) and (b) whichever tab the reader picks. With the subfigures inside the tabset, the Python tab’s panels become (c) and (d), and the R panels’ cross-references point at a tab the reader may not be on.
The same choice holds for tables:
#tbl- div with layout-ncol, each with its own subcaption.tab_spanner() in the R package gt, so the rows line up and each row label appears once.The R package patchwork combines several ggplot2 figures into one figure with panel labels. The + operator places plots side by side, and the / operator stacks them. In this demo, p1, p2, and p3 stand for three ggplot2 plots that you have already made:
The expression puts p1 and p2 side by side above p3:
plot_layout(guides = "collect") merges the plots’ legends into one.plot_annotation(tag_levels = "a", tag_prefix = "(", tag_suffix = ")") labels the panels (a), (b), and (c), the style of Quarto’s subcaptions.& applies the bottom legend position to every panel.A function of two variables, such as a loss function or a residual sum of squares as a function of a regression’s intercept and slope, is a surface. A contour plot shows where the function takes given values and is easy to read values from. A three-dimensional surface plot shows the same function as a landscape, so valleys, ridges and minima are visible at once. Show both when the shape of the surface matters.
Use a 3D plot for a surface, with the function’s value on the vertical axis, and pair it with a contour plot of the same function.
Pair the other way too: every contour plot or level-curve plot of a function of two variables gets a corresponding 3D surface plot of the same function (Ezra, 2026-11-03), with the same colors and marked points.
Put a contour plot and its 3D surface plot in one figure, as two subfigures (Ezra, 2026-11-12: “why didn’t you pair the 2d and 3d figures as subfigures? that should be a psw principle by now”), so one caption describes both views, and a cross-reference such as “Figure 3b” names one of them (see Combine panels with Quarto layout or in code). In HTML the 3D subfigure is the interactive plot; in PDF, Word, and PowerPoint it is the static view at a fixed angle.
Do not use 3D for data that are not a surface, such as bars or pie charts: perspective makes lengths and areas harder to compare.
Choose the viewing angle so that the feature you discuss, such as the minimum, is not hidden, and label all three axes.
Give the plot a cube-shaped box, with every axis the same length on screen (Ezra, 2026-11-10). When all three axes are in the same units, such as a surface in physical space, scale the axes equally in data units instead. In plotly, set the scene’s aspectmode to "cube" or to "data". Do not leave it unset: plotly’s default, "auto", then draws each axis in proportion to the span of its data unless one axis range is more than 4 times another, so a function whose values span a wider range than its inputs draws a tall, thin column.
An interactive plot that readers can rotate, such as one made with plotly, helps on the web, but static formats such as PDF, Word, and PowerPoint show no interactive plot, so render a static view at a fixed angle there (see Scientific figures and Make figures accessible).
In a Python tab, draw the interactive 3D plot with plotly too, so the R and Python tabs of a figure look alike. Keep the matplotlib version for the static formats, where it is the static view at a fixed angle, and run it only in those outputs, the way the R tab runs its plotly code only in HTML output. In a knitr chunk, print the figure as one raw HTML block under results: asis, because fig.show() needs ipython and several print calls split the chunk’s output:
End each call that returns the figure, such as add_trace and update_layout, with ; so that reticulate does not print the figure after every call, and add plotly to the page’s reticulate::py_require list.
Give a 3D plot alt text that states what the surface looks like, such as “a bowl with its lowest point at …”.
Source
Several of the resource links in the slide deck and figure sections began in the Benjamin-Chung Lab Manual by Jade Benjamin-Chung. The section on three-dimensional graphs is original to this guide.
For further reading, see the Professional Development and Resources chapters of the UCD-SERG lab manual.