diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f53dc0b4..99c8470b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,7 +1,7 @@ Changelog ========= -Version v1.1.0 +Version v2.0.0 -------------- New Features @@ -21,8 +21,16 @@ Improvements - Added kwarg: ``raise_missing_property`` to ``NodePopulation.get`` - Undeprecated calling ``Edges.get`` and ``EdgePopulation.get`` with ``properties=None`` +Bug Fixes +~~~~~~~~~ +- Fixed the `Same property with different dtype` issue with ``nodes.get``, ``edges.get`` + Breaking Changes ~~~~~~~~~~~~~~~~ +- ``nodes.get`` and ``edges.get`` (and ``network.get``) no longer return a dataframe + - returns a generator yielding tuples of ``(, )`` instead + - to get the previous behavior (all in one dataframe): ``pd.concat(df for _, df in circuit.nodes.get(*args, **kwargs))`` +- Removed ``Network.property_dtypes``, ``CircuitIds.index_schema`` - ``Circuit.node_sets``, ``Simulation.node_sets`` returns ``NodeSets`` object initialized with empty dict when node sets file is not present - ``NodeSet.resolved`` is no longer available - ``FrameReport.node_set`` returns node_set name instead of resolved node set query diff --git a/bluepysnap/circuit_ids.py b/bluepysnap/circuit_ids.py index f015dc4d..26cb0490 100644 --- a/bluepysnap/circuit_ids.py +++ b/bluepysnap/circuit_ids.py @@ -55,14 +55,6 @@ def __init__(self, index, sort_index=True): index = index.sortlevel()[0] self.index = index - @property - def index_schema(self): - """Return an empty index with the same names and dtypes of the wrapped index.""" - # NOTE: Since pandas 2.1.0, the index needs to contain the explicit dtypes. In pd.concat, - # the dtypes of multi-index are coerced to 'object' if any dataframe has indices with - # dtype='object' - return self.index[:0] - @classmethod def _instance(cls, index, sort_index=True): """The instance returned by the functions.""" diff --git a/bluepysnap/network.py b/bluepysnap/network.py index b0cc61de..6820c241 100644 --- a/bluepysnap/network.py +++ b/bluepysnap/network.py @@ -48,22 +48,6 @@ def _populations(self): def population_names(self): """Should define all sorted NetworkObjects population names from the Circuit.""" - @cached_property - def property_dtypes(self): - """Returns all the NetworkObjects property dtypes for the Circuit.""" - - def _update(d, index, value): - if d.setdefault(index, value) != value: - raise BluepySnapError( - f"Same property with different dtype. {index}: {value}!= {d[index]}" - ) - - res = {} - for pop in self.values(): - for varname, dtype in pop.property_dtypes.items(): - _update(res, varname, dtype) - return pd.Series(res) - def keys(self): """Returns iterator on the NetworkObjectPopulation names. @@ -149,7 +133,7 @@ def ids(self, group=None, sample=None, limit=None): @abc.abstractmethod def get(self, group=None, properties=None): - """Returns the properties of the NetworkObject.""" + """Yields the properties of the NetworkObject.""" ids = self.ids(group) properties = utils.ensure_list(properties) # We don t convert to set properties itself to keep the column order. @@ -159,14 +143,6 @@ def get(self, group=None, properties=None): if unknown_props: raise BluepySnapError(f"Unknown properties required: {unknown_props}") - # Retrieve the dtypes of the selected properties. - # However, the int dtype may not be preserved if some values are NaN. - dtypes = { - column: dtype - for column, dtype in self.property_dtypes.items() - if column in properties_set - } - dataframes = [pd.DataFrame(columns=properties, index=ids.index_schema).astype(dtypes)] for name, pop in sorted(self.items()): # since ids is sorted, global_pop_ids should be sorted as well global_pop_ids = ids.filter_population(name) @@ -177,10 +153,9 @@ def get(self, group=None, properties=None): # However, it's a bit more performant than converting the Series to numpy arrays. pop_df = pd.DataFrame({prop: pop.get(pop_ids, prop) for prop in pop_properties}) pop_df.index = global_pop_ids.index - dataframes.append(pop_df) - res = pd.concat(dataframes) - assert res.index.is_monotonic_increasing, "The index should be already sorted" - return res + + # Sort the columns in the given order + yield name, pop_df[[p for p in properties if p in pop_properties]] @abc.abstractmethod def __getstate__(self): diff --git a/doc/source/notebooks/03_node_properties.ipynb b/doc/source/notebooks/03_node_properties.ipynb index 12e6f08f..259a14a4 100644 --- a/doc/source/notebooks/03_node_properties.ipynb +++ b/doc/source/notebooks/03_node_properties.ipynb @@ -14,13 +14,407 @@ "metadata": {}, "source": [ "## Preamble\n", - "The code in this section is identical to the code in the sections from \"Preamble\" to \"Properties and methods\" from the previous tutorial. It assumumes that you have already downloaded the circuit. If not, take a look to the notebook **01_circuits** (Downloading a circuit)." + "The code in this section is identical to the code in the sections from \"Preamble\" to \"Properties and methods\" from the previous tutorial. It assumes that you have already downloaded the circuit. If not, take a look to the notebook **01_circuits** (Downloading a circuit)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.cm as cm\n", + "%matplotlib inline\n", + "\n", + "import bluepysnap\n", + "\n", + "POINT_SIZE = 1\n", + "SAMPLE_SIZE = 30000\n", + "\n", + "# To keep the plots constant\n", + "np.random.seed(0)\n", + "\n", + "# load the circuit and store the node population\n", + "circuit_path = \"sonata/circuit_sonata.json\"\n", + "circuit = bluepysnap.Circuit(circuit_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Node properties and methods\n", + "Node populations provide information about the collection of nodes, and what information is available for each of the nodes themselves.\n", + "\n", + "### Acquiring data from all populations\n", + "\n", + "To gather data from all populations `circuit.nodes.get` can be used. \n", + "\n", + "It returns a generator object of tuples of `(, )`:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "generator_all_nodes = circuit.nodes.get(properties=['layer', 'synapse_class'])\n", + "generator_all_nodes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can easily convert this to a dictionary with the population names acting as keys. Let's try that and print out the dataframes by population:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---CorticoThalamic_projections---\n", + "\n", + "\n", + "---MedialLemniscus_projections---\n", + "\n", + "\n", + "---thalamus_neurons---\n" + ] + }, + { + "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", + "
layersynapse_class
populationnode_ids
thalamus_neurons0RtINH
1RtINH
2RtINH
3RtINH
4RtINH
.........
100760VPLINH
100761VPLINH
100762VPLINH
100763VPLINH
100764VPLINH
\n", + "

100765 rows × 2 columns

