"""
Module for plotting analysed data
"""
from netpyne import __gui__
if __gui__:
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.offsetbox import AnchoredOffsetbox
import numpy as np
from copy import deepcopy
import pickle, json
import os
try:
basestring
except NameError:
basestring = str
colorList = [
[0.42, 0.67, 0.84],
[0.90, 0.76, 0.00],
[0.42, 0.83, 0.59],
[0.90, 0.32, 0.00],
[0.34, 0.67, 0.67],
[0.90, 0.59, 0.00],
[0.42, 0.82, 0.83],
[1.00, 0.85, 0.00],
[0.33, 0.67, 0.47],
[1.00, 0.38, 0.60],
[0.57, 0.67, 0.33],
[0.50, 0.20, 0.00],
[0.71, 0.82, 0.41],
[0.00, 0.20, 0.50],
[0.70, 0.32, 0.10],
] * 3
[docs]
class MultiPlotter:
"""NetPyNE object to generate line plots on multiple axes"""
def __init__(self, data, kind, metafig=None, **kwargs):
self.kind = kind
self.data = data
numLines = len(self.data['y'])
if metafig is None:
metafig = MetaFigure(kind=kind, subplots=numLines, **kwargs)
self.metafig = metafig
[docs]
def plot(self, **kwargs):
x = np.array(self.data.get('x'))
y = np.array(self.data.get('y'))
color = self.data.get('color')
marker = self.data.get('marker')
markersize = self.data.get('markersize')
linewidth = self.data.get('linewidth')
alpha = self.data.get('alpha')
label = self.data.get('label')
if len(np.shape(y)) == 1:
numLines = 1
y = [y]
else:
numLines = len(y)
if type(color) != list:
colors = [color for line in range(numLines)]
else:
colors = color
if type(marker) != list:
markers = [marker for line in range(numLines)]
else:
markers = marker
if type(markersize) != list:
markersizes = [markersize for line in range(numLines)]
else:
markersizes = markersize
if type(linewidth) != list:
linewidths = [linewidth for line in range(numLines)]
else:
linewidths = linewidth
if type(alpha) != list:
alphas = [alpha for line in range(numLines)]
else:
alphas = alpha
if label is None:
labels = [None for line in range(numLines)]
else:
labels = label
for index, line in enumerate(y):
curAx = self.metafig.ax[index]
curData = {}
curData['x'] = x
curData['y'] = [y[index]]
curData['color'] = colors[index]
curData['marker'] = markers[index]
curData['markersize'] = markersizes[index]
curData['linewidth'] = linewidths[index]
curData['alpha'] = alphas[index]
curData['label'] = labels[index]
curPlotter = LinesPlotter(data=curData, kind='LFPPSD', axis=curAx, **kwargs)
curPlotter.plot(**kwargs)
self.metafig.finishFig(**kwargs)
if 'returnPlotter' in kwargs and kwargs['returnPlotter']:
return self.metafig
else:
return self.metafig.fig
[docs]
class GeneralPlotter:
"""NetPyNE object to hold a Matplotlib axis along with its settings and standardized methods
Parameters
----------
data : dict, str
The data to be used in the plot. If a ``str``, it must be the path and filename of a previously saved data set.
kind : str
The kind of figure, used in saving.
axis : matplotlib axis
The axis to plot into. If axis is set to ``None``, a new figure and axis are created and plotted into.
twinx : bool
If plotting into an existing axis, whether to twin that x axis (i.e. allow plotting at a different y scale).
*Default:* ``False``
twiny : bool
If plotting into an existing axis, whether to twin that y axis (i.e. allow plotting at a different x scale).
*Default:* ``False``
rcParams : dict
A dictionary containing any or all Matplotlib settings to use for this figure. To see all settings and their defaults, execute ``import matplotlib; matplotlib.rcParams``.
"""
def __init__(self, data, kind, axis=None, twinx=False, twiny=False, sim=None, metafig=None, **kwargs):
self.kind = kind
# Load data
if type(data) == str:
if os.path.isfile(data):
self.data = self.loadData(data)
else:
raise Exception('In Plotter, if data is a string, it must be the path to a data file.')
else:
self.data = data
if not sim:
from .. import sim
self.sim = sim
self.axis = axis
self.metafig = metafig
# If an axis is input, plot there; otherwise make a new figure and axis
if self.axis is None:
if self.metafig is None:
if self.kind == 'raster&signal':
kwargs.update({'constrained_layout': True})
kwargs.update({'gridspec_kw': {'height_ratios': [2,1],
'right': 0.2}})
# 'left':0.2,
# 'top':0.3,
# 'bottom':0.2}})
self.metafig = MetaFigure(kind=self.kind, subplots=2, sharex=True, **kwargs)
else:
self.metafig = MetaFigure(kind=self.kind, **kwargs)
self.fig = self.metafig.fig
self.axis = self.metafig.ax
else:
self.fig = self.axis.figure
if hasattr(self.axis, 'metafig'):
self.metafig = self.axis.metafig
if twinx:
if twiny:
self.axis = axis.twinx().twiny()
else:
self.axis = axis.twinx()
elif twiny:
self.axis = axis.twiny()
# Attach plotter to its MetaFigure
if self.metafig:
self.metafig.plotters.append(self)
[docs]
def loadData(self, fileName, fileDir=None, sim=None):
"""Method to load data from file
Parameters
----------
fileName : str
Name of the file to be loaded.
fileDir : str
Path of the file to be loaded.
*Default:* ``None`` uses the current directory.
"""
from ..analysis import loadData
self.data = loadData(fileName=fileName, fileDir=fileDir, sim=None)
[docs]
def saveData(self, fileName=None, fileDesc=None, fileType=None, fileDir=None, sim=None, **kwargs):
"""Method to save data to file
Parameters
----------
fileName : str
Name of the file to be saved.
*Default:* ``None`` uses ``simConfig.filename``.
fileDesc : str
String to be appended to fileName.
*Default:* ``None`` adds the 'kind' of MetaFigure.
fileType : str
Extension of file type to save.
*Default:* ``None`` chooses automatically.
fileDir : str
Path to save the file to.
*Default:* ``None`` uses the current directory.
"""
from ..analysis import saveData as saveFigData
saveFigData(
self.data, fileName=fileName, fileDesc=fileDesc, fileType=fileType, fileDir=fileDir, sim=sim, **kwargs
)
[docs]
def addLegend(self, handles=None, labels=None, **kwargs):
"""Method to add a legend to the axis
Parameters
----------
axis : None or object
the axis to add the legends
handles : list
List of Matplotlib legend handles.
*Default:* ``None`` finds handles in the current axis.
labels : list
List of Matplotlib legend labels.
*Default:* ``None`` finds labels in the current axis.
legendKwargs : dict
Dictionary of Matplotlib legend parameters with their values.
kwargs : str
You can enter any Matplotlib legend parameter as a kwarg. See https://matplotlib.org/3.5.1/api/_as_gen/matplotlib.pyplot.legend.html
"""
legendParams = [
'loc',
'bbox_to_anchor',
'fontsize',
'numpoints',
'scatterpoints',
'scatteryoffsets',
'markerscale',
'markerfirst',
'frameon',
'fancybox',
'shadow',
'framealpha',
'facecolor',
'edgecolor',
'mode',
'bbox_transform',
'title',
'title_fontsize',
'borderpad',
'labelspacing',
'handlelength',
'handletextpad',
'borderaxespad',
'columnspacing',
'handler_map',
]
# Check for and apply any legend parameters in the kwargs
legendKwargs = {}
for kwarg in kwargs:
if kwarg in legendParams:
legendKwargs[kwarg] = kwargs[kwarg]
# If 'legendKwargs' is found in kwargs, use those values instead of the defaults
if 'legendKwargs' in kwargs:
legendKwargs_new = kwargs['legendKwargs']
for key in legendKwargs_new:
if key in legendParams:
legendKwargs[key] = legendKwargs_new[key]
cur_handles, cur_labels = self.axis.get_legend_handles_labels()
if not handles:
handles = cur_handles
if not labels:
labels = cur_labels
self.axis.legend(handles, labels, **legendKwargs)
[docs]
def addScalebar(
self,
axis=None,
matchx=True,
matchy=True,
hidex=True,
hidey=True,
unitsx=None,
unitsy=None,
scalex=1.0,
scaley=1.0,
xmax=None,
ymax=None,
space=None,
**kwargs
):
"""Method to add a scale bar to the axis
Parameters
----------
axis : None or object
the axis to add the scalebar
matchx : bool
If True, set size of scale bar to spacing between ticks, if False, set size using sizex params.
*Default:* ``True``
matchy : bool
If True, set size of scale bar to spacing between ticks, if False, set size using sizey params.
*Default:* ``True``
sizex : float
The size of the x scale bar if matchx is False.
sizey : float
The size of the y scale bar if matchy is False.
hidex : bool
Whether to hide the x-axis labels and ticks.
*Default:* ``True``
hidey : bool
Whether to hide the x-axis labels and ticks.
*Default:* ``True``
unitsx : str
The units to use on the scale bar label.
*Default:* ``None``
unitsy : str
The units to use on the scale bar label.
*Default:* ``None``
scalex : float
Desired scaling in x direction.
*Default:* ``1.0``
scaley : float
Desired scaling in y direction.
*Default:* ``1.0``
xmax : float
Maximum size of x scale bar.
*Default:* ``None``
ymax : float
Maximum size of y scale bar.
*Default:* ``None``
space : float
Amount of space to add to y axis for scale bar.
*Default:* ``None``
"""
curAx = axis
if curAx==None:
curAx = self.axis
_add_scalebar(
curAx,
matchx=matchx,
matchy=matchy,
hidex=hidex,
hidey=hidey,
unitsx=unitsx,
unitsy=unitsy,
scalex=scalex,
scaley=scaley,
xmax=xmax,
ymax=ymax,
space=space,
**kwargs
)
[docs]
def addColorbar(self, axis=None, **kwargs):
"""Method to add a color bar to the axis
Parameters
----------
axis : None or object
the axis to add the colorbar
kwargs : str
You can enter any Matplotlib colorbar parameter as a kwarg. See https://matplotlib.org/3.5.1/api/_as_gen/matplotlib.pyplot.colorbar.html
"""
curAx = axis
if curAx==None:
curAx = self.axis
if 'vmin' in kwargs.keys():
vmin = kwargs['vmin']
kwargs.pop('vmin')
if 'vmax' in kwargs.keys():
vmax = kwargs['vmax']
kwargs.pop('vmax')
if 'vmin' in locals() and 'vmax' in locals():
cbar = plt.colorbar(mappable=mpl.cm.ScalarMappable(norm=mpl.colors.Normalize(vmin=vmin, vmax=vmax), cmap=self.cmap),
ax=curAx, ticks=[vmin, vmin/2, 0, vmax/2, vmax], **kwargs)
cbar.set_label('Phase', rotation=270)
else:
plt.colorbar(mappable=self.axis.get_images()[0], ax=curAx, **kwargs)
[docs]
def addBackground(self, axis=None, **kwargs):
"""Method to add striped gray background to populations - used in plotRaster with "colorbyPhase"
Parameters
----------
axis : None or object
the axis to add the background
kwargs : str
You can enter any Matplotlib colorbar parameter as a kwarg. See https://matplotlib.org/3.5.1/api/_as_gen/matplotlib.pyplot.colorbar.html
"""
from matplotlib.pyplot import yticks, barh
curAx = axis
if curAx==None:
curAx = self.axis
if 'popNumCells' in kwargs.keys():
popSizes = kwargs['popNumCells']
yTicksPos = []
accum = 0
for i, size in enumerate(popSizes):
yTicksPos.append(accum + popSizes[i] / 2) # yCenter of pop
accum += popSizes[i]
curAx.set_yticks(np.array(yTicksPos))
curAx.set_yticklabels(kwargs['popLabels'])
curAx.set_xlim(kwargs['timeRange'])
curAx.set_ylim([0,accum])
curAx.barh(yTicksPos, left=kwargs['timeRange'][0], width=kwargs['timeRange'][1]-kwargs['timeRange'][0]-1,
height=popSizes, align='center', color=['#E3E3E3', 'w'], alpha=0.5)
[docs]
def finishAxis(self, axis=None, **kwargs):
"""Method to finalize an axis
Parameters
----------
saveData : bool
Whether to save the data.
*Default:* ``False``
legend : bool, dict
Whether to add a legend. If a ``dict``, must be legend parameters and their values.
*Default:* ``False``
scalebar : bool, dict
Whether to add a scale bar. If a ``dict``, must be scalebar parameters and their values.
*Default:* ``False``
colorbar : bool, dict
Whether to add a color bar. If a ``dict``, must be colorbar parameters and their values.
*Default:* ``False``
grid : bool, dict
Whether to add a grid lines to the axis. If a ``dict``, must be grid parameters and their values.
*Default:* ``False``
"""
curAx = axis
if curAx==None:
curAx = self.axis
self.formatAxis(axis=curAx, **kwargs)
if 'saveData' in kwargs:
if kwargs['saveData']:
self.saveData(**kwargs)
if 'legend' in kwargs:
if kwargs['legend'] is True:
self.addLegend(axis=curAx,**kwargs)
elif type(kwargs['legend']) == dict:
self.addLegend(axis=curAx,**kwargs['legend'])
if 'scalebar' in kwargs:
if kwargs['scalebar'] is True:
self.addScalebar(axis=curAx)
elif type(kwargs['scalebar']) == dict:
self.addScalebar(axis=curAx,**kwargs['scalebar'])
if 'colorbar' in kwargs:
if kwargs['colorbar'] is True:
self.addColorbar(axis=curAx)
elif type(kwargs['colorbar']) == dict:
self.addColorbar(axis=curAx, **kwargs['colorbar'])
if 'grid' in kwargs:
curAx.minorticks_on()
if kwargs['grid'] is True:
curAx.grid()
elif type(kwargs['grid']) == dict:
curAx.grid(**kwargs['grid'])
if 'background' in kwargs:
if type(kwargs['background']) == dict:
self.addBackground(axis=curAx, **kwargs['background'])
# If this is the only axis on the figure, finish the figure
if (type(self.metafig.ax) != np.ndarray) and (type(self.metafig.ax) != list):
self.metafig.finishFig(**kwargs)
# Reset the matplotlib rcParams to their original settings
mpl.style.use(self.metafig.orig_rcParams)
[docs]
class ScatterPlotter(GeneralPlotter):
"""NetPyNE plotter for scatter plots"""
def __init__(self, data, axis=None, **kwargs):
super().__init__(data=data, axis=axis, **kwargs)
self.kind = 'scatter'
if 'signal' in data:
self.kind = 'scatter&signal'
self.time = data.get('time')
self.signal = data.get('signal')
self.timeRange = data.get('timeRange')
self.x = data.get('x')
self.y = data.get('y')
self.s = data.get('s')
self.c = data.get('c')
self.marker = data.get('marker')
self.linewidth = data.get('linewidth')
self.cmap = data.get('cmap')
self.norm = data.get('norm')
self.alpha = data.get('alpha')
self.linewidths = data.get('linewidths')
[docs]
def plot(self, **kwargs):
if self.kind=='scatter':
scatterPlot = self.axis.scatter(
x=self.x,
y=self.y,
s=self.s,
c=self.c,
marker=self.marker,
linewidth=self.linewidth,
cmap=self.cmap,
norm=self.norm,
alpha=self.alpha,
linewidths=self.linewidths,
)
self.finishAxis(**kwargs)
elif self.kind=='scatter&signal':
scatterPlot = self.axis[0].scatter(
x=self.x,
y=self.y,
s=self.s,
c=self.c,
marker=self.marker,
linewidth=self.linewidth,
cmap=self.cmap,
norm=self.norm,
alpha=self.alpha,
linewidths=self.linewidths,
)
linePlot = self.axis[1].plot(
self.time,
self.signal,
)
self.finishAxis(axis=self.axis[0],**kwargs)
self.axis[0].set_xlabel('')
self.axis[0].set_ylabel('Cells (grouped by populations)')
self.axis[1].set_xlabel('Time (ms)')
self.axis[1].set_ylabel('Filtered signal')
self.metafig.finishFig(**kwargs)
return self.fig
[docs]
class LinePlotter(GeneralPlotter):
"""NetPyNE plotter for plotting one line per axis"""
def __init__(self, data, axis=None, options={}, **kwargs):
super().__init__(data=data, axis=axis, **kwargs)
self.kind = 'line'
self.x = np.array(data.get('x'))
self.y = np.array(data.get('y'))
self.color = data.get('color')
self.marker = data.get('marker')
self.markersize = data.get('markersize')
self.linewidth = data.get('linewidth')
self.alpha = data.get('alpha')
[docs]
def plot(self, **kwargs):
linePlot = self.axis.plot(
self.x,
self.y,
color=self.color,
marker=self.marker,
markersize=self.markersize,
linewidth=self.linewidth,
alpha=self.alpha,
)
self.finishAxis(**kwargs)
return self.fig
[docs]
class LinesPlotter(GeneralPlotter):
"""NetPyNE plotter for plotting multiple lines on the same axis"""
def __init__(self, data, axis=None, options={}, **kwargs):
super().__init__(data=data, axis=axis, **kwargs)
self.kind = 'lines'
self.x = np.array(data.get('x'))
self.y = np.array(data.get('y'))
self.color = data.get('color')
self.marker = data.get('marker')
self.markersize = data.get('markersize')
self.linewidth = data.get('linewidth')
self.alpha = data.get('alpha')
self.label = data.get('label')
[docs]
def plot(self, **kwargs):
if len(np.shape(self.y)) == 1:
numLines = 1
else:
numLines = len(self.y)
if type(self.color) != list:
colors = [self.color for line in range(numLines)]
else:
colors = self.color
if len(self.color) == 3 and type(self.color[0] == float):
colors = [self.color]
if type(self.marker) != list:
markers = [self.marker for line in range(numLines)]
else:
markers = self.marker
if type(self.markersize) != list:
markersizes = [self.markersize for line in range(numLines)]
else:
markersizes = self.markersize
if type(self.linewidth) != list:
linewidths = [self.linewidth for line in range(numLines)]
else:
linewidths = self.linewidth
if type(self.alpha) != list:
alphas = [self.alpha for line in range(numLines)]
else:
alphas = self.alpha
if self.label is None:
labels = [None for line in range(numLines)]
else:
labels = self.label
if type(self.label) != list:
labels = [self.label]
for index in range(numLines):
self.axis.plot(
self.x,
self.y[index],
color=colors[index],
marker=markers[index],
markersize=markersizes[index],
linewidth=linewidths[index],
alpha=alphas[index],
label=labels[index],
)
self.finishAxis(**kwargs)
return self.fig
[docs]
class HistPlotter(GeneralPlotter):
"""NetPyNE plotter for histogram plotting"""
def __init__(self, data, axis=None, options={}, **kwargs):
super().__init__(data=data, axis=axis, **kwargs)
self.kind = 'histogram'
self.x = data.get('x')
self.bins = data.get('bins', None)
self.range = data.get('range', None)
self.density = data.get('density', False)
self.weights = data.get('weights', None)
self.cumulative = data.get('cumulative', False)
self.bottom = data.get('bottom', None)
self.histtype = data.get('histtype', 'bar')
self.align = data.get('align', 'mid')
self.orientation = data.get('orientation', 'vertical')
self.rwidth = data.get('rwidth', None)
self.log = data.get('log', False)
self.color = data.get('color', None)
self.alpha = data.get('alpha', None)
self.label = data.get('label', None)
self.stacked = data.get('stacked', False)
self.data = data.get('data', None)
self.linewidth = data.get('linewidth', None)
[docs]
def plot(self, **kwargs):
histPlot = self.axis.hist(
self.x,
bins=self.bins,
range=self.range,
density=self.density,
weights=self.weights,
cumulative=self.cumulative,
bottom=self.bottom,
histtype=self.histtype,
align=self.align,
orientation=self.orientation,
rwidth=self.rwidth,
log=self.log,
color=self.color,
alpha=self.alpha,
label=self.label,
stacked=self.stacked,
linewidth=self.linewidth,
data=self.data,
)
self.finishAxis(**kwargs)
return self.fig
[docs]
class ImagePlotter(GeneralPlotter):
"""NetPyNE plotter for image plotting using plt.imshow"""
def __init__(self, data, axis=None, options={}, **kwargs):
super().__init__(data=data, axis=axis, **kwargs)
self.kind = 'image'
self.X = data.get('X')
self.cmap = data.get('cmap', None)
self.norm = data.get('norm', None)
self.aspect = data.get('aspect', None)
self.interpolation = data.get('interpolation', None)
self.alpha = data.get('alpha', None)
self.vmin = data.get('vmin', None)
self.vmax = data.get('vmax', None)
self.origin = data.get('origin', None)
self.extent = data.get('extent', None)
self.aspect = data.get('aspect', None)
self.interpolation = data.get('interpolation', None)
self.filternorm = data.get('filternorm', True)
self.filterrad = data.get('filterrad', 4.0)
self.resample = data.get('resample', None)
self.url = data.get('url', None)
self.data = data.get('data', None)
[docs]
def plot(self, **kwargs):
imagePlot = self.axis.imshow(
self.X,
cmap=self.cmap,
norm=self.norm,
aspect=self.aspect,
interpolation=self.interpolation,
alpha=self.alpha,
vmin=self.vmin,
vmax=self.vmax,
origin=self.origin,
extent=self.extent,
filternorm=self.filternorm,
filterrad=self.filterrad,
resample=self.resample,
url=self.url,
data=self.data,
)
self.finishAxis(**kwargs)
return self.fig
try:
class _AnchoredScaleBar(AnchoredOffsetbox):
"""
A class used for adding scale bars to plots
Modified from here: https://gist.github.com/dmeliza/3251476
"""
def __init__(
self,
axis,
sizex=0,
sizey=0,
labelx=None,
labely=None,
loc=4,
pad=0.1,
borderpad=0.1,
sep=2,
prop=None,
barcolor="black",
barwidth=None,
**kwargs
):
"""
Draw a horizontal and/or vertical bar with the size in data coordinate
of the give axes. A label will be drawn underneath (center-aligned).
- transform : the coordinate frame (typically axes.transData)
- sizex,sizey : width of x,y bar, in data units. 0 to omit
- labelx,labely : labels for x,y bars; None to omit
- loc : position in containing axes
- pad, borderpad : padding, in fraction of the legend font size (or prop)
- sep : separation between labels and bars in points.
- **kwargs : additional arguments passed to base class constructor
"""
from matplotlib.patches import Rectangle
from matplotlib.offsetbox import AuxTransformBox, VPacker, HPacker, TextArea, DrawingArea
bars = AuxTransformBox(axis.transData)
if sizex:
if axis.xaxis_inverted():
sizex = -sizex
bars.add_artist(Rectangle((0, 0), sizex, 0, ec=barcolor, lw=barwidth, fc="none"))
if sizey:
if axis.yaxis_inverted():
sizey = -sizey
bars.add_artist(Rectangle((0, 0), 0, sizey, ec=barcolor, lw=barwidth, fc="none"))
if sizex and labelx:
self.xlabel = TextArea(labelx)
bars = VPacker(children=[bars, self.xlabel], align="center", pad=0, sep=sep)
if sizey and labely:
self.ylabel = TextArea(labely)
bars = HPacker(children=[self.ylabel, bars], align="center", pad=0, sep=sep)
AnchoredOffsetbox.__init__(
self, loc, pad=pad, borderpad=borderpad, child=bars, prop=prop, frameon=False, **kwargs
)
except NameError:
print("-nogui passed, matplotlib is unavailable")
def _add_scalebar(
axis,
matchx=True,
matchy=True,
hidex=True,
hidey=True,
unitsx=None,
unitsy=None,
scalex=1.0,
scaley=1.0,
xmax=None,
ymax=None,
space=None,
**kwargs
):
"""
Add scalebars to axes
Adds a set of scale bars to *ax*, matching the size to the ticks of the plot and optionally hiding the x and y axes
- axis : the axis to attach ticks to
- matchx,matchy : if True, set size of scale bars to spacing between ticks, if False, set size using sizex and sizey params
- hidex,hidey : if True, hide x-axis and y-axis of parent
- **kwargs : additional arguments passed to AnchoredScaleBars
Returns created scalebar object
Modified from here: https://gist.github.com/dmeliza/3251476
"""
def get_tick_size(subaxis):
tick_size = None
tick_locs = subaxis.get_majorticklocs()
if len(tick_locs) > 1:
tick_size = np.abs(tick_locs[1] - tick_locs[0])
return tick_size
if matchx:
sizex = get_tick_size(axis.xaxis)
if matchy:
sizey = get_tick_size(axis.yaxis)
if 'sizex' in kwargs:
sizex = kwargs['sizex']
if 'sizey' in kwargs:
sizey = kwargs['sizey']
def autosize(value, maxvalue, scale, n=1, m=10):
round_to_n = (
lambda value, n, m: int(np.ceil(round(value, -int(np.floor(np.log10(abs(value)))) + (n - 1)) / m)) * m
)
while value > maxvalue:
try:
value = round_to_n(0.8 * maxvalue * scale, n, m) / scale
except:
value /= 10.0
m /= 10.0
return value
if ymax is not None and sizey > ymax:
sizey = autosize(sizey, ymax, scaley)
if xmax is not None and sizex > xmax:
sizex = autosize(sizex, xmax, scalex)
kwargs['sizex'] = sizex
kwargs['sizey'] = sizey
if unitsx is None:
unitsx = ''
if unitsy is None:
unitsy = ''
if 'labelx' not in kwargs or kwargs['labelx'] is None:
kwargs['labelx'] = '%.3g %s' % (kwargs['sizex'] * scalex, unitsx)
if 'labely' not in kwargs or kwargs['labely'] is None:
kwargs['labely'] = '%.3g %s' % (kwargs['sizey'] * scaley, unitsy)
# add space for scalebar
if space is not None:
ylim0, ylim1 = axis.get_ylim()
ylim = (ylim0 - space, ylim1)
if ylim0 > ylim1: # if y axis is inverted
ylim = (ylim0 + space, ylim1)
axis.set_ylim(ylim)
scalebar = _AnchoredScaleBar(axis, **kwargs)
axis.add_artist(scalebar)
if hidex:
axis.xaxis.set_visible(False)
if hidey:
axis.yaxis.set_visible(False)
if hidex and hidey:
axis.set_frame_on(False)
return scalebar