Skip to content

Note

Click here to download the full example code

Skeletons#

Load and save skeletons from SWC and other formats, or build them from scratch.

Skeletons are probably the most common representation of neurons and are stored as a series of connected nodes (the "skeleton"). In NAVis, skeletons are represented by the navis.Skeleton class.

Build them manually (see the bottom of this page) or use one of the built-in readers for the various skeleton file formats:

Note

NAVis has dedicated interfaces for loading skeletons from remote data sources (e.g. the MICrONS, neuromorpho, Virtual Fly Brain or Janelia hemibrain datasets). These are covered in separate tutorials.

If you have light-level microscopy data, see the tutorial on skeletons from light-level data.

From SWC files#

SWC is the most common format for storing neuron skeletons, and NAVis reads and writes it. The examples below use Supplemental Data S1 from Bates, Schlegel et al. (2020) - download it to follow along and adjust the filepaths to match where you saved it.

The archive holds a bunch of metadata CSVs, but the file we want is skeletons_swc.zip. No need to unzip it - NAVis reads directly from (and writes to) zip archives.

import navis
skeletons = navis.read_swc(
    'mmc2/skeletons_swc.zip',
    include_subdirs=True
)
skeletons
<class 'navis.core.neuronlist.NeuronList'> containing 480 neurons (4.0MiB)
type name id n_nodes n_connectors n_branches n_leafs cable_length soma units created_at origin file
0 navis.Skeleton af245389-3c04-4a9c-983e-a0cc95546942 af245389-3c04-4a9c-983e-a0cc95546942 11634 None 600 611 2.899338e+06 None 1 dimensionless 2026-11-03 14:29:41.802347 mmc2/skeletons_swc.zip af245389-3c04-4a9c-983e-a0cc95546942.swc
1 navis.Skeleton a6f13d47-997e-4934-8f1c-e6fd54ab7241 a6f13d47-997e-4934-8f1c-e6fd54ab7241 1670 None 24 25 6.282917e+05 None 1 dimensionless 2026-11-03 14:29:41.798180 mmc2/skeletons_swc.zip a6f13d47-997e-4934-8f1c-e6fd54ab7241.swc
... ... ... ... ... ... ... ... ... ... ... ... ... ...
478 navis.Skeleton a76da20e-43ff-4e0f-a455-a1cca325ffb7 a76da20e-43ff-4e0f-a455-a1cca325ffb7 14782 None 663 673 2.714858e+06 None 1 dimensionless 2026-11-03 14:29:43.129536 mmc2/skeletons_swc.zip a76da20e-43ff-4e0f-a455-a1cca325ffb7.swc
479 navis.Skeleton 90db0156-7aa0-42e1-a1ad-162a3c2168e9 90db0156-7aa0-42e1-a1ad-162a3c2168e9 6256 None 367 371 1.229948e+06 None 1 dimensionless 2026-11-03 14:29:43.126254 mmc2/skeletons_swc.zip 90db0156-7aa0-42e1-a1ad-162a3c2168e9.swc

Let's say you are looking at a huge collection of SWC files and you only want to sample a few of them:

Load only the first 10 skeletons

sample = navis.read_swc(
    './mmc2/skeletons_swc.zip',
    include_subdirs=True,
    limit=10
)
sample
<class 'navis.core.neuronlist.NeuronList'> containing 11 neurons (1.2MiB)
type name id n_nodes n_connectors n_branches n_leafs cable_length soma units created_at origin file
0 navis.Skeleton af245389-3c04-4a9c-983e-a0cc95546942 af245389-3c04-4a9c-983e-a0cc95546942 11634 None 600 611 2.899338e+06 None 1 dimensionless 2026-11-03 14:29:43.634955 mmc2/skeletons_swc.zip af245389-3c04-4a9c-983e-a0cc95546942.swc
1 navis.Skeleton a6f13d47-997e-4934-8f1c-e6fd54ab7241 a6f13d47-997e-4934-8f1c-e6fd54ab7241 1670 None 24 25 6.282917e+05 None 1 dimensionless 2026-11-03 14:29:43.641281 mmc2/skeletons_swc.zip a6f13d47-997e-4934-8f1c-e6fd54ab7241.swc
... ... ... ... ... ... ... ... ... ... ... ... ... ...
9 navis.Skeleton f2c2d64e-531b-4df8-94a9-52bdefa365b5 f2c2d64e-531b-4df8-94a9-52bdefa365b5 2694 None 112 115 8.098384e+05 None 1 dimensionless 2026-11-03 14:29:43.701402 mmc2/skeletons_swc.zip f2c2d64e-531b-4df8-94a9-52bdefa365b5.swc
10 navis.Skeleton 9f0f8e35-dac0-46d5-9947-de46871fe4ea 9f0f8e35-dac0-46d5-9947-de46871fe4ea 0 None 0 0 0.000000e+00 None 1 dimensionless 2026-11-03 14:29:43.706169 mmc2/skeletons_swc.zip 9f0f8e35-dac0-46d5-9947-de46871fe4ea.swc

We can also point navis.read_swc() at single files instead of folders or zip archives:

For this I extracted the skeletons_swc.zip archive

