Source code for brian2tools.plotting.base

'''
Base module for the plotting facilities.
'''
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np

from brian2.core.variables import VariableView
from brian2.spatialneuron.morphology import Morphology
from brian2.monitors import SpikeMonitor, StateMonitor, PopulationRateMonitor
from brian2.monitors.statemonitor import StateMonitorView
from brian2.units.fundamentalunits import Quantity
from brian2.units.stdunits import ms
from brian2.utils.logger import get_logger
from brian2.synapses.synapses import Synapses, SynapticPathway

from .data import plot_raster, plot_state, plot_rate, _get_best_unit
from .morphology import plot_dendrogram
from .synapses import plot_synapses

__all__ = ['brian_plot', 'add_background_pattern']

logger = get_logger(__name__)


def _setup_axes_matplotlib(axes):
    '''
    Helper function to create new figures/axes for matplotlib, depending on
    arguments provided by the user.
    '''
    if axes is None:
        axes = plt.gca()
    return axes


def _setup_axes_mayavi(axes):
    '''
    Helper function to create new figures/axes for mayavi, depending on
    arguments provided by the user.
    '''
    import mayavi.mlab as mayavi
    if axes is None:
        axes = mayavi.figure(bgcolor=(0.95, 0.95, 0.95))
    return axes


[docs]def brian_plot(brian_obj, axes=None, **kwds): ''' Plot the data of the given object (e.g. a monitor). This function will call an adequate plotting function for the object, e.g. `plot_raster` for a `~brian2.monitors.spikemonitor.SpikeMonitor`. The plotting may apply heuristics to get a generally useful plot (e.g. for a `~brian2.monitors.ratemonitor.PopulationRateMonitor`, it will plot the rates smoothed with a Gaussian window of 1 ms), the exact details are subject to change. This function is therefore mostly meant as a quick and easy way to plot an object, for full control use one of the specific plotting functions. Parameters ---------- brian_obj : object The Brian object to plot. axes : `~matplotlib.axes.Axes`, optional The `~matplotlib.axes.Axes` instance used for plotting. Defaults to ``None`` which means that a new `~matplotlib.axes.Axes` will be created for the plot. kwds : dict, optional Any additional keywords command will be handed over to matplotlib's `~matplotlib.axes.Axes.plot` command. This can be used to set plot properties such as the ``color``. Returns ------- axes : `~matplotlib.axes.Axes` The `~matplotlib.axes.Axes` instance that was used for plotting. This object allows to modify the plot further, e.g. by setting the plotted range, the axis labels, the plot title, etc. ''' if isinstance(brian_obj, SpikeMonitor): return plot_raster(brian_obj.i, brian_obj.t, axes=axes, **kwds) elif isinstance(brian_obj, StateMonitor): if len(brian_obj.record_variables) != 1: raise TypeError('brian_plot only works for a StateMonitor that ' 'records a single variable.') values = getattr(brian_obj, brian_obj.record_variables[0]).T if 'var_name' not in kwds: kwds['var_name'] = brian_obj.record_variables[0] if 'var_unit' not in kwds and isinstance(values, Quantity): kwds['var_unit'] = _get_best_unit(values) return plot_state(brian_obj.t, values, axes=axes, **kwds) elif isinstance(brian_obj, StateMonitorView): monitor = brian_obj.monitor if len(monitor.record_variables) != 1: raise TypeError('brian_plot only works for a StateMonitor that ' 'records a single variable.') var_name = monitor.record_variables[0] values = getattr(brian_obj, var_name).T if 'var_name' not in kwds: kwds['var_name'] = var_name if 'var_unit' not in kwds and isinstance(values, Quantity): kwds['var_unit'] = _get_best_unit(values) return plot_state(brian_obj.t, values, axes=axes, **kwds) elif isinstance(brian_obj, PopulationRateMonitor): smooth_rate = brian_obj.smooth_rate(width=1*ms) if 'rate_unit' not in kwds: kwds['rate_unit'] = _get_best_unit(smooth_rate) return plot_rate(brian_obj.t, smooth_rate, axes=axes, **kwds) elif isinstance(brian_obj, Morphology): if kwds: logger.warn('plot_dendrogram does not take any additional keyword ' 'arguments, ignoring them.') return plot_dendrogram(brian_obj, axes=axes) elif isinstance(brian_obj, Synapses): if len(brian_obj) == 0: raise TypeError('Synapses object does not have any synapses.') min_sources, max_sources = np.min(brian_obj.i[:]), np.max(brian_obj.i[:]) min_targets, max_targets = np.min(brian_obj.j[:]), np.max(brian_obj.j[:]) source_range = max_sources - min_sources target_range = max_targets - min_targets if source_range < 1000 and target_range < 1000: plot_type = 'image' elif len(brian_obj) < 10000: plot_type = 'scatter' else: plot_type = 'hexbin' return plot_synapses(brian_obj.i, brian_obj.j, plot_type=plot_type, axes=axes) # brian_obj.group can be a weak proxy, we can therefore not use isinstance elif (isinstance(brian_obj, VariableView) and issubclass(brian_obj.group.__class__, (Synapses, SynapticPathway))): # synaptic variable synapses = brian_obj.group sources = synapses.i[:] targets = synapses.j[:] min_sources, max_sources = np.min(sources), np.max(sources) min_targets, max_targets = np.min(targets), np.max(targets) source_range = max_sources - min_sources target_range = max_targets - min_targets if source_range < 1000 and target_range < 1000: plot_type = 'image' elif len(brian_obj) < 10000: plot_type = 'scatter' else: plot_type = 'hexbin' values = brian_obj[:] if 'var_name' not in kwds: kwds['var_name'] = brian_obj.name if 'var_unit' not in kwds and isinstance(values, Quantity): kwds['var_unit'] = _get_best_unit(values) return plot_synapses(sources, targets, values, plot_type=plot_type, axes=axes, **kwds) else: raise NotImplementedError('Do not know how to plot object of type ' '%s' % type(brian_obj))
[docs]def add_background_pattern(axes, hatch='xxx', fill=True, fc=(0.9, 0.9, 0.9), ec=(0.8, 0.8, 0.8), zorder=-10, **kwds): ''' Add a "hatching" pattern to the background of the axes (can be useful to make a difference between "no value" and a value mapped to a color value that is identical to the background color). By default, it uses a cross hatching pattern in gray which can be changed by providing the respective arguments. All additional keyword arguments are passed on to the `~matplotlib.patches.Rectangle` initializer. Parameters ---------- axes : `matplotlib.axes.Axes` The axes where the background pattern should be added. hatch : str, optional See `matplotlib.patches.Patch.set_hatch`. Defaults to `'xxx'`. fill : bool, optional See `matplotlib.patches.Patch.set_fill`. Defaults to `True`. fc : mpl color spec or None or 'none' See `matplotlib.patches.Patch.set_facecolor`. Defaults to `(0.9, 0.9, 0.9)`. ec : mpl color spec or None or 'none' See `matplotlib.patches.Patch.set_edgecolor`. Defaults to `(0.8, 0.8, 0.8)`. zorder : int See `matplotlib.artist.Artist.set_zorder`. Defaults to `-10`. ''' xmin, xmax = axes.get_xlim() ymin, ymax = axes.get_ylim() xy = (xmin, ymin) width = xmax - xmin height = ymax - ymin p = patches.Rectangle(xy, width, height, hatch=hatch, fill=fill, fc=fc, ec=ec, zorder=zorder, **kwds) axes.add_patch(p)