"""
Some useful functions for creating
publishable jupyter notebooks
usage:
.. code-block:: python
from ipypublish import nb_setup
plt = nb_setup.setup_matplotlib(
print_errors=True,
output=('pdf',))
pd = nb_setup.setup_pandas(escape_latex=True)
sym = nb_setup.setup_sympy()
import numpy as np
from IPython.display import Image, Latex
"""
# Py2/Py3 compatibility
# =====================
# from __future__ import division as _division
# from __future__ import print_function as _print_function
import json
from io import BytesIO
# from IPython.display import Image, Latex
MPL_OPTIONS = (
("lines.linewidth", 1.5),
("lines.markeredgewidth", 1.0),
("lines.markersize", 8),
("lines.antialiased", True),
("lines.dashed_pattern", (3.7, 1.6)),
("lines.dashdot_pattern", (6.4, 1.6, 1, 1.6)),
("lines.dotted_pattern", [1, 1.65]),
("lines.scale_dashes", True),
("patch.linewidth", 1.0),
("patch.force_edgecolor", False),
("patch.antialiased", True),
("hatch.linewidth", 1.0),
("hist.bins", 10),
("boxplot.notch", False),
("boxplot.vertical", True),
("boxplot.whiskers", 1.5),
("boxplot.patchartist", False),
("boxplot.showmeans", False),
("boxplot.showcaps", True),
("boxplot.showbox", True),
("boxplot.showfliers", True),
("boxplot.meanline", False),
("boxplot.flierprops.markersize", 6),
("boxplot.flierprops.linewidth", 1.0),
("boxplot.boxprops.linewidth", 1.0),
("boxplot.whiskerprops.linewidth", 1.0),
("boxplot.capprops.linewidth", 1.0),
("boxplot.medianprops.linewidth", 1.0),
("boxplot.meanprops.markersize", 6),
("boxplot.meanprops.linewidth", 1.0),
("font.family", "sans-serif"),
("font.serif", "cm"),
("font.size", 16),
("text.usetex", True),
# ('text.latex.unicode', False),
("text.latex.preamble", ("\\usepackage{subdepth}", "\\usepackage{type1cm}")),
("text.latex.preview", False),
("text.hinting_factor", 8),
("text.antialiased", True),
("mathtext.fallback_to_cm", True),
("image.lut", 256),
("image.resample", True),
("image.composite_image", True),
("contour.corner_mask", True),
("errorbar.capsize", 0),
("axes.labelsize", 18),
("axes.linewidth", 2.0),
("axes.spines.left", True),
("axes.spines.right", True),
("axes.spines.bottom", True),
("axes.spines.top", True),
("axes.titlesize", 20),
("axes.titlepad", 6.0),
("axes.grid", False),
("axes.labelpad", 4.0),
("axes.formatter.limits", (-7, 7)),
("axes.formatter.use_locale", False),
("axes.formatter.use_mathtext", False),
("axes.formatter.min_exponent", 0),
("axes.formatter.useoffset", True),
("axes.formatter.offset_threshold", 4),
("axes.unicode_minus", True),
("axes.xmargin", 0.05),
("axes.ymargin", 0.05),
("polaraxes.grid", True),
("axes3d.grid", True),
("legend.fontsize", 14),
("legend.fancybox", True),
("legend.numpoints", 1),
("legend.scatterpoints", 1),
("legend.markerscale", 1.0),
("legend.shadow", False),
("legend.frameon", True),
("legend.framealpha", 0.8),
("legend.borderpad", 0.4),
("legend.labelspacing", 0.5),
("legend.handlelength", 2.0),
("legend.handleheight", 0.7),
("legend.handletextpad", 0.8),
("legend.borderaxespad", 0.5),
("legend.columnspacing", 2.0),
("xtick.top", False),
("xtick.bottom", True),
("xtick.labeltop", False),
("xtick.labelbottom", True),
("xtick.major.size", 3.5),
("xtick.minor.size", 2),
("xtick.major.width", 0.8),
("xtick.minor.width", 0.6),
("xtick.major.pad", 3.5),
("xtick.minor.pad", 3.4),
("xtick.minor.visible", False),
("xtick.minor.top", True),
("xtick.minor.bottom", True),
("xtick.major.top", True),
("xtick.major.bottom", True),
("ytick.left", True),
("ytick.right", False),
("ytick.labelleft", True),
("ytick.labelright", False),
("ytick.major.size", 3.5),
("ytick.minor.size", 2),
("ytick.major.width", 0.8),
("ytick.minor.width", 0.6),
("ytick.major.pad", 3.5),
("ytick.minor.pad", 3.4),
("ytick.minor.visible", False),
("ytick.minor.left", True),
("ytick.minor.right", True),
("ytick.major.left", True),
("ytick.major.right", True),
("grid.linewidth", 0.8),
("grid.alpha", 1.0),
("figure.figsize", (6.4, 4.8)),
("figure.dpi", 100),
("figure.frameon", True),
("figure.autolayout", False),
("figure.max_open_warning", 20),
("figure.subplot.left", 0.125),
("figure.subplot.right", 0.9),
("figure.subplot.bottom", 0.11),
("figure.subplot.top", 0.88),
("figure.subplot.wspace", 0.2),
("figure.subplot.hspace", 0.2),
("figure.constrained_layout.use", False),
("figure.constrained_layout.hspace", 0.02),
("figure.constrained_layout.wspace", 0.02),
("figure.constrained_layout.h_pad", 0.04167),
("figure.constrained_layout.w_pad", 0.04167),
("savefig.jpeg_quality", 95),
("savefig.pad_inches", 0.1),
("savefig.transparent", False),
("savefig.dpi", 75),
("svg.image_inline", True),
("path.simplify", True),
("path.simplify_threshold", 0.1111111111111111),
("path.snap", True),
("path.effects", ()),
("agg.path.chunksize", 0),
("animation.embed_limit", 20),
("animation.bitrate", -1),
("animation.html_args", ()),
("animation.ffmpeg_args", ()),
("animation.avconv_args", ()),
("animation.convert_args", ()),
)
[docs]def setup_matplotlib(
output=("pdf", "svg"), rcparams=None, usetex=True, print_errors=False
):
""" import and setup matplotlib in the jupyter notebook
Parameters
----------
output: tuple[str]
the output formats to save to the notebook
rcparams: None or dict
update default parameters set for matplotlib
usetex: bool
if True, and the 'latex' command is available,
create figures with LaTeX
print_errors: bool
print errors for unavailable rcparams
"""
from IPython import get_ipython
from IPython.display import set_matplotlib_formats
import matplotlib as mpl
try:
from shutil import which
except ImportError:
from shutilwhich import which
ipython = get_ipython()
latex_available = which("latex") is not None
# if not latex_available:
# output = [o for o in output if o != "pdf"]
set_matplotlib_formats(*output)
ipython.magic("matplotlib inline")
if "svg" in output:
ipython.magic("config InlineBackend.figure_format = 'svg'")
final_params = dict(MPL_OPTIONS)
if rcparams is not None:
final_params.update(rcparams)
if usetex and latex_available:
final_params.update({"text.usetex": True})
else:
final_params.update({"text.usetex": False})
keyerrors = []
valerrors = {}
for key, val in final_params.items():
try:
mpl.rcParams[key] = val
except KeyError:
keyerrors.append(key)
except ValueError:
valerrors[key] = val
if print_errors:
if keyerrors:
print("KeyErrors:")
for key in keyerrors:
print("- key")
if valerrors:
print("ValueError:")
print(json.dumps(valerrors, indent=2))
return mpl.pyplot
[docs]def setup_pandas(escape_latex=False, use_longtable=False):
""" import and setup pandas in the jupyter notebook
Parameters
----------
escape_latex: bool
whether to escape special latex character, e.g. `_` -> `\\_`
"""
import pandas as pd
pd.set_option("display.latex.repr", True)
pd.set_option("display.latex.longtable", use_longtable)
pd.set_option("display.latex.escape", escape_latex)
return pd
[docs]def setup_sympy():
import sympy
sympy.init_printing(use_latex=True)
return sympy
[docs]def get_pimage():
try:
from PIL import Image
except ImportError:
raise ImportError("to use this function; pip install pillow")
return Image
[docs]def create_test_image(size=(50, 50)):
file = BytesIO()
image = get_pimage().new("RGBA", size=size, color=(155, 0, 0))
image.save(file, "png")
file.name = "test.png"
file.seek(0)
return file
[docs]def images_read(paths):
"""read a list of image paths to a list of PIL.IMAGE instances """
return [get_pimage().open(i).convert("RGBA") for i in paths]
[docs]def images_hconcat(images, width=700, height=700, gap=0, aspaths=True):
"""concatenate multiple images horizontally
Parameters
----------
images : list
if aspaths=True, list of path strings, else list of PIL.Image instances
width : int or list[int]
maximum width of final image, or of individual images
height : int or list[int]
maximum height of final image, or of individual images
gap : int
size of space between images
Returns
-------
image : PIL.Image
Examples
--------
>>> img_path = create_test_image(size=(50,50))
>>> img = images_hconcat([img_path,img_path])
>>> img.size
(100, 50)
>>> img_path = create_test_image(size=(50,50))
>>> img = images_hconcat([img_path,img_path],width=40,height=40)
>>> img.size
(40, 20)
>>> img_path = create_test_image(size=(50,50))
>>> img = images_hconcat([img_path,img_path],width=[40,30])
>>> img.size
(70, 40)
>>> img_path = create_test_image(size=(50,50))
>>> img = images_hconcat([img_path,img_path],gap=10)
>>> img.size
(110, 50)
"""
pimage = get_pimage()
images = images_read(images) if aspaths else images
if not isinstance(width, list):
widths = [width for _ in images]
else:
widths = width[:]
width = sum(widths) + gap * (len(images) - 1)
if not isinstance(height, list):
heights = [height for _ in images]
else:
heights = height[:]
height = sum(heights)
for im, w, h in zip(images, widths, heights):
im.thumbnail((w, h), pimage.ANTIALIAS)
widths, heights = zip(*(i.size for i in images))
total_width = sum(widths) + gap * (len(images) - 1)
max_height = max(heights)
new_im = pimage.new("RGBA", (total_width, max_height))
x_offset = 0
for im in images:
new_im.paste(im, (x_offset, 0), mask=im)
x_offset += im.size[0] + gap
new_im.thumbnail((width, height), pimage.ANTIALIAS)
return new_im
[docs]def images_vconcat(images, width=700, height=700, gap=0, aspaths=True):
"""concatenate multiple images vertically
Parameters
----------
images : list
if aspaths=True, list of path strings, else list of PIL.Image instances
width : int or list[int]
maximum width of final image, or of individual images
height : int or list[int]
maximum height of final image, or of individual images
gap : int
size of space between images
Returns
-------
image : PIL.Image
Examples
--------
>>> img_path = create_test_image(size=(50,50))
>>> img = images_vconcat([img_path,img_path])
>>> img.size
(50, 100)
>>> img_path = create_test_image(size=(50,50))
>>> img = images_vconcat([img_path,img_path],width=40,height=40)
>>> img.size
(20, 40)
>>> img_path = create_test_image(size=(50,50))
>>> img = images_vconcat([img_path,img_path],width=[40,30])
>>> img.size
(40, 70)
>>> img_path = create_test_image(size=(50,50))
>>> img = images_vconcat([img_path,img_path],gap=10)
>>> img.size
(50, 110)
"""
pimage = get_pimage()
images = images_read(images) if aspaths else images
if not isinstance(width, list):
widths = [width for _ in images]
else:
widths = width[:]
width = sum(widths)
if not isinstance(height, list):
heights = [height for _ in images]
else:
heights = height[:]
height = sum(heights) + gap * (len(images) - 1)
for im, w, h in zip(images, widths, heights):
im.thumbnail((w, h), pimage.ANTIALIAS)
widths, heights = zip(*(i.size for i in images))
max_width = max(widths)
total_height = sum(heights) + gap * (len(images) - 1)
new_im = pimage.new("RGBA", (max_width, total_height))
y_offset = 0
for im in images:
new_im.paste(im, (0, y_offset), mask=im)
y_offset += im.size[1] + gap
new_im.thumbnail((width, height), pimage.ANTIALIAS)
return new_im
[docs]def images_gridconcat(pathslist, width=700, height=700, aspaths=True, hgap=0, vgap=0):
"""concatenate multiple images in a grid
Parameters
----------
pathslist : list[list]
if aspaths=True, list of path strings, else list of PIL.Image instances
each sub list constitutes a row
width : int
maximum width of final image
height : int
maximum height of final image
hgap : int
size of horizontal space between images
vgap : int
size of vertical space between images
Returns
-------
image : PIL.Image
"""
pimage = get_pimage()
himages = [images_hconcat(paths, gap=hgap, aspaths=aspaths) for paths in pathslist]
new_im = images_vconcat(himages, gap=vgap, aspaths=False)
new_im.thumbnail((width, height), pimage.ANTIALIAS)
return new_im
if __name__ == "__main__":
import matplotlib as mpl
print(
json.dumps(
{
k: v[0]
for k, v in mpl.rcsetup.defaultParams.items()
if isinstance(v[0], (int, float, list, tuple, set))
},
indent=2,
)
)