Skip to content

Note

Click here to download the full example code

Plotting Overview#

Overview of 2D and 3D plotting and how NAVis' rendering backends compare.

NAVis contains functions for (static) 2D and (interactive) 3D plotting. These functions can use various different backends for plotting. For 2D plots we use matplotlib and for 3D plots we use either octarine, plotly or k3d.

Which plotting method (2D/3D) and which backend (octarine, plotly, etc.) to use depends on what you are after (e.g. static, publication quality figures vs interactive data exploration) and your environment (e.g. Jupyter/VS code or terminal). Here's a quick summary:

Backend Used in Pros Cons
matplotlib navis.plot2d
navis.plot1d
navis.plot_flat
- high quality (vector graphics!)
- works in Jupyter and terminal
- exports to vector graphics
- myriads of ways to adjust plots
- struggles with correct depth layering in complex scenes
- not very interactive (although you can adjust perspective)
- slow for large scenes
- not good for voxel data (e.g. image volumes)
octarine navis.plot3d - blazingly fast thanks to WGPU backend
- works in terminal and Jupyter
- very interactive
- may not work on older systems (use plotly instead)
- not persistent (i.e. dies with notebook kernel)
- can't share interactive plot (screenshots only)
plotly navis.plot3d - works "inline" for Jupyter environments
- persistent (i.e. plots get saved alongside notebook)
- can produce offline HTML plots for sharing
- not very fast for large scenes
- large file sizes (i.e. makes for large .ipynb notebook files)
- horrendous for voxel data (i.e. images)
k3d navis.plot3d - works "inline" for Jupyter environments
- super fast and performant
- in memory (i.e. does not increase notebook file size)
- does not work in terminal sessions
- not persistent (i.e. dies with notebook kernel)
- can't share interactive plot (screenshots only)

In theory there is feature parity across backends but due to built-in limitations there are minor differences.

If you installed NAVis via pip install navis[all] all of the above backends should be available to you. If you ran a minimal install via pip install navis you may need to install the backends separately - NAVis will complain if you try to use a backend that is not installed!

Note

The plots in this tutorial are optimized for light-mode. If you are using dark-mode, you may have trouble seeing e.g. axis or labels.

2D plots#

This uses matplotlib to generate static 2D plots. The big advantage is that you can save these plots as vector graphics. Unfortunately, matplotlib's capabilities regarding 3D data are limited. The main problem is that depth (z) is only simulated by trying to layer objects (lines, vertices, etc.) according to their z-order rather than doing proper rendering which is why you might see some neurons being plotted in front of others even though they are actually behind them. It's still great for plotting individual neurons or small groups thereof!

Let's demonstrate with a simple example using the default settings:

import navis
import matplotlib.pyplot as plt

nl = navis.example_neurons(kind="skeleton")

# Plot using default settings
fig, ax = navis.plot2d(nl, view=("x", "-z"), method="2d")
plt.tight_layout()

tutorial plotting 00 intro

Note

We set view("x", "-z") above to get a frontal view of the example neurons. You may need to adjust this depending on the orientation of your neurons.

Above plot used the default matplotlib 2D plot. You might notice that the plot looks rather "flat" - i.e. neurons seem to be layered on top of each other without intertwining. That is one of the limitations of matplotlib's 3d backend. We can try to ameliorate this by adjusting the method parameter:

# Plot settings for more complex scenes - comes at a small performance cut though
fig, ax = navis.plot2d(nl, method="3d_complex", view=("x", "-z"))
plt.tight_layout()

tutorial plotting 00 intro

Looks better now, doesn't it? Now what if we wanted to adjust the perspective? For 3d axes, matplotlib lets us adjust the viewing angle by setting the elev, azim and roll attributes. See also this official explanation.

Let's give that a shot:

Plot again

fig, ax = nl.plot2d(
    method="3d_complex", view=("x", "-z"), non_view_axes3d="show", radius=True
)

# Change view to see the neurons from a different angle
ax.elev = -20
ax.azim = 45
ax.roll = 180

plt.tight_layout()

tutorial plotting 00 intro

Note

Did you note that we set non_view_axes3d='show' in above example? By default, NAVis hides the axis that is parallel to the viewing direction to not clutter the image. Because we were going to change the perspective, we set it to show. FYI: if the plot is rendered in a separate window (e.g. if you run Python from terminal), you can change the perspective by dragging the image.

We can use this to generate small animations:

# Render 3D rotation
for i in range(0, 360, 10):
   # Change rotation
   ax.azim = i
   # Save each incremental rotation as frame
   plt.savefig('frame_{0}.png'.format(i), dpi=200)

rotation

3D plots#

By "3D plots" we typically mean interactive 3D plots as opposed to the (mostly) static 2D or semi-3D plots above. 3D plots are great for exploring your data interactively but you can also use them to generate high-quality static images.

As laid out at the top of this page: for 3D plots, we are using either octarine, plotly or k3d. In brief:

