Tutorial

This tutorial will introduce you to the basics of using pymaid. This is not supposed to be comprehensive but rather to give you a flavor of how things work. For inspiriation, explore the example gallery and for detailed explanations have a look at the API documentation.

Before we get started: pymaid is built on top of navis and you will note that we are using many of its functions throughout this tutorial and the examples. I highly recommend you familiarize yourself with it!

Connecting to your Catmaid Server

At the beginning of each session, you have to initialise a connection to your CATMAID server. These connections are represented by CatmaidInstance which holds the URL and your credentials.

You will find that all functions that fetch data accept such connections as remote_instance parameters. In most situations, however, you won’t use that parameter. That’s because once you have initialized a connection it becomes the “global default” connection and pymaid will use that connection unless told otherwise. Sounds complicated but is actually straight forward - let’s look at some code:

First things first: import pymaid!/

[1]:
import pymaid

Now there are two ways to initialize a connection. The first one is to simply create an instance of CatmaidInstance with your credentials:

[2]:
rm = pymaid.CatmaidInstance(server='https://www.your.catmaid-server.org',
                            api_token='YOURTOKEN',
                            http_user='user', # omit if not required
                            http_password='pw') # omit if not required
INFO  : Global CATMAID instance set. Caching is ON. (pymaid)

HTTP_USER and HTTP_PASSWORD are credentials you might use before even getting to the CATMAID landing page, not your CATMAID credentials. If your server does not require them, just leave them blank (''). Instead of your CATMAID credentials, you use an API TOKEN. See here how to generate yours.

The above way of making a connection works perfectly fine but it can get tedious to always type in (or copy-paste) your credentials. It also makes you vulnerable to accidentally reveal your credentials when sharing code with others. Not good!

Hence this second, much more convenient and much safer way of storing credentials: as environment variables. Store your credentials as CATMAID_SERVER_URL, CATMAID_API_TOKEN, CATMAID_HTTP_USER and CATMAID_HTTP_PASSWORD and then you can do this instead:

[3]:
rm = pymaid.connect_catmaid()
INFO  : Global CATMAID instance set. Caching is ON. (pymaid)

Much easier and cleaner, isn’t it? The way to set up environment variables depends a bit on your operating system and what terminal (e.g. bash or zsh) you are using. I recommend you Google it if you are unsure but here are two articles that do a decent job describing it:

rm now holds your credentials and mediates fetching data from your server. This CatmaidInstance has a few additional perks that we won’t cover just yet - check out the API docs and the examples for CatmaidInstance’ to learn, for example, how to access different projects on your server or limit the number of parallel queries.

Fetching data

Now that we have set up the connection, we can query the server for data. The examples below are by no means comprehensive! Have a look at all the functions for fetching data. Most of these functions will follow a pymaid.get_{data} naming convention:

For starters, let’s get a list of skeleton IDs from a single annotation:

[4]:
skids = pymaid.get_skids_by_annotation('glomerulus DL4')
skids
[4]:
[6170457, 483289, 23829, 6698696]

Your Catmaid server will provide most data as simple list or dictionary, like the list of skeleton IDs above. Whenever this makes sense, pymaid will convert that data to a pandas.DataFrame.

pandas is the data science library for Python and will help you analyze and visualize your data. I highly recommend familiarizing yourself with pandas! There are plenty of good tutorials out there but pandas’ own 10 Minutes to pandas is a good place to start.

As example, let’s get the treenode table for one of the above neurons:

[5]:
tn_table = pymaid.get_node_table(skids[0])
tn_table.head()
INFO  : Retrieving 1 node table(s)... (pymaid)
INFO  : 7061 nodes retrieved. Creating table... (pymaid)
[5]:
node_id parent_node_id confidence x y z radius creator last_edited skeleton_id reviewers tags
0 32048046 63373873 5 504762 216453 45160 -1 vallas 2020-07-15 16:55:44+00:00 6170457 NaN NaN
1 63373873 32048044 5 504748 216392 45120 -1 helmickl 2020-07-15 16:55:44+00:00 6170457 NaN NaN
2 32047785 63373858 5 503798 218009 46040 -1 vallas 2020-07-15 16:55:07+00:00 6170457 NaN NaN
3 63373858 32047777 5 503820 218043 46080 -1 helmickl 2020-07-15 16:55:07+00:00 6170457 NaN NaN
4 32042427 63373515 5 513298 222217 41240 -1 vallas 2020-07-15 16:36:29+00:00 6170457 NaN NaN

Neurons

Catmaid neurons are complex structures: they consist of (tree)nodes and connectors, have names, annotations, tags, review status and so on. We could query our Catmaid server for all these data separately and manage it using lists or dictionaries… a pain in the neck, trust me.

