Figure guide

2026-03-03 → 2026-08-29

The “spine” of a scientific paper is often a sequence of figures. Many scientists “read” a paper by (1) skimming the title and abstract, then (2) looking at the figures and captions. If the figures convey the main results clearly, readers will have a much better idea about the paper quickly and are more likely to read & remember the paper.

Consider a figure not as a plot, but as a message; think of 4–5 key messages rather than individual plots. Each figure communicates a message, usually supported by multiple panels.

Making a good figure involves applying a hierarchy of principles:

  1. Visible and readable: Can the reader actually see and read the figure?
  2. Effective and informative: Does the figure convey the right message clearly and efficiently?
  3. Engaging and beautiful: Does it draw the reader in, reward their attention, and make the result a pleasure to look at?

Most figure problems live at level 1. Get that mechanical part right first, then focus on 2. But don’t forget that the last point also makes a big difference.

1. Visible and readable—the basic mechanics#

Design at the final (or reasonable) size#

When working on a computer, you don’t often realize that figures have *physical dimensions. On a screen you can zoom freely, but on paper, size is fixed.

Know your target size. These are examples, not universal journal dimensions:

Context Width Basis
Nature, single column 89 mm (3.5”) Nature figure guide
Nature, double column 183 mm (7.2”) Nature figure guide
US Letter content area with 1” margins 6.5” × 9” Derived layout size; not a journal standard

If you create a figure at matplotlib’s default size (6.4” wide) and it gets shrunk to fit a 3.5” column, text shrinks by roughly half—10 pt becomes ~5 pt and illegible. Often your figure is just a small panel within a larger figure, so the shrinkage can be even worse. So, you end up with a figure panel that’s impossible to read.

The fix: set the correct figure size from the start.

Python (matplotlib)#

Matplotlib interprets a two-value figsize tuple as (width, height) in inches.

# Wrong: create large, then shrink
fig, ax = plt.subplots(figsize=(14, 8))   # 14 × 8 inches—way too big
ax.set_xlabel('Time (s)', fontsize=7)     # fontsize is meaningless at this scale

# Right: create at target size
fig, ax = plt.subplots(figsize=(3.5, 2.5)) # 3.5 × 2.5 inches; single-column width
ax.set_xlabel('Time (s)', fontsize=7)      # 7 pt on paper
plt.savefig('plot.pdf')                    # vector format preferred

You can set defaults so every figure starts at the right size:

plt.rcParams.update({
    'figure.figsize': (3.5, 2.5),  # inches
    'font.size': 10,
    'axes.labelsize': 10,
    'axes.titlesize': 10,
    'xtick.labelsize': 8,
    'ytick.labelsize': 8,
})

R (ggplot2)#

ggsave("plot.pdf", p, width = 89, height = 60, units = "mm")

R’s ggsave supports units directly ("in", "cm", "mm", "px"). Default is 7 × 7 inches—quite large.

Design tools#

Use the same principle in Illustrator, PowerPoint, and similar tools: set the correct physical size first. Don’t design at arbitrary pixel dimensions and scale later.

Preserve the aspect ratio#

The aspect ratio is the ratio of width to height. When resizing an existing image, preserve its original aspect ratio. Otherwise, scaling its width and height by different amounts will stretch or compress the image. Lock the aspect ratio and set one dimension; let the other follow. For example, a 1200 × 800 image has a 3:2 aspect ratio, so resizing it to 600 pixels wide should make it 400 pixels high.

If the target frame has a different aspect ratio, crop the image intentionally or redesign the layout. Do not force the image into both dimensions.

For a bitmap, preserving the aspect ratio prevents distortion but does not prevent quality loss. Changing its pixel dimensions resamples the image; enlarging it cannot restore detail that is not in the original. Keep the original and resize a copy for the final output.

Choose the format: vector almost always, raster only when necessary#

If your scientific plots are in bitmaps, something has likely gone wrong. You should feel a visceral, physical pain when you see a pixelated plot in a paper—jagged edges on lines, fuzzy text on axis labels. You should develop the taste to feel this pain. It means the figure was created, saved, or converted in a way that destroyed information.

Vector graphics (PDF, SVG, EPS) store shapes, lines, and text as instructions (e.g., ‘draw a line from (x1,y1) to (x2,y2)’). They scale to any size with no loss of quality. This is what we want for plots, diagrams, and anything with text or lines.

Bitmap (raster) graphics (PNG, JPG, TIFF) store a grid of pixels. They have a fixed resolution. Zoom in and you see squares; shrink them and you discard detail. Some raster formats add another source of loss: JPG usually uses lossy compression, discarding image information to reduce file size. That loss comes on top of any detail discarded during resizing and is especially visible around sharp lines and text. Raster graphics are appropriate for photographs, microscopy images, and heatmaps of large matrices—cases where the data is inherently pixel-based.

The rule is simple: never turn a vector-native figure (most plots) into a bitmap. If your figure was generated from data or drawn from geometric primitives (e.g., lines, circles, etc.), save it as a vector. Use raster only when the data is inherently raster, such as photographs or microscopy images—and even then, keep axes, labels, and annotations as vectors. If a vector file is too large because of millions of scatter points or a dense heatmap, rasterize just that layer and keep everything else as vectors. Reaching for PNG or JPG should feel like a last resort, not a default.

A PDF can still contain a bitmap#

A PDF is a container whose pages can include text, vector paths, bitmap images, or a mixture. The .pdf extension therefore does not prove that the figure inside is vector.

Common scenario: You screenshot a plot or save it as PNG or JPG, place that bitmap in PowerPoint, Illustrator, Word, or LaTeX, and then export the document as PDF. The outer file is now a PDF, but the plot inside remains a bitmap. Converting PDF → PNG → PDF causes the same problem.

Fix: Return to the plotting code and export the plot directly as PDF or SVG. Assemble multipanel figures from those vector files without an intermediate raster conversion. If a layer is inherently raster, keep only that layer raster at an appropriate resolution and overlay axes, text, and annotations as vectors. Resaving an existing bitmap as PDF cannot reconstruct the original vector information.

Save vector output#

plt.savefig('plot.pdf')   # vector—always preferred
ggsave("plot.pdf", p, width = 89, height = 60, units = "mm")

When you have to use raster#

Sometimes you have no choice—photographs, microscopy, screenshots.

Resolution describes how densely a bitmap’s pixels are packed at its final physical size. Technically, image resolution is measured in pixels per inch (PPI), although publication guidelines often call it DPI. Two images can therefore have the same physical dimensions but very different resolution. At 3.5 inches wide, a 350-pixel image is 100 PPI, while a 1,050-pixel image is 300 PPI. The latter packs three times as many pixels into the same width. Simply changing the resolution setting or enlarging the image does not create missing detail.

pixels = inches × PPI

In practice:

plt.savefig('plot.png', dpi=300)   # 300 DPI minimum
ggsave("plot.png", p, width = 89, height = 60, units = "mm", dpi = 300)

Common DPI values:

Common traps#

2. Effective and informative#

(in progress)

3. Engaging and beautiful#

(in progress)

Checklist#

Resources#

Receive my updates

YY's Random Walks — Science, academia, and occasional rabbit holes.

YY's Bike Shed — Sustainable mobility, urbanism, and the details that matter.

×