Source code for gpe.plot_utils

try:
    import holoviews.plotting.mpl

[docs] hv = holoviews
except (ImportError, RuntimeError): hv = False from matplotlib import pyplot as plt from matplotlib.figure import Figure from matplotlib.gridspec import GridSpec, GridSpecFromSubplotSpec # hv.extension('matplotlib') # Only use this in notebooks. if hv:
[docs] class MPLFigure(hv.Element): """Wraps a matplotlib figure.""" def __init__(self, data, **kwargs): if not isinstance(data, Figure): raise ValueError("MPLFigure data must be a matplotlib figure") super(MPLFigure, self).__init__(data, **kwargs)
class FigurePlot(hv.plotting.mpl.ElementPlot): def initialize_plot(self, ranges=None): element = self.hmap.last return element.data def update_frame(self, key, ranges=None): element = self._get_frame(key) self.handles["fig"] = element.data return element.data hv.Store.register({MPLFigure: FigurePlot}, "matplotlib") """ from matplotlib import pyplot as plt def figure_fn(x): fig, ax = plt.subplots() ax.plot([0,1,x]) plt.close(fig) return MPLFigure(fig) hv.HoloMap({i: figure_fn(i) for i in range(10)}) """
[docs] class MPLGrid: """Object for stacking plots. After initializing the object, call next() to get the next axis. By default axes will stack vertically. Attributes ---------- down : bool If True, then stack elements vertically growing down. Otherwise stack elements horizontally growing right. ratios : [float] List of relative size of each entry. axes : [Axes, MPLGrid] List of axes of nested grid objects. direction : ['down', 'right'] Direction of growth. right : bool Specify if y axis labels should be on the right or left. scale : bool If False, then when a parent MPLGrid is being adjusted, the ratio for this grid component will be the sum of the ratios rather than the specified parent ratio. If True, then all the components will be scaled to fit into the parents ratio. share : bool If `True`, then axes in the growth direction are shared, and tick labels etc. are suppressed except for the final one. space : float, None Space (hspace or wspace) between grids. If this is None, then the space is set of zero if the axes are shared and 0.1 otherwise. size : float Default size for new elements. """ def __init__( self, fig=None, subplot_spec=None, ax=None, direction="down", right=False, scale=True, space=None, share=False, size=1, **kw, ): if fig is None: fig = plt.gcf() else: if ax is not None: fig.delaxes(ax) if subplot_spec is None: if ax is not None: try: subplot_spec = ax.get_subplotspec() except AttributeError: pass if subplot_spec is None: subplot_spec = GridSpec(1, 1)[0, 0]
[docs] self.fig = fig
[docs] self.subplot_spec = subplot_spec
[docs] self.shape = [0, 0]
[docs] self.kw = kw
[docs] self.right = right
[docs] self.share = share
if direction not in set(["down", "right"]): raise ValueError( "direction must be 'down' or 'right': got direction={}".format(direction) )
[docs] self.direction = direction
[docs] self._ratios = []
[docs] self.axes = []
[docs] self.space = space
[docs] self.scale = scale
[docs] self.size = 1
[docs] def __getitem__(self, key): return self.axes[key]
[docs] def adjust(self): """Adjust the positions of all the axes.""" ax_ = None # Previous axis axs = list(zip(self.axes, self._gridspec)) if self.direction == "right" and not self.right: # Make sure the last axis is the one that should have labels. axs = reversed(axs) for ax, subplot_spec in axs: if isinstance(ax, MPLGrid): ax.subplot_spec = subplot_spec ax.adjust() ax_ = None continue if not ax: break ax.set_position(subplot_spec.get_position(self.fig)) ax.set_subplotspec(subplot_spec) # necessary if using tight_layout() if self.direction == "down": if ax_ and ax.get_shared_x_axes().joined(ax, ax_): plt.setp(ax_.get_xticklabels(), visible=False) ax.tick_params(axis="x", direction="inout") else: if ax_ and ax.get_shared_y_axes().joined(ax, ax_): plt.setp(ax_.get_yticklabels(), visible=False) ax.tick_params(axis="y", direction="inout") if self.right: ax.yaxis.tick_right() ax.yaxis.set_label_position("right") ax_ = ax
[docs] def __repr__(self): res = [] for ratio, ax in zip(self.ratios, self.axes): if isinstance(ax, MPLGrid): res.append(ax) else: res.append(ratio) return repr(res)
@property
[docs] def ratios(self): """Return the size ratios for the various axes. If the sub-axis is an MPLGrid instance with scale == False, then the ratio is the sum of the sub-grid's ratios. """ ratios = [] for ratio, axis in zip(self._ratios, self.axes): if isinstance(axis, MPLGrid) and not axis.scale: ratio = sum(axis.ratios) ratios.append(ratio) return ratios
@property
[docs] def _gridspec(self): ratios = self.ratios N = len(ratios) if N == 0: # This can happen during initialization N = 1 ratios = [1] space = self.space if space is None: space = 0 if self.share else 0.1 args = dict( nrows=1, ncols=1, wspace=space, hspace=space, subplot_spec=self.subplot_spec ) if self.direction == "down": args["nrows"] = N args["height_ratios"] = ratios else: args["ncols"] = N args["width_ratios"] = ratios args.update(self.kw) if hasattr(self, "debug"): print(id(self), self.subplot_spec) return GridSpecFromSubplotSpec(**args)
[docs] def next(self, size=None): """Return the next axis.""" if size is None: size = self.size if size == 0: return None self._ratios.append(size) ax_prev = self.axes[-1] if self.axes else None self.axes.append(None) args = {} if self.share and ax_prev: if self.direction == "down": args["sharex"] = ax_prev else: args["sharey"] = ax_prev ax = self.fig.add_subplot(self._gridspec[-1], **args) self.axes[-1] = ax self.adjust() return ax
[docs] def empty(self, size=None): """Return the next axis but hide it so it acts as a space.""" ax = self.next(size=size) ax.set_visible(False) return ax
[docs] def grid(self, size=None, **kw): """Insert a sub-grid and return it.""" if size is None: size = self.size if size == 0: return None self._ratios.append(size) self.axes.append(None) grid = MPLGrid(fig=self.fig, subplot_spec=self._gridspec[-1], **kw) self.axes[-1] = grid self.adjust() return grid
[docs] def rescale(self, tight=None, scalex=True, scaley=True, force=False): for ax in self.axes: if isinstance(ax, MPLGrid): ax.rescale(tight=tight, scalex=scalex, scaley=scaley) else: if force: ax.set_autoscale_on(True) ax.relim() ax.autoscale_view(tight=tight, scalex=scalex, scaley=scaley)