matplotlib-tufte
styles figures to make them easier on the eyes, and keep only essential ink.
Based on : Tufte, Edward. The Visual Display of Quantitative Information. 2nd ed., Graphics Press, 2007.
python -m pip install "matplotlib_tufte @ git+https://github.com/ninivert/matplotlib_tufte.git"
setup
tries to load the New Computer Modern font (comes packaged with matplotlib_tufte
) and the tufte.mplstyle
stylesheet.
Changes made by the custom style :
- better readability using a serif font
- New Computer Modern (used to be : Latin Modern Roman) font
- applied to all text (ticks, labels, legends, titles, etc.)
- in math mode, gives LaTeX-like math results, without needing a LaTeX installation
- smaller figures (increase data density)
- figsize is defaulted to (4, 3)
- tight layout applied automatically
- thinner axes to put emphasis on data ink
- remove chartjunk and non-data ink
- remove top and right spines
- remove legend fancybox and frame
- insert black as the first color of the cycler
Documentation for breathe
Add some space between the axes and the spines Parameters ---------- ax : matplotlib.axes.Axes which : str or AxisWhich, optional which axis to apply the spacing on, by default AxisWhich.BOTH pad_frac_start : float, optional space to add to the start of the axis, as a fraction of the data span, by default 0.04 pad_frac_end : float, optional space to add to the end of the axis, as a fraction of the data span, by default 0.04
Documentation for data_lim
Sets the axis to use the limits of the data Parameters ---------- ax : matplotlib.axes.Axes which : str or AxisWhich, optional which axis to apply the spacing on, by default AxisWhich.BOTH
In other cases, you might just need to get your hands dirty, using methods such as
ax.spines.bottom.set_visible(False)
to disable the bottom lineax.set_xticks(...)
andax.set_xticklabels(...)
to customize ticksax.set_xlim(...)
andax.set_xticklabels(...)
to have nice integer limits. Use this in combination withbreathe
if you were used to matplotlib adding padding to the limits
from matplotlib_tufte import * setup() import matplotlib.pyplot as plt import matplotlib as mpl import numpy as np x = np.linspace(1, 9, 100) y1 = np.exp(-(x-2)) - np.sin(x-1) + 2 y2 = 5 * 2/x * np.exp(-(np.log(x-0.99)-0.5)**2) plt.close('all') fig, ax = plt.subplots() ax.set_xlabel('$x$') ax.set_ylabel('$y$') ax.plot(x, y1, label='data 1') ax.plot(x, y2, label='data 2') ax.legend() plt.show() fig.savefig('fig1.png')
Letting the axis breathe
breathe(ax)
Setting data limits
data_lim(ax)
Getting your hands dirty
np.random.seed(0) N = 50 sample1 = np.random.lognormal(0.9, 0.5, N) sample2 = np.random.lognormal(0.2, 0.7, N) means = [np.mean(sample1), np.mean(sample2)] stds = [np.std(sample1) * np.sqrt(N/(N-1)), np.std(sample2) * np.sqrt(N/(N-1))] # sample stdev x = [0, 1] fig, ax = plt.subplots(figsize=(2, 3)) for x_, sample, m, s in zip(x, (sample1, sample2), means, stds): ax.plot([x_]*len(sample), sample, linestyle='', marker='_', color='k') ax.hlines(np.mean(sample), x_-0.3, x_+0.3, linewidth=0.5, color='k') ax.add_patch(mpl.patches.FancyArrowPatch( arrowstyle='|-|,widthA=2.0,widthB=2.0', linewidth=0.5, path=mpl.path.Path([(x_-0.3, m-s), (x_-0.3, m+s)])) ) ax.set_xticks(x) ax.set_xticklabels(['S1', 'S2']) ax.set_xlim((-1, 2)) ax.set_ylim((0, 8)) ax.set_ylabel('measurement') breathe(ax, 'y') ax.spines.bottom.set_visible(False) plt.show()
It is also possible to use matplotlib_tufte
without using setup
# demo/demo_nosetup.py from matplotlib_tufte import * import matplotlib.pyplot as plt # by default, will use gca # this is however discouraged plt.plot([0,1,2],[-2,4,5]) despine() data_lim() breathe() plt.savefig('fig_nosetup.png')
v0.2: changed default font from "Latin Modern Roman" to "New Computer Modern", which is more readable at smaller sizes.
- Range frames
- Data ticks
- Axis histograms