"""
Utility functions for plotting
"""
[docs]def roundup(x, order):
"""Round a number to the passed order
Args
----
x: float
Number to be rounded
order: int
Order to which `x` should be rounded
Returns
-------
x_round: float
The passed value rounded to the passed order
"""
return x if x % 10 ** order == 0 else x + 10 ** order - x % 10 ** order
[docs]def magnitude(x):
"""Determine the magnitude of a number
Args
----
x: float
Number whose magnitude to find
Returns
-------
mag: int
Magnitude of passed number
"""
import math
return int(math.floor(math.log10(x)))
[docs]def hourminsec(n_seconds):
"""Generate a string of hours and minutes from total number of seconds
Args
----
n_seconds: int
Total number of seconds to calculate hours, minutes, and seconds from
Returns
-------
hours: int
Number of hours in `n_seconds`
minutes: int
Remaining minutes in `n_seconds` after number of hours
seconds: int
Remaining seconds in `n_seconds` after number of minutes
"""
hours, remainder = divmod(n_seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return abs(hours), abs(minutes), abs(seconds)
[docs]def nsamples_to_hourminsec(x, pos):
"""Convert axes labels to experiment duration in hours/minutes/seconds
Notes
-----
Matplotlib FuncFormatter function
https://matplotlib.org/examples/pylab_examples/custom_ticker1.html
"""
h, m, s = hourminsec(x / 16.0)
return "{:.0f}h {:2.0f}′ {:2.1f}″".format(h, m, s)
[docs]def nsamples_to_hourmin(x, pos):
"""Convert axes labels to experiment duration in hours/minutes
Notes
-----
Matplotlib FuncFormatter function
https://matplotlib.org/examples/pylab_examples/custom_ticker1.html
"""
h, m, s = hourminsec(x / 16.0)
return "{:.0f}h {:2.0f}′".format(h, m + round(s))
[docs]def nsamples_to_minsec(x, pos):
"""Convert axes labels to experiment duration in minutes/seconds
Notes
-----
Matplotlib FuncFormatter function
https://matplotlib.org/examples/pylab_examples/custom_ticker1.html
"""
h, m, s = hourminsec(x / 16.0)
return "{:2.0f}′ {:2.1f}″".format(m + (h * 60), s)
[docs]def add_alpha_labels(
axes,
xpos=0.03,
ypos=0.95,
suffix="",
color=None,
fontsize=14,
fontweight="normal",
boxstyle="square",
facecolor="white",
edgecolor="white",
alpha=1.0,
):
"""Add sequential alphbet labels to subplot axes
Args
----
axes: list of pyplot.ax
A list of matplotlib axes to add the label labels to
xpos: float or array_like
X position(s) of labels in figure coordinates
ypos: float or array_like
Y position(s) of labels in figure coordinates
suffix: str
String to append to labels (e.g. '.' or ' name)
color: matplotlib color
Color of labels
fontsize: int
Alppa fontsize
fontweight: matplotlib fontweight
Alpha fontweight
boxstyle: matplotlib boxstyle
Alpha boxstyle
facecolor: matplotlib facecolor
Color of box containing label
edgecolor: matplotlib edgecolor
Color of box'es border containing label
alpha: float
Transparency of label
Returns
-------
axes: list of pyplot.ax
A list of matplotlib axes objects with alpha labels added
"""
import seaborn
import string
import numpy
if not numpy.iterable(xpos):
xpos = [xpos] * len(axes)
ypos = [ypos] * len(axes)
if (len(xpos) > 1) or (len(ypos) > 1):
try:
assert len(axes) == len(xpos)
except AssertionError as e:
e.args += "xpos iterable must be same length as axes"
raise
try:
assert len(axes) == len(ypos)
except AssertionError as e:
e.args += "ypos iterable must be same length as axes"
raise
else:
xpos = [xpos]
ypos = [ypos]
colors = seaborn.color_palette()
abc = string.ascii_uppercase
for i, (label, ax) in enumerate(zip(abc[: len(axes)], axes)):
if color is None:
color = colors[i]
kwargs = dict(color=color, fontweight=fontweight)
bbox = dict(
boxstyle=boxstyle, facecolor=facecolor, edgecolor=edgecolor, alpha=alpha
)
ax.text(
xpos[i],
ypos[i],
"{}{}".format(label, suffix),
transform=ax.transAxes,
fontsize=fontsize,
verticalalignment="top",
bbox=bbox,
**kwargs
)
return axes
[docs]def merge_limits(axes, xlim=True, ylim=True):
"""Set maximum and minimum limits from list of axis objects to each axis
Args
----
axes: iterable
list of `matplotlib.pyplot` axis objects whose limits should be modified
xlim: bool
Flag to set modification of x axis limits
ylim: bool
Flag to set modification of y axis limits
"""
# Compile lists of all x/y limits
xlims = list()
ylims = list()
for ax in axes:
[xlims.append(lim) for lim in ax.get_xlim()]
[ylims.append(lim) for lim in ax.get_ylim()]
# Iterate over axes objects and set limits
for ax in axes:
if xlim:
ax.set_xlim(min(xlims), max(xlims))
if ylim:
ax.set_ylim(min(ylims), max(ylims))
return None
[docs]def plot_noncontiguous(
ax, data, ind, color="black", label="", offset=0, linewidth=0.5, linestyle="-"
):
"""Plot non-contiguous slice of data
Args
----
data: ndarray
The data with non continguous regions to plot
ind: ndarray
indices of data to be plotted
color: matplotlib color
Color of plotted line
label: str
Name to be shown in legend
offset: int
The number of index positions to reset start of data to zero
linewidth: float
The width of the plotted line
linstyle: str
The char representation of the plotting style for the line
Returns
-------
ax: pyplot.ax
Axes object with line glyph added for non-contiguous regions
"""
def slice_with_nans(ind, data, offset):
"""Insert nans in indices and data where indices non-contiguous"""
import copy
import numpy
ind_nan = numpy.zeros(len(data))
ind_nan[:] = numpy.nan
# prevent ind from overwrite with deepcopy
ind_nan[ind - offset] = copy.deepcopy(ind)
# ind_nan = ind_nan[ind[0]-offset:ind[-1]-offset]
# prevent data from overwrite with deepcopy
data_nan = copy.deepcopy(data)
data_nan[numpy.isnan(ind_nan)] = numpy.nan
return ind_nan, data_nan
x, y = slice_with_nans(ind, data, offset)
ax.plot(x, y, color=color, linewidth=linewidth, linestyle=linestyle, label=label)
return ax
[docs]def plot_shade_mask(ax, ind, mask, facecolor="gray", alpha=0.5):
"""Shade across x values where boolean mask is `True`
Args
----
ax: pyplot.ax
Axes object to plot with a shaded region
ind: ndarray
The indices to use for the x-axis values of the data
mask: ndarray
Boolean mask array to determine which regions should be shaded
facecolor: matplotlib color
Color of the shaded area
Returns
-------
ax: pyplot.ax
Axes object with the shaded region added
"""
ymin, ymax = ax.get_ylim()
ax.fill_between(ind, ymin, ymax, where=mask, facecolor=facecolor, alpha=alpha)
return ax