\n", + "
" + ], + "text/plain": [ + " layer synapse_class\n", + "population node_ids \n", + "thalamus_neurons 0 Rt INH\n", + " 1 Rt INH\n", + " 2 Rt INH\n", + " 3 Rt INH\n", + " 4 Rt INH\n", + "... ... ...\n", + " 100760 VPL INH\n", + " 100761 VPL INH\n", + " 100762 VPL INH\n", + " 100763 VPL INH\n", + " 100764 VPL INH\n", + "\n", + "[100765 rows x 2 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "dict_all_nodes = dict(generator_all_nodes)\n", + "\n", + "for population, df in dict_all_nodes.items():\n", + " print(f\"---{population}---\")\n", + " if df.empty:\n", + " print('\\n')\n", + " else:\n", + " display(df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Please note, as with generators in python in general, once the items of the generator are exhausted, it will no longer return anything:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[*generator_all_nodes]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Combining output of `circuit.nodes.get`\n", + "To combine the dataframes from all populations, we can use `pandas.concat`. We can combine the dictionary values by \n", + "```python\n", + "pd.concat(dict_all_nodes.values())\n", + "```\n", + "or we can skip the dictionary creation part by just concatenating the dataframes from the generator:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "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", + "
layersynapse_class
populationnode_ids
CorticoThalamic_projections0NaNNaN
1NaNNaN
2NaNNaN
3NaNNaN
4NaNNaN
............
thalamus_neurons100760VPLINH
100761VPLINH
100762VPLINH
100763VPLINH
100764VPLINH
\n", + "

189208 rows × 2 columns

\n", + "
" + ], + "text/plain": [ + " layer synapse_class\n", + "population node_ids \n", + "CorticoThalamic_projections 0 NaN NaN\n", + " 1 NaN NaN\n", + " 2 NaN NaN\n", + " 3 NaN NaN\n", + " 4 NaN NaN\n", + "... ... ...\n", + "thalamus_neurons 100760 VPL INH\n", + " 100761 VPL INH\n", + " 100762 VPL INH\n", + " 100763 VPL INH\n", + " 100764 VPL INH\n", + "\n", + "[189208 rows x 2 columns]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "generator_nodes_all = circuit.nodes.get(properties=['layer', 'synapse_class'])\n", + "df_all_nodes = pd.concat(df for _, df in generator_nodes_all) # \"_, df\": ignore the population names of the tuples\n", + "df_all_nodes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, as can be seen from the output above, combining the dataframes oftentimes results in there being a whole lot of `NaN` values in the dataframe, due to the properties missing from the other population.\n", + "\n", + "Therefore, if you know you're only working with one population, it is strongly recommended to use the node population object.\n", + "\n", + "### Working with node population objects\n", + "\n", + "Accessing a node population is as easy as accessing a dictionary. Just use the same `dict` syntax with the `circuit.nodes` object. Let's try that and print all the available properties for a population:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, "outputs": [ { "data": { @@ -48,28 +442,12 @@ " 'z'}" ] }, - "execution_count": 1, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import matplotlib.cm as cm\n", - "%matplotlib inline\n", - "\n", - "import bluepysnap\n", - "\n", - "POINT_SIZE = 1\n", - "SAMPLE_SIZE = 30000\n", - "\n", - "# To keep the plots constant\n", - "np.random.seed(0)\n", - "\n", - "# load the circuit and store the node population\n", - "circuit_path = \"sonata/circuit_sonata.json\"\n", - "circuit = bluepysnap.Circuit(circuit_path)\n", "node_population = circuit.nodes[\"thalamus_neurons\"]\n", "node_population.property_names" ] @@ -78,20 +456,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Node properties and methods\n", - "Node populations provide information about the collection of nodes, and what information is available for each of the nodes themselves." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's begin by retrieving all nodes with their associated layer, synapse type, and position in 3D space. We can then use this to understand how the synapse types are distributed between layers." + "Let's now retrieve all nodes with their associated layer, synapse type, and position in 3D space. We can then use this to understand how the synapse types are distributed between layers." ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -168,7 +538,7 @@ " VPL 342 342 342" ] }, - "execution_count": 2, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -187,7 +557,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -215,7 +585,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -309,7 +679,7 @@ "4 Rt INH 156.274872 572.608337 235.786240" ] }, - "execution_count": 4, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -320,7 +690,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -356,7 +726,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -394,16 +764,16 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 7, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -421,7 +791,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -469,7 +839,7 @@ " 'CT_afferents': {'population': 'CorticoThalamic_projections'}}" ] }, - "execution_count": 8, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -487,7 +857,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -503,7 +873,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -519,7 +889,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -566,7 +936,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -601,7 +971,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -610,7 +980,7 @@ "{'Rt_RC', 'VPL_IN', 'VPL_TC'}" ] }, - "execution_count": 13, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -628,7 +998,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -641,7 +1011,7 @@ " ['VPL_IN', 'bAC_IN']], dtype=object)" ] }, - "execution_count": 14, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -660,7 +1030,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -754,7 +1124,7 @@ "28607 mc2;Rt INH 173.007538 552.684753 837.944153" ] }, - "execution_count": 15, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } diff --git a/doc/source/notebooks/04_edge_properties.ipynb b/doc/source/notebooks/04_edge_properties.ipynb index 64139c01..824d57fb 100644 --- a/doc/source/notebooks/04_edge_properties.ipynb +++ b/doc/source/notebooks/04_edge_properties.ipynb @@ -14,13 +14,45 @@ "metadata": {}, "source": [ "## Preamble\n", - "The code in this section is similar to the code in sections \"Introduction\" and \"Loading\" from the previous tutorial, but applied to edges. It assumumes that you have already downloaded the circuit. If not, take a look to the notebook **01_circuits** (Downloading a circuit)." + "The code in this section is similar to the code in sections \"Introduction\" and \"Loading\" from the previous tutorial, but applied to edges. It assumes that you have already downloaded the circuit. If not, take a look to the notebook **01_circuits** (Downloading a circuit)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, + "outputs": [], + "source": [ + "import bluepysnap\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "# load the circuit and store the node population\n", + "circuit_path = \"sonata/circuit_sonata.json\"\n", + "circuit = bluepysnap.Circuit(circuit_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Properties and methods\n", + "\n", + "### Getting properties from all populations\n", + "Working with the output of `circuit.edges.get` follows the principles of that of `circuit.nodes.get` and won't be covered here. Please have a look at the previous notebook `03_node_properties.ipynb`.\n", + "\n", + "### Working with edge population objects\n", + "\n", + "\n", + "Edge populations provide information about the collection of edges, and what information is available for each of the edges themselves.\n", + "\n", + "Let's start by grabbing a population and printing out its available properties:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, "outputs": [ { "data": { @@ -61,20 +93,12 @@ " 'u_syn'}" ] }, - "execution_count": 1, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "import bluepysnap\n", - "import matplotlib.pyplot as plt\n", - "%matplotlib inline\n", - "\n", - "# load the circuit and store the node population\n", - "circuit_path = \"sonata/circuit_sonata.json\"\n", - "circuit = bluepysnap.Circuit(circuit_path)\n", - "\n", "# we can also find other edge names with \"circuit.edges.population_names\"\n", "edge_population = circuit.edges[\"thalamus_neurons__thalamus_neurons__chemical\"]\n", "edge_population.property_names" @@ -84,15 +108,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Properties and methods\n", - "Edge populations provide information about the collection of edges, and what information is available for each of the edges themselves.\n", - "\n", - "For example, the edge population `name` and `size` (that is, the number of nodes it contains) can be retrieved:" + "Also, there are additional object level properties. For example, the edge population `name` and `size` (that is, the number of nodes it contains) can be retrieved with:" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -118,7 +139,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": { "scrolled": true }, @@ -140,19 +161,17 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZcAAAEGCAYAAACpXNjrAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAiCklEQVR4nO3de7gddX3v8feHEDAiEC4RYxJI1FRPUIiwC/FSS6FCQGqiDyAclJQi0QotttaTwNGiIiq1iNIqNUIkeCyYgi1pjMY0AupzDpcA4RIusg0gSQNEEi6iBBK+54/5bhk2a609O5m1Nnvvz+t55lkz37n8fjN7ZX0zM7/5jSICMzOzOm030BUwM7Ohx8nFzMxq5+RiZma1c3IxM7PaObmYmVntth/oCrxc7LnnnjFx4sSBroaZ2aBy8803/zoixvSOO7mkiRMnsmLFioGuhpnZoCLpwUbxtl0Wk/QKSTdKuk3SKkmfzfilku6XtDKHqRmXpAsldUu6XdIBpW3NknRfDrNK8QMl3ZHrXChJGd9d0rJcfpmk3dq1n2Zm9lLtvOeyCTg0IvYHpgLTJU3LeZ+MiKk5rMzYkcDkHGYDF0GRKICzgYOBg4CzS8niIuDU0nrTMz4XWB4Rk4HlOW1mZh3StuQShd/k5MgcWnUHMAO4LNe7HhgtaSxwBLAsIjZExEZgGUWiGgvsEhHXR9HNwGXAzNK2FuT4glLczMw6oK2txSSNkLQSeJQiQdyQs87NS18XSNoxY+OAh0qrr8lYq/iaBnGAvSJiXY4/DOxV0y6ZmVkFbU0uEbElIqYC44GDJL0ZOBN4E/CHwO7AnDbXIWhyxiRptqQVklasX7++ndUwMxtWOvKcS0Q8DlwDTI+IdXnpaxPwbYr7KABrgQml1cZnrFV8fIM4wCN52Yz8fLRJveZFRFdEdI0Z85KWdGZmtpXa2VpsjKTROT4KeDdwT+lHXxT3Qu7MVRYBJ2WrsWnAE3lpaylwuKTd8kb+4cDSnPekpGm5rZOAq0vb6mlVNqsUNzOzDmjncy5jgQWSRlAksYURsVjSTySNAQSsBD6ayy8BjgK6gd8CJwNExAZJ5wA35XKfi4gNOf4x4FJgFPDDHAC+BCyUdArwIHBcu3bSzMxeSn6fS6Grqyv8EKWZWf9IujkiunrH3bdYDcaO3xtJTYex4/ce6CqamXWUu3+pwcNrH2KfOYubzn/wvKM7WBszs4HnMxczM6udk4uZmdXOycXMzGrn5GJmZrVzcjEzs9o5uZiZWe2cXMzMrHZOLmZmVjsnFzMzq52Ti5mZ1c7JxczMaufkYmZmtXNyMTOz2jm5mJlZ7ZxczMysdk4uZmZWOycXMzOrnZOLmZnVzsnFzMxq5+RiZma1a1tykfQKSTdKuk3SKkmfzfgkSTdI6pb0PUk7ZHzHnO7O+RNL2zoz4/dKOqIUn56xbklzS/GGZZiZWWe088xlE3BoROwPTAWmS5oGnAdcEBFvADYCp+TypwAbM35BLoekKcDxwL7AdOAbkkZIGgF8HTgSmAKckMvSogwzM+uAtiWXKPwmJ0fmEMChwJUZXwDMzPEZOU3OP0ySMn5FRGyKiPuBbuCgHLojYnVEPAtcAczIdZqVYWZmHdDWey55hrESeBRYBvwSeDwiNucia4BxOT4OeAgg5z8B7FGO91qnWXyPFmX0rt9sSSskrVi/fv027KmZmZW1NblExJaImAqMpzjTeFM7y+uviJgXEV0R0TVmzJiBro6Z2ZDRkdZiEfE4cA3wNmC0pO1z1nhgbY6vBSYA5PxdgcfK8V7rNIs/1qIMMzPrgHa2FhsjaXSOjwLeDdxNkWSOycVmAVfn+KKcJuf/JCIi48dna7JJwGTgRuAmYHK2DNuB4qb/olynWRlmZtYB2/e9yFYbCyzIVl3bAQsjYrGku4ArJH0euBW4JJe/BPiOpG5gA0WyICJWSVoI3AVsBk6LiC0Akk4HlgIjgPkRsSq3NadJGWZm1gFtSy4RcTvw1gbx1RT3X3rHnwGObbKtc4FzG8SXAEuqlmFmZp3hJ/TNzKx2Ti5mZlY7JxczM6udk4uZmdXOycXMzGrn5GJmZrVzcjEzs9o5uZiZWe2cXMzMrHZOLmZmVjsnFzMzq12fyUXSP0jaRdJIScslrZf0wU5UzszMBqcqZy6HR8STwNHAA8AbgE+2s1JmZja4VUkuPT0nvwf4t4h4oo31MTOzIaBKl/uLJd0D/A74S0ljgGfaWy0zMxvM+jxziYi5wNuBroh4DngamNHuipmZ2eBV9WVhbwImlt5LD3BZG+pjZmZDQJ/JRdJ3gNcDK4EtGQ6cXMzMrIkqZy5dwJSIiHZXxszMhoYqrcXuBF7T7oqYmdnQUeXMZU/gLkk3Apt6ghHx3rbVyszMBrUqZy6fAWYCXwDOLw0tSZog6RpJd0laJemMjH9G0lpJK3M4qrTOmZK6Jd0r6YhSfHrGuiXNLcUnSboh49+TtEPGd8zp7pw/sdLRMDOzWlRpinwdcA+wcw53Z6wvm4FPRMQUYBpwmqQpOe+CiJiawxKAnHc8sC8wHfiGpBGSRgBfB44EpgAnlLZzXm7rDcBG4JSMnwJszPgFuZyZmXVIlb7FjgNuBI4FjgNukHRMX+tFxLqIuCXHnwLuBsa1WGUGcEVEbIqI+4Fu4KAcuiNidUQ8C1wBzJAk4FDgylx/AcUZVs+2FuT4lcBhubyZmXVAlcti/xv4w4iYFREnUfzYf7o/heRlqbcCN2TodEm3S5ovabeMjQMeKq22JmPN4nsAj0fE5l7xF20r5z+Ry/eu12xJKyStWL9+fX92yczMWqiSXLaLiEdL049VXA8ASa8CrgI+nh1gXkTx3MxUYB0V7t+0S0TMi4iuiOgaM2bMQFXDzGzIqdJa7EeSlgKX5/QHgCVVNi5pJEVi+W5EfB8gIh4pzf8WsDgn1wITSquPzxhN4o8BoyVtn2cn5eV7trUmexXYNZc3M7MOqHJD/5PAPGC/HOZFxJy+1st7HJdQNAD4Sik+trTY+yieowFYBByfLb0mAZMp7vXcBEzOlmE7UNz0X5QPdV4D9Nz/mQVcXdrWrBw/BviJHwI1M+ucSn2LRcRVFGcg/fEO4EPAHZJWZuwsitZeUym6kHkA+EiWsUrSQuAuipZmp0XEFgBJpwNLgRHA/IhYldubA1wh6fPArRTJjPz8jqRuYANFQjIzsw5pmlwk/Twi3inpKYpE8PtZQETELq02HBE/z2V7a3pJLSLOBc5tEF/SaL2IWE3RwKB3/BmK1m1mZjYAmiaXiHhnfu7cueqYmdlQUOU5l+9UiZmZmfWo0qR43/JEtr46sD3VMTOzoaBpcsl+vp4C9pP0ZA5PAY/wQqssMzOzl2iaXCLii3m/5csRsUsOO0fEHhFxZgfraGZmg0yVy2I3Stq1Z0LSaEkz21clMzMb7Kokl7Mj4omeiYh4HDi7bTUyM7NBr1LfYg1ilR6+NDOz4alKclkh6SuSXp/DV4Cb210xMzMbvKokl78CngW+R/EulWeA09pZKTMzG9z6vLwVEU8DcyXtlONmZmYtVXlC/+2S7qJ4kySS9pf0jbbXzMzMBq0ql8UuAI4g34cSEbcB72pnpczMbHCr9EbJiHioV2hLG+piZmZDRJUmxQ9JejsQ+WbJM8hLZGZmZo1UOXP5KEXrsHEUrw+eiluLmZlZC1Vai/0aOLEDdTEzsyGiz+QiaQxwKjCxvHxE/EX7qmVmZoNZlXsuVwM/A/4L38g3M7MKqiSXV0bEnLbXxMzMhowqN/QXSzqq7TUxM7Mho0pyOYMiwfyu522Ukp7sayVJEyRdI+kuSasknZHx3SUtk3Rffu6WcUm6UFK3pNslHVDa1qxc/j5Js0rxAyXdketcKEmtyjAzs87oM7nk2ye3i4hRpbdR7lJh25uBT0TEFGAacJqkKcBcYHlETAaW5zTAkcDkHGYDF0GRKCjeH3MwcBBwdilZXETR2KBnvekZb1aGmZl1QKUn9CWNyz7G3tUz9LVORKyLiFty/CmKBy/HATOABbnYAmBmjs8ALovC9cBoSWMpup5ZFhEbImIjsAyYnvN2iYjrIyKAy3ptq1EZZmbWAVWaIp8HfAC4ixdaiwXw06qFSJoIvBW4AdgrItblrIeBvXJ8HFDuZmZNxlrF1zSI06IMMzPrgCqtxWYCb4yITVtTgKRXAVcBH4+IJ/O2CAAREZJia7ZbVasyJM2muATH3nvv3c5qmJkNK1Uui60GRm7NxrMvsquA70bE9zP8SF7SIj8fzfhaYEJp9fEZaxUf3yDeqowXiYh5EdEVEV1jxozZml00M7MGqiSX3wIrJX0zW2RdKOnCvlbKlluXAHdHxFdKsxYBPS2+ZlE8pNkTPylbjU0DnshLW0uBwyXtljfyDweW5rwnJU3Lsk7qta1GZZiZWQdUuSy2KIf+egfwIeAOSSszdhbwJWChpFOAB4Hjct4S4CigmyKhnQwQERsknQPclMt9LiI25PjHgEuBUcAPc6BFGWZm1gFVOq5cIGkH4A8ydG9EPFdhvZ8DajL7sAbLB016W46I+cD8BvEVwJsbxB9rVIaZmXVGldZih1A0532AIllMkDQrIiq3FjMzs+GlymWx84HDI+JeAEl/AFwOHNjOipmZ2eBV5Yb+yJ7EAhARv2ArW4+ZmdnwUOXMZYWki4H/k9MfBFa0r0pmZjbYVUkuf0lxo/2vc/qnZL9fZmZmjTRNLvkGyjERcRfwlRyQtC+wC7C+IzU0M7NBp9U9l38C9mwQ3x34WnuqY2ZmQ0Gr5PKGRs2NI+JnwH7tq5KZmQ12rZLLzi3mubWYmZk11Sq5dDd6vbGkIyk6szQzM2uoVWuxjwM/kHQccHPGuoC3AUe3uV5mZjaINT1ziYj7gLcA1wETc7gO2C8fpDQzM2uo5XMu+YKwb3eoLmZmNkRU6f7FzMysX5xczMysdk2Ti6Tl+Xle56pjZmZDQat7LmMlvR14r6Qr6PXir4i4pa01MzOzQatVcvl74NPAeLJfsZIADm1XpczMbHBrmlwi4krgSkmfjohzOlgnMzMb5Prscj8izpH0XuBdGbo2Iha3t1pmZjaY9dlaTNIXgTOAu3I4Q9IX2l0xMzMbvKq8LOw9wNSIeB5A0gLgVuCsdlbMzMwGr6rPuYwuje9aZQVJ8yU9KunOUuwzktZKWpnDUaV5Z0rqlnSvpCNK8ekZ65Y0txSfJOmGjH9P0g4Z3zGnu3P+xIr7aGZmNamSXL4I3Crp0jxruRk4t8J6lwLTG8QviIipOSwBkDQFOB7YN9f5hqQRkkYAXweOBKYAJ+SyAOfltt4AbAROyfgpwMaMX5DLmZlZB/WZXCLicmAa8H3gKuBtEfG9Cuv9FNhQsR4zgCsiYlNE3A90Awfl0B0RqyPiWeAKYIYkUTSFvjLXXwDMLG1rQY5fCRyWy5uZWYdUuiwWEesiYlEOD29jmadLuj0vm+2WsXHAQ6Vl1mSsWXwP4PGI2Nwr/qJt5fwncvmXkDRb0gpJK9avX7+Nu2VmZj063bfYRcDrganAOuD8Dpf/IhExLyK6IqJrzJgxA1kVM7MhpaPJJSIeiYgt2fLsWxSXvQDWAhNKi47PWLP4Y8BoSdv3ir9oWzl/11zezMw6pGVyyZvq99RVmKSxpcn3AT0tyRYBx2dLr0nAZOBG4CZgcrYM24Hipv+iiAjgGuCYXH8WcHVpW7Ny/BjgJ7m8mZl1SF8vC9uSzYD3johf9WfDki4HDgH2lLQGOBs4RNJUir7JHgA+kuWskrSQ4iHNzcBpEbElt3M6sBQYAcyPiFVZxBzgCkmfp3ju5pKMXwJ8R1I3RYOC4/tTbzMz23ZVHqLcDVgl6Ubg6Z5gRLy31UoRcUKD8CUNYj3Ln0uDJs7ZXHlJg/hqXrisVo4/Axzbqm5mZtZeVZLLp9tei6FuxEiatYZ+zbgJrFvTr5NCM7OXvSodV14naR9gckT8l6RXUlyisqq2PMc+cxr39fngeUd3uDJmZu1XpePKUykeRvxmhsYB/9HGOpmZ2SBXpSnyacA7gCcBIuI+4NXtrJSZmQ1uVZLLpux6Bfj9syNu2mtmZk1VSS7XSToLGCXp3cC/Af/Z3mqZmdlgViW5zAXWA3dQPJeyBPhUOytlZmaDW5XWYs9nV/s3UFwOu9dPvJuZWSt9JhdJ7wH+BfglIGCSpI9ExA/bXTkzMxucqjxEeT7wJxHRDSDp9cAPACcXMzNrqMo9l6d6EktaDTzVpvqYmdkQ0PTMRdL7c3SFpCXAQop7LsdS9FZsZmbWUKvLYn9WGn8E+OMcXw+MaluNzMxs0GuaXCLi5E5WxMzMho4qrcUmAX8FTCwv31eX+2ZmNnxVaS32HxTvYflP4Pm21sbMzIaEKsnlmYi4sO01MTOzIaNKcvmapLOBHwObeoIRcUvbamVmZoNaleTyFuBDwKG8cFksctrMzOwlqiSXY4HXlbvdNzMza6XKE/p3AqPbXA8zMxtCqiSX0cA9kpZKWtQz9LWSpPmSHpV0Zym2u6Rlku7Lz90yLkkXSuqWdLukA0rrzMrl75M0qxQ/UNIduc6FktSqDDMz65wqyeVs4H3AFyg6sewZ+nIpML1XbC6wPCImA8tzGuBIYHIOs4GLoEgUWf7BwEHA2aVkcRFwamm96X2UYWZmHVLlfS7Xbc2GI+Knkib2Cs8ADsnxBcC1wJyMX5bvible0mhJY3PZZRGxAUDSMmC6pGuBXSLi+oxfBsyk6Km5WRlmZtYhVZ7Qf4qidRjADsBI4OmI2GUrytsrItbl+MPAXjk+DniotNyajLWKr2kQb1XGS0iaTXGmxN57793ffTEzsyaqnLns3DOe9zVmANO2teCICEltfaNlX2VExDxgHkBXV5ffrmlmVpMq91x+Lwr/ARyxleU9kpe7yM9HM74WmFBabnzGWsXHN4i3KsPMzDqkz+Qi6f2l4RhJXwKe2cryFgE9Lb5mAVeX4idlq7FpwBN5aWspcLik3fJG/uHA0pz3pKRpeTZ1Uq9tNSrDzMw6pMpDlOX3umwGHqC4NNaSpMspbqzvKWkNRauvLwELJZ0CPAgcl4svAY4CuoHfAicDRMQGSefwwsvJPtdzcx/4GEWLtFEUN/J7XrvcrIyXpxEjyVbUDb1m3ATWrflVBytkZrbtqtxz2ar3ukTECU1mHdZg2QBOa7Kd+cD8BvEVwJsbxB9rVMbL1pbn2GfO4qazHzzv6A5WxsysHq1ec/z3LdaLiDinDfUxM7MhoNWZy9MNYjsBpwB7AE4uZmbWUKvXHP/+KXxJOwNnUNwLuYJqT+ibmdkw1fKeS3a/8rfAiRRPux8QERs7UTEzMxu8Wt1z+TLwfoqHDN8SEb/pWK3MzGxQa/WcyyeA1wKfAv5b0pM5PCXpyc5Uz8zMBqNW91z69fS+mZlZDycQMzOrnZOLmZnVzsnFzMxq5+RiZma1c3IxM7PaObmYmVntnFzMzKx2Ti5mZlY7JxczM6udk4uZmdXOycXMzGrn5GJmZrVzcjEzs9o5uZiZWe0GJLlIekDSHZJWSlqRsd0lLZN0X37ulnFJulBSt6TbJR1Q2s6sXP4+SbNK8QNz+925rjq/l2Zmw9dAnrn8SURMjYiunJ4LLI+IycDynAY4Epicw2zgIvj9K5jPBg4GDgLO7klIucyppfWmt393zMysx8vpstgMYEGOLwBmluKXReF6YLSkscARwLKI2BARG4FlwPSct0tEXB8RAVxW2paZmXXAQCWXAH4s6WZJszO2V0Ssy/GHgb1yfBzwUGndNRlrFV/TIP4SkmZLWiFpxfr167dlf8zMrKTpa47b7J0RsVbSq4Flku4pz4yIkBTtrkREzAPmAXR1dbW9PDOz4WJAzlwiYm1+Pgr8O8U9k0fykhb5+WguvhaYUFp9fMZaxcc3iJuZWYd0PLlI2knSzj3jwOHAncAioKfF1yzg6hxfBJyUrcamAU/k5bOlwOGSdssb+YcDS3Pek5KmZSuxk0rbMjOzDhiIy2J7Af+erYO3B/41In4k6SZgoaRTgAeB43L5JcBRQDfwW+BkgIjYIOkc4KZc7nMRsSHHPwZcCowCfpiDmZl1SMeTS0SsBvZvEH8MOKxBPIDTmmxrPjC/QXwF8OZtrqyZmW2Vl1NTZDMzGyKcXMzMrHZOLmZmVjsnFzMzq52Ti5mZ1c7JxczMaufk8nI3YiSSmg5jx+890DU0M3uJgepbzKra8hz7zFncdPaD5x3dwcqYmVXjMxczM6udk4uZmdXOycXMzGrn5GJmZrVzcjEzs9o5uQx2LZoqu5mymQ0UN0Ue7Fo0VXYzZTMbKD5zGcr8AKaZDRCfuQxlfgDTzAaIz1zMzKx2Ti5mZlY7JxczM6udk4uZmdXOyWU48zMyZtYmQ7a1mKTpwNeAEcDFEfGlAa7Sy0+rZ2T+8X1IarrqiB1ewZZnn2k6/zXjJrBuza+2uYpmNjgNyeQiaQTwdeDdwBrgJkmLIuKuga3ZIFKhGbObOZtZM0P1sthBQHdErI6IZ4ErgBkDXKfhpcUlt+13HNXy4c5W8/ta15fzzF4eFBEDXYfaSToGmB4RH87pDwEHR8TpvZabDczOyTcC9/ajmD2BX9dQ3cFsuB+D4b7/4GMAPgb7RMSY3sEheVmsqoiYB8zbmnUlrYiIrpqrNKgM92Mw3PcffAzAx6CZoXpZbC0woTQ9PmNmZtYBQzW53ARMljRJ0g7A8cCiAa6TmdmwMSQvi0XEZkmnA0spmiLPj4hVNRezVZfThpjhfgyG+/6DjwH4GDQ0JG/om5nZwBqql8XMzGwAObmYmVntnFz6SdJ0SfdK6pY0d6Dr0wmSJki6RtJdklZJOiPju0taJum+/NxtoOvabpJGSLpV0uKcniTphvw+fC8bkAxZkkZLulLSPZLulvS24fQ9kPQ3+W/gTkmXS3rFcPsOVOXk0g+lbmWOBKYAJ0iaMrC16ojNwCciYgowDTgt93susDwiJgPLc3qoOwO4uzR9HnBBRLwB2AicMiC16pyvAT+KiDcB+1Mci2HxPZA0DvhroCsi3kzRWOh4ht93oBInl/4Zlt3KRMS6iLglx5+i+EEZR7HvC3KxBcDMAalgh0gaD7wHuDinBRwKXJmLDOljIGlX4F3AJQAR8WxEPM7w+h5sD4yStD3wSmAdw+g70B9OLv0zDnioNL0mY8OGpInAW4EbgL0iYl3OehjYa6Dq1SFfBf4X8HxO7wE8HhGbc3qofx8mAeuBb+elwYsl7cQw+R5ExFrgH4FfUSSVJ4CbGV7fgcqcXKwySa8CrgI+HhFPludF0aZ9yLZrl3Q08GhE3DzQdRlA2wMHABdFxFuBp+l1CWwofw/yXtIMiiT7WmAnYPqAVuplzMmlf4ZttzKSRlIklu9GxPcz/IiksTl/LPDoQNWvA94BvFfSAxSXQw+luP8wOi+RwND/PqwB1kTEDTl9JUWyGS7fgz8F7o+I9RHxHPB9iu/FcPoOVObk0j/DsluZvLdwCXB3RHylNGsRMCvHZwFXd7punRIRZ0bE+IiYSPF3/0lEnAhcAxyTiw31Y/Aw8JCkN2boMOAuhs/34FfANEmvzH8TPfs/bL4D/eEn9PtJ0lEU1957upU5d2Br1H6S3gn8DLiDF+43nEVx32UhsDfwIHBcRGwYkEp2kKRDgL+LiKMlvY7iTGZ34FbggxGxaQCr11aSplI0aNgBWA2cTPGf1GHxPZD0WeADFC0obwU+THGPZdh8B6pycjEzs9r5spiZmdXOycXMzGrn5GJmZrVzcjEzs9o5uZiZWe2cXGyrSQpJ55em/07SZ2ra9qWSjul7yW0u59js3feaXvHtJF2Yvd/eIekmSZPaXZ/BQNKrJH1T0i8l3SzpWkkHD3Cd/lzSa0vTFw+TTmVftobka46tYzYB75f0xYj49UBXpoek7Ut9PfXlFODUiPh5r/gHKLr42C8ins9OK5+us56DQZNjeTFwPzA5j80kil7CB9KfA3cC/w0QER8e0NqYz1xsm2ymeH/43/Se0fvMQ9Jv8vMQSddJulrSaklfknSipBvzDOH1pc38qaQVkn6RfXv1vE/ly3kmcbukj5S2+zNJiyiemu5dnxNy+3dKOi9jfw+8E7hE0pd7rTIWWBcRzwNExJqI2CjpLyR9tbTdUyVdIGlingF9K9/38WNJo0rL3CTpNklXSXpl6Rj9S4N93DePx8rcx8kZ/2Ap/s08FiNyOz1nWM3+Fo3K6fexzL/PwcCnSsfm/oj4Qc7/26zLnZI+nrFWx+ZaSeflfv1C0h+1qlvOm5P7elt+f44BuoDv5rEZldvtava37/lOSjo3t3O9pCHZ4eaAiQgPHrZqAH4D7AI8AOwK/B3wmZx3KXBMedn8PAR4nOLHe0eKfpg+m/POAL5aWv9HFP8BmkzRr9UrgNkUP2zk+isoOhI8hOLMYlKDer6WouuOMRRn6z8BZua8aynez9F7nfG5XyuB84G3ZvxVwC+BkTn9f4G3ABMpku3UjC+keFIbYI/Sdj8P/FUf+/hPwIm5zA7AKOB/AP9ZKvcbwEnAgcCy0vZHN9iXOo/le4F/b/J9OJCiF4ed8jitouhBu9WxuRY4P8ePAv4rx5vV7cg85q/Mebs3+jv2TPfxtw/gz3L8H3rK81DP4DMX2yZR9I58GcVLlKq6KYp3xGyi+KH+ccbvoPgh6rEwIp6PiPsouhp5E3A4cJKklRTdz+xB8YMJcGNE3N+gvD8Ero2iw8HNwHcp3kvSar/WAG8EzqTo8ma5pMMi4jcUP1BHS3oTxY/9Hbna/RGxMsdvLu3Lm/NM4A7gRGDfPvbx/wFnSZoD7BMRv6Pox+pA4Kbc98OA1+U6r5P0T5KmAy/qrbqPcrbmWLbyTorE83Qep+8Df9THsSGX6x1vVrc/Bb4dEb8FiL67mWn1t38WWNykTraNfM/F6vBV4Bbg26XYZvKyq6TtKP4H3qPc79LzpennefF3snffRAGI4n/+S8szVPT3Ves9kUx+PwR+KOkRipdALae453AWcA8v3ufyfm2hOOOA4sxhZkTcJunPKc4Mfl/MS4uNf5V0A8WLyZbkJSEBCyLizN71lLQ/cATwUeA44C8a7U6D6a05lquA/SWNiIgtTZZppNmxKc/bwgt//2Z1O6IfZfblucjTll5lWw185mLbLP/3uJAXv971AYr/aUNxKWXkVmz6WBWttl5P8b/0e4GlwF+qeAUAkv5AxQurWrkR+GNJe6p4VfUJwHWtVpB0gLL1USbH/Sg6ZSSKLucnAP8TuLzCfuwMrMs6n9jXPqroDHN1RFxI0cPufhRJ7RhJr8467S5pH0l7AttFxFXApyi6wG+klmMZEb+kuET1WUnK9SZKeg9F56YzVfQavBPwvoxtjWZ1WwacrBfuW+2eyz9FcZx76/ff3urhTG11OR84vTT9LeBqSbdRXO/fmrOKX1H8OOwCfDQinpF0McXli1vyx209fbxWNiLWSZpL0TW6gB9ERF/dor8a+JakHXP6RuCfS/MXUtxD2FhhPz5NcWlnfX6WfwQb7eNxwIckPUfxZscvRMQGSZ8CfpzJ7jngNOB3FG+G7PmP4kvObFqU0+9jmT5M8ffulvQ74NfAJyPiFkmXZjkAF0fErSreXtpfDesWET9S0TPzCknPAksoziIvBf4l6/O2no1s5d/eauBekc22gqTFwAURsXwbtnEpsDgiruxr2W3RqXLMynxZzKwfJI2W9Avgd9uSWMyGOp+5mJlZ7XzmYmZmtXNyMTOz2jm5mJlZ7ZxczMysdk4uZmZWu/8Pr+TTUCwCyIgAAAAASUVORK5CYII=\n", + "image/png": "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\n", "text/plain": [ - "
" + "
" ] }, - "metadata": { - "needs_background": "light" - }, + "metadata": {}, "output_type": "display_data" } ], @@ -172,19 +191,17 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "
" + "
" ] }, - "metadata": { - "needs_background": "light" - }, + "metadata": {}, "output_type": "display_data" } ], diff --git a/tests/test_circuit.py b/tests/test_circuit.py index 5b990017..b7aa77d9 100644 --- a/tests/test_circuit.py +++ b/tests/test_circuit.py @@ -1,6 +1,7 @@ import json import pickle +import pandas as pd import pytest from libsonata import SonataError @@ -78,6 +79,7 @@ def test_integration(): edge_ids = circuit.edges.afferent_edges(node_ids) edge_props = circuit.edges.get(edge_ids, properties=["syn_weight", "delay"]) edge_reduced = edge_ids.limit(2) + edge_props = pd.concat(df for _, df in edge_props) edge_props_reduced = edge_props.loc[edge_reduced] assert edge_props_reduced["syn_weight"].tolist() == [1, 1] diff --git a/tests/test_circuit_ids.py b/tests/test_circuit_ids.py index 09c874da..87dac57b 100644 --- a/tests/test_circuit_ids.py +++ b/tests/test_circuit_ids.py @@ -81,12 +81,6 @@ def test_init(self): assert isinstance(self.test_obj_sorted, self.ids_cls) - def test_index_schema(self): - schema = self.test_obj_unsorted.index_schema - index = self.test_obj_unsorted.index - npt.assert_array_equal(schema.dtypes, index.dtypes) - npt.assert_array_equal(schema.names, index.names) - def test_from_arrays(self): tested = self.ids_cls.from_arrays(["a", "b"], [0, 1]) pdt.assert_index_equal(tested.index, self._circuit_ids(["a", "b"], [0, 1])) diff --git a/tests/test_edges.py b/tests/test_edges.py index df21c9d8..b940aaae 100644 --- a/tests/test_edges.py +++ b/tests/test_edges.py @@ -85,80 +85,6 @@ def test_property_names(self): "syn_weight", } - def test_property_dtypes(self): - expected = pd.Series( - data=[ - dtype("float32"), - dtype("float64"), - dtype("float64"), - dtype("float64"), - dtype("float32"), - dtype("float64"), - dtype("float32"), - dtype("float64"), - dtype("int64"), - dtype("int64"), - dtype("float64"), - dtype("float64"), - dtype("float64"), - dtype("float64"), - dtype("float64"), - dtype("float64"), - dtype("float32"), - dtype("float32"), - dtype("float64"), - dtype("float64"), - IDS_DTYPE, - IDS_DTYPE, - dtype("O"), - dtype("int32"), - ], - index=[ - "syn_weight", - "@dynamics:param1", - "afferent_surface_y", - "afferent_surface_z", - "conductance", - "efferent_center_x", - "delay", - "afferent_center_z", - "efferent_section_id", - "afferent_section_id", - "efferent_center_y", - "afferent_center_x", - "efferent_surface_z", - "afferent_center_y", - "afferent_surface_x", - "efferent_surface_x", - "afferent_section_pos", - "efferent_section_pos", - "efferent_surface_y", - "efferent_center_z", - "@source_node", - "@target_node", - "other1", - "other2", - ], - ).sort_index() - pdt.assert_series_equal(self.test_obj.property_dtypes.sort_index(), expected) - - def test_property_dtypes_fail(self): - a = pd.Series( - data=[dtype("int64"), dtype("float64")], index=["syn_weight", "efferent_surface_z"] - ).sort_index() - b = pd.Series( - data=[dtype("int32"), dtype("float64")], index=["syn_weight", "efferent_surface_z"] - ).sort_index() - - with patch( - "bluepysnap.edges.EdgePopulation.property_dtypes", new_callable=PropertyMock - ) as mock: - mock.side_effect = [a, b] - circuit = Circuit(str(TEST_DATA_DIR / "circuit_config.json")) - test_obj = test_module.Edges(circuit) - with pytest.raises(BluepySnapError): - test_obj.property_dtypes.sort_index() - def test_ids(self): np.random.seed(0) # single edge ID --> CircuitEdgeIds return populations with the 0 id @@ -266,6 +192,8 @@ def test_get(self): assert tested == ids tested = self.test_obj.get(ids, properties=self.test_obj.property_names) + tested = pd.concat(df for _, df in tested) + assert len(tested) == 8 assert len(list(tested)) == 24 @@ -274,9 +202,9 @@ def test_get(self): # the index of the dataframe is indentical to the CircuitEdgeIds index pdt.assert_index_equal(tested.index, ids.index) - pdt.assert_frame_equal( - self.test_obj.get([0, 1, 2, 3], properties=self.test_obj.property_names), tested - ) + tested2 = self.test_obj.get([0, 1, 2, 3], properties=self.test_obj.property_names) + tested2 = pd.concat(df for _, df in tested2) + pdt.assert_frame_equal(tested2, tested) # tested columns tested = self.test_obj.get(ids, properties=["other2", "other1", "@source_node"]) @@ -302,7 +230,8 @@ def test_get(self): names=["population", "edge_ids"], ), ) - pdt.assert_frame_equal(tested, expected) + tested = pd.concat(df for _, df in tested) + pdt.assert_frame_equal(tested[expected.columns], expected) tested = self.test_obj.get( CircuitEdgeIds.from_dict({"default2": [0, 1, 2, 3]}), @@ -325,6 +254,7 @@ def test_get(self): names=["population", "edge_ids"], ), ) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) with pytest.raises(KeyError, match="'default'"): @@ -336,8 +266,6 @@ def test_get(self): ) expected = pd.DataFrame( { - "other2": np.array([np.NaN, np.NaN, np.NaN, np.NaN], dtype=float), - "other1": np.array([np.NaN, np.NaN, np.NaN, np.NaN], dtype=object), "@source_node": np.array([2, 0, 0, 2], dtype=int), }, index=pd.MultiIndex.from_tuples( @@ -350,6 +278,7 @@ def test_get(self): names=["population", "edge_ids"], ), ) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) tested = self.test_obj.get(ids, properties="@source_node") @@ -371,6 +300,7 @@ def test_get(self): names=["population", "edge_ids"], ), ) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) tested = self.test_obj.get(ids, properties="other2") @@ -392,13 +322,14 @@ def test_get(self): names=["population", "edge_ids"], ), ) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) with pytest.raises(BluepySnapError, match="Unknown properties required: {'unknown'}"): - self.test_obj.get(ids, properties=["other2", "unknown"]) + next(self.test_obj.get(ids, properties=["other2", "unknown"])) with pytest.raises(BluepySnapError, match="Unknown properties required: {'unknown'}"): - self.test_obj.get(ids, properties="unknown") + next(self.test_obj.get(ids, properties="unknown")) def test_afferent_nodes(self): assert self.test_obj.afferent_nodes(0) == CircuitNodeIds.from_arrays(["default"], [2]) @@ -466,8 +397,10 @@ def test_pathway_edges(self): target = CircuitNodeIds.from_dict({"default": [1, 2]}) expected_index = CircuitEdgeIds.from_dict({"default": [1, 2], "default2": [1, 2]}) + tested = self.test_obj.pathway_edges(source=source, target=target, properties=properties) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( - self.test_obj.pathway_edges(source=source, target=target, properties=properties), + tested, pd.DataFrame( [ [88.1862], @@ -483,8 +416,10 @@ def test_pathway_edges(self): properties = [Synapse.SOURCE_NODE_ID, "other1"] expected_index = CircuitEdgeIds.from_dict({"default": [1, 2], "default2": [1, 2]}) + tested = self.test_obj.pathway_edges(source=source, target=target, properties=properties) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( - self.test_obj.pathway_edges(source=source, target=target, properties=properties), + tested, pd.DataFrame( [ [0, np.nan], @@ -520,8 +455,10 @@ def test_pathway_edges(self): source = CircuitNodeId("default", 0) target = CircuitNodeId("default", 1) expected_index = CircuitEdgeIds.from_dict({"default": [1, 2], "default2": [1, 2]}) + tested = self.test_obj.pathway_edges(source=source, target=target, properties=properties) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( - self.test_obj.pathway_edges(source=source, target=target, properties=properties), + tested, pd.DataFrame( [ [0, 1], @@ -555,8 +492,10 @@ def test_afferent_edges(self): assert self.test_obj.afferent_edges(CircuitNodeId("default", 1), None) == expected properties = [Synapse.AXONAL_DELAY] + tested = self.test_obj.afferent_edges(1, properties) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( - self.test_obj.afferent_edges(1, properties), + tested, pd.DataFrame( [ [88.1862], @@ -577,10 +516,12 @@ def test_afferent_edges(self): expected_index = CircuitEdgeIds.from_dict( {"default": [0, 1, 2, 3], "default2": [0, 1, 2, 3]} ) + tested = self.test_obj.afferent_edges( + CircuitNodeIds.from_dict({"default": [0, 1]}), properties=properties + ) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( - self.test_obj.afferent_edges( - CircuitNodeIds.from_dict({"default": [0, 1]}), properties=properties - ), + tested, pd.DataFrame( [ [2, np.nan], @@ -606,8 +547,10 @@ def test_efferent_edges(self): assert self.test_obj.efferent_edges(CircuitNodeId("default", 2), None) == expected properties = [Synapse.AXONAL_DELAY] + tested = self.test_obj.efferent_edges(2, properties) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( - self.test_obj.efferent_edges(2, properties), + tested, pd.DataFrame( [ [99.8945], @@ -625,8 +568,10 @@ def test_efferent_edges(self): properties = [Synapse.TARGET_NODE_ID, "other1"] expected_index = CircuitEdgeIds.from_dict({"default": [0, 3], "default2": [0, 3]}) + tested = self.test_obj.efferent_edges(2, properties) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( - self.test_obj.efferent_edges(2, properties), + tested, pd.DataFrame( [ [0, np.nan], @@ -644,15 +589,17 @@ def test_pair_edges(self): # no connection between 0 and 2 assert self.test_obj.pair_edges(0, 2, None) == CircuitEdgeIds.from_arrays([], []) actual = self.test_obj.pair_edges(0, 2, [Synapse.AXONAL_DELAY]) - assert actual.empty + assert next(actual, None) is None assert self.test_obj.pair_edges(2, 0, None) == CircuitEdgeIds.from_tuples( [("default", 0), ("default2", 0)] ) properties = [Synapse.AXONAL_DELAY] + tested = self.test_obj.pair_edges(2, 0, properties) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( - self.test_obj.pair_edges(2, 0, properties), + tested, pd.DataFrame( [ [99.8945], @@ -756,7 +703,6 @@ def test_pickle(self, tmp_path): # trigger some cached properties, to makes sure they aren't being pickeld self.test_obj.size self.test_obj.property_names - self.test_obj.property_dtypes with open(pickle_path, "wb") as fd: pickle.dump(self.test_obj, fd) diff --git a/tests/test_nodes.py b/tests/test_nodes.py index ec70b043..84be8af7 100644 --- a/tests/test_nodes.py +++ b/tests/test_nodes.py @@ -79,60 +79,6 @@ def test_property_value(self): assert self.test_obj.property_values("mtype") == {"L2_X", "L7_X", "L9_Z", "L8_Y", "L6_Y"} assert self.test_obj.property_values("other2") == {10, 11, 12, 13} - def test_property_dtypes(self): - expected = pd.Series( - data=[ - dtype("int64"), - dtype("O"), - dtype("O"), - dtype("O"), - dtype("O"), - dtype("float64"), - dtype("float64"), - dtype("float64"), - dtype("float64"), - dtype("float64"), - dtype("float64"), - dtype("float64"), - dtype("O"), - dtype("int64"), - ], - index=[ - "layer", - "model_template", - "model_type", - "morphology", - "mtype", - "rotation_angle_xaxis", - "rotation_angle_yaxis", - "rotation_angle_zaxis", - "x", - "y", - "z", - "@dynamics:holding_current", - "other1", - "other2", - ], - ).sort_index() - pdt.assert_series_equal(self.test_obj.property_dtypes.sort_index(), expected) - - def test_property_dtypes_fail(self): - a = pd.Series( - data=[dtype("int64"), dtype("O")], index=["layer", "model_template"] - ).sort_index() - b = pd.Series( - data=[dtype("int32"), dtype("O")], index=["layer", "model_template"] - ).sort_index() - - with patch( - "bluepysnap.nodes.NodePopulation.property_dtypes", new_callable=PropertyMock - ) as mock: - mock.side_effect = [a, b] - circuit = Circuit(str(TEST_DATA_DIR / "circuit_config.json")) - test_obj = test_module.Nodes(circuit) - with pytest.raises(BluepySnapError): - test_obj.property_dtypes.sort_index() - def test_ids(self): np.random.seed(0) @@ -291,6 +237,7 @@ def test_ids(self): def test_get(self): # return all properties for all the ids tested = self.test_obj.get() + tested = pd.concat(df for _, df in tested) assert tested.shape == (self.test_obj.size, len(self.test_obj.property_names)) # put NaN for the undefined values : only values for default2 in dropna @@ -305,6 +252,7 @@ def test_get(self): # tested columns tested = self.test_obj.get(properties=["other2", "other1", "layer"]) + tested = pd.concat(df for _, df in tested) expected = pd.DataFrame( { "other2": np.array([np.NaN, np.NaN, np.NaN, 10, 11, 12, 13], dtype=float), @@ -324,7 +272,7 @@ def test_get(self): names=["population", "node_ids"], ), ) - pdt.assert_frame_equal(tested, expected) + pdt.assert_frame_equal(tested[expected.columns], expected) tested = self.test_obj.get( group={"population": "default2"}, properties=["other2", "other1", "layer"] @@ -345,6 +293,7 @@ def test_get(self): names=["population", "node_ids"], ), ) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) with pytest.raises(KeyError, match="'default'"): @@ -355,8 +304,6 @@ def test_get(self): ) expected = pd.DataFrame( { - "other2": np.array([np.NaN, np.NaN, np.NaN], dtype=float), - "other1": np.array([np.NaN, np.NaN, np.NaN], dtype=object), "layer": np.array([2, 6, 6], dtype=int), }, index=pd.MultiIndex.from_tuples( @@ -368,6 +315,7 @@ def test_get(self): names=["population", "node_ids"], ), ) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) tested = self.test_obj.get(properties="layer") @@ -388,6 +336,7 @@ def test_get(self): names=["population", "node_ids"], ), ) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) tested = self.test_obj.get(properties="other2") @@ -408,13 +357,14 @@ def test_get(self): names=["population", "node_ids"], ), ) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) with pytest.raises(BluepySnapError, match="Unknown properties required: {'unknown'}"): - self.test_obj.get(properties=["other2", "unknown"]) + next(self.test_obj.get(properties=["other2", "unknown"])) with pytest.raises(BluepySnapError, match="Unknown properties required: {'unknown'}"): - self.test_obj.get(properties="unknown") + next(self.test_obj.get(properties="unknown")) def test_functionality_with_separate_node_set(self): with pytest.raises(BluepySnapError, match="Undefined node set"): @@ -427,12 +377,11 @@ def test_functionality_with_separate_node_set(self): ) with pytest.raises(BluepySnapError, match="Undefined node set"): - self.test_obj.get("ExtraLayer2") + next(self.test_obj.get("ExtraLayer2")) - pdt.assert_frame_equal( - self.test_obj.get(node_sets["ExtraLayer2"]), - self.test_obj.get("Layer2"), - ) + tested = pd.concat(df for _, df in self.test_obj.get(node_sets["ExtraLayer2"])) + expected = pd.concat(df for _, df in self.test_obj.get("Layer2")) + pdt.assert_frame_equal(tested, expected) def test_pickle(self, tmp_path): pickle_path = tmp_path / "pickle.pkl" @@ -440,7 +389,6 @@ def test_pickle(self, tmp_path): # trigger some cached properties, to makes sure they aren't being pickeld self.test_obj.size self.test_obj.property_names - self.test_obj.property_dtypes with open(pickle_path, "wb") as fd: pickle.dump(self.test_obj, fd)