Source code for ipymd.plotting.plotter

# -*- coding: utf-8 -*-
"""
Created on Fri Jul  1 16:45:06 2016

@author: cjs14
"""
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
from PIL import Image
from io import BytesIO
from IPython import get_ipython, display

from IPython.display import HTML
from .JSAnimation.IPython_display import display_animation

[docs]def style(style): """A context manager to apply matplotlib style settings from a style specification. Popular styles include; default, ggplot, xkcd, and are used in the the following manner: :: with ipymd.plotting.style('default'): plot = ipymd.plotting.Plotter() plot.display_plot() Parameters ---------- style : str, dict, or list A style specification. Valid options are: +------+-------------------------------------------------------------+ | str | The name of a style or a path/URL to a style file. For a | | | list of available style names, see `style.available`. | +------+-------------------------------------------------------------+ | dict | Dictionary with valid key/value pairs for | | | `matplotlib.rcParams`. | +------+-------------------------------------------------------------+ | list | A list of style specifiers (str or dict) applied from first | | | to last in the list. | +------+-------------------------------------------------------------+ """ if style=='xkcd': return plt.xkcd() else: return plt.style.context(style)
[docs]class Plotter(object): """ a class to deal with data plotting """ def __init__(self,nrows=1,ncols=1,figsize=(5,4)): """ a class to deal with data plotting Attributes ---------- figure : matplotlib.figure the figure axes : list or single matplotlib.axes if more than one then returns a list (ordered in reading direction), else returns one instance """ #ensure IPython shows matplotlib in inline mode ipython = get_ipython() if ipython is not None: ipython.run_line_magic('matplotlib', 'inline') if nrows==0 and ncols==0: self._fig = plt.figure(figsize=figsize) self._axes = [] else: self._fig, axes = plt.subplots(nrows,ncols,squeeze=False,figsize=figsize) self._axes = axes.flatten() plt.close() def _get_mplfigure(self): return self._fig def _set_mplfigure(self, fig): self._fig = fig self._axes = fig.get_axes() figure = property(_get_mplfigure, _set_mplfigure) def _get_axes(self): if len(self._axes) == 1: return self._axes[0] else: return self._axes axes = property(_get_axes)
[docs] def display_plot(self, tight_layout=False): """ display plot in IPython if tight_layout is True it may crop anything outside axes """ ipython = get_ipython() if ipython is None: self._fig.show() else: current_config = ipython.run_line_magic('config', "InlineBackend.print_figure_kwargs") new_config = current_config.copy() if tight_layout: new_config['bbox_inches'] = 'tight' else: new_config['bbox_inches'] = None ipython.run_line_magic('config', 'InlineBackend.print_figure_kwargs = {0}'.format(new_config)) display.display(self._fig) ipython.run_line_magic('config', 'InlineBackend.print_figure_kwargs = {0}'.format(current_config))
[docs] def get_image(self,size=300,dpi=300, tight_layout=False): """return as PIL image if tight_layout is True it may crop anything outside axes """ bbox_inches = 'tight' if tight_layout else None buf = BytesIO() self._fig.savefig(buf, dpi=dpi,format='png',bbox_inches=bbox_inches) buf.seek(0) img = Image.open(buf) img.thumbnail((int(size),int(size)),Image.ANTIALIAS) buf.close() return img
[docs] def resize_axes(self,width=0.8,height=0.8,left=0.1,bottom=0.1, axes=0): """ resiaze axes, for instance to fit object outside of it """ self._axes[axes].set_position([left,bottom,width,height])
[docs] def add_image(self, image, axes=0, interpolation="bicubic", hide_axes=True, width=1., height=1.,origin=(0.,0.), **kwargs): """add image to axes """ x0,y0=origin self._axes[axes].imshow(image, interpolation="bicubic", extent=(x0,x0+width,y0,y0+height) ,**kwargs) if hide_axes: self._axes[axes].get_xaxis().set_visible(False) self._axes[axes].get_yaxis().set_visible(False) self._axes[axes].set_frame_on(False)
# from from http://matplotlib.org/examples/pylab_examples/demo_annotation_box.html # TODO resize axes automatically
[docs] def add_image_annotation(self, img, xy=(0,0), arrow_xy=None, axes=0, zoom=1, xytype='axes points', arrow_xytype='data', arrowprops=dict(facecolor='black', arrowstyle="simple", connectionstyle="arc3,rad=0.2", alpha=0.4)): """ add an image to the plot coordtype: ==================== ==================================================== argument coordinate system ==================== ==================================================== 'figure points' points from the lower left corner of the figure 'figure pixels' pixels from the lower left corner of the figure 'figure fraction' 0,0 is lower left of figure and 1,1 is upper right 'axes points' points from lower left corner of axes 'axes pixels' pixels from lower left corner of axes 'axes fraction' 0,0 is lower left of axes and 1,1 is upper right 'data' use the axes data coordinate system ==================== ==================================================== for arrowprops see http://matplotlib.org/users/annotations_guide.html#annotating-with-arrow """ imagebox = OffsetImage(img, zoom=zoom) if arrow_xy is None: arrow_xy = (0,0) arrow_xytype='data' arrowprops={} ab = AnnotationBbox(imagebox, xy=arrow_xy, xybox=xy, xycoords=arrow_xytype, boxcoords=xytype,#"offset points", pad=0.5, arrowprops=arrowprops, ) self._axes[axes].add_artist(ab)
[docs]def animation_line(x_iter, y_iter, interval=20, xlim=(0,1),ylim=(0,1), incl_controls=True, plot=None,ax=0,**plot_kwargs): """create an animation of multiple x,y data sets x_iter : iterable any iterable of x data sets, e.g. [[1,2,3],[4,5,6]] y_iter : iterable an iterable of y data sets, e.g. [[1,2,3],[4,5,6]] interval : int draws a new frame every *interval* milliseconds xlim : tuple the x_limits for the axes (ignored if using existing plotter) ylim : tuple the y_limits for the axes (ignored if using existing plotter) incl_controls : bool include Javascript play controls plot : ipymd.plotting.Plotter an existing plotter object ax : int the id number of the axes on which to plot (if using existing plotter) plot_kwargs : various key word arguments to pass to plot method, e.g. marker='o', color='b', ... Returns ------- html : IPython.core.display.HTML a html object Notes ----- x_iter and y_iter can be yield functions such as: :: def y_iter(x_iter): for xs in x_iter: yield [i**2 for i in xs] This means that the values do not have to be necessarily pre-computed. """ if plot is None: plotter = Plotter() else: plotter = plot if isinstance(plotter.axes, list): ax = plotter.axes[ax] else: ax = plotter.axes if plot is None: ax.set_xlim(xlim) ax.set_ylim(ylim) xiter = iter(x_iter) yiter = iter(y_iter) xy_plot, = ax.plot([], [], animated=True, **plot_kwargs) # initialization function: plot the background of each frame def init(): xy_plot.set_data([], []) return (xy_plot,) # animation function. This is called sequentially def animate(i): try: xs = xiter.next() ys = yiter.next() except StopIteration: xs = [] ys = [] xy_plot.set_data(xs,ys) return (xy_plot,) anim = animation.FuncAnimation(plotter.figure, animate, init_func=init, frames=x_iter, interval=interval, blit=True) if incl_controls: html = display_animation(anim) else: html = HTML(anim.to_html5_video()) # cleanup xy_plot.remove() return html
[docs]def animation_scatter(x_iter, y_iter, interval=20, xlim=(0,1),ylim=(0,1), incl_controls=True, plot=None,ax=0,**plot_kwargs): """create an animation of multiple x,y data sets x_iter : iterable any iterable of x data sets, e.g. [[1,2,3],[4,5,6]] y_iter : iterable an iterable of y data sets, e.g. [[1,2,3],[4,5,6]] interval : int draws a new frame every *interval* milliseconds xlim : tuple the x_limits for the axes (ignored if using existing plotter) ylim : tuple the y_limits for the axes (ignored if using existing plotter) incl_controls : bool include Javascript play controls plot : ipymd.plotting.Plotter an existing plotter object ax : int the id number of the axes on which to plot (if using existing plotter) plot_kwargs : various key word arguments to pass to plot method, e.g. marker='o', color='b', ... Returns ------- html : IPython.core.display.HTML a html object Notes ----- x_iter and y_iter can be yield functions such as: :: def y_iter(x_iter): for xs in x_iter: yield [i**2 for i in xs] This means that the values do not have to be necessarily pre-computed. """ if plot is None: plotter = Plotter() else: plotter = plot if isinstance(plotter.axes, list): ax = plotter.axes[ax] else: ax = plotter.axes if plot is None: ax.set_xlim(xlim) ax.set_ylim(ylim) xiter = iter(x_iter) yiter = iter(y_iter) xy_plot = ax.scatter([], [], animated=True, **plot_kwargs) # initialization function: plot the background of each frame def init(): xy_plot.set_offsets([]) return (xy_plot,) # animation function. This is called sequentially def animate(i): try: xs = xiter.next() ys = yiter.next() except StopIteration: xs = [] ys = [] xy_plot.set_offsets(zip(xs,ys)) return (xy_plot,) anim = animation.FuncAnimation(plotter.figure, animate, init_func=init, frames=x_iter, interval=interval, blit=True) if incl_controls: html = display_animation(anim) else: html = HTML(anim.to_html5_video()) # cleanup xy_plot.remove() return html
[docs]def animation_contourf(x_iter, y_iter, z_iter, interval=20, xlim=(0,1),ylim=(0,1),zlim=(0,1.), cmap='viridis', cbar=True, incl_controls=True, plot=None,ax=0,**plot_kwargs): """create an animation of multiple x,y data sets x_iter : iterable any iterable of x data sets, e.g. [[1,2,3],[4,5,6]] y_iter : iterable an iterable of y data sets, e.g. [[1,2,3],[4,5,6]] y_iter : iterable an iterable of z(x,y) data sets, each set must be of shape (len(x), len(y)) interval : int draws a new frame every *interval* milliseconds xlim : tuple the x_limits for the axes (ignored if using existing plotter) ylim : tuple the y_limits for the axes (ignored if using existing plotter) zlim : tuple the z_limits for the colormap cmap : str or matplotlib.cm the colormap to use (see http://matplotlib.org/examples/color/colormaps_reference.html) incl_controls : bool include Javascript play controls plot : ipymd.plotting.Plotter an existing plotter object ax : int the id number of the axes on which to plot (if using existing plotter) plot_kwargs : various key word arguments to pass to plot method, e.g. marker='o', color='b', ... Returns ------- html : IPython.core.display.HTML a html object Notes ----- x_iter and y_iter can be yield functions such as: :: def y_iter(x_iter): for xs in x_iter: yield [i**2 for i in xs] This means that the values do not have to be necessarily pre-computed. """ if plot is None: plotter = Plotter() else: plotter = plot if isinstance(plotter.axes, list): ax = plotter.axes[ax] else: ax = plotter.axes if plot is None: ax.set_xlim(xlim) ax.set_ylim(ylim) xiter = iter(x_iter) yiter = iter(y_iter) ziter = iter(z_iter) zmin, zmax = zlim # has to be in a list to pass between nested functions c_plot = [ax.contourf([0,1], [0,1], [[0,1],[0,1]], vmin=zmin,vmax=zmax,cmap=cmap, animate=True,**plot_kwargs)] if cbar: plt.colorbar(c_plot[0], ax=ax) plt.close() # initialization function: plot the background of each frame def init(): for coll in c_plot[0].collections: ax.collections.remove(coll) c_plot[0] = ax.contourf([0,1], [0,1], [[0,0],[0,0]], vmin=zmin,vmax=zmax,cmap=cmap, animate=True,**plot_kwargs) return c_plot[0].collections # animation function. This is called sequentially def animate(i): try: xs = xiter.next() ys = yiter.next() zs = ziter.next() except StopIteration: xs = [] ys = [] zs = [] X, Y = np.meshgrid(xs, ys) for coll in c_plot[0].collections: ax.collections.remove(coll) c_plot[0] = ax.contourf(X,Y,zs,vmin=zmin,vmax=zmax,cmap=cmap, animate=True,**plot_kwargs) return c_plot[0].collections anim = animation.FuncAnimation(plotter.figure, animate, init_func=init, frames=x_iter, interval=interval, blit=True) if incl_controls: html = display_animation(anim) else: html = HTML(anim.to_html5_video()) # cleanup for coll in c_plot[0].collections: ax.collections.remove(coll) return html