Boilerplate Code for Publication-quality Scientific Plotting
Draft – work in progress.
- Control figure dimensions, with sensible defaults (e.g. specify either height or weight and derive the other using the golden ratio)
- Command-line options for different contexts — for embedding in papers, on the web (e.g. in blog posts), in presentation slides or on academic posters.
- Further ability to fine-tune these defaults by modifying the DPI.
- Control other stylistic choices, color palettes and aesthetics (borders, background colors, grids etc.)
- Simultaneously save as more than one format (
pdf
orpgf
for LaTeX,svg
for web andpng
for general purpose) - Background transparency.
fig, ax = plt.subplots()
# plotting code goes here...
plt.tight_layout()
for ext in extension:
fig.savefig(output_path.joinpath(f"prior_{context}_{suffix}.{ext}"),
dpi=dpi, transparent=transparent)
plt.show()
Styles
$ python plot_example.py gallery -w 5 --dpi 200 -e png -e pdf --style=darkgrid
$ python plot_example.py gallery -w 5 --dpi 200 -e png -e pdf --style=whitegrid
$ python plot_example.py gallery -w 5 --dpi 200 -e png -e pdf --style=white
$ python plot_example.py gallery -w 5 --dpi 200 -e png -e pdf --style=ticks
Contexts
$ python plot_example.py gallery -w 5 --dpi 200 -e png -e pdf --context=paper
$ python plot_example.py gallery -w 5 --dpi 200 -e png -e pdf --context=notebook
$ python plot_example.py gallery -w 5 --dpi 200 -e png -e pdf --context=talk
$ python plot_example.py gallery -w 5 --dpi 200 -e png -e pdf --context=poster
Full Example
import sys
import click
import numpy as np
import tensorflow_probability as tfp
import matplotlib.pyplot as plt
import seaborn as sns
from pathlib import Path
from utils import GOLDEN_RATIO, WIDTH, pt_to_in
@click.command()
@click.argument("name")
@click.argument("output_dir", default="figures/",
type=click.Path(file_okay=False, dir_okay=True))
@click.option('--transparent', is_flag=True)
@click.option('--context', default="paper")
@click.option('--style', default="ticks")
@click.option('--palette', default="muted")
@click.option('--width', '-w', type=float, default=pt_to_in(WIDTH))
@click.option('--height', '-h', type=float)
@click.option('--aspect', '-a', type=float, default=GOLDEN_RATIO)
@click.option('--dpi', type=float, default=300)
@click.option('--extension', '-e', multiple=True, default=["png"])
def main(name, output_dir, transparent, context, style, palette, width, height,
aspect, dpi, extension):
# preamble
if height is None:
height = width / aspect
# height *= num_iterations
# figsize = size(width, aspect)
figsize = (width, height)
suffix = f"{width*dpi:.0f}x{height*dpi:.0f}"
rc = {
"figure.figsize": figsize,
"font.serif": ["Times New Roman"],
"text.usetex": True,
}
sns.set(context=context, style=style, palette=palette, font="serif", rc=rc)
output_path = Path(output_dir).joinpath(name)
output_path.mkdir(parents=True, exist_ok=True)
# / preamble
# shortcuts
tfd = tfp.distributions
kernels = tfp.math.psd_kernels
# constants
num_features = 1 # dimensionality
num_index_points = 512 # nbr of index points
num_samples = 8
x_min, x_max = -5.0, 5.0
X_grid = np.linspace(x_min, x_max, num_index_points).reshape(-1, num_features)
seed = 23 # set random seed for reproducibility
kernel = kernels.ExponentiatedQuadratic()
gp = tfd.GaussianProcess(kernel=kernel, index_points=X_grid)
samples = gp.sample(num_samples, seed=seed)
fig, ax = plt.subplots()
ax.plot(X_grid, samples.numpy().T)
ax.set_xlabel(r'$x$')
ax.set_ylabel(r'$f(x)$')
ax.set_title(r'Draws of $f(x)$ from GP prior')
plt.tight_layout()
for ext in extension:
fig.savefig(output_path.joinpath(f"prior_{context}_{suffix}.{ext}"),
dpi=dpi, transparent=transparent)
plt.show()
return 0
if __name__ == "__main__":
sys.exit(main()) # pragma: no cover
Helper utilities
import numpy as np
GOLDEN_RATIO = 0.5 * (1 + np.sqrt(5))
WIDTH = 397.48499
def pt_to_in(x):
pt_per_in = 72.27
return x / pt_per_in
def size(width, aspect=GOLDEN_RATIO):
width_in = pt_to_in(width)
return (width_in, width_in / aspect)