s = navis.read_swc('./mmc2/swc/CENT/11519759.swc')
s
type navis.Skeleton
name 11519759
n_nodes 14782
n_connectors None
n_branches 663
n_leafs 673
cable_length 2714858.5
soma None
units 1 dimensionless
created_at 2026-11-03 14:29:43.734539
origin mmc2/swc/CENT/11519759.swc
file 11519759.swc

You can even use URLs or FTP servers directly:

# From URL:
s = navis.read_swc('https://v2.virtualflybrain.org/data/VFB/i/jrch/jup2/VFB_00101567/volume.swc')

You can even read straight from an FTP folder (this particular server is currently offline, shown for reference):

nl = navis.read_swc('ftp://download.brainimagelibrary.org/biccn/zeng/pseq/morph/200526/', limit=3)

read_swc is flexible

read_swc handles a whole range of inputs - file names, folders, archives, URLs and more - and lets you customise which and how neurons are loaded:

  • limit can also take a pattern to load only matching files
  • fmt controls how filenames are parsed into neuron names and IDs

Many of the other navis.read_* functions share these features!

To SWC files#

Saving skeletons back to disk works the same way. Write a single neuron:

navis.write_swc(s, './mmc2/my_neuron.swc')

The magic is all in the filepath: use a {neuron.name} placeholder to name files by a neuron property, and append @archive.zip to write straight into a zip. These compose freely:

Filepath pattern Result
my_neuron.swc A single neuron to one file.
{neuron.name}.swc One file per neuron in a NeuronList, named by each neuron's .name.
skeletons.zip The whole NeuronList bundled into a single zip archive.
{neuron.name}.swc@skeletons.zip One file per neuron, named by .name, inside a zip archive.

The placeholder can reference any neuron property, e.g. {neuron.id}.swc. See navis.write_swc for further details.

From NMX files#

NMX is an xml-based format used e.g. by pyKNOSSOS to store skeletons plus meta data. NAVis supports reading (but not writing) this format. If you want to follow along download this dataset by Wanner et al. (2016). Just like the SWCs, I extracted the archive to my downloads folder:

Read a single file

s = navis.read_nmx('./WannerAA201605_SkeletonsGlomeruli/Neuron_id0001.nmx')
s
Traceback (most recent call last):
  File "/home/runner/work/navis/navis/docs/examples/0_io/tutorial_io_00_skeletons.py", line 156, in <module>
    s = navis.read_nmx('./WannerAA201605_SkeletonsGlomeruli/Neuron_id0001.nmx')
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/navis/navis/navis/io/nmx_io.py", line 303, in read_nmx
    neurons = reader.read_any(
              ^^^^^^^^^^^^^^^^
  File "/home/runner/work/navis/navis/navis/io/base.py", line 1221, in read_any
    return self.read_any_single(obj, attrs=attrs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/navis/navis/navis/io/base.py", line 1092, in read_any_single
    return self.read_file_path(p, attrs=attrs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/navis/navis/navis/io/base.py", line 491, in read_file_path
    return self.read_buffer(f, attrs=merge_dicts(props, attrs))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/navis/navis/navis/io/base.py", line 174, in wrapper
    raise ReadError(
navis.io.base.ReadError: Error reading Neuron_id0001/E085L01_neuron_id1.0_skeleton__201607230656.nml. See above traceback for details.

Read all files in folder

nl = navis.read_nmx('./WannerAA201605_SkeletonsGlomeruli/')
nl
navis.plot2d(nl[:10], method='2d', radius=False)

Note

If you encounter an error message while reading: NMX files don't always contain skeletons. If NAVis comes across one that can't be turned into a navis.Skeleton, it will skip the file and produce a warning.

From Neuroglancer Precomputed#

Among other formats, neuroglancer supports a "precomputed" format for skeletons (see specs here. This binary format is more compact than uncompressed SWC files but is not used outside of neuroglancer as far as I know. That said: NAVis lets you read and write skeletons from/to precomputed format using navis.read_precomputed and navis.write_precomputed. Note that these functions work on both precomputed skeletons and meshes.

Also check out the tutorial on reading skeletons straight from a neuroglancer source using cloud-volume.

Manual construction#

What if you have some obscure data format for which NAVis does not have a read function? The data underlying a navis.Skeleton is a simple SWC table - so as long as you can produce that from your data, you can create your own skeletons.

Here's a quick & dirty example:

import pandas as pd

# Create a mock SWC table for a 2-node skeleton
swc = pd.DataFrame()
swc['node_id'] = [0, 1]
swc['parent_id'] = [-1, 0]   # negative indices indicate roots
swc['x'] = [0, 1]
swc['y'] = [0, 1]
swc['z'] = [0, 1]
swc['radius'] = 0

swc

This SWC can now be used to construct a Skeleton:

s = navis.Skeleton(swc, name='my_neuron', units='microns')
s

There are a few other ways to construct a navis.Skeleton (e.g. using a graph) - see the docstring for details.

Also note that all NAVis neurons can be stored to disk using pickle - see the pickling tutorial.

Hopefully the above has given you some entry points on how to load your data. See also the I/O API reference.

Please also keep in mind that you can also convert one neuron type into another - for example by skeletonizing Meshes (see also the API reference on neuron conversion).

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

Download Python source code: tutorial_io_00_skeletons.py

Download Jupyter notebook: tutorial_io_00_skeletons.ipynb

Gallery generated by mkdocs-gallery