.. _neuronlist_math: NeuronList math *************** :class:`~pymaid.CatmaidNeuronList` implement some of the basic arithmetic and comparison operators that you might know from standard ``lists`` or ``numpy.arrays``. Most this should be fairly intuitive (I hope) but there are a few things you should be aware of. The following examples will illustrate that: As per usual, we need to get some neurons first (I'm assuming you've already imported and setup pymaid): .. code:: ipython3 nl = pymaid.get_neurons('annotation:glomerulus DA1 right') nl.head() .. raw:: html
neuron_name skeleton_id n_nodes n_connectors n_branch_nodes n_end_nodes open_ends cable_length review_status soma
0 PN glomerulus DA1 57316 ML 2863105 2863104 5222 439 149 157 63 1109.886700 NA True
1 PN glomerulus DA1 57382 ML 57381 7730 360 153 162 71 1215.920600 NA True
2 PN glomerulus DA1 61222 AJ 61221 7875 534 135 140 26 1182.642823 NA True
3 PN glomerulus DA1 57354 GA 57353 4898 327 90 95 52 1113.156682 NA True
4 PN glomerulus DA1 57324 LK JSL 57323 4585 438 120 127 59 1035.099284 NA True
Comparisons ----------- The ``==`` operator compares two elements: .. code:: ipython3 1 == 1 .. parsed-literal:: True .. code:: ipython3 2 == 1 .. parsed-literal:: False For :class:`~pymaid.CatmaidNeuron` this is done by comparing the neurons' morphologies (somas, root nodes, cable length, ...) and meta data (skeleton ID, name, ...). .. code:: ipython3 n1 = nl[0] n2 = nl[0] n1 == n2 .. parsed-literal:: True .. code:: ipython3 n1 = nl[0] n2 = nl[1] n1 == n2 .. parsed-literal:: False For :class:`~pymaid.CatmaidNeuronList`, we do the same comparison pairwise between neurons in both neuronlists - so order matters!. .. code:: ipython3 nl == nl .. parsed-literal:: True .. code:: ipython3 nl == nl[:-1] .. parsed-literal:: False .. code:: ipython3 nl[[0, 1, 2]] == nl[[2, 1, 0]] .. parsed-literal:: False This is safe against copying but making any changes to the neurons will cause inequality: .. code:: ipython3 n1 = nl[0] n2 = n1.copy() n1 == n2 .. parsed-literal:: True .. code:: ipython3 n1 = nl[0] n2 = n1.reroot(nl[0].tags['ends'][0], inplace=False) n1 == n2 .. parsed-literal:: False You can also ask if a neuron is in a given ``CatmaidNeuronList``: .. code:: ipython3 n1 = nl[0] n1 in nl .. parsed-literal:: True .. code:: ipython3 n1 in nl[1:] .. parsed-literal:: False This also works with skeleton IDs .. code:: ipython3 n1.skeleton_id in nl .. parsed-literal:: True .. code:: ipython3 n1.skeleton_id in nl[1:] .. parsed-literal:: False Addition -------- To merge two lists in Python, you can simply add them: .. code:: ipython3 a = [1] b = [3] a + b .. parsed-literal:: [1, 3] :class:`~pymaid.CatmaidNeuronList` works exactly the same: .. code:: ipython3 a = nl[0] b = nl[[1, 2]] a + b .. raw:: html
neuron_name skeleton_id n_nodes n_connectors n_branch_nodes n_end_nodes open_ends cable_length review_status soma
0 PN glomerulus DA1 57382 ML 57381 7729 360 153 162 71 1215.920599 NA True
1 AL.L(DA1) -{mALT}-> CAL.L-LH.L 2379518 PN022 D... 2379517 4657 411 86 90 31 836.067253 NA True
2 PN glomerulus DA1 57312 LK 57311 4882 429 157 164 105 1182.102458 NA True
This also works on with two single :class:`~pymaid.CatmaidNeuron`! You can use that to combine them into a list: .. code:: ipython3 a = nl[0] b = nl[1] a + b .. raw:: html
neuron_name skeleton_id n_nodes n_connectors n_branch_nodes n_end_nodes open_ends cable_length review_status soma
0 PN glomerulus DA1 57316 ML 2863105 2863104 5222 439 149 157 63 1109.88670 NA True
1 AL.L(DA1) -{mALT}-> CAL.L-LH.L 2319458 PN036 D... 2319457 10209 964 431 458 90 1747.51171 NA True
.. note:: If you want to combine two CatmaidNeurons into a single neuron instead of creating a neuronlist, check out :func:`pymaid.stitch_neurons`. Substraction ------------ To remove an item from a Python list, you would call the ``.pop()`` method: .. code:: ipython3 a = [1, 2, 3] b = 2 a.pop(b) a .. parsed-literal:: [1, 2] :class:`~pymaid.CatmaidNeuronList` lets you simply use substraction: .. code:: ipython3 a = nl[[0, 1, 2]] b = nl[2] a - b .. raw:: html
neuron_name skeleton_id n_nodes n_connectors n_branch_nodes n_end_nodes open_ends cable_length review_status soma
0 PN glomerulus DA1 57382 ML 57381 7729 360 153 162 71 1215.920599 NA True
1 AL.L(DA1) -{mALT}-> CAL.L-LH.L 2379518 PN022 D... 2379517 4657 411 86 90 31 836.067253 NA True
Bitwise AND ----------- To find the intersection between two lists, you would use ``sets`` and the ``&`` operator: .. code:: ipython3 a = set([0, 1, 2]) b = set([2, 3, 4]) a & b .. parsed-literal:: {2} :class:`~pymaid.CatmaidNeuronList` work similarly: .. code:: ipython3 a = nl[[0, 1, 2]] b = nl[[2, 3, 4]] a & b .. raw:: html
neuron_name skeleton_id n_nodes n_connectors n_branch_nodes n_end_nodes open_ends cable_length review_status soma
0 PN glomerulus DA1 57312 LK 57311 4882 429 157 164 105 1182.102458 NA True
All of the above also work with skeleton IDs as one of the operators .. code:: ipython3 a = nl[[0, 1, 2]] b = nl[[2, 3, 4]] a & b.skeleton_id .. raw:: html
neuron_name skeleton_id n_nodes n_connectors n_branch_nodes n_end_nodes open_ends cable_length review_status soma
0 PN glomerulus DA1 57312 LK 57311 4882 429 157 164 105 1182.102458 NA True
Multiplication and Division --------------------------- So far, all operations have led to changes in the structure of the :class:`~pymaid.CatmaidNeuronList`. Multiplication and division are **different**! If you multiply/divide a :class:`~pymaid.CatmaidNeuron` or :class:`~pymaid.CatmaidNeuronList` by a number, you will change the *coordinates* of nodes and connectors (plus node radii): .. code:: ipython3 n = nl[0] n.nodes.head() .. raw:: html
treenode_id parent_id creator_id x y z radius confidence type
0 3046710 32963981 53 442883 212802 44240 -1 5 slab
1 32963981 3046707 150 442931 212892 44040 -1 5 slab
2 652935 652934 22 444126 151826 216240 -1 5 slab
3 3245741 3245737 61 414617 243177 52400 3451 5 end
4 3042776 27298300 53 436753 225519 38000 -1 5 slab
.. code:: ipython3 n2 = n / 1000 n2.nodes.head() .. raw:: html
treenode_id parent_id creator_id x y z radius confidence type
0 3046710 32963981 53 442.883 212.802 44.24 -0.001 5 slab
1 32963981 3046707 150 442.931 212.892 44.04 -0.001 5 slab
2 652935 652934 22 444.126 151.826 216.24 -0.001 5 slab
3 3245741 3245737 61 414.617 243.177 52.40 3.451 5 end
4 3042776 27298300 53 436.753 225.519 38.00 -0.001 5 slab
The main use of this is to convert units from e.g. nm to um. Dealing with unequal neuron objects ----------------------------------- As we demonstrated earlier, making changes to ``CatmaidNeuron/Lists`` will cause comparisons to fail. This also propagates into substractions and bitwise comparisons: .. code:: ipython3 n1 = nl[0] n1 in nl .. parsed-literal:: True .. code:: ipython3 n2 = n1.reroot(nl[0].tags['ends'][0], inplace=False) n2 in nl .. parsed-literal:: False .. code:: ipython3 nl & n1 .. raw:: html
neuron_name skeleton_id n_nodes n_connectors n_branch_nodes n_end_nodes open_ends cable_length review_status soma
0 PN glomerulus DA1 57316 ML 2863105 2863104 5222 439 149 157 63 1109.8867 NA True
.. code:: ipython3 nl & n2 .. parsed-literal:: WARNING : Skeleton IDs overlap but neuron not identical! Bitwise cancelled! Try using .skeleton_id instead. (pymaid) See how the comparison fails because we've made changes? The same happens if you tried ``nl - n2``. As mentioned in the warning, the workaround is to use the skeleton ID instead: .. code:: ipython3 n2.skeleton_id in nl .. parsed-literal:: True .. code:: ipython3 nl & n2.skeleton_id .. raw:: html
neuron_name skeleton_id n_nodes n_connectors n_branch_nodes n_end_nodes open_ends cable_length review_status soma
0 PN glomerulus DA1 57316 ML 2863105 2863104 5222 439 149 157 63 1109.8867 NA True