This repository has been archived on 2021-05-17. You can view files and clone it, but cannot push or open issues or pull requests.
scientificComputing/plotstyle.py

431 lines
16 KiB
Python

import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Set to true if you want xkcd-type sketchy plots:
xkcd_style = True
# matplotlib major version:
mpl_major = int(mpl.__version__.split('.')[0])
# default size of figure:
figure_width = 15.0 # cm, should be set according to \textwidth in the latex document
figure_height = 6.0 # cm, for a 1 x 2 figure
# points per inch:
ppi = 72.0
# colors:
colors = {}
colors['red'] = '#DD1000'
colors['orange'] = '#FF9900'
colors['lightorange'] = '#FFCC00'
colors['yellow'] = '#FFF720'
colors['green'] = '#99FF00'
colors['blue'] = '#0020BB'
colors['gray'] = '#A7A7A7'
colors['black'] = '#000000'
colors['white'] = '#FFFFFF'
#colors_bendalab_vivid['green'] = '#30D700'
#colors_bendalab_vivid['blue'] = '#0020C0'
# general settings for plot styles:
lwthick = 3.0
lwthin = 1.8
edgewidth = 0.0 if xkcd_style else 1.0
mainline = {'linestyle': '-', 'linewidth': lwthick}
minorline = {'linestyle': '-', 'linewidth': lwthin}
largemarker = {'marker': 'o', 'markersize': 9, 'markeredgecolor': colors['white'], 'markeredgewidth': edgewidth}
largeopenmarker = {'marker': 'o', 'markersize': 7, 'markerfacecolor': colors['white'], 'markeredgewidth': 2}
smallmarker = {'marker': 'o', 'markersize': 6, 'markeredgecolor': colors['white'], 'markeredgewidth': edgewidth}
largelinepoints = {'linestyle': '-', 'linewidth': lwthick, 'marker': 'o', 'markersize': 10, 'markeredgecolor': colors['white'], 'markeredgewidth': 1}
smalllinepoints = {'linestyle': '-', 'linewidth': 1.4, 'marker': 'o', 'markersize': 7, 'markeredgecolor': colors['white'], 'markeredgewidth': 1}
filllw = edgewidth
fillec = colors['white']
fillalpha = 0.4
filledge = {'linewidth': filllw, 'joinstyle': 'round'}
if mpl_major < 2:
del filledge['joinstyle']
# helper lines:
lsSpine = {'c': colors['black'], 'linestyle': '-', 'linewidth': 1, 'clip_on': False}
lsGrid = {'c': colors['gray'], 'linestyle': '--', 'linewidth': 1}
lsMarker = {'c': colors['black'], 'linestyle': '-', 'linewidth': 2}
psMarker = dict({'color': colors['black'], 'linestyle': 'none'}, **largemarker)
# line (ls), point (ps), and fill styles (fs).
# Each style is derived from a main color as indicated by the capital letter.
# Line styles come in two variants:
# - plain style with a thick/solid line (e.g. lsA), and
# - minor style with a thinner or dashed line (e.g. lsAm).
# Point (marker) styles come in two variants:
# - plain style with large solid markers (e.g. psB), and
# - minor style with smaller markers (e.g. psBm).
# Linepoint styles (markers connected by lines) come in two variants:
# - plain style with large solid markers (e.g. lpsA), and
# - minor style with smaller markers (e.g. lpsAm).
# Fill styles come in three variants:
# - plain (e.g. fsB) for a solid fill color and a darker edge color,
# - solid (e.g. fsBs) for a solid fill color and without edge color, and
# - alpha (e.g. fsBa) for a transparent fill color without edge color.
lsA = dict({'color': colors['blue']}, **mainline)
lsAm = dict({'color': colors['blue']}, **minorline)
psA = dict({'color': colors['blue'], 'linestyle': 'none'}, **largemarker)
psAm = dict({'color': colors['blue'], 'linestyle': 'none'}, **smallmarker)
lpsA = dict({'color': colors['blue']}, **largelinepoints)
lpsAm = dict({'color': colors['blue']}, **smalllinepoints)
fsA = dict({'facecolor': colors['blue'], 'edgecolor': fillec}, **filledge)
fsAs = {'facecolor': colors['blue'], 'edgecolor': 'none'}
fsAa = {'facecolor': colors['blue'], 'edgecolor': 'none', 'alpha': fillalpha}
lsB = dict({'color': colors['red']}, **mainline)
lsBm = dict({'color': colors['red']}, **minorline)
psB = dict({'color': colors['red'], 'linestyle': 'none'}, **largemarker)
psBo = dict({'markeredgecolor': colors['red'], 'linestyle': 'none'}, **largeopenmarker)
psBm = dict({'color': colors['red'], 'linestyle': 'none'}, **smallmarker)
lpsB = dict({'color': colors['red']}, **largelinepoints)
lpsBm = dict({'color': colors['red']}, **smalllinepoints)
fsB = dict({'facecolor': colors['red'], 'edgecolor': fillec}, **filledge)
fsBs = {'facecolor': colors['red'], 'edgecolor': 'none'}
fsBa = {'facecolor': colors['red'], 'edgecolor': 'none', 'alpha': fillalpha}
lsC = dict({'color': colors['lightorange']}, **mainline)
lsCm = dict({'color': colors['lightorange']}, **minorline)
psC = dict({'color': colors['lightorange'], 'linestyle': 'none'}, **largemarker)
psCm = dict({'color': colors['lightorange'], 'linestyle': 'none'}, **smallmarker)
fsC = dict({'facecolor': colors['lightorange'], 'edgecolor': fillec}, **filledge)
fsCs = {'facecolor': colors['lightorange'], 'edgecolor': 'none'}
fsCa = {'facecolor': colors['lightorange'], 'edgecolor': 'none', 'alpha': fillalpha}
lsD = dict({'color': colors['orange']}, **mainline)
lsDm = dict({'color': colors['orange']}, **minorline)
psD = dict({'color': colors['orange'], 'linestyle': 'none'}, **largemarker)
psDm = dict({'color': colors['orange'], 'linestyle': 'none'}, **smallmarker)
fsD = dict({'facecolor': colors['orange'], 'edgecolor': fillec}, **filledge)
fsDs = {'facecolor': colors['orange'], 'edgecolor': 'none'}
lsE = dict({'color': colors['yellow']}, **mainline)
lsEm = dict({'color': colors['yellow']}, **minorline)
psE = dict({'color': colors['yellow'], 'linestyle': 'none'}, **largemarker)
psEm = dict({'color': colors['yellow'], 'linestyle': 'none'}, **smallmarker)
fsE = dict({'facecolor': colors['yellow'], 'edgecolor': fillec}, **filledge)
fsEs = {'facecolor': colors['yellow'], 'edgecolor': 'none'}
fsF = dict({'facecolor': colors['green'], 'edgecolor': fillec}, **filledge)
fsFs = {'facecolor': colors['green'], 'edgecolor': 'none'}
# factor for scaling widths of bars in a bar plot:
bar_fac = 1.0
def cm_size(*args):
""" Convert dimensions from cm to inch.
Use this function to set the size of a figure in centimeter:
```
fig = plt.figure(figsize=cm_size(16.0, 10.0))
```
Parameters
----------
args: one or many float
Size in centimeter.
Returns
-------
inches: float or list of floats
Input arguments converted to inch.
"""
cm_per_inch = 2.54
if len(args) == 1:
return args[0]/cm_per_inch
else:
return [v/cm_per_inch for v in args]
def adjust_fs(fig=None, left=5.5, right=0.5, bottom=2.8, top=0.5):
""" Compute plot margins from multiples of the current font size.
Parameters
----------
fig: matplotlib.figure or None
The figure from which the figure size is taken. If None use the current figure.
left: float
the left margin of the plots given in multiples of the width of a character
(in fact, simply 60% of the current font size).
right: float
the right margin of the plots given in multiples of the width of a character
(in fact, simply 60% of the current font size).
*Note:* in contrast to the matplotlib `right` parameters, this specifies the
width of the right margin, not its position relative to the origin.
bottom: float
the bottom margin of the plots given in multiples of the height of a character
(the current font size).
top: float
the right margin of the plots given in multiples of the height of a character
(the current font size).
*Note:* in contrast to the matplotlib `top` parameters, this specifies the
width of the top margin, not its position relative to the origin.
Example
-------
```
fig, axs = plt.subplots(2, 2, figsize=(10, 5))
fig.subplots_adjust(**adjust_fs(fig, left=4.5)) # no matter what the figsize is!
```
"""
if fig is None:
fig = plt.gcf()
w, h = fig.get_size_inches()*ppi
fs = plt.rcParams['font.size']
return { 'left': left*0.6*fs/w,
'right': 1.0 - right*0.6*fs/w,
'bottom': bottom*fs/h,
'top': 1.0 - top*fs/h }
def show_spines(ax, spines='lb'):
""" Show and hide spines.
Parameters
----------
ax: matplotlib figure, matplotlib axis, or list of matplotlib axes
Axis whose spines and ticks are manipulated.
If figure, then apply manipulations on all axes of the figure.
If list of axes, apply manipulations on each of the given axes.
spines: string
Specify which spines and ticks should be shown. All other ones or hidden.
'l' is the left spine, 'r' the right spine, 't' the top one and 'b' the bottom one.
E.g. 'lb' shows the left and bottom spine, and hides the top and and right spines,
as well as their tick marks and labels.
'' shows no spines at all.
'lrtb' shows all spines and tick marks.
"""
# collect spine visibility:
xspines = []
if 't' in spines:
xspines.append('top')
if 'b' in spines:
xspines.append('bottom')
yspines = []
if 'l' in spines:
yspines.append('left')
if 'r' in spines:
yspines.append('right')
# collect axes:
if isinstance(ax, (list, tuple)):
axs = ax
else:
if hasattr(ax, "get_axes"):
axs = ax.get_axes()
else:
axs = [ax]
if not isinstance(axs, (list, tuple)):
axs = [axs]
for ax in axs:
# hide spines:
if not 'top' in xspines:
ax.spines['top'].set_visible(False)
if not 'bottom' in xspines:
ax.spines['bottom'].set_visible(False)
if not 'left' in yspines:
ax.spines['left'].set_visible(False)
if not 'right' in yspines:
ax.spines['right'].set_visible(False)
# ticks:
if len(xspines) == 0:
ax.xaxis.set_ticks_position('none')
ax.set_xticks([])
elif len(xspines) == 1:
ax.xaxis.set_ticks_position(xspines[0])
else:
ax.xaxis.set_ticks_position('both')
if len(yspines) == 0:
ax.yaxis.set_ticks_position('none')
ax.set_yticks([])
elif len(yspines) == 1:
ax.yaxis.set_ticks_position(yspines[0])
else:
ax.yaxis.set_ticks_position('both')
def __axes__init__(ax, *args, **kwargs):
""" Set some default formatting for a new Axes instance.
"""
ax.__init__orig(*args, **kwargs)
ax.show_spines('lb')
def axis_label(label, unit=None):
""" Format an axis label from a label and a unit
Parameters
----------
label: string
The name of the axis.
unit: string
The unit of the axis values.
Returns
-------
label: string
An axis label formatted from `label` and `unit`.
"""
if not unit:
return label
elif xkcd_style:
return '%s/%s' % (label, unit)
else:
return '%s [%s]' % (label, unit)
def set_xlabel(ax, label, unit=None, **kwargs):
""" Format the xlabel from a label and an unit.
Uses the axis_label() function to format the axis label.
Parameters
----------
label: string
The name of the axis.
unit: string
The unit of the axis values.
kwargs: key-word arguments
Further arguments passed on to the set_xlabel() function.
"""
ax.set_xlabel_orig(axis_label(label, unit), **kwargs)
def set_ylabel(ax, label, unit=None, **kwargs):
""" Format the ylabel from a label and an unit.
Uses the axis_label() function to format the axis label.
Parameters
----------
label: string
The name of the axis.
unit: string
The unit of the axis values.
kwargs: key-word arguments
Further arguments passed on to the set_ylabel() function.
"""
ax.set_ylabel_orig(axis_label(label, unit), **kwargs)
def set_zlabel(ax, label, unit=None, **kwargs):
""" Format the zlabel from a label and an unit.
Uses the axis_label() function to format the axis label.
Parameters
----------
label: string
The name of the axis.
unit: string
The unit of the axis values.
kwargs: key-word arguments
Further arguments passed on to the set_zlabel() function.
"""
ax.set_zlabel_orig(axis_label(label, unit), **kwargs)
def common_style():
""" Set some rc parameter.
"""
mpl.rcParams['figure.figsize'] = cm_size(figure_width, figure_height)
mpl.rcParams['figure.subplot.left'] = 5.5*0.6*mpl.rcParams['font.size']/cm_size(figure_width)/ppi
mpl.rcParams['figure.subplot.right'] = 1.0 - 0.5*0.6*mpl.rcParams['font.size']/cm_size(figure_width)/ppi
mpl.rcParams['figure.subplot.bottom'] = 2.8*mpl.rcParams['font.size']/cm_size(figure_height)/ppi
mpl.rcParams['figure.subplot.top'] = 1.0 - 0.5*mpl.rcParams['font.size']/cm_size(figure_height)/ppi
mpl.rcParams['figure.subplot.wspace'] = 0.4
mpl.rcParams['figure.subplot.hspace'] = 0.6
mpl.rcParams['figure.facecolor'] = 'white'
mpl.rcParams['figure.edgecolor'] = 'none'
mpl.rcParams['figure.frameon'] = False
mpl.rcParams['xtick.direction'] = 'out'
mpl.rcParams['ytick.direction'] = 'out'
mpl.rcParams['xtick.major.width'] = 1.25
mpl.rcParams['ytick.major.width'] = 1.25
mpl.rcParams['grid.color'] = lsGrid['c']
mpl.rcParams['grid.linestyle'] = lsGrid['linestyle']
mpl.rcParams['grid.linewidth'] = lsGrid['linewidth']
mpl.rcParams['legend.frameon'] = False
mpl.rcParams['axes.facecolor'] = 'none'
mpl.rcParams['axes.edgecolor'] = lsSpine['c']
mpl.rcParams['axes.linewidth'] = lsSpine['linewidth']
if 'axes.prop_cycle' in mpl.rcParams:
from cycler import cycler
mpl.rcParams['axes.prop_cycle'] = cycler(color=[colors['blue'], colors['red'],
colors['lightorange'], colors['orange'],
colors['yellow'], colors['green']])
else:
mpl.rcParams['axes.color_cycle'] = [colors['blue'], colors['red'],
colors['lightorange'], colors['orange'],
colors['yellow'], colors['green']]
# overwrite axes constructor:
if not hasattr(mpl.axes.Subplot, '__init__orig'):
mpl.axes.Subplot.__init__orig = mpl.axes.Subplot.__init__
mpl.axes.Subplot.__init__ = __axes__init__
mpl.axes.Axes.show_spines = show_spines
# overwrite axes set_[xyz]label() member functions:
if not hasattr(mpl.axes.Axes, 'set_xlabel_orig'):
mpl.axes.Axes.set_xlabel_orig = mpl.axes.Axes.set_xlabel
mpl.axes.Axes.set_xlabel = set_xlabel
if not hasattr(mpl.axes.Axes, 'set_ylabel_orig'):
mpl.axes.Axes.set_ylabel_orig = mpl.axes.Axes.set_ylabel
mpl.axes.Axes.set_ylabel = set_ylabel
if not hasattr(Axes3D, 'set_zlabel_orig'):
Axes3D.set_zlabel_orig = Axes3D.set_zlabel
Axes3D.set_zlabel = set_zlabel
def sketch_style():
""" Activate xkcd style and adapt some rc parameter.
"""
global bar_fac
bar_fac = 0.9
plt.xkcd()
common_style()
mpl.rcParams['legend.fontsize'] = 'medium'
mpl.rcParams['xtick.labelsize'] = 'medium'
mpl.rcParams['ytick.labelsize'] = 'medium'
mpl.rcParams['xtick.major.size'] = 6
mpl.rcParams['ytick.major.size'] = 6
def plain_style():
""" Deactivate xkcd style and adapt some rc parameter.
"""
global bar_fac
bar_fac = 1.0
plt.rcdefaults()
common_style()
mpl.rcParams['font.family'] = 'sans-serif'
mpl.rcParams['legend.fontsize'] = 'x-small'
mpl.rcParams['xtick.labelsize'] = 'small'
mpl.rcParams['ytick.labelsize'] = 'small'
mpl.rcParams['xtick.major.size'] = 2.5
mpl.rcParams['ytick.major.size'] = 2.5
def plot_style():
""" Set rc parameter in dependence on xkcd_style.
"""
if xkcd_style:
sketch_style()
else:
plain_style()
# automatic initialization:
plot_style()