pymaid to the rescue! CatmaidNeuron and their collections, CatmaidNeuronList, make managing and accessing neuron data a breeze as you will see below.

As of pymaid version 2.0.0, CatmaidNeuron/Lists are subclasses of navis TreeNeurons and NeuronList, respectively. This means we can plug and play with all navis functions.

Fetching CatmaidNeurons

There are two functions that will return CatmaidNeuron/Lists: pymaid.get_neuron() and pymaid.find_neurons(). The latter is a bit more sophisticated and allows intersections of search criteria - we’ll cover it elsewhere. For starters, let’s get two neurons by their skeleton IDs:

[6]:
neuron_list = pymaid.get_neuron(['57311', '27295'])

neuron_list is a CatmaidNeuronList containing our two neurons. Calling it will give us a small summary:

[7]:
neuron_list
[7]:
type name skeleton_id n_nodes n_connectors n_branches n_leafs cable_length soma units
0 CatmaidNeuron Uniglomerular mALT DA1 lPN 57312 LK 57311 5543 463 211 219 1364103.25 [3059181] 1 nanometer
1 CatmaidNeuron Uniglomerular mALT DA1 lPN 27296 BH 27295 10216 507 304 314 1806000.25 [3005291] 1 nanometer

You can access individual CatmaidNeuron within the CatmaidNeuronList by various means. See example gallery for an in-depth explanation. For now, the easiest is to just index the CatmaidNeuronList like you would a list:

[8]:
neuron_list[0]
[8]:
type CatmaidNeuron
name Uniglomerular mALT DA1 lPN 57312 LK
id 57311
n_nodes 5543
n_connectors 463
n_branches 211
n_leafs 219
cable_length 1.3641e+06
soma [3059181]
units 1 nanometer

Accessing neuron data

Treenodes, connectors, etc. are stored as attributes of CatmaidNeuron/Lists:

[9]:
n = neuron_list[0]

# .nodes is a pandas DataFrame and we can use .head() to show the first couple entries
n.nodes.head()
[9]:
node_id parent_id creator_id x y z radius confidence type
0 1204977 1204976 4 438669.71875 140775.484375 196840.0 -1.0 5 slab
1 1204978 1204977 4 438669.53125 140795.328125 196880.0 -1.0 5 slab
2 861285 861279 25 488491.90625 183344.390625 139640.0 -1.0 5 slab
3 860950 860949 25 489096.68750 170746.453125 149040.0 -1.0 5 slab
4 862662 862649 25 464031.31250 143550.625000 173320.0 -1.0 5 slab
[10]:
n.connectors.head()
[10]:
node_id connector_id type x y z
0 1205514 2163979 0 437151.25000 145751.25000 205040.0
1 1207394 2884058 1 449176.09375 150388.56250 216920.0
2 1207044 2882175 0 446111.00000 152505.00000 215400.0
3 5400609 5400608 0 340840.62500 166648.68750 168240.0
4 5400758 5400757 0 347488.00000 165468.09375 160680.0
[11]:
n.skeleton_id
[11]:
'57311'

Almost all attributes exist in both CatmaidNeuron and CatmaidNeuronList. In the latter case, you will simply get that attribute over all neurons in the CatmaidNeuronList.

[12]:
neuron_list.skeleton_id
[12]:
array(['57311', '27295'], dtype='<U5')

Note

CatmaidNeuron/Lists support auto-completion. Try typing neuron_list. and hitting TAB.

Deal with missing data

Going back to the neuron’s summary, you will notice that review_status is NA:

[13]:
neuron_list[0]
[13]:
type CatmaidNeuron
name Uniglomerular mALT DA1 lPN 57312 LK
id 57311
n_nodes 5543
n_connectors 463
n_branches 211
n_leafs 219
cable_length 1.3641e+06
soma [3059181]
units 1 nanometer

That’s because we are currently lacking that data. It will be retrieved/computed on-demand upon first explicit request:

[14]:
neuron_list[0].review_status
[14]:
79

Now review_status is known for the first neuron but not the second:

[15]:
neuron_list
[15]:
type name skeleton_id n_nodes n_connectors n_branches n_leafs cable_length soma units
0 CatmaidNeuron Uniglomerular mALT DA1 lPN 57312 LK 57311 5543 463 211 219 1364103.25 [3059181] 1 nanometer
1 CatmaidNeuron Uniglomerular mALT DA1 lPN 27296 BH 27295 10216 507 304 314 1806000.25 [3005291] 1 nanometer

Attributes are not automatically updated once they have been retrieved. You can force an update by calling the according get_... method:

[16]:
neuron_list[0].get_review()

Most of these methods and attributes work on both, CatmaidNeuron and CatmaidNeuron:

[17]:
neuron_list.review_status
[17]:
array([79, 77])

For more advanced stuff (plotting, pruning, cutting, etc.) have a look at the example gallery.