About the Implementation

Why different backends inheriting from the Plot class? Why not using something like holoviews, which allows to plot numerical data with different plotting libraries using a common interface? In short:

  • Holoviews only support Matplotlib, Bokeh, Plotly. This would make impossible to add support for further libraries, such as K3D, …

  • Not all needed features might be implemented on Holoviews. Think for example to plotting a gradient-colored line. Matplotlib and Bokeh are able to visualize it correctly, Plotly doesn’t support this functionality. By not using Holoviews, we can more easily implement some work around.

class spb.backends.base_backend.Plot(*args, **kwargs)[source]

Base class for all backends. A backend represents the plotting library, which implements the necessary functionalities in order to use SymPy plotting functions.

How the plotting module works:

  1. The user creates the symbolic expressions and calls one of the plotting functions.

  2. The plotting functions generate a list of instances of BaseSeries, containing the necessary information to generate the appropriate numerical data and create the proper visualization (eg the expression, ranges, series name, …).

  3. The plotting functions instantiate the Plot class, which stores the list of series and the main attributes of the plot (eg axis labels, title, etc.). Among the keyword arguments, there must be backend=, where a subclass of Plot can be specified in order to use a particular plotting library.

  4. Each data series will be associated to a corresponding renderer, which receives the numerical data and add it to the figure. A renderer is also responsible to keep objects up-to-date when interactive-widgets plots are used. The figure is populated with numerical data when the show() method or the fig attribute are called.

The backend should check if it supports the data series that it’s given. Please, explore MatplotlibBackend source code to understand how a backend should be coded.

Also note that setting attributes to plot objects or to data series after they have been instantiated is strongly unrecommended, as it is not guaranteed that the figure will be updated.

titlestr, optional

Set the title of the plot. Default to an empty string.

xlabel, ylabel, zlabelstr, optional

Set the labels of the plot. Default to an empty string.

legendbool, optional

Show or hide the legend. By default, the backend will automatically set it to True if multiple data series are shown.

xscale, yscale, zscalestr, optional

Discretization strategy for the provided domain along the specified direction. Can be either ‘linear’ or ‘log’. Default to ‘linear’. If the backend supports it, the specified direction will use the user-provided scale. By default, all backends uses linear scales for both axis. None of the backends support logarithmic scale for 3D plots.

gridbool, optional

Show/Hide the grid. The default value depends on the backend.

xlim, ylim, zlim(float, float), optional

Focus the plot to the specified range. The tuple must be in the form (min_val, max_val).

aspect(float, float) or str, optional

Set the aspect ratio of the plot. It only works for 2D plots. The values depends on the backend. Read the interested backend’s documentation to find out the possible values.


The subclass to be used to generate the plot.

size(float, float) or None, optional

Set the size of the plot, (width, height). Default to None.

See also

MatplotlibBackend, PlotlyBackend, BokehBackend, K3DBackend


In order to be used by SymPy plotting functions, a backend must implement the following methods and attributes:

  • show(self): used to loop over the data series, generate the numerical data, plot it and set the axis labels, title, …

  • save(self, path, **kwargs): used to save the current plot to the specified file path.

  • self._fig: an instance attribute to store the backend-specific plot object, which can be retrieved with the Plot.fig attribute. This object can then be used to further customize the resulting plot, using backend-specific commands.

  • update_interactive(self, params): this method receives a dictionary mapping parameters to their values from the iplot function, which are going to be used to update the objects of the figure.


Combine multiple plots together to create a new plot:

>>> from sympy import symbols, sin, cos, log, S
>>> from spb import plot, plot3d
>>> x, y = symbols("x, y")
>>> p1 = plot(sin(x), cos(x), show=False)
>>> p2 = plot(sin(x) * cos(x), log(x), show=False)
>>> p3 = p1 + p2

(Source code, png)


Use the index notation to access the data series. Let’s generate the numerical data associated to the first series:

>>> p1 = plot(sin(x), cos(x), show=False)
>>> xx, yy = p1[0].get_data()

(Source code)

Create a new backend with a custom colorloop:

>>> from spb.backends.matplotlib import MB
>>> class MBchild(MB):
...     colorloop = ["r", "g", "b"]
>>> plot(sin(x) / 3, sin(x) * S(2) / 3, sin(x), backend=MBchild)
Plot object containing:
[0]: cartesian line: sin(x)/3 for x over (-10.0, 10.0)
[1]: cartesian line: 2*sin(x)/3 for x over (-10.0, 10.0)
[2]: cartesian line: sin(x) for x over (-10.0, 10.0)

(Source code, png)


Create a new backend with custom color maps for 3D plots. Note that it’s possible to use Plotly/Colorcet/Matplotlib colormaps interchangeably.

>>> from spb.backends.matplotlib import MB
>>> import colorcet as cc
>>> class MBchild(MB):
...     colormaps = ["plotly3", cc.bmy]
>>> plot3d(
...     (cos(x**2 + y**2), (x, -2, 0), (y, -2, 2)),
...     (cos(x**2 + y**2), (x, 0, 2), (y, -2, 2)),
...     backend=MBchild, n1=25, n2=50, use_cm=True)
Plot object containing:
[0]: cartesian surface: cos(x**2 + y**2) for x over (-2.0, 0.0) and y over (-2.0, 2.0)
[1]: cartesian surface: cos(x**2 + y**2) for x over (0.0, 2.0) and y over (-2.0, 2.0)

(Source code, png)

spb.backends.base_backend.Plot.append(self, arg)

Adds an element from a plot’s series to an existing plot.


An instance of BaseSeries which will be used to generate the numerical data.

See also



Consider two Plot objects, p1 and p2. To add the second plot’s first series object to the first, use the append method, like so:

>>> from sympy import symbols
>>> from spb import plot
>>> x = symbols('x')
>>> p1 = plot(x*x, show=False)
>>> p2 = plot(x, show=False)
>>> p1.append(p2[0])
>>> p1
Plot object containing:
[0]: cartesian line: x**2 for x over (-10.0, 10.0)
[1]: cartesian line: x for x over (-10.0, 10.0)

(Source code, png)

spb.backends.base_backend.Plot.extend(self, arg)

Adds all series from another plot.

argPlot or sequence of BaseSeries

See also



Consider two Plot objects, p1 and p2. To add the second plot to the first, use the extend method, like so:

>>> from sympy import symbols
>>> from spb import plot
>>> x = symbols('x')
>>> p1 = plot(x**2, show=False)
>>> p2 = plot(x, -x, show=False)
>>> p1.extend(p2)
>>> p1
Plot object containing:
[0]: cartesian line: x**2 for x over (-10.0, 10.0)
[1]: cartesian line: x for x over (-10.0, 10.0)
[2]: cartesian line: -x for x over (-10.0, 10.0)

(Source code, png)

spb.backends.base_backend.Plot.update_interactive(self, params)

Implement the logic to update the data generated by interactive-widget plots.


Map parameter-symbols to numeric values.

Plot.colorloop = []

List of colors to be used in line plots or solid color surfaces.

Plot.colormaps = []

List of color maps to render surfaces.

Plot.cyclic_colormaps = []

List of cyclic color maps to render complex series (the phase/argument ranges over [-pi, pi]).


Returns the figure used to render/display the plots.