pymaid

pymaid (python-catmaid) lets you interface with a CATMAID server such as those provided by VFB.

Overview

pymaid lets you interface with a CATMAID server. It’s built on top of navis and returns data (neurons, volumes) in a way that you can plug them straight into navis to use features such as plotting.

Official documentation here.

Connecting

The VFB CATMAID servers (see here for what’s available) are public and don’t require an API token for read-only access which makes connecting simple:

import pymaid
import navis

navis.set_pbars(jupyter=False)
pymaid.set_pbars(jupyter=False)

# Connect to the VFB CATMAID server hosting the FAFB data
rm = pymaid.connect_catmaid(server="https://fafb.catmaid.virtualflybrain.org/", api_token=None, max_threads=10)

# Test call to see if connection works 
print(f'Server is running CATMAID version {rm.catmaid_version}')
WARNING: Could not load OpenGL library.
INFO  : Global CATMAID instance set. Caching is ON. (pymaid)
Server is running CATMAID version 2020.02.15-905-g93a969b37

Retrieving neurons

Let’s start with pulling a neuron based on its ID:

# Find a neuron from its ID (16) -> this is an olfactory projection neuron
n = pymaid.get_neurons(16)
n

type CatmaidNeuron
name Uniglomerular mALT VA6 adPN 017 DB
id 16
n_nodes 16840
n_connectors 2158
n_branches 1172
n_leafs 1230
cable_length 4003103.232861
soma [2941309]
units 1 nanometer

This neuron’s type is pymaid.CatmaidNeuron, which is a subclass of navis.TreeNeuron. The list version is pymaid.CatmaidNeuronList, which a subclass of navis.NeuronList. This adds a bit of extra functionality (such as lazy loading of data) and allows CatmaidNeuron and CatmaidNeuronList work as drop in replacements for their parent classes.

# Plot CatmaidNeuron with navis
navis.plot3d(n, width=1000, connectors=True, c='k')

get_neurons() returns neurons including their “connectors” - i.e. pre- (red) and postsynapses (blue). For this particular neuron, the published data comprehensively labels the axonal synapses but not the dendrites. Analogous to the nodes table, you can access the connectors like so:

n.connectors.head()

node_id connector_id type x y z
0 97891 97895 0 436882.09375 161840.453125 212160.0
1 2591 97954 0 437120.00000 160998.000000 211920.0
2 2665 98300 0 437183.75000 162323.515625 214880.0
3 2646 98373 0 437041.68750 162451.937500 214120.0
4 2654 98415 0 436760.90625 163689.796875 214440.0

Let’s run a bigger example and pull all data published with Bates, Schlegel et al. 2020. For this, we will use “annotations”. These are effectively text labels that group neurons together, in this case by paper. Instead of get_neurons we can use find_neurons to avoid downloading unnecessary data.

bates = pymaid.find_neurons(annotations='Paper: Bates and Schlegel et al 2020')
len(bates)
INFO  : Found 583 neurons matching the search parameters (pymaid)





583

bates is a CatmaidNeuronList containing 583 neurons. Importantly pymaid has not yet loaded any data other than names! Note all the “NAs” in the summary:

bates.head()

type name skeleton_id n_nodes n_connectors n_branches n_leafs cable_length soma units
0 CatmaidNeuron Uniglomerular mALT DA1 lPN 57316 2863105 ML 2863104 NA NA NA NA NA NA 1 nanometer
1 CatmaidNeuron Uniglomerular mALT DA3 adPN 57350 HG 57349 NA NA NA NA NA NA 1 nanometer
2 CatmaidNeuron Uniglomerular mALT DA1 lPN 57354 GA 57353 NA NA NA NA NA NA 1 nanometer
3 CatmaidNeuron Uniglomerular mALT VA6 adPN 017 DB 16 NA NA NA NA NA NA 1 nanometer
4 CatmaidNeuron Uniglomerular mALT VA5 lPN 57362 ML 57361 NA NA NA NA NA NA 1 nanometer

We could have used pymaid.get_neurons(annotations='Paper: Bates and Schlegel et al 2020') instead to load all data up-front, but this would increase memory usage.

The CatmaidNeuronList we have created will lazy load data from the server when required.

# Access the first neuron's nodes 
# -> this will trigger a data download
_ = bates[0].nodes 

# Run summary again 
bates.head()

type name skeleton_id n_nodes n_connectors n_branches n_leafs cable_length soma units
0 CatmaidNeuron Uniglomerular mALT DA1 lPN 57316 2863105 ML 2863104 6774 470 280 292 1522064.513255 [3245741] 1 nanometer
1 CatmaidNeuron Uniglomerular mALT DA3 adPN 57350 HG 57349 NA NA NA NA NA NA 1 nanometer
2 CatmaidNeuron Uniglomerular mALT DA1 lPN 57354 GA 57353 NA NA NA NA NA NA 1 nanometer
3 CatmaidNeuron Uniglomerular mALT VA6 adPN 017 DB 16 NA NA NA NA NA NA 1 nanometer
4 CatmaidNeuron Uniglomerular mALT VA5 lPN 57362 ML 57361 NA NA NA NA NA NA 1 nanometer

We have now loaded data for the first neuron.

Next we willl find and plot all uniglomelar DA1 projection neurons by their name.

# Name will be match pattern "Uniglomerular {tract} DA1 {lineage}"
import re 
prog = re.compile("Uniglomerular(.*?) DA1 ")

# Match all neuron names in `bates` against that pattern
is_da1 = list(map(lambda x: prog.match(x) != None, bates.name))

# Subset list 
da1 = bates[is_da1]
da1.head()

type name skeleton_id n_nodes n_connectors n_branches n_leafs cable_length soma units
0 CatmaidNeuron Uniglomerular mALT DA1 lPN 57316 2863105 ML 2863104 6774 470 280 292 1522064.513255 [3245741] 1 nanometer
1 CatmaidNeuron Uniglomerular mALT DA1 lPN 57354 GA 57353 NA NA NA NA NA NA 1 nanometer
2 CatmaidNeuron Uniglomerular mALT DA1 lPN 57382 ML 57381 NA NA NA NA NA NA 1 nanometer
3 CatmaidNeuron Uniglomerular mlALT DA1 vPN mlALTed Milk 23348... 2334841 NA NA NA NA NA NA 1 nanometer
4 CatmaidNeuron Uniglomerular mALT DA1 lPN PN021 2345090 DB RJVR 2345089 NA NA NA NA NA NA 1 nanometer
# Plot neurons by their lineage  
for n in da1:
    # Split name into components and keep the lineage
    n.lineage = n.name.split(' ')[3]    

# Generate a color per lineage
import seaborn as sns
import numpy as np 

lineages = np.unique(da1.lineage) 
lin_cmap = dict(zip(lineages, sns.color_palette('muted', len(lineages))))
neuron_cmap = {n.id: lin_cmap[n.lineage] for n in da1}

navis.plot3d(da1, color=neuron_cmap, hover_name=True)