Overview¶
NAVis contains functions for 2D and 3D plotting. These functions use matplotlib for 2D, and vispy, plotly or k3d for 3D.
Which plotting method (2D/3D) and which backend (plotly, vispy, etc.) to use depends on what you are after (e.g. publication quality figures vs interactive inspection) and your environment (Jupyter or terminal). Here’s a quick summary:
backend |
Pros |
Cons |
---|---|---|
matplotlib ( |
|
|
vispy ( |
|
|
plotly ( |
|
|
k3d ( |
|
|
In theory there is feature parity across backends but due to some built-in limitations there are some differences. Also note that the k3d backend is a recent addition and the underlying library has to be installed separately (pip3 install k3d -U
).
2D plots¶
This uses matplotlib to generate 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 according to their z-order rather than doing proper rendering. You have several options to deal with this: see the method
parameter in navis.plot2d()
. It is important to be aware of this issue as e.g. neuron A might be plotted in front of neuron B even though it is actually spatially behind it. The more busy your plot and the more neurons intertwine, the more likely this is to happen.
Let’s start with a simple example using default settings:
import navis
import matplotlib.pyplot as plt
nl = navis.example_neurons(kind='skeleton')
# Plot using default settings
fig, ax = nl.plot2d() # equivalent to `navis.plot2d(nl)`
plt.show()

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 adjust the method
parameter:
# Plot settings for more complex scenes - comes at a performance cut though
fig, ax = nl.plot2d(method='3d_complex')
plt.show()

Looks better now, doesn’t it? Now what if we wanted to adjust the perspective?
# Plot again
fig, ax = nl.plot2d(method='3d_complex')
# Adjust `azim` (azimuth) and `elev` (elevation) to change view
ax.azim, ax.elev = -90, -90
# Zoom in a bit
ax.dist = 6
plt.show()

Note
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)

Plotting meshes¶
We can also plot meshes (MeshNeurons
, trimesh
, or Volumes
) using matplotlib. To illustrate, we will use one of the neuropil meshes that ship with NAVis:
# Load the Lateral Horn ("LH") volume
lh = navis.example_volume('LH')
# Adjust color and alpha if you like
lh.color = (0, 1, 0, .1)
# Plot
fig, ax = navis.plot2d([nl ,lh], method='3d_complex')
ax.elev = ax.azim = -90
ax.dist = 6
plt.show()

Note how this time we used navis.plot2d()
instead of the NeuronList
short-hand method n.plot3d
to plot multiple objects at a time.
Fine-tuning plots¶
These 2D plots (and also the 3D plots further down) can be adjusted and fine-tuned to your liking.
Changing colors¶
NAVis plot3d()
and plot2d()
accept various ways to specify colors for neurons. We’ll demonstrate this using some examples.
Same color for all neurons:
fig, ax = navis.plot2d(nl, color=(1, 0, 0))
ax.axim = ax.elev = -90
plt.show()

Passing a list of colors (one for each neuron in sequence):
fig, ax = navis.plot2d(nl, color=['r', 'g', 'b', 'm', 'c'], method='3d_complex')
ax.axim = ax.elev = -90
plt.show()

You can map colors to neurons by either using the neuron itself, its .id
or its .name
attribute.
cmap = {nl[0]: 'r',
nl[1].id: (0, 1, 0),
nl[2].name: 'g',
nl[3]: 'm',
nl[4]: 'k'}
fig, ax = navis.plot2d(nl, color=cmap, method='3d_complex')
ax.axim = ax.elev = -90
plt.show()

navis.Volume
colors work similarly with the difference that the fallback color is defined by the .color
attribute of the volume:
lh = navis.example_volume('LH')
# Set color implicitly
lh.color = (.8, .8, .8, .2)
fig, ax = navis.plot2d([nl, lh])
ax.axim = ax.elev = -90
plt.show()

Adjusting lines¶
plot2d()
accepts a range of parameters to fine-tune plots. See these examples:
Change linewidth
fig, ax = navis.plot2d(nl, linewidth=2, method='3d_complex')
ax.axim = ax.elev = -90
plt.show()

Change line style
fig, ax = navis.plot2d(nl, linewidth=1.5, linestyle='--', method='3d_complex')
ax.axim = ax.elev = -90
plt.show()

Change alpha
fig, ax = navis.plot2d(nl, linewidth=1.5, alpha=.25, method='3d_complex')
ax.axim = ax.elev = -90
plt.show()

Depth-coloring¶
To facilitate perception of depth in 2D plots, plot2d()
can color neurons based on their z-depth. This is only available for method="2d"
and method="3d"
, not method="3d_complex"
.
With method="2d"
:
fig, ax = navis.plot2d(nl, method='2d',
linewidth=1.5,
depth_coloring=True, view=('x', '-y'))
plt.show()

Shading¶
In the above example we colored individual nodes based on their z-depth. We can use the same mechanics to apply our own color scheme. For example, let’s color branches based on their Strahler order.
# Fist calculate Strahler indices
n = nl[0].reroot(nl[0].soma)
navis.strahler_index(n)
# Note the new column in the node table
n.nodes.head()
node_id | label | x | y | z | radius | parent_id | type | strahler_index | |
---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 15784.0 | 37250.0 | 28062.0 | 10.000000 | 2 | end | 1 |
1 | 2 | 0 | 15764.0 | 37230.0 | 28082.0 | 18.284300 | 3 | slab | 1 |
2 | 3 | 0 | 15744.0 | 37190.0 | 28122.0 | 34.721401 | 4 | slab | 1 |
3 | 4 | 0 | 15744.0 | 37150.0 | 28202.0 | 34.721401 | 5 | slab | 1 |
4 | 5 | 0 | 15704.0 | 37130.0 | 28242.0 | 34.721401 | 6 | slab | 1 |
Both plot2d
and plot3d
accept color_by
and shade_by
parameters. These can either be arrays (same length as node table) or point to a column in the node table. They can deal with numeric and categorical variables which will be translated into colors using a given palette
and alpha values, respectively.
fig, ax = navis.plot2d(n, method='3d',
color_by='strahler_index', # color based on Strahler index column
shade_by='strahler_index', # shade (alpha) based on Strahler index column
palette='cool', # accepts any matplotlib palette
linewidth=1.5)
ax.elev = ax.azim = -90
ax.dist = 6
plt.show()

3D plots¶
As laid out at the top of this page: for 3D plots, we are using either vispy, plotly or k3d. In brief:
backend |
Jupyter |
terminal |
---|---|---|
plotly |
yes |
yes but only export to html |
vispy |
no |
yes |
k3d |
yes |
no |
By default, NAVis will detect whether you are working in a Jupyter notebook or not, and choose a backend automatically: vispy for terminal, plotly for Jupyter. Because only plotly allows us to embed interactive plots into these docs, we will focus mainly on this backend but we will briefly demo the others too:
Using Vispy¶
Our first two example use Vispy (for demonstration only and will NOT work in Jupyter):
# Plot using Vispy (will open 3D viewer)
viewer = nl.plot3d(backend='vispy')

The navis.Viewer
(viewer
here) is persistent and survives simply closing the window. Calling plot3d()
again will add objects to the canvas and open it again.
# Clear existing viewer
navis.close3d()
# Add neurons to viewer
navis.plot3d(nl, backend='vispy')
# Add volume
navis.plot3d(lh, backend='vispy')
# Clear viewer again...
navis.clear3d()
# ... or close altogether
navis.close3d()
Alternatively, you can also work on the viewer directly:
# Clear viewer
viewer.clear()
# Add an object (e.g. a NeuronList)
viewer.add(nl)
If working with multiple viewers, you can specify which navis.Viewer
to add the neurons to.
# Open 2 viewers
v1 = navis.Viewer()
v2 = navis.Viewer()
# Add neurons to each one separately
v1.add(nl[0])
v2.add(nl[1])
# Clear first viewer
v1.clear()
# Close the second viewer
v2.close()
If you’ve lost track of your viewer, simply use navis.get_viewer()
to get it back:
v = navis.get_viewer()
Using 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 vispy for that matter)!
p = navis.plot3d(nl, backend='k3d')

Using plotly¶
Now let’s have a look at Plotly as backend:
# Using plotly as backend generates "inline" plots by default (i.e. they are rendered right away)
fig = nl.plot3d(backend='plotly', connectors=False)