{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "![](images/blue_brain_neurons.colorful.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Analyzing Connectomics Data\n", "---\n", "The `netsci` package comes with an out-of-box dataset describing the [neuronal network](https://en.wikipedia.org/wiki/Neural_network) among ~2,000 neurons from a rat brain. In this notebook, we shortly explain the nature of this data and demonstrate a basic network analysis applied upon it.\n", "\n", "Essentially, after completing this tutorial, you will know:\n", "1. What are the brain's neural networks and what is the connectome.\n", "2. How to load the (toy) connectomics data and convert it to a network.\n", "3. How to identify and count three-node motifs in a network.\n", "4. That, in neuronal networks, a motif connectivity is highly related to the spatial embedding of its composing neurons.\n", "\n", "A deeper description of this network study, together with its neuroscientific context and implication, can be found in [this recent paper](https://doi.org/10.1101/656058).\n", "\n", "\n", "Let’s get started.\n", "\n", "\n", "## Connectomics\n", "> *You are more than your genes. You are your connectome.* --Sebastian Seung\n", "\n", "The human brain is composed of about 100 billion specialized nerve cells, [**neurons**](https://en.wikipedia.org/wiki/Neuron). Neurons can connect to up to 10,000 other target neurons each, altogether forming the vast and complex [biological neural networks](https://en.wikipedia.org/wiki/Neural_circuit). The inter-connections between neurons are formed by [**synapses**](https://en.wikipedia.org/wiki/Synapse) - structural \"junctions\" that allow transferring electric impulses, [**action potentials**](https://en.wikipedia.org/wiki/Action_potential), from one neuron the another. The precise wiring diagram of all neuronal connections, or [**the connectome**](https://en.wikipedia.org/wiki/Connectome), shapes the network-wide electric activity and underlies the different brain functions like information processing, memory, and, ultimately, behavior. However, how exactly does the network structure affect brain activity and function, is a long-standing and still open question in neuroscience for more than a century. \n", "\n", "

\n", " \n", " from www.khanacademy.org\n", "

\n", "\n", "The biological connectome can be further modeled as a mathematical [**directed graph**](https://en.wikipedia.org/wiki/Directed_graph) whose [**nodes**](https://en.wikipedia.org/wiki/Glossary_of_graph_theory_terms#node) correspond to the neurons, and its [**edges**](https://en.wikipedia.org/wiki/Glossary_of_graph_theory_terms#edge) are the synaptic connections between those neurons. This network-based perspective of the brain allows utilizing tools from [**graph theory**](https://en.wikipedia.org/wiki/Graph_theory) and modern [**network science**](https://en.wikipedia.org/wiki/Network_science) to uncover key network structures that support brain functions. Such methods have already identified several network features in cortex architecture, such as the rare but highly-connected hub neurons, cliques of all-to-all connected neurons, and overall small-world topology of the cortical microcircuit. \n", "\n", "![](images/nn.4576.sketch.reduced.png)\n", "\n", "Arguably, one of the most basic, yet still not fully explained, network structures observed in neuronal circuits are the 3-neuron subgraphs (triplets). When counting the frequency of all possible 3-nodes connectivity patterns in the network, the distribution appears highly unexpected. Specifically, few specific configurations (out of all 16 possible) stand-out and are significantly over-expressed (motifs) when compared with reference randomized networks. More surprisingly, different microcircuits across different brain regions commonly display similar over- and under-expression of the same motifs. However, despite their cross-region universality, both the origin of these motifs and their functional implication have remained elusive.\n", "\n", "![](images/ANNs.cropped.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**So, what is the origin of neuronal network motifs?**\n", "\n", "In the absence of a concrete theory about the principles underlying these motifs, their emergence may be hypothetically associated with active plasticity and learning processes. But another, much more parsimonious, possibility comes to mind. Most types of cortical neurons display highly asymmetric geometry with dendritic and axonal trees typically extending in different directions. As a consequence, the probability of forming a connection in one direction (e.g., down) may be higher than in the other direction (e.g., up). This symmetry breaking may distinctively promote some motifs while depressing others. Thus, it could be the case that the geometry per se “enforces” the complex profile of brain microcircuit motifs. \n", "\n", "The importance of this hypothesis is that if it is indeed so, then we can now see learning processes as operating on top of an innate, already structured, cortical skeleton rather than on a _tabula rasa_ network connectivity. This will strongly constrain the degree by which plasticity could further shape neural connectivity, and possible reduce the room for learning.\n", "\n", "We will test this hypothesis below. Toward this end, we will utilize the publicly available dataset of the [Blue Brain model](https://bbp.epfl.ch/nmc-portal/welcome). Specifically, we analyze here a subcircuit of it (pyramidal neurons for layer 5), that is now accessible via `netsci` API's." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:51:59.962075Z", "start_time": "2020-01-22T16:51:56.421345Z" } }, "outputs": [], "source": [ "# # Install packages (uncomment if necessary, e.g., for Google Colab)\n", "# !pip install plotly\n", "# !pip install holoviews\n", "# !pip install netsci" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:52:03.360686Z", "start_time": "2020-01-22T16:51:59.973748Z" } }, "outputs": [ { "data": { "text/html": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "(function(root) {\n", " function now() {\n", " return new Date();\n", " }\n", "\n", " const force = true;\n", " const py_version = '3.7.3'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", " const reloading = false;\n", " const Bokeh = root.Bokeh;\n", "\n", " // Set a timeout for this load but only if we are not already initializing\n", " if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n", " root._bokeh_timeout = Date.now() + 5000;\n", " root._bokeh_failed_load = false;\n", " }\n", "\n", " function run_callbacks() {\n", " try {\n", " root._bokeh_onload_callbacks.forEach(function(callback) {\n", " if (callback != null)\n", " callback();\n", " });\n", " } finally {\n", " delete root._bokeh_onload_callbacks;\n", " }\n", " console.debug(\"Bokeh: all callbacks have finished\");\n", " }\n", "\n", " function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n", " if (css_urls == null) css_urls = [];\n", " if (js_urls == null) js_urls = [];\n", " if (js_modules == null) js_modules = [];\n", " if (js_exports == null) js_exports = {};\n", "\n", " root._bokeh_onload_callbacks.push(callback);\n", "\n", " if (root._bokeh_is_loading > 0) {\n", " // Don't load bokeh if it is still initializing\n", " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", " return null;\n", " } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n", " // There is nothing to load\n", " run_callbacks();\n", " return null;\n", " }\n", "\n", " function on_load() {\n", " root._bokeh_is_loading--;\n", " if (root._bokeh_is_loading === 0) {\n", " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", " run_callbacks()\n", " }\n", " }\n", " window._bokeh_on_load = on_load\n", "\n", " function on_error(e) {\n", " const src_el = e.srcElement\n", " console.error(\"failed to load \" + (src_el.href || src_el.src));\n", " }\n", "\n", " const skip = [];\n", " if (window.requirejs) {\n", " window.requirejs.config({'packages': {}, 'paths': {'plotly': 'https://cdn.plot.ly/plotly-3.0.1.min'}, 'shim': {}});\n", " require([\"plotly\"], function(Plotly) {\n", " window.Plotly = Plotly\n", " on_load()\n", " })\n", " root._bokeh_is_loading = css_urls.length + 1;\n", " } else {\n", " root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n", " }\n", "\n", " const existing_stylesheets = []\n", " const links = document.getElementsByTagName('link')\n", " for (let i = 0; i < links.length; i++) {\n", " const link = links[i]\n", " if (link.href != null) {\n", " existing_stylesheets.push(link.href)\n", " }\n", " }\n", " for (let i = 0; i < css_urls.length; i++) {\n", " const url = css_urls[i];\n", " const escaped = encodeURI(url)\n", " if (existing_stylesheets.indexOf(escaped) !== -1) {\n", " on_load()\n", " continue;\n", " }\n", " const element = document.createElement(\"link\");\n", " element.onload = on_load;\n", " element.onerror = on_error;\n", " element.rel = \"stylesheet\";\n", " element.type = \"text/css\";\n", " element.href = url;\n", " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", " document.body.appendChild(element);\n", " } if (((window.Plotly !== undefined) && (!(window.Plotly instanceof HTMLElement))) || window.requirejs) {\n", " var urls = [];\n", " for (var i = 0; i < urls.length; i++) {\n", " skip.push(encodeURI(urls[i]))\n", " }\n", " } var existing_scripts = []\n", " const scripts = document.getElementsByTagName('script')\n", " for (let i = 0; i < scripts.length; i++) {\n", " var script = scripts[i]\n", " if (script.src != null) {\n", " existing_scripts.push(script.src)\n", " }\n", " }\n", " for (let i = 0; i < js_urls.length; i++) {\n", " const url = js_urls[i];\n", " const escaped = encodeURI(url)\n", " if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n", " if (!window.requirejs) {\n", " on_load();\n", " }\n", " continue;\n", " }\n", " const element = document.createElement('script');\n", " element.onload = on_load;\n", " element.onerror = on_error;\n", " element.async = false;\n", " element.src = url;\n", " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", " document.head.appendChild(element);\n", " }\n", " for (let i = 0; i < js_modules.length; i++) {\n", " const url = js_modules[i];\n", " const escaped = encodeURI(url)\n", " if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n", " if (!window.requirejs) {\n", " on_load();\n", " }\n", " continue;\n", " }\n", " var element = document.createElement('script');\n", " element.onload = on_load;\n", " element.onerror = on_error;\n", " element.async = false;\n", " element.src = url;\n", " element.type = \"module\";\n", " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", " document.head.appendChild(element);\n", " }\n", " for (const name in js_exports) {\n", " const url = js_exports[name];\n", " const escaped = encodeURI(url)\n", " if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n", " if (!window.requirejs) {\n", " on_load();\n", " }\n", " continue;\n", " }\n", " var element = document.createElement('script');\n", " element.onerror = on_error;\n", " element.async = false;\n", " element.type = \"module\";\n", " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", " element.textContent = `\n", " import ${name} from \"${url}\"\n", " window.${name} = ${name}\n", " window._bokeh_on_load()\n", " `\n", " document.head.appendChild(element);\n", " }\n", " if (!js_urls.length && !js_modules.length) {\n", " on_load()\n", " }\n", " };\n", "\n", " function inject_raw_css(css) {\n", " const element = document.createElement(\"style\");\n", " element.appendChild(document.createTextNode(css));\n", " document.body.appendChild(element);\n", " }\n", "\n", " const js_urls = [\"https://cdn.holoviz.org/panel/1.7.5/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.holoviz.org/panel/1.7.5/dist/bundled/plotlyplot/plotly-3.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.7.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.7.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.7.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.7.3.min.js\", \"https://cdn.holoviz.org/panel/1.7.5/dist/panel.min.js\"];\n", " const js_modules = [];\n", " const js_exports = {};\n", " const css_urls = [\"https://cdn.holoviz.org/panel/1.7.5/dist/bundled/plotlyplot/maplibre-gl@4.4.1/dist/maplibre-gl.css?v=1.7.5\"];\n", " const inline_js = [ function(Bokeh) {\n", " Bokeh.set_log_level(\"info\");\n", " },\n", "function(Bokeh) {} // ensure no trailing comma for IE\n", " ];\n", "\n", " function run_inline_js() {\n", " if ((root.Bokeh !== undefined) || (force === true)) {\n", " for (let i = 0; i < inline_js.length; i++) {\n", " try {\n", " inline_js[i].call(root, root.Bokeh);\n", " } catch(e) {\n", " if (!reloading) {\n", " throw e;\n", " }\n", " }\n", " }\n", " // Cache old bokeh versions\n", " if (Bokeh != undefined && !reloading) {\n", " var NewBokeh = root.Bokeh;\n", " if (Bokeh.versions === undefined) {\n", " Bokeh.versions = new Map();\n", " }\n", " if (NewBokeh.version !== Bokeh.version) {\n", " Bokeh.versions.set(NewBokeh.version, NewBokeh)\n", " }\n", " root.Bokeh = Bokeh;\n", " }\n", " } else if (Date.now() < root._bokeh_timeout) {\n", " setTimeout(run_inline_js, 100);\n", " } else if (!root._bokeh_failed_load) {\n", " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", " root._bokeh_failed_load = true;\n", " }\n", " root._bokeh_is_initializing = false\n", " }\n", "\n", " function load_or_wait() {\n", " // Implement a backoff loop that tries to ensure we do not load multiple\n", " // versions of Bokeh and its dependencies at the same time.\n", " // In recent versions we use the root._bokeh_is_initializing flag\n", " // to determine whether there is an ongoing attempt to initialize\n", " // bokeh, however for backward compatibility we also try to ensure\n", " // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n", " // before older versions are fully initialized.\n", " if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n", " // If the timeout and bokeh was not successfully loaded we reset\n", " // everything and try loading again\n", " root._bokeh_timeout = Date.now() + 5000;\n", " root._bokeh_is_initializing = false;\n", " root._bokeh_onload_callbacks = undefined;\n", " root._bokeh_is_loading = 0\n", " console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n", " load_or_wait();\n", " } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n", " setTimeout(load_or_wait, 100);\n", " } else {\n", " root._bokeh_is_initializing = true\n", " root._bokeh_onload_callbacks = []\n", " const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n", " if (!reloading && !bokeh_loaded) {\n", " if (root.Bokeh) {\n", " root.Bokeh = undefined;\n", " }\n", " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", " }\n", " load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n", " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", " run_inline_js();\n", " });\n", " }\n", " }\n", " // Give older versions of the autoload script a head-start to ensure\n", " // they initialize before we start loading newer version.\n", " setTimeout(load_or_wait, 100)\n", "}(window));" ], "application/vnd.holoviews_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n const py_version = '3.7.3'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {'plotly': 'https://cdn.plot.ly/plotly-3.0.1.min'}, 'shim': {}});\n require([\"plotly\"], function(Plotly) {\n window.Plotly = Plotly\n on_load()\n })\n root._bokeh_is_loading = css_urls.length + 1;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } if (((window.Plotly !== undefined) && (!(window.Plotly instanceof HTMLElement))) || window.requirejs) {\n var urls = [];\n for (var i = 0; i < urls.length; i++) {\n skip.push(encodeURI(urls[i]))\n }\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.7.5/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.holoviz.org/panel/1.7.5/dist/bundled/plotlyplot/plotly-3.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.7.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.7.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.7.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.7.3.min.js\", \"https://cdn.holoviz.org/panel/1.7.5/dist/panel.min.js\"];\n const js_modules = [];\n const js_exports = {};\n const css_urls = [\"https://cdn.holoviz.org/panel/1.7.5/dist/bundled/plotlyplot/maplibre-gl@4.4.1/dist/maplibre-gl.css?v=1.7.5\"];\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (let i = 0; i < inline_js.length; i++) {\n try {\n inline_js[i].call(root, root.Bokeh);\n } catch(e) {\n if (!reloading) {\n throw e;\n }\n }\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));" }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "\n", "if ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n", " window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n", "}\n", "\n", "\n", " function JupyterCommManager() {\n", " }\n", "\n", " JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n", " if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", " comm_manager.register_target(comm_id, function(comm) {\n", " comm.on_msg(msg_handler);\n", " });\n", " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", " window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n", " comm.onMsg = msg_handler;\n", " });\n", " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", " google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n", " var messages = comm.messages[Symbol.asyncIterator]();\n", " function processIteratorResult(result) {\n", " var message = result.value;\n", " var content = {data: message.data, comm_id};\n", " var buffers = []\n", " for (var buffer of message.buffers || []) {\n", " buffers.push(new DataView(buffer))\n", " }\n", " var metadata = message.metadata || {};\n", " var msg = {content, buffers, metadata}\n", " msg_handler(msg);\n", " return messages.next().then(processIteratorResult);\n", " }\n", " return messages.next().then(processIteratorResult);\n", " })\n", " }\n", " }\n", "\n", " JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n", " if (comm_id in window.PyViz.comms) {\n", " return window.PyViz.comms[comm_id];\n", " } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", " var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n", " if (msg_handler) {\n", " comm.on_msg(msg_handler);\n", " }\n", " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", " var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n", " let retries = 0;\n", " const open = () => {\n", " if (comm.active) {\n", " comm.open();\n", " } else if (retries > 3) {\n", " console.warn('Comm target never activated')\n", " } else {\n", " retries += 1\n", " setTimeout(open, 500)\n", " }\n", " }\n", " if (comm.active) {\n", " comm.open();\n", " } else {\n", " setTimeout(open, 500)\n", " }\n", " if (msg_handler) {\n", " comm.onMsg = msg_handler;\n", " }\n", " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", " var comm_promise = google.colab.kernel.comms.open(comm_id)\n", " comm_promise.then((comm) => {\n", " window.PyViz.comms[comm_id] = comm;\n", " if (msg_handler) {\n", " var messages = comm.messages[Symbol.asyncIterator]();\n", " function processIteratorResult(result) {\n", " var message = result.value;\n", " var content = {data: message.data};\n", " var metadata = message.metadata || {comm_id};\n", " var msg = {content, metadata}\n", " msg_handler(msg);\n", " return messages.next().then(processIteratorResult);\n", " }\n", " return messages.next().then(processIteratorResult);\n", " }\n", " })\n", " var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n", " return comm_promise.then((comm) => {\n", " comm.send(data, metadata, buffers, disposeOnDone);\n", " });\n", " };\n", " var comm = {\n", " send: sendClosure\n", " };\n", " }\n", " window.PyViz.comms[comm_id] = comm;\n", " return comm;\n", " }\n", " window.PyViz.comm_manager = new JupyterCommManager();\n", " \n", "\n", "\n", "var JS_MIME_TYPE = 'application/javascript';\n", "var HTML_MIME_TYPE = 'text/html';\n", "var EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\n", "var CLASS_NAME = 'output';\n", "\n", "/**\n", " * Render data to the DOM node\n", " */\n", "function render(props, node) {\n", " var div = document.createElement(\"div\");\n", " var script = document.createElement(\"script\");\n", " node.appendChild(div);\n", " node.appendChild(script);\n", "}\n", "\n", "/**\n", " * Handle when a new output is added\n", " */\n", "function handle_add_output(event, handle) {\n", " var output_area = handle.output_area;\n", " var output = handle.output;\n", " if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", " return\n", " }\n", " var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", " if (id !== undefined) {\n", " var nchildren = toinsert.length;\n", " var html_node = toinsert[nchildren-1].children[0];\n", " html_node.innerHTML = output.data[HTML_MIME_TYPE];\n", " var scripts = [];\n", " var nodelist = html_node.querySelectorAll(\"script\");\n", " for (var i in nodelist) {\n", " if (nodelist.hasOwnProperty(i)) {\n", " scripts.push(nodelist[i])\n", " }\n", " }\n", "\n", " scripts.forEach( function (oldScript) {\n", " var newScript = document.createElement(\"script\");\n", " var attrs = [];\n", " var nodemap = oldScript.attributes;\n", " for (var j in nodemap) {\n", " if (nodemap.hasOwnProperty(j)) {\n", " attrs.push(nodemap[j])\n", " }\n", " }\n", " attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n", " newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n", " oldScript.parentNode.replaceChild(newScript, oldScript);\n", " });\n", " if (JS_MIME_TYPE in output.data) {\n", " toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n", " }\n", " output_area._hv_plot_id = id;\n", " if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n", " window.PyViz.plot_index[id] = Bokeh.index[id];\n", " } else {\n", " window.PyViz.plot_index[id] = null;\n", " }\n", " } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", " var bk_div = document.createElement(\"div\");\n", " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", " var script_attrs = bk_div.children[0].attributes;\n", " for (var i = 0; i < script_attrs.length; i++) {\n", " toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n", " }\n", " // store reference to server id on output_area\n", " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", " }\n", "}\n", "\n", "/**\n", " * Handle when an output is cleared or removed\n", " */\n", "function handle_clear_output(event, handle) {\n", " var id = handle.cell.output_area._hv_plot_id;\n", " var server_id = handle.cell.output_area._bokeh_server_id;\n", " if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n", " var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n", " if (server_id !== null) {\n", " comm.send({event_type: 'server_delete', 'id': server_id});\n", " return;\n", " } else if (comm !== null) {\n", " comm.send({event_type: 'delete', 'id': id});\n", " }\n", " delete PyViz.plot_index[id];\n", " if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n", " var doc = window.Bokeh.index[id].model.document\n", " doc.clear();\n", " const i = window.Bokeh.documents.indexOf(doc);\n", " if (i > -1) {\n", " window.Bokeh.documents.splice(i, 1);\n", " }\n", " }\n", "}\n", "\n", "/**\n", " * Handle kernel restart event\n", " */\n", "function handle_kernel_cleanup(event, handle) {\n", " delete PyViz.comms[\"hv-extension-comm\"];\n", " window.PyViz.plot_index = {}\n", "}\n", "\n", "/**\n", " * Handle update_display_data messages\n", " */\n", "function handle_update_output(event, handle) {\n", " handle_clear_output(event, {cell: {output_area: handle.output_area}})\n", " handle_add_output(event, handle)\n", "}\n", "\n", "function register_renderer(events, OutputArea) {\n", " function append_mime(data, metadata, element) {\n", " // create a DOM node to render to\n", " var toinsert = this.create_output_subarea(\n", " metadata,\n", " CLASS_NAME,\n", " EXEC_MIME_TYPE\n", " );\n", " this.keyboard_manager.register_events(toinsert);\n", " // Render to node\n", " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", " render(props, toinsert[0]);\n", " element.append(toinsert);\n", " return toinsert\n", " }\n", "\n", " events.on('output_added.OutputArea', handle_add_output);\n", " events.on('output_updated.OutputArea', handle_update_output);\n", " events.on('clear_output.CodeCell', handle_clear_output);\n", " events.on('delete.Cell', handle_clear_output);\n", " events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n", "\n", " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", " safe: true,\n", " index: 0\n", " });\n", "}\n", "\n", "if (window.Jupyter !== undefined) {\n", " try {\n", " var events = require('base/js/events');\n", " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", " register_renderer(events, OutputArea);\n", " }\n", " } catch(err) {\n", " }\n", "}\n" ], "application/vnd.holoviews_load.v0+json": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n let retries = 0;\n const open = () => {\n if (comm.active) {\n comm.open();\n } else if (retries > 3) {\n console.warn('Comm target never activated')\n } else {\n retries += 1\n setTimeout(open, 500)\n }\n }\n if (comm.active) {\n comm.open();\n } else {\n setTimeout(open, 500)\n }\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n })\n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n" }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
\n", "
\n", "
\n", "" ] }, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": "bb363093-b99f-4cf5-b11e-925cbaa28073" } }, "output_type": "display_data" }, { "data": { "text/html": [ "\n", "\n", "\n", "\n", "
\n", "\n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", " \n", "\n", "\n", "\n", "\n", "\n", "
\n", "\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import numpy as np\n", "import pandas as pd\n", "\n", "import holoviews as hv\n", "from holoviews import dim, opts\n", "\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "\n", "from netsci.datasets import load_connectome\n", "import netsci.metrics.motifs as nsm\n", "\n", "\n", "hv.extension(\"plotly\")\n", "plt.rcParams[\"figure.dpi\"] = 144" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The connectome dataset is composed of two parts:\n", "1. The *nodes* are the neurons. The data lists of all neurons, their `gid`-s and 3d position $ (x,y,z) $.\n", "2. The *edges* depict the synapses interconnecting all neurons. The data lists all pairs of connected neurons and the number of contacts (synapses) realizing each connection." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:52:03.480376Z", "start_time": "2020-01-22T16:52:03.372062Z" } }, "outputs": [ { "data": { "text/plain": [ "{'title': str,\n", " 'nodes': pandas.core.frame.DataFrame,\n", " 'edges': pandas.core.frame.DataFrame}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "connectome = load_connectome()\n", "display({k: type(v) for k, v in connectome.items()})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The magnitude of the network is" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:52:03.494381Z", "start_time": "2020-01-22T16:52:03.487395Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Connectome (L5-TTPC\n", "\t2,003 neurons\n", " \t102,732 connections (p = 2.56%)\n", "\t650,949 contacts\n" ] } ], "source": [ "print(connectome[\"title\"])\n", "neurons, synapses = connectome[\"nodes\"], connectome[\"edges\"]\n", "\n", "n = len(neurons)\n", "print(\n", " f\"\\t{n:,} neurons\\n\",\n", " f\"\\t{len(synapses):,} connections (p = {len(synapses)/(n*(n-1)):.2%})\\n\"\n", " f'\\t{synapses[\"contacts\"].sum():,} contacts',\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Neurons" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:52:03.512607Z", "start_time": "2020-01-22T16:52:03.497909Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
gidxyz
075211341.846181773.977844636.437126
175214305.8693161200.585727608.787329
275217312.4110251184.177732647.120290
375218275.2618061110.341752630.010095
475221239.869853880.629815609.945456
...............
199881314472.1980461087.211926409.227347
199981317308.608095947.743964624.308077
200081318325.5103901029.783942574.410863
200181319383.3619521161.047906655.450589
200281322331.4647651062.599933651.448519
\n", "

2003 rows × 4 columns

\n", "
" ], "text/plain": [ " gid x y z\n", "0 75211 341.846181 773.977844 636.437126\n", "1 75214 305.869316 1200.585727 608.787329\n", "2 75217 312.411025 1184.177732 647.120290\n", "3 75218 275.261806 1110.341752 630.010095\n", "4 75221 239.869853 880.629815 609.945456\n", "... ... ... ... ...\n", "1998 81314 472.198046 1087.211926 409.227347\n", "1999 81317 308.608095 947.743964 624.308077\n", "2000 81318 325.510390 1029.783942 574.410863\n", "2001 81319 383.361952 1161.047906 655.450589\n", "2002 81322 331.464765 1062.599933 651.448519\n", "\n", "[2003 rows x 4 columns]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "display(neurons)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The *y-axis* denotes the cortical depth. The positive direction is defined to be the [_pia_](https://en.wikipedia.org/wiki/Pia_mater#Cranial_pia_mater) direction in the column." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:52:04.252904Z", "start_time": "2020-01-22T16:52:03.517008Z" } }, "outputs": [ { "data": {}, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
\n", "
\n", "
\n", "" ], "text/plain": [ ":Scatter3D [x,y,z]" ] }, "execution_count": 6, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": "6f9f080c-8a73-4588-bd57-851dd996c24a" } }, "output_type": "execute_result" } ], "source": [ "# hv.Scatter3D((neurons.x, neurons.y, neurons.z)).opts(\n", "# opts.Scatter3D(azimuth=40, elevation=20, color='z', s=50, cmap='fire'))\n", "\n", "hv.Scatter3D((neurons.x, neurons.y, neurons.z)).opts(\n", " cmap=\"fire\", color=\"y\", size=3, alpha=0.75, width=800, height=800\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Synaptic connections" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:52:04.269558Z", "start_time": "2020-01-22T16:52:04.260977Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
fromtocontacts
075211752215
175211752765
275211754735
375211760127
475211760953
............
10272781322809915
10272881322810515
10272981322811114
10273081322811169
10273181322811474
\n", "

102732 rows × 3 columns

\n", "
" ], "text/plain": [ " from to contacts\n", "0 75211 75221 5\n", "1 75211 75276 5\n", "2 75211 75473 5\n", "3 75211 76012 7\n", "4 75211 76095 3\n", "... ... ... ...\n", "102727 81322 80991 5\n", "102728 81322 81051 5\n", "102729 81322 81111 4\n", "102730 81322 81116 9\n", "102731 81322 81147 4\n", "\n", "[102732 rows x 3 columns]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "display(synapses)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Adjaceny matrix \n", "\n", "Although representing the network as an edge list as above might space-efficient, sometimes it is more convenient to analyze the adjacency matrix directly. You could, obviously, transform the edge list to the matrix representation yourself (a quick exercise in [table operations](https://pandas.pydata.org/pandas-docs/stable/user_guide/merging.html) acrobatics, or straightforward by nested loops). Still, we can simply get it using the `adjacency=True` flag as below:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:52:05.244741Z", "start_time": "2020-01-22T16:52:04.274168Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2003x2003 adjacency matrix\n", " \t2,003 neurons\n", " \t102,732 connections\n", "\t650,949.0 contacts\n" ] } ], "source": [ "connectome = load_connectome(adjacency=True)\n", "neurons, synapses, A, W = (\n", " connectome[\"nodes\"],\n", " connectome[\"edges\"],\n", " connectome[\"A\"],\n", " connectome[\"W\"],\n", ")\n", "\n", "print(\n", " f\"{A.shape[0]}x{A.shape[1]} adjacency matrix\\n\",\n", " f\"\\t{len(A):,} neurons\\n\",\n", " f\"\\t{A.sum():,} connections\\n\" f\"\\t{W.sum():,} contacts\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that for convenience, in addition to the binary adjacency matrix `A`, a weighted matrix `W`, whose values describe the number of synapses per connection, is also returned. For the rest of our analysis here, we consider only the connections (without the contacts)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Motif embedding in space" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Scan triplet motifs\n", "Let's examine the how each motif is embedded in space." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:52:53.102188Z", "start_time": "2020-01-22T16:52:05.250525Z" } }, "outputs": [], "source": [ "frequency, participations = nsm.motifs(A, participation=True)\n", "\n", "# Reorder according to nn4576 paper\n", "tid_order = nsm.triad_order_nn4576\n", "frequency = np.take(frequency, tid_order)\n", "participations = [participations[t] for t in tid_order]" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:52:53.110935Z", "start_time": "2020-01-22T16:52:53.106713Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[4187617 2738360 3162677 281224 208612 174242 29961 4328 7555\n", " 7808 6694 689 15]\n" ] } ], "source": [ "print(frequency)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:52:57.915056Z", "start_time": "2020-01-22T16:52:53.126044Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
RGBmotif
07521175221752611
17521175221752711
27521175221752771
37521175221753961
47521175221756911
...............
1080977781129803707695613
1080977878718779487752013
1080977980751798997754613
1080978080386803357770413
1080978178422777737770813
\n", "

10809782 rows × 4 columns

\n", "
" ], "text/plain": [ " R G B motif\n", "0 75211 75221 75261 1\n", "1 75211 75221 75271 1\n", "2 75211 75221 75277 1\n", "3 75211 75221 75396 1\n", "4 75211 75221 75691 1\n", "... ... ... ... ...\n", "10809777 81129 80370 76956 13\n", "10809778 78718 77948 77520 13\n", "10809779 80751 79899 77546 13\n", "10809780 80386 80335 77704 13\n", "10809781 78422 77773 77708 13\n", "\n", "[10809782 rows x 4 columns]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "gids = neurons[\"gid\"].values\n", "motifs = pd.concat(\n", " [\n", " pd.DataFrame(gids.take(p), columns=[\"R\", \"G\", \"B\"]).assign(motif=i + 1)\n", " for i, p in enumerate(participations)\n", " if p\n", " ],\n", " ignore_index=True,\n", ")\n", "display(motifs)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:52:58.242084Z", "start_time": "2020-01-22T16:52:57.920793Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
motif12345678910111213
Frequency4,187,6172,738,3603,162,677281,224208,612174,24229,9614,3287,5557,8086,69468915
\n" ], "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "assert np.array_equal(frequency, motifs.groupby(\"motif\").size())\n", "\n", "motifs.groupby(\"motif\").size().to_frame(\"Frequency\").T.style.format(\"{:,}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Triplet motifs spatial embedding\n", "\n", "Lastly, we examine the positions of the neurons forming each of the motifs. Since the pyramidal neurons are vertical, we'll focus on the y-coordinate (cortical depth)." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "ExecuteTime": { "end_time": "2020-01-22T16:53:02.299937Z", "start_time": "2020-01-22T16:52:58.248202Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
RGBmotifRxRyRzGxGyGzBxByBz
07521175221752611341.846181773.977844636.437126239.869853880.629815609.945456203.276337968.822791481.909118
17521175221752711341.846181773.977844636.437126239.869853880.629815609.945456320.1836231180.844858570.809167
27521175221752771341.846181773.977844636.437126239.869853880.629815609.945456293.1998731090.600882580.749479
37521175221753961341.846181773.977844636.437126239.869853880.629815609.945456393.4500291067.014389642.183122
47521175221756911341.846181773.977844636.437126239.869853880.629815609.945456497.1375871024.840713725.135217
..........................................
1080977781129803707695613192.844296948.897651737.201974178.4754771061.766714692.749867248.7437351104.252847703.846591
1080977878718779487752013472.534544787.782031597.521670385.660359797.075622501.540998359.745437878.731038649.375373
1080977980751798997754613214.454828907.813569437.465360226.882608883.506021430.744574251.4921901077.677984499.352161
1080978080386803357770413335.8237051079.200210626.482609340.1069081018.695726604.070499337.106262998.329943660.917740
1080978178422777737770813277.6091041140.425748652.055906271.0516191144.976403535.146850355.0845091117.287910510.178296
\n", "

10809782 rows × 13 columns

\n", "
" ], "text/plain": [ " R G B motif Rx Ry Rz \\\n", "0 75211 75221 75261 1 341.846181 773.977844 636.437126 \n", "1 75211 75221 75271 1 341.846181 773.977844 636.437126 \n", "2 75211 75221 75277 1 341.846181 773.977844 636.437126 \n", "3 75211 75221 75396 1 341.846181 773.977844 636.437126 \n", "4 75211 75221 75691 1 341.846181 773.977844 636.437126 \n", "... ... ... ... ... ... ... ... \n", "10809777 81129 80370 76956 13 192.844296 948.897651 737.201974 \n", "10809778 78718 77948 77520 13 472.534544 787.782031 597.521670 \n", "10809779 80751 79899 77546 13 214.454828 907.813569 437.465360 \n", "10809780 80386 80335 77704 13 335.823705 1079.200210 626.482609 \n", "10809781 78422 77773 77708 13 277.609104 1140.425748 652.055906 \n", "\n", " Gx Gy Gz Bx By \\\n", "0 239.869853 880.629815 609.945456 203.276337 968.822791 \n", "1 239.869853 880.629815 609.945456 320.183623 1180.844858 \n", "2 239.869853 880.629815 609.945456 293.199873 1090.600882 \n", "3 239.869853 880.629815 609.945456 393.450029 1067.014389 \n", "4 239.869853 880.629815 609.945456 497.137587 1024.840713 \n", "... ... ... ... ... ... \n", "10809777 178.475477 1061.766714 692.749867 248.743735 1104.252847 \n", "10809778 385.660359 797.075622 501.540998 359.745437 878.731038 \n", "10809779 226.882608 883.506021 430.744574 251.492190 1077.677984 \n", "10809780 340.106908 1018.695726 604.070499 337.106262 998.329943 \n", "10809781 271.051619 1144.976403 535.146850 355.084509 1117.287910 \n", "\n", " Bz \n", "0 481.909118 \n", "1 570.809167 \n", "2 580.749479 \n", "3 642.183122 \n", "4 725.135217 \n", "... ... \n", "10809777 703.846591 \n", "10809778 649.375373 \n", "10809779 499.352161 \n", "10809780 660.917740 \n", "10809781 510.178296 \n", "\n", "[10809782 rows x 13 columns]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "embedding = (\n", " motifs.merge(\n", " neurons.set_index(\"gid\").add_prefix(\"R\"),\n", " how=\"left\",\n", " left_on=\"R\",\n", " right_index=True,\n", " )\n", " .merge(\n", " neurons.set_index(\"gid\").add_prefix(\"G\"),\n", " how=\"left\",\n", " left_on=\"G\",\n", " right_index=True,\n", " )\n", " .merge(\n", " neurons.set_index(\"gid\").add_prefix(\"B\"),\n", " how=\"left\",\n", " left_on=\"B\",\n", " right_index=True,\n", " )\n", ")\n", "embedding" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'Height (µm)')" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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Dz+MqVOnxmGHHfad50+VNyng2XDDDeOVV17J17vqqqvihRdeyNU5KQCaMmVKrTYpAEoBT69evWLixIlx8cUXxw033BCPP/54tGzZMgdFn332WYNfOwCw9OFCQ26lPk9T6x8A0DQ0uUqenXfeebGPTcOz9thjjyU6/zXXXJO3Z5xxRrRv375mf5cuXeLYY4+N8847L0aOHBnnnntuzXPVYVOqMmrTpk3N/m222SYGDBgQN998cw6B0rAxAICmaucryjMM/fFhvcpyXQBoappcyLOsPfbYY3m7++67L/BcCoxSyJOOqQ55Zs+enat/UtVQmieovjYp5ElthDwAUF4vH71XWa67+TULn9dvfje9eEuUw6Ce9c9vCAA0LUKeOnP4fPDBB3m+n3XWWWeB57t165a3abhYtbfeeitPuty1a9do1arVYrVZlK222qre/a+//npsuummi/1aACqJ+TwAAGDZE/LMZ+bMmXmbVtOqT/X+GTNmNKgNAAAATcviVmVCOQl5Kkya4HlJKnwAmtKXD0NRAABg2RHy1FN1U12dU1f1/tVWW61BbSjuL7AAAEAxlWtet8TvQSwuIc98VlpppejUqVOel+ejjz5aYF6eN998M2833njjmn1pqfW0TPrbb78dc+fOXWBenvraADTXSWUBAIBlR8hTR9++ffNqWI888sgCq2E9/PDDNcdUS0um9+rVK5566ql8q7vEe31tWHp+gQUAAID6LbeQ/c3W0UcfnbcXXHBBTJ8+vWb/lClT4qqrrooVVlhhgfDnmGOOydszzzwzL6le7bnnnovbb7891lhjjdh///0b7TUAAAAAzU+Tq+S599578y2ZOnVq3k6YMCEGDx6c76+++uoxfPjwmuMvuuiimDRpUr4/ceLEvB05cmT87W9/y/d32GGH+PWvf11zfKrKOeGEE+LSSy+NHj16RP/+/WPOnDk5rJk2bVqMGDEiunTpUqtPAwcOjLvvvjvGjh0bW265ZfTr1y8+/fTT3CYtr37ddddFu3btlvl/GwAAAKD5anIhTwpqRo8eXWtfmg8n3ZL111+/VsiThl2NGzeu1vHjx4/Pt2rzhzzJJZdcEj/84Q9z5c61114byy23XPTs2TNOPvnk2GuvBYcLtWjRIm677bYcEN144405CErDuHr37p2re9J+AAAAgGWpyYU855xzTr4trieeeGKprpMqg6qrgxZHmnD5t7/9bb4BAAAANDZz8gAAAAAUgJAHAAAAoACEPAAAAAAFIOQBAAAAKIAmN/EyAAAAUHvF50o4T1VVVUn6wdJTyQMAAABQACp5AAAAoABuevGWslx3UM+DynJdFqSSBwAAAKAAhDwAAAAABWC4FgAAALDMmBi68Qh5AACAJsG8HwCLJuQBAAAAlrmdrxhflus+PqxXNBdCHigB5YcAAMuelYMAGiHk+ctf/pJvTz75ZLz33nvxySefRNu2bWPNNdeMLbbYIvr27Rt77713dOrUqRSXAwAAAKBUIc+XX34ZV1xxRfznf/5nDnaqKwjatGmTw52vvvoq3n777XjrrbfirrvuiuOOOy769esXJ510Umy33XZLe1moaJX61yWVRgAAAMW3VEuo33jjjdGtW7c4/fTTc8XO2WefnSt5ZsyYkcOf999/Pz799NOYO3duvPbaa/n4/fffPx5++OHYYYcdYsCAATkYAgAAAKCMlTy//vWvY999943TTjstttlmm0X+1X+TTTbJt8GDB8esWbNi9OjRcdFFF8WoUaPirLPOakjfgSVkojMAgGXHdx6gSYY8zz//fPTs2XOJ27Vr1y6GDh0aRxxxREyZMmVpLg0AAABAqUKepQl45pfm7UnVPQAAAEWhahpoknPyAAAAAFDAJdSrffvtt/HBBx/kiZe/+eabeo/p3bt3KS8JAAAAQClDnosvvjiGDx8en3zyySKPmzdvXqkuCQAAAEApQ55zzjknfve730XHjh3jkEMOiU6dOkWrViUtEgIAAABgEUqSxNxwww3RtWvXeOGFF2LVVVctxSkBAAAAaOyJlz/99NPYe++9BTwAAAAATTnk2WijjWL69OmlOBUAAAAA5Qp5fvOb38SDDz4YU6dOLcXpAAAAACjHnDxHH310TJ48Obbffvs466yzomfPngsduvW9732vFJcEAAAAYD4lWwJr8803j1GjRsVhhx220GNatGgRc+fOLdUlAQAAAChlyHP99dfHUUcdlZdN79OnT6y77rqWUAcAAABoRCVJYoYPHx5rrrlmjB8/PjbYYINSnBIAAACAxp54+d13343+/fsLeAAAAACacsjTqVOn+Oabb0pxKgAAAADKFfIMGjQoHnroofjss89KcToAAKARpQVSGnIr9XkAKGPIc/rpp8e2224bP/nJT+KJJ54Q9gAAAAA0xYmXV1hhhbytqqqKXXbZZaHHWUIdAAAq18tH71WW625+zYNluS5A0ZQk5Nlxxx2VVwIAAAA09ZAnDdECAAAAoImHPAAATYEhIQAsLf+G0GwmXl4cxx9/fOy5556NdTkAAACAZqUklTy/+93vFvl8mmz5gQceiKlTp5bicgAAS8WksgAsqbTAUENVz2FbinPBMg95zjnnnMX6gT744INLcTkAgEIa1POgcncBAGjuIc/jjz9e7/6vvvoqJk+eHCNGjMhBz8UXX1yKywHQRKsF/AILTdvjw3qVuwsAwLIOeXbaaaeFPrf77rvHoEGD4vvf/3789re/jVtuuaVB1xo7dmyMGzcuJk6cGC+//HJ89tlnceCBBy7yvOPHj4/zzz8/nnnmmRw8devWLQ477LAYOnRotGzZst42Dz74YAwfPjxeeumlmDdvXu7/b37zmzjkkEMWep3Ro0fHVVddFa+99lo+75ZbbhknnXRS7LVXeUrDAYCm5aYXG/Y9aWkJYAGgGBplda3VVlstT7qcApqGSmFNCndWXnnl6Ny5c0yaNGmRx993332x//77R5s2bWLAgAHRoUOHPD9QCpyefvrpuPPOOxdoc+WVV+YAqGPHjnHQQQdF69atc98HDx4cf//733P4U1cKcy655JLcpyOOOCLmzJkTY8aMiX79+uVKpiFDhjT4tQMsTEPHdzfWOHG/wELTtvMV48tyXRVEAFBhq2utuuqquSKmoS677LI8BGzWrFlx9dVXL/LYdEwKXFJVzRNPPBE33HBDHjKWqoC22267HNykIGZ+U6ZMyYFNCoOef/75XJmTrvnKK6/EhhtumIOcCRMmLFAplPan59Nx6fjU7oUXXsjnSedL5wUAAABo0pU8yVNPPRU77rhjg8+z8847L/axKcT5+OOP83CxrbfeumZ/qupJFUG77LJLDooGDhxY89yNN94YX3/9dZxyyinRpUuXmv3t27eP008/PQ4//PC45pprckhULT1OzjjjjHxctdT+2GOPjfPOOy9GjhwZ5557boNeO5U/3wjQNFVXMpX7PFbcAACg7JU87733Xr23d999N1e5pJAl3T/55JMXOGZZeuyxx2rmBaqrd+/eseKKK+b+pVBncdrssccetY5pSBsAAACAiqvkSRUri/rrZfVfJlPlzPxSm7lz58ay8sYbb+TtxhtvvMBzrVq1ig022CBeffXVePvtt2PTTTf9zjbrrLNOrLTSSvH+++/Hl19+mUOiL774Ij744IM8R1B6vq40yXOShpgtjq222qre/a+//npNH5ujpjLfCNC0mW8EAIBo7iFPqtQpVal7Kc2cObNmPqD6VO+fMWPGErVJwU46LoU8S3MNAAAAgIoMeUaNGlWK0xCRJ2tekgofAAAAgEZdXascqqtoqqtt6qren5Z4X9I21cctzTUAAAAASq3QIU/37t0XOh9OmgvonXfeyXPzdO3adbHafPTRR3moVufOnfNQrSTN0dOpU6f4/PPP8/N1vfnmmwud4wcAAACgrCHPXnvtFS+//PJSXTCtZHXZZZflpcuXtb59++btI488ssBzTz75ZJ48uVevXrHCCissVpuHH3641jENaQMAAABQ9pAnrUDVs2fPvDz47bffHrNnz/7ONml1qNNOOy1XzZxyyimxyiqrxLLWv3//WH311WPMmDHx/PPP1+xP/T3zzDPz/WOOOaZWm0MPPTSHPldeeWVMmTKlZv/06dPjwgsvzPePPvroWm2qH19wwQX5uGqp/VVXXZXPl84LAAAAUFETL7/22mtx+eWX59Djz3/+c7Ru3TqHPltvvXVeRrx9+/Y5SPn0009j0qRJ8cwzz+RlxtPy1bvttlsMHz48fvCDHyxVh++99958S6ZOnZq3EyZMiMGDB+f7KdRJ50/atWsX1113XQ57+vTpEwMHDowOHTrE/fffn4OqtH/AgAG1zp+WVb/44otj2LBh+fWk59PrGzt2bF46/cQTT4ztttuuVptUDXTCCSfEpZdeGj169MjnnTNnTg7Apk2bFiNGjMjLzAMAAABUVMiz/PLLx0knnRS/+c1v4tZbb40bbrghnn322Ry2JGk59RToVFtjjTXiiCOOyMenEKQhJk6cGKNHj6617+233863ZP31168JeZJ99903xo0bl6ts7rrrrhw+bbTRRjmQSUFOfUu/Dx06NIcy6Tw33XRTfPvtt7HZZpvF+eefH4cccki9/brkkkvihz/8Ya7cufbaa2O55ZbLwdfJJ5+ch7cBAAAAVOwS6mny4RTepNusWbNyyPPee+/lCp62bdvGmmuumUOd73//+yXr8DnnnJNvS2L77bePhx56aIna9OvXL9+WRKomqq4oAgAAAGgyIc/80tCon/70p6U6HQAAAABLoNBLqAMAAAA0F0IeAAAAgAIQ8gAAAAAUgJAHAAAAoACEPAAAAAAFIOQBAAAAKAAhDwAAAEABlCTk6dq1a1xxxRWLPOaqq67KxwEAAABQoSHPlClTYsaMGYs8Jj3/7rvvluJyAAAAAJRruNZnn30WrVu3bqzLAQAAADQrrZa24XvvvbdApU7dfcm8efPy/rvuustwLQAAAFhGBvU8qNxdoKmGPF26dIkWLVrUPL788svzbWGqqqri0ksvXdrLAQAAAE3Y48N6lbsLhbfUIc+gQYNyyJPCm5tuuil69OgRW2yxxQLHtWzZMjp27Bi77LJL7Lbbbg3tLwAAAFCPm168pSzXVUFUgJBn1KhRNfdTyLPffvvFWWedVap+AQAA1OIXSWjadr5ifFmu+3gzqiBa6pBnft9++20pTgMAAABAOUMeoGloTgk2AFAcaYqIhqieS7Sh5wFoNiHPtGnT4sYbb4xnn302pk+fnlfVqu9/ro8++mipLgkVRwkxAAAATTrkmTRpUvTp0yc+/vjjRabj86/GBTQ+Y2ABAACKqyQhz0knnRT/+te/4tRTT40jjzwy1ltvvbyqFjQ3ZrMHAACgSYc8Tz31VPzsZz+LCy+8sBSnAwAAAGAJLRclkIZobbbZZqU4FQAAAADlquTZaqut4o033ijFqQAAAJok8xAChajkOeuss+Khhx6KJ554ohSnAwAAAKAxKnluuummBfbts88+sdtuu8UBBxyQK3tWW221etsOGjRoaS4JAABQkRa1wvDiqF6FuKHnAViqkGfw4MELLIde/T+km2++Od/qez7tE/IAAAAAVEjIM3LkyNL3BAAAAIDGDXkOOeSQpb8iAAAAAJU58TIAAAAA5SXkAQAAAGiuw7Xq6tq163ces9xyy0W7du1i0003jZ///Oex//77l+LSAABAiWx+zYPl7gIA5Q55vv3225g7d258+OGH//+krVpFx44d49NPP837k3XXXTf+9a9/xcSJE2PMmDGx5557xr333hstW7YsRRcAAAAAmrWShDyvvPJK7LrrrrHhhhvG73//+/jxj3+cK3dS+DNhwoQ4/fTT4+uvv46//OUvMXXq1Dj++OPjoYceissvvzxOOOGEUnQBAABooJeP3qss11VBBFBBc/KcccYZMXPmzHj00UejV69eOeDJJ19uudh+++1zuDNjxox8XLdu3eLOO++MTp06xa233lqKywMAAAA0eyUJee65557Ye++98zCt+rRu3Tr69esXd999d3684oorxi677BKTJ08uxeUBAAAAmr2ShDxp7p05c+Ys8phvvvkmH1dt7bXXrpmvBwAAAIAKCHnS6lp33XVXfPbZZ/U+P2vWrPz8BhtsULPvo48+ig4dOpTi8gAAAADNXklCniOPPDI++OCD+NGPfpTn2ZkyZUp89dVXeXvLLbfk/WnlraOOOiofX1VVFU888URsscUWpbg8AAAAQLNXktW1jjvuuHjjjTfimmuuiUGDBi3wfAp1UhCUjkvSUuoHHHBAXpELAAAAgAoJeZI//vGP8atf/SpGjRoVEydOzKtttWvXLrbccssc/PTu3bvm2LXWWisvtQ4AAABAhYU8yQ477JBvAAAAADTBOXkAAAAAaIIhz3vvvZdv8+bNq/V4cW7lkOYEuu666/IE0CuvvHKstNJKsfXWW+c5hL799tt62zz44IPRp0+fWHXVVXOb1Hb06NGLvE56ftttt83Hp3apfToPAAAAQEUO1+rSpUu0aNEiXn/99dh4441rHn+XdMzcuXOjsR100EHxpz/9KdZcc8084fOKK64Yf/nLX+KYY46J8ePHx0033VTr+CuvvDKGDh0aHTt2zG1bt24dY8eOjcGDB8ff//73GD58+ALXOOmkk+KSSy6Jzp07xxFHHBFz5syJMWPGRL9+/WLEiBExZMiQRnzFACyNx4f1KncXAACgcUOeNJFyCmxStcr8jyvRPffckwOeDTbYIJ599tlYffXV8/4Uwuy///5x8803x7777hs///nP8/607HsKbDp06BDPP/98DrCSs846K7bZZpsc5KR22223Xc01UlCU9m+44Ybx3HPPRfv27fP+k08+Obbaaqt8vr322qvmXAAAAAAVEfKkFbQW9bjSQp7kxBNPrAl4klSdc9555+XhVKlypzrkufHGG+Prr7+OU045pVYok4Kb008/PQ4//PA8zGv+kCc9Ts4444yagCdJ7Y899th8nZEjR8a5557bKK8ZoFIN6nlQVLKdrxhfluuqIAIAoBQKP/Hy1KlT87Zr164LPFe976mnnsqVPcljjz2Wt7vvvvsCx++xxx61jqm2NG0AAAAAKnYJ9WTSpEl5rp7PP/88Dj744Ci36uqdd955Z4Hn3n777bxN8wSl+5tsskm88cYbeV+a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psqf2267rVbYM2XKlNhkk01ipZVWihdeeCG6dOmS90+fPj222WabeOutt3Jl0HbbbbfUr2OrrbbK23R+ijcpqv5RbpX+Husf5Vbp77H+UW6V/h7rH+VW6e+x/hVH4St53n333fj222/z0Kn5A55k5513jlVWWSU+/vjjmn1jx47Nj1OIUx3wJG3atInzzz8/37/66qtrnefGG2+Mr7/+Olf8VAc8Sfv27eP000/P96+55ppl9hoBAACoTGmkSLX999+/1mMotcKHPN26dYvWrVvHs88+G5988kmt59JwrM8++yx+8pOf1Ox77LHH8jYN66orDeFaccUVc1VOCnUWp80ee+xR6xgAAACK7+KLL47VVlst9t1335p9d999d36c9qfnodRaRcGliZP//d//PU444YTYbLPN8geqY8eOeQjV/fffH7vuumv853/+Z83xb7zxRt5uvPHGC5yrVatWeWjXq6++mod3bbrppt/ZZp111snDuN5///348ssvc0i0OMOy6nr99ddrrgcAAEDlSnOzLmoRnjQ9yP/5P/8nXnvttRg5cmSj9o1iK3zIkxx//PF5GNVhhx0W1113Xc3+NPdO+vDNP4wrfdiStEJWfar3z5gxY4nafPHFF/m47wp5AAAAaLpShc7irrI8atSoXIxw8sknL/N+0TwUfrhW8h//8R/Rv3//HOikCp4UuKQJjNMy6gceeGBOUCtF6ld9N1U8AAAAUbETA1fflvT3y3R8dVtoqMKHPE888UReQn3vvfeOSy+9NAc7qZomLaF+zz335JW3LrnkkprVtaqrcaqrc+qq3p/GUFZb3DYLq/QBAAAAaKjChzwPPvhgzUpadaWwZ9ttt82rb7300kt5X/fu3fN28uTJCxw/d+7ceOedd/LcPCksqraoNh999FGuHOrcubOhWgAAAAWUlvZu6PLelgenFAof8lSvgjX/Munzq96fVuBK+vbtm7ePPPLIAsem1bjS5Mm9evWKFVZYoWb/oto8/PDDtY4BAAAAWBYKH/LsuOOOeXvttdfGBx98sEAA8/TTT0ebNm1ycJOkuXtWX331GDNmTDz//PM1x86ePTvOPPPMfP+YY46pdZ5DDz00hz5XXnllTJkypWb/9OnT48ILL8z3jz766GX4KgGW3vzjv40HBwAoveWWWy4XFqQtLEstqgpeE5aGYv30pz+Nv/71r7HKKqvEfvvtF2uvvXZekjwN5Uov/w9/+EMcd9xxNW3uvffeHPak8GfgwIF5Gfa03HpaKj3tv+OOOxb4JWjEiBExbNiwvDz7gAED8gd47Nixeen0E088MYYPH96g11G9tHqahJklV/1+VeqPu/5RDosT5lTKe17pP4OV3j+K/x7rH+VW6e+x/lGu71cp1Em/k9ZV3/5yvv+V/jNY6f2rJIUPeZJvvvkmrrrqqlyd89prr+UhVym4SfPxpGBmt912W6BNqvC54IILYsKECbmKJy23npZgT8e3bNmy3us88MADOcx58cUX8wc2LYU3ZMiQOOSQQxr8GoQ8xf6fgv7R2JakWqcS3vdK/xms9P5R/PdY/yi3Sn+P9Y/G0pCKaCFP0+1fJWkWIU8RCHmK/T8F/aPSv3yU+72v9J/BSu8fxX+P9Y9yq/T3WP9oLEKe5tm/SmJAIAAAAEABtCp3BwBY9ho6mbK/ngAAQOVTyQMAAABQAEIegGYgVeA0tApHFQ8AwLKpnm5o1TVUE/IAAABACSztH8X8MY1SEfIAAAAAFICQBwAAAKAAhDwU3vzjW9N9410BAAAoIkuoU1iLCnMsBw0AAEDRqOShkBa3WkdVDwAAAEUh5KFwljS4EfQAAACl0KpVq0ZtB3UJeQAAAKAELKFOuYkLKYSGVuOYowcAAICmTiUPAAAAlMC8efMatR3UJeShEFIFTkOrcFTxAAAA0JQJeQAAAAAKQMgDAAAAUABCHgAAAIACEPIAAAAAFICQBwAAAKAAhDwAAABQAq1atWrUdlCXkAcAAABKYO7cuY3aDuoS8gAAAAAUgJAHAAAAoACEPAAAAAAFIOQBAACAElh++eUbtR3UJeQBAACAElh77bUbtR3UJeQBAACAEnj//fcbtR3UJeQBAACAEqiqqmrUdlCXkAcAAACgAIQ8AAAAAAUg5AEAAIAS2HzzzRu1HdQl5AEAAIASOOywwxq1HdQl5AEAAIASmDVrVqO2g7qEPAAAQGG9+uqrNfevuOKKWo+h1L744otGbQd1CXkAAIDCefTRR2OnnXaKH/zgBzX7jjvuuPw47U/PQ6m9+eabjdoO6hLyAAAAhXLDDTfET37yk3jyySfrfT7tT8/feOONjd43iu2ll16qud+9e/fo3bt3vcel/en5+tpBQ7SoqqqqatAZaBRbbbVV3r7wwgvl7kpFa9GixVK3LedHobrflfpxrPT+sfh8Rppn/yj+e6x/lFslvcepQicFOIvrr3/9a+yyyy5RTpX034+G6dChQ0yfPn2J27Vv3z6mTZsW5VLpP4OV3r9KopIHAAAojD333LPW4xVWWKHm/rbbblvrcX3HQ0Mst1z9v2K3atUqVl555bxdknawpPwkAQAATVr6K3/1bc6cObWe+/rrr2vuP/vss7UeJ+n46rbQUOuvv36tx61bt87buXPnxueff5638+9fWDtYWvXHiAAAAMAS6dmzZ7z44os1j0888cTYZ5994r//+7/zMunt2rWLH/3oR3HffffF73//+1rtoBSEPAAAQJNWPU9H3Wqc9dZbL/75z38ucHx9+831QSm8++67tR6nIOfSSy+NtdZaK/98pp+z//3f/12goqxuO1haQh4AAKCQUpCT5kHp379/rLvuuvHhhx/G2LFj6w1+oBReeeWVBfalQOe9995b4nawNAo/J8+oUaNqjdGt79ayZcsF2o0fPz5PwpZmR2/btm306NEj/vCHP8S8efMWeq0HH3ww+vTpE6uuumr+xySV4Y0ePXoZv0IAAKC+Sp7TTjstBzsjR46MCy64IG/T47R/Ue1gaVXPudNY7aDZLaE+ceLEuPfee+t97qmnnorHHnssfvazn+WAploaH7n//vtHmzZtYsCAATnoeeCBB+KNN97IfwW48847FzjXlVdeGUOHDo2OHTvmNmkirfRXgvfffz+Pwxw+fHiDXocl1BeP5aGbZ/9YfD4jzbN/FP891j/KrVLe4zQk5l//+le+v/HGG+fv7wuTnn/zzTfz/TXXXDMPoWnu//1ouO7du8fkyZOXuN13/bw2VKmCzHL9jPqMLL7ChzyLst1228UzzzyTQ529994770uTYW200UYxc+bMePrpp2PrrbfO+2fPnh19+/aNCRMmxG233RYDBw6sOc+UKVNik002iZVWWimHMF26dMn7p0+fHttss0289dZbuTIoXW9pCXkWj19gm2f/WHw+I82zfxT/PdY/yq1S3uMDDzww/vSnP9U8ThU7p556ap7stlr6vn/RRRfVmvT2V7/6Vdx6663R3P/70XAjRoyIYcOG5furrbZabL755jFu3LgFjttpp53i5ZdfjhkzZuTHV1xxRS4aWFaEPM1H4YdrLczf//73HPB06tQpV/JUS9U3H3/8cQ5xqgOeJFX1nH/++fn+1VdfXetcN954Yx5nOWTIkJqAJ2nfvn2cfvrp+f4111zTCK8KAACar/SH2fmlICdV9wwePDh/L0/b9Hj+gKe+drC0vve979XcTwFOr1698u+dl19+eZx33nl5mx6n/dUBT912y0IKR0pxo/I124mXr7322rw9/PDDa83Jk4ZvJbvvvvsCbXr37h0rrrhirspJoc4KK6zwnW322GOPWsfQPC1ucv5dxy2r/7FWev8ovkr/Gaz0/lH891j/KLem8h6nyZXrSgHOd82TWV+75vjfj4a76aabaj1OgWIKdn7xi1/kn7N//OMfucLsyy+/XKBdWmq9ufIZKZ1mGfJ89dVXccstt+Rw59e//nWt56rHQaYxkXW1atUqNthgg3j11Vfj7bffjk033fQ726yzzjp5GFeamyd9kFNItDjDsup6/fXXa64HAAAsqFu3bo3aDupK034kaSGezz//PN9PvwcuLGisPq66HTRUsxyudccdd+TSuFR5s95669V6rvrDlVbIqk/1/vlL6xa3TUM+uGm4WJr3h6ap0ssjK71/FF+l/wxWev8o/nusf5RbU3mPd9lll1qPFzYEpu7+uu2a638/Gq76d7+11147jjjiiHpXck7S/vR8Gj44f7vmymekdFo156FaRx11VFQaEysDAMDS+f73v5+nWHjyySfzsI733nsv2rZtGz/84Q/zH03T0K00N2fan55PvxSmCXBTOyiFQYMGxd13352HZaUJl9Pvnmky5uuvvz5X7KTKnTSaJE2ynEZ7XHfddTXtoBSa3epaaajVD37wg+jcuXNeFatusppWw3r++efzrb6hU6ltOsdrr71WM3xqjTXWiE8++STf0hLqdaUP8hdffJFv3zVci/LMHN/MPgY0cz4jABTZo48+Grvttlt8++23uWInBTp1Ve9fbrnl4s9//vMyr+SheUmraqVRHGl15TSf68KkyZfT6s2pimf+kSLQEM1uuNbCJlyu1r1797ydPHnyAs/NnTs33nnnnTw3T9euXRerzUcffZTDnRQqCXgAAGDZSoFN+s6fApzqSp5tt902V/ikbXpcHfCkKgoBD6V2xhln5G0KcFKQkyp25pceVwc88x8PpdCsQp5UnnnzzTfncCeFPPXp27dv3j7yyCMLPJfKPtOkWekDWb2y1ne1efjhh2sdw7K3pBUHKhRo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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "motifs_order = [3, 4, 7, 10, 11]\n", "colors = [\"#D1684C\", \"#A2CEA4\", \"#4192C6\"]\n", "\n", "data = embedding[embedding[\"motif\"].isin(motifs_order)].melt(\n", " id_vars=\"motif\", value_vars=[\"Ry\", \"Gy\", \"By\"]\n", ")\n", "\n", "plt.figure(figsize=(9, 3))\n", "sns.boxplot(\n", " x=\"motif\",\n", " y=\"value\",\n", " hue=\"variable\",\n", " data=data,\n", " palette=colors,\n", " saturation=1,\n", " linecolor=\"k\",\n", " width=0.6,\n", " gap=0.2,\n", " legend=False,\n", ")\n", "sns.despine(offset=10, trim=True)\n", "plt.ylabel(\"Height (µm)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The analysis uncovers a close match between the physical positions of the neurons and the synaptic connectivity they form. For instance, for motif #4 the “source” neuron (the presynaptic neuron that projects to the other two) is located, on average, above the other two cells whereas the “sink” neuron (the postsynaptic neuron receiving projections from the other two) is, on average, the lowest." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## References and Further Readings\n", "Books\n", "1. [Networks of the Brain](https://mitpress.mit.edu/books/networks-brain#:~:targetText=In%20Networks%20of%20the%20Brain%2C%20Olaf%20Sporns%20describes%20how%20the,from%20a%20complex%20network%20perspective.&targetText=Networks%20of%20the%20Brain%20provides,essential%20foundation%20for%20future%20research.) by [Olaf Sporns](http://twitter.com/spornslab)\n", "2. [Connectome](http://connectomethebook.com/) by [Sebastian Seung](http://seunglab.org/)\n", "\n", "Papers\n", "* Markram, Muller, Ramaswamy, Reimann, Abdellah, Sanchez et al. (2015). [Reconstruction and Simulation of Neocortical Microcircuitry](https://www.cell.com/fulltext/S0092-8674(15)01191-5). *Cell*, **163**, 456–492. https://doi.org/10.1016/j.cell.2015.09.029.\n", " * The _in silico_ digital reconstruction of the network of all synaptic connections in a cortical microcircuit.\n", "* Gal, London, Globerson, Ramaswamy, Reimann, Muller, Markram, and Segev. (2017). [Rich cell-type-specific network topology in neocortical microcircuitry](http://www.rdcu.be/tffO). *Nat Neurosci*, **20(7)**, 1004–1013. http://doi.org/10.1038/nn.4576.\n", " * A network science analysis of the cortical microcircuit.\n", "* Gal, Perin, Markram, London, and Segev. (2019). [Neuron Geometry Underlies a Universal Local Architecture in Neuronal Networks](https://doi.org/10.1101/656058). *bioRxiv*, 656058. https://doi.org/10.1101/656058.\n", " * Developing a general framework for spatially embedded networks that, when applied to biological neuronal networks, uncovers the geometric origin of network motifs universally emergence in different brain regions.\n", "\n", "Web resources\n", "1. [The neuron and nervous system](https://www.khanacademy.org/science/biology/human-biology#neuron-nervous-system). From the inspiring [Khan Academy](https://www.khanacademy.org/).\n", "2. [Blue Brain's portal](https://bbp.epfl.ch/nmc-portal/welcome)." ] } ], "metadata": { "hide_input": false, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.18" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": { "height": "calc(100% - 180px)", "left": "10px", "top": "150px", "width": "173px" }, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 4 }