Produce figures that meet journal submission standards: correct DPI, font sizes,
line widths, colorblind-friendly palettes, and vector/raster export formats.
Journal Figure Standards
Journal Family
Single Column
Double Column
DPI
Preferred Format
Nature / Science
89 mm
183 mm
300
TIFF / EPS
Cell Press
85 mm
170 mm
300
PDF / TIFF
PLOS ONE
83 mm
171 mm
300
TIFF / EPS
ACS Journals
84 mm
176 mm
600
TIFF
General use
3.5 in
7 in
300
PDF / SVG
Setup
pip install matplotlib seaborn numpy pandas scipy palettable
# For LaTeX rendering in labels (optional but recommended)# Requires a local LaTeX installation (e.g. TeX Live or MiKTeX)
"""
Configure matplotlib/seaborn for publication-quality output.
Parameters
----------
font_size : int
Base font size in pt. Typical range: 6–10 pt for journals.
font_family : str
Font family ('sans-serif', 'serif').
use_latex : bool
Render text with LaTeX (requires a local LaTeX install).
style : str
Seaborn style: 'whitegrid', 'ticks', 'white', 'dark'.
context : str
Seaborn context: 'paper', 'notebook', 'talk', 'poster'.
color_palette : list, optional
List of hex colors. Defaults to Okabe-Ito.
line_width : float
Default line width for axes and lines.
tick_major_size : float
Major tick length in pt.
axes_spines_right : bool
Whether to show right spine.
axes_spines_top : bool
Whether to show top spine.
"""
if
is
None
1.0
# Font
"font.size"
"axes.titlesize"
"axes.labelsize"
"xtick.labelsize"
1
"ytick.labelsize"
1
"legend.fontsize"
1
"legend.title_fontsize"
# Lines
"lines.linewidth"
1.5
"axes.linewidth"
"patch.linewidth"
# Ticks
"xtick.major.size"
"ytick.major.size"
"xtick.minor.size"
0.6
"ytick.minor.size"
0.6
"xtick.major.width"
"ytick.major.width"
# Spines
"axes.spines.right"
"axes.spines.top"
# Saving
"savefig.dpi"
300
"savefig.bbox"
"tight"
"savefig.pad_inches"
0.02
# Legend
"legend.frameon"
False
"legend.handlelength"
1.5
# LaTeX
"text.usetex"
def
mm_to_inches
mm: float
float
"""Convert millimetres to inches for figure sizing."""
"""
Save figure to one or more formats.
Parameters
----------
fig : matplotlib Figure
path : str
Base path without extension (e.g. 'figures/fig1').
formats : list, optional
Defaults to ['pdf', 'tiff']. Options: 'pdf', 'svg', 'tiff', 'png', 'eps'.
dpi : int
Resolution for raster formats.
"""
if
is
None
"pdf"
"tiff"
for
in
f"{path}.{fmt}"
format
print
f"Saved: {full_path}"
Figure 1 — Scatter Plot with Regression Line
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
from pub_style import setup_publication_style, mm_to_inches, save_figure, OKABE_ITO
setup_publication_style(font_size=8)
rng = np.random.default_rng(42)
n = 80
x = rng.normal(5, 1.5, n)
y = 2.3 * x + rng.normal(0, 2, n)
group = rng.choice(["A", "B"], size=n)
df = pd.DataFrame({"x": x, "y": y, "group": group})
fig, ax = plt.subplots(figsize=(mm_to_inches(89), mm_to_inches(70)))
colors = {"A": OKABE_ITO[0], "B": OKABE_ITO[1]}
for grp, gdf in df.groupby("group"):
ax.scatter(gdf["x"], gdf["y"], color=colors[grp], s=18, alpha=0.75,
linewidths=0.3, edgecolors="white", label=grp, zorder=3)
# Per-group regression line
slope, intercept, r, p, se = stats.linregress(gdf["x"], gdf["y"])
x_line = np.linspace(gdf["x"].min(), gdf["x"].max(), 100)
ax.plot(x_line, slope * x_line + intercept, color=colors[grp],
linewidth=1.2, linestyle="--")
ax.annotate(f"r={r:.2f}", xy=(x_line[-1], slope * x_line[-1] + intercept),
fontsize=6, color=colors[grp], ha="left")
ax.set_xlabel("Predictor variable (units)")
ax.set_ylabel("Outcome variable (units)")
ax.legend(title="Group", loc="upper left", markerscale=1.2)
ax.set_title("Figure 1: Group-stratified regression")
fig.tight_layout()
save_figure(fig, "figure1_scatter", formats=["pdf", "tiff"])
plt.show()