backend Jupyter Terminal
octarine yes yes
plotly yes yes but only via export to html
k3d yes no

By default, the choice is automatic and depends on (1) what backends are installed and (2) the context. The first available backend in each row wins:

Context Backend priority (first available wins)
IPython / Terminal / scripts octarine plotly
Jupyter Lab / Notebook plotly octarine k3d

You can always force a specific backend using the backend parameter in navis.plot3d:

n = navis.example_neurons()
navis.plot3d(n)
n = navis.example_neurons()
navis.plot3d(n, backend='octarine')
n = navis.example_neurons()
navis.plot3d(n, backend='plotly')
n = navis.example_neurons()
navis.plot3d(n, backend='k3d')

Alternatively, you can also set the default backend using an environment variable. For example:

export NAVIS_PLOT3D_BACKEND="octarine"

Google Colaboratory

The jupyter_rfb that Octarine uses to render 3D plots in Jupyter does not work in Google Colaboratory. There, use the plotly backend instead.

With that out of the way, let's have a look at some 3D plots! You will notice that for the octarine and k3d backends we're just showing screenshots - that's because their interactive plots can't be embedded into this documentation.

Octarine#

Octarine works via a Viewer object which allows you to interactively add/remove objects, change colors, etc. It uses modern WGPU (rather than OpenGL) which makes it very fast even for large scenes:

nl = navis.example_neurons()
viewer = navis.plot3d(nl, backend='octarine')
octarine

Showing the viewer in Jupyter

From Jupyter, you may need to call viewer.show() in the last line of the cell for the Octarine widget to appear.

A few things to know about the Octarine backend:

  • The viewer is dynamic - keep adding/removing items from other cells - but it dies with the kernel (unlike plotly)!
  • NAVis tracks a "primary" viewer; subsequent navis.plot3d calls add to it. Force a new one with navis.plot3d(nl, viewer='new') or target a specific one with navis.plot3d(nl, viewer=viewer).
  • You can resize the canvas (in Jupyter by dragging the lower right corner).
  • In Jupyter, rendering runs in your kernel and frames are streamed via a remote frame buffer (jupyter_rfb); a remote kernel may add some lag.

Some important methods for the viewer object:

viewer.close()                   # (1)!
navis.close3d()                  # (2)!
navis.plot3d(nl)                 # (3)!
navis.plot3d(nl, viewer=viewer)  # (4)!
viewer.clear3d()                 # (5)!
navis.clear3d()                  # (6)!
  1. Close the viewer.
  2. Close the current primary viewer.
  3. Add neurons to the primary viewer.
  4. Add neurons to a specific viewer.
  5. Clear the viewer.
  6. Clear the primary viewer.

The Octarine viewer has many more neat features - check out its documentation to learn more.

K3d#

k3d plots work in Jupyter (and only there) but unlike plotly don't persist across sessions. Hence we will only briefly demo them using static screenshots and then move on to plotly. Almost everything you can do with the plotly backend can also be done with k3d (or octarine for that matter)!

p = navis.plot3d(nl, backend="k3d")

k3d

Plotly#

Last but not least, we have the plotly backend. This is the only backend which allows us to embed interactive 3D plots into this documentation. The main advantage of plotly is that it works "inline" in Jupyter notebooks and that you can export the plots as standalone HTML files. The main disadvantage is that it can be quite slow for large scenes and that the resulting .ipynb notebook files can get quite large.

Using plotly as backend generates "inline" plots by default (i.e. they are rendered right away):

navis.plot3d(
    nl,
    backend="plotly",
    connectors=False,
    radius=True,  # use node radii for skeletons
    legend_orientation="h",  # horizontal legend (more space for plot)
)

Instead of inline plotting, you can also export your plotly figure as 3D html file that can be opened in any browser:

import plotly

# Prevent inline plotting
fig = nl.plot3d(backend='plotly', connectors=False, width=1400, height=1000, inline=False)

# Save figure to html file
plotly.offline.plot(fig, filename='~/Documents/3d_plot.html')
Action Octarine Plotly K3d
Rotate Left Button + drag Left Button + drag Left Button + drag
Zoom scroll wheel scroll wheel scroll wheel
Pan Right Button + drag Right Button + drag Right Button + drag
Hide/Unhide
objects
viewer.hide()
viewer.show()
click on legend click on legend

Camera rotation

If the camera rotation using plotly causes problems, try clicking on the "Orbital rotation" in the upper right tool bar. plotly orbital

%%

High-quality renderings#

Above we demo'ed making a little GIF using matplotlib. While that's certainly fun, it's not very high production value. For high quality videos and renderings I recommend you check out the tutorial on navis' Blender interface. Here's a little taster:

Total running time of the script: ( 0 minutes 12.490 seconds)

Download Python source code: tutorial_plotting_00_intro.py

Download Jupyter notebook: tutorial_plotting_00_intro.ipynb

Gallery generated by mkdocs-gallery