From 2c73409e7ce39a976b56d2d824eaaa7b189c90af Mon Sep 17 00:00:00 2001 From: Joni Herttuainen Date: Thu, 8 Jun 2023 12:05:17 +0200 Subject: [PATCH 1/8] Fixing dtype issues in network.get --- CHANGELOG.rst | 13 +++++++++ bluepysnap/edges/edges.py | 5 +++- bluepysnap/network.py | 15 +++------- tests/test_circuit.py | 2 ++ tests/test_edges.py | 59 ++++++++++++++++++++++++++++----------- tests/test_nodes.py | 12 ++++++-- 6 files changed, 74 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f658c730..9cebeea3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,19 @@ Changelog ========= +Version v1.2.0 +-------------- + +Breaking Changes +~~~~~~~~~~~~~~~~ +- ``nodes.get`` and ``edges.get`` (and ``network.get``) no longer return a dataframe + - return iterators of tuples of ``(population_name, pd.DataFrame)`` instead +- ``nodes.property_dtypes``, ``edges.property_dtypes`` no longer available + +Bug Fixes +~~~~~~~~~ +- Fixed the `Same property with different dtype` issue with ``nodes.get``, ``edges.get`` + Version v1.1.0 -------------- diff --git a/bluepysnap/edges/edges.py b/bluepysnap/edges/edges.py index b3325ee4..11d22a94 100644 --- a/bluepysnap/edges/edges.py +++ b/bluepysnap/edges/edges.py @@ -79,7 +79,10 @@ def ids(self, group=None, sample=None, limit=None): diff = np.setdiff1d(group.get_populations(unique=True), self.population_names) if diff.size != 0: raise BluepySnapError(f"Population {diff} does not exist in the circuit.") - fun = lambda x: (x.ids(group), x.name) + + def fun(x): + return (x.ids(group), x.name) + return self._get_ids_from_pop(fun, CircuitEdgeIds, sample=sample, limit=limit) def get(self, edge_ids=None, properties=None): # pylint: disable=arguments-renamed diff --git a/bluepysnap/network.py b/bluepysnap/network.py index b0cc61de..8a0edbc9 100644 --- a/bluepysnap/network.py +++ b/bluepysnap/network.py @@ -159,14 +159,7 @@ 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)] + res = {} 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,9 +170,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" + + # Sort the columns in the given order + res[name] = pop_df[[p for p in properties if p in pop_properties]] return res @abc.abstractmethod diff --git a/tests/test_circuit.py b/tests/test_circuit.py index 5b990017..452b84ff 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(edge_props.values()) edge_props_reduced = edge_props.loc[edge_reduced] assert edge_props_reduced["syn_weight"].tolist() == [1, 1] diff --git a/tests/test_edges.py b/tests/test_edges.py index c9a3290a..14960c60 100644 --- a/tests/test_edges.py +++ b/tests/test_edges.py @@ -269,6 +269,8 @@ def test_get(self): assert tested == ids tested = self.test_obj.get(ids, properties=self.test_obj.property_names) + + tested = pd.concat(tested.values()) assert len(tested) == 8 assert len(list(tested)) == 24 @@ -277,9 +279,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(tested2.values()) + pdt.assert_frame_equal(tested2, tested) # tested columns tested = self.test_obj.get(ids, properties=["other2", "other1", "@source_node"]) @@ -305,7 +307,8 @@ def test_get(self): names=["population", "edge_ids"], ), ) - pdt.assert_frame_equal(tested, expected) + tested = pd.concat(tested.values()) + pdt.assert_frame_equal(tested[expected.columns], expected) tested = self.test_obj.get( CircuitEdgeIds.from_dict({"default2": [0, 1, 2, 3]}), @@ -328,6 +331,7 @@ def test_get(self): names=["population", "edge_ids"], ), ) + tested = pd.concat(tested.values()) pdt.assert_frame_equal(tested, expected) with pytest.raises(KeyError, match="'default'"): @@ -339,8 +343,8 @@ 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), + # "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( @@ -353,6 +357,7 @@ def test_get(self): names=["population", "edge_ids"], ), ) + tested = pd.concat(tested.values()) pdt.assert_frame_equal(tested, expected) tested = self.test_obj.get(ids, properties="@source_node") @@ -374,6 +379,7 @@ def test_get(self): names=["population", "edge_ids"], ), ) + tested = pd.concat(tested.values()) pdt.assert_frame_equal(tested, expected) tested = self.test_obj.get(ids, properties="other2") @@ -395,6 +401,7 @@ def test_get(self): names=["population", "edge_ids"], ), ) + tested = pd.concat(tested.values()) pdt.assert_frame_equal(tested, expected) with pytest.raises(BluepySnapError, match="Unknown properties required: {'unknown'}"): @@ -418,6 +425,8 @@ def test_properties_deprecated(self): ): tested = self.test_obj.properties(ids, properties=["other2", "@source_node"]) expected = self.test_obj.get(ids, properties=["other2", "@source_node"]) + tested = pd.concat(tested.values()) + expected = pd.concat(expected.values()) pdt.assert_frame_equal(tested, expected, check_exact=False) def test_afferent_nodes(self): @@ -486,8 +495,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(tested.values()) pdt.assert_frame_equal( - self.test_obj.pathway_edges(source=source, target=target, properties=properties), + tested, pd.DataFrame( [ [88.1862], @@ -503,8 +514,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(tested.values()) pdt.assert_frame_equal( - self.test_obj.pathway_edges(source=source, target=target, properties=properties), + tested, pd.DataFrame( [ [0, np.nan], @@ -540,8 +553,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(tested.values()) pdt.assert_frame_equal( - self.test_obj.pathway_edges(source=source, target=target, properties=properties), + tested, pd.DataFrame( [ [0, 1], @@ -575,8 +590,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(tested.values()) pdt.assert_frame_equal( - self.test_obj.afferent_edges(1, properties), + tested, pd.DataFrame( [ [88.1862], @@ -597,10 +614,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(tested.values()) pdt.assert_frame_equal( - self.test_obj.afferent_edges( - CircuitNodeIds.from_dict({"default": [0, 1]}), properties=properties - ), + tested, pd.DataFrame( [ [2, np.nan], @@ -626,8 +645,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(tested.values()) pdt.assert_frame_equal( - self.test_obj.efferent_edges(2, properties), + tested, pd.DataFrame( [ [99.8945], @@ -645,8 +666,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(tested.values()) pdt.assert_frame_equal( - self.test_obj.efferent_edges(2, properties), + tested, pd.DataFrame( [ [0, np.nan], @@ -664,15 +687,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 actual == {} 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(tested.values()) pdt.assert_frame_equal( - self.test_obj.pair_edges(2, 0, properties), + tested, pd.DataFrame( [ [99.8945], diff --git a/tests/test_nodes.py b/tests/test_nodes.py index ec70b043..f33c5c90 100644 --- a/tests/test_nodes.py +++ b/tests/test_nodes.py @@ -291,6 +291,7 @@ def test_ids(self): def test_get(self): # return all properties for all the ids tested = self.test_obj.get() + tested = pd.concat(tested.values()) 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 +306,7 @@ def test_get(self): # tested columns tested = self.test_obj.get(properties=["other2", "other1", "layer"]) + tested = pd.concat(tested.values()) expected = pd.DataFrame( { "other2": np.array([np.NaN, np.NaN, np.NaN, 10, 11, 12, 13], dtype=float), @@ -324,7 +326,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 +347,7 @@ def test_get(self): names=["population", "node_ids"], ), ) + tested = pd.concat(tested.values()) pdt.assert_frame_equal(tested, expected) with pytest.raises(KeyError, match="'default'"): @@ -355,8 +358,8 @@ 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), + # "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 +371,7 @@ def test_get(self): names=["population", "node_ids"], ), ) + tested = pd.concat(tested.values()) pdt.assert_frame_equal(tested, expected) tested = self.test_obj.get(properties="layer") @@ -388,6 +392,7 @@ def test_get(self): names=["population", "node_ids"], ), ) + tested = pd.concat(tested.values()) pdt.assert_frame_equal(tested, expected) tested = self.test_obj.get(properties="other2") @@ -408,6 +413,7 @@ def test_get(self): names=["population", "node_ids"], ), ) + tested = pd.concat(tested.values()) pdt.assert_frame_equal(tested, expected) with pytest.raises(BluepySnapError, match="Unknown properties required: {'unknown'}"): From a1f91b69ed538dfe3eb08c1ed4bbaaf4c0f1ec29 Mon Sep 17 00:00:00 2001 From: Joni Herttuainen Date: Thu, 8 Jun 2023 13:01:29 +0200 Subject: [PATCH 2/8] dict -> list of (pop_name, df) --- bluepysnap/edges/edges.py | 5 +---- bluepysnap/network.py | 4 ++-- tests/test_circuit.py | 2 +- tests/test_edges.py | 36 ++++++++++++++++++------------------ tests/test_nodes.py | 12 ++++++------ 5 files changed, 28 insertions(+), 31 deletions(-) diff --git a/bluepysnap/edges/edges.py b/bluepysnap/edges/edges.py index 11d22a94..b3325ee4 100644 --- a/bluepysnap/edges/edges.py +++ b/bluepysnap/edges/edges.py @@ -79,10 +79,7 @@ def ids(self, group=None, sample=None, limit=None): diff = np.setdiff1d(group.get_populations(unique=True), self.population_names) if diff.size != 0: raise BluepySnapError(f"Population {diff} does not exist in the circuit.") - - def fun(x): - return (x.ids(group), x.name) - + fun = lambda x: (x.ids(group), x.name) return self._get_ids_from_pop(fun, CircuitEdgeIds, sample=sample, limit=limit) def get(self, edge_ids=None, properties=None): # pylint: disable=arguments-renamed diff --git a/bluepysnap/network.py b/bluepysnap/network.py index 8a0edbc9..3b852738 100644 --- a/bluepysnap/network.py +++ b/bluepysnap/network.py @@ -159,7 +159,7 @@ def get(self, group=None, properties=None): if unknown_props: raise BluepySnapError(f"Unknown properties required: {unknown_props}") - res = {} + res = [] 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) @@ -172,7 +172,7 @@ def get(self, group=None, properties=None): pop_df.index = global_pop_ids.index # Sort the columns in the given order - res[name] = pop_df[[p for p in properties if p in pop_properties]] + res.append((name, pop_df[[p for p in properties if p in pop_properties]])) return res @abc.abstractmethod diff --git a/tests/test_circuit.py b/tests/test_circuit.py index 452b84ff..b7aa77d9 100644 --- a/tests/test_circuit.py +++ b/tests/test_circuit.py @@ -79,7 +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(edge_props.values()) + 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_edges.py b/tests/test_edges.py index 14960c60..976eaf8f 100644 --- a/tests/test_edges.py +++ b/tests/test_edges.py @@ -269,8 +269,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) - tested = pd.concat(tested.values()) assert len(tested) == 8 assert len(list(tested)) == 24 @@ -280,7 +280,7 @@ def test_get(self): # the index of the dataframe is indentical to the CircuitEdgeIds index pdt.assert_index_equal(tested.index, ids.index) tested2 = self.test_obj.get([0, 1, 2, 3], properties=self.test_obj.property_names) - tested2 = pd.concat(tested2.values()) + tested2 = pd.concat(df for _, df in tested2) pdt.assert_frame_equal(tested2, tested) # tested columns @@ -307,7 +307,7 @@ def test_get(self): names=["population", "edge_ids"], ), ) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested[expected.columns], expected) tested = self.test_obj.get( @@ -331,7 +331,7 @@ def test_get(self): names=["population", "edge_ids"], ), ) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) with pytest.raises(KeyError, match="'default'"): @@ -357,7 +357,7 @@ def test_get(self): names=["population", "edge_ids"], ), ) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) tested = self.test_obj.get(ids, properties="@source_node") @@ -379,7 +379,7 @@ def test_get(self): names=["population", "edge_ids"], ), ) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) tested = self.test_obj.get(ids, properties="other2") @@ -401,7 +401,7 @@ def test_get(self): names=["population", "edge_ids"], ), ) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) with pytest.raises(BluepySnapError, match="Unknown properties required: {'unknown'}"): @@ -425,8 +425,8 @@ def test_properties_deprecated(self): ): tested = self.test_obj.properties(ids, properties=["other2", "@source_node"]) expected = self.test_obj.get(ids, properties=["other2", "@source_node"]) - tested = pd.concat(tested.values()) - expected = pd.concat(expected.values()) + tested = pd.concat(df for _, df in tested) + expected = pd.concat(df for _, df in expected) pdt.assert_frame_equal(tested, expected, check_exact=False) def test_afferent_nodes(self): @@ -496,7 +496,7 @@ def test_pathway_edges(self): 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(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( tested, pd.DataFrame( @@ -515,7 +515,7 @@ 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(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( tested, pd.DataFrame( @@ -554,7 +554,7 @@ def test_pathway_edges(self): 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(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( tested, pd.DataFrame( @@ -591,7 +591,7 @@ def test_afferent_edges(self): properties = [Synapse.AXONAL_DELAY] tested = self.test_obj.afferent_edges(1, properties) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( tested, pd.DataFrame( @@ -617,7 +617,7 @@ def test_afferent_edges(self): tested = self.test_obj.afferent_edges( CircuitNodeIds.from_dict({"default": [0, 1]}), properties=properties ) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( tested, pd.DataFrame( @@ -646,7 +646,7 @@ def test_efferent_edges(self): properties = [Synapse.AXONAL_DELAY] tested = self.test_obj.efferent_edges(2, properties) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( tested, pd.DataFrame( @@ -667,7 +667,7 @@ def test_efferent_edges(self): expected_index = CircuitEdgeIds.from_dict({"default": [0, 3], "default2": [0, 3]}) tested = self.test_obj.efferent_edges(2, properties) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( tested, pd.DataFrame( @@ -687,7 +687,7 @@ 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 == {} + assert actual == [] assert self.test_obj.pair_edges(2, 0, None) == CircuitEdgeIds.from_tuples( [("default", 0), ("default2", 0)] @@ -695,7 +695,7 @@ def test_pair_edges(self): properties = [Synapse.AXONAL_DELAY] tested = self.test_obj.pair_edges(2, 0, properties) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal( tested, pd.DataFrame( diff --git a/tests/test_nodes.py b/tests/test_nodes.py index f33c5c90..4374426e 100644 --- a/tests/test_nodes.py +++ b/tests/test_nodes.py @@ -291,7 +291,7 @@ def test_ids(self): def test_get(self): # return all properties for all the ids tested = self.test_obj.get() - tested = pd.concat(tested.values()) + 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 @@ -306,7 +306,7 @@ def test_get(self): # tested columns tested = self.test_obj.get(properties=["other2", "other1", "layer"]) - tested = pd.concat(tested.values()) + 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), @@ -347,7 +347,7 @@ def test_get(self): names=["population", "node_ids"], ), ) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) with pytest.raises(KeyError, match="'default'"): @@ -371,7 +371,7 @@ def test_get(self): names=["population", "node_ids"], ), ) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) tested = self.test_obj.get(properties="layer") @@ -392,7 +392,7 @@ def test_get(self): names=["population", "node_ids"], ), ) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) tested = self.test_obj.get(properties="other2") @@ -413,7 +413,7 @@ def test_get(self): names=["population", "node_ids"], ), ) - tested = pd.concat(tested.values()) + tested = pd.concat(df for _, df in tested) pdt.assert_frame_equal(tested, expected) with pytest.raises(BluepySnapError, match="Unknown properties required: {'unknown'}"): From d2b7db0b20595c3d21fa2c31763a29074989a3a0 Mon Sep 17 00:00:00 2001 From: Joni Herttuainen Date: Thu, 8 Jun 2023 15:20:42 +0200 Subject: [PATCH 3/8] Yield tuples in network get, remove redundant/commented code --- CHANGELOG.rst | 2 +- bluepysnap/network.py | 22 ++---------- tests/test_edges.py | 83 ++----------------------------------------- tests/test_nodes.py | 61 ++----------------------------- 4 files changed, 8 insertions(+), 160 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9cebeea3..041ca4d1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,7 +7,7 @@ Version v1.2.0 Breaking Changes ~~~~~~~~~~~~~~~~ - ``nodes.get`` and ``edges.get`` (and ``network.get``) no longer return a dataframe - - return iterators of tuples of ``(population_name, pd.DataFrame)`` instead + - returns a generator yielding tuples of ``(, )`` instead - ``nodes.property_dtypes``, ``edges.property_dtypes`` no longer available Bug Fixes diff --git a/bluepysnap/network.py b/bluepysnap/network.py index 3b852738..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,7 +143,6 @@ def get(self, group=None, properties=None): if unknown_props: raise BluepySnapError(f"Unknown properties required: {unknown_props}") - res = [] 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) @@ -172,8 +155,7 @@ def get(self, group=None, properties=None): pop_df.index = global_pop_ids.index # Sort the columns in the given order - res.append((name, pop_df[[p for p in properties if p in pop_properties]])) - return res + yield name, pop_df[[p for p in properties if p in pop_properties]] @abc.abstractmethod def __getstate__(self): diff --git a/tests/test_edges.py b/tests/test_edges.py index 976eaf8f..e444862f 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 @@ -343,8 +269,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( @@ -405,10 +329,10 @@ def test_get(self): 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")) with pytest.deprecated_call( match=( @@ -687,7 +611,7 @@ 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 == [] + assert next(actual, None) is None assert self.test_obj.pair_edges(2, 0, None) == CircuitEdgeIds.from_tuples( [("default", 0), ("default2", 0)] @@ -801,7 +725,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 4374426e..09f937ee 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) @@ -358,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( @@ -417,10 +361,10 @@ def test_get(self): 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"): @@ -446,7 +390,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) From 082aa5e96832bcbc67e6b57e5dcd710e8f5ddc74 Mon Sep 17 00:00:00 2001 From: Joni Herttuainen Date: Thu, 8 Jun 2023 15:31:50 +0200 Subject: [PATCH 4/8] fixed coverage tests --- CHANGELOG.rst | 2 +- bluepysnap/circuit_ids.py | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 041ca4d1..55e9904e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,7 +8,7 @@ Breaking Changes ~~~~~~~~~~~~~~~~ - ``nodes.get`` and ``edges.get`` (and ``network.get``) no longer return a dataframe - returns a generator yielding tuples of ``(, )`` instead -- ``nodes.property_dtypes``, ``edges.property_dtypes`` no longer available +- Removed ``Network.property_dtypes``, ``CircuitIds.index_schema`` Bug Fixes ~~~~~~~~~ diff --git a/bluepysnap/circuit_ids.py b/bluepysnap/circuit_ids.py index e2843c7f..26cb0490 100644 --- a/bluepysnap/circuit_ids.py +++ b/bluepysnap/circuit_ids.py @@ -55,11 +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 of the wrapped index.""" - return pd.MultiIndex.from_tuples([], names=self.index.names) - @classmethod def _instance(cls, index, sort_index=True): """The instance returned by the functions.""" From 5c839af6ec3c9927087fc8ef129ce51e3d7846ee Mon Sep 17 00:00:00 2001 From: Joni Herttuainen Date: Mon, 26 Jun 2023 18:40:21 +0200 Subject: [PATCH 5/8] fix test_nodes.py --- tests/test_nodes.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/test_nodes.py b/tests/test_nodes.py index 09f937ee..84be8af7 100644 --- a/tests/test_nodes.py +++ b/tests/test_nodes.py @@ -377,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" From b86e6317f5d69c23908cae579f6fafc512237a05 Mon Sep 17 00:00:00 2001 From: Joni Herttuainen Date: Wed, 12 Jul 2023 13:46:30 +0200 Subject: [PATCH 6/8] update changelog --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 55e9904e..18d2cb4f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,7 @@ 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`` Bug Fixes From db8f200ac68de78380ec6a32933e94f6b5477967 Mon Sep 17 00:00:00 2001 From: Joni Herttuainen Date: Wed, 12 Jul 2023 13:49:30 +0200 Subject: [PATCH 7/8] Add entries in notebooks regarding {nodes,edges}.get --- doc/source/notebooks/03_node_properties.ipynb | 468 ++++++++++++++++-- doc/source/notebooks/04_edge_properties.ipynb | 73 +-- 2 files changed, 464 insertions(+), 77 deletions(-) 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": "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\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": "iVBORw0KGgoAAAANSUhEUgAAAlUAAAGwCAYAAACAZ5AeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy88F64QAAAACXBIWXMAAA9hAAAPYQGoP6dpAABJz0lEQVR4nO3de1hVdd7//9cGBdQEDyTIhIiHPKOpI2FaeEuiOY3eNeUxzVCr0VIoT42nsHs0D3hIiruDYqWZTmWTdaNIqaWoI0qeSQ3DSiw8oaiAsn5/+GN93YGHrQthw/NxXfsa11rv/dnvz16NvVpr7bVshmEYAgAAwG1xKe0GAAAAygNCFQAAgAUIVQAAABYgVAEAAFiAUAUAAGABQhUAAIAFCFUAAAAWqFTaDVQkBQUF+vXXX1W9enXZbLbSbgcAANwEwzB09uxZ+fn5ycXl2sejCFV30K+//ip/f//SbgMAANyCo0eP6p577rnmdkLVHVS9enVJV3aKp6dnKXcDAABuRnZ2tvz9/c1/j18LoeoOKjzl5+npSagCAMDJ3OjSHS5UBwAAsAChCgAAwAKEKgAAAAsQqgAAACxAqAIAALAAoQoAAMAChCoAAAALEKoAAAAsQKgCAACwAKEKAADAAoQqAAAACxCqAAAALECoAgAAsAChCgAAwAKVSrsBACgvMjIylJWVZfm43t7eqlevnuXjArAWoQoALJCRkaEmTZvp4oXzlo/tUaWq0g7sJ1gBZRyhCgAskJWVpYsXzqv2X15S5dr+lo2bf+KoTqyeo6ysLEIVUMYRqgDAQpVr+8vdt1FptwGgFBCqAFQoJXXd0/79+y0fE4BzIVQBqDBK8ronACBUAagwSuq6J0m68ON2nfn2Q0vHBOBcCFUAyqSSOE1XeIquJK57yj9x1NLxADgfQhWAMofTdACcEaEKQJlTUqfpnPkUXUldCM+NRQHrEKoAlFlWn6ZzxlN0l8+dkmw2DRw4sETG58aigHUIVQBQhhXknpMMo0QurufGooC1CFUA4AS4qShQ9hGqAKCC43otwBqEKgC3jLuTOzeu1wKsRagCcEu47YHz43otwFqEKgC3hLuTlx9crwVYg1AF4LZwd3IAuMKltBsAAAAoDwhVAAAAFiBUAQAAWIBQBQAAYAFCFQAAgAX49R8AoMSUxI1cuVM7yipCFVABlMSdz7nrOa6nJO/Wzp3aUVaVaqjauHGjZs2apZSUFB07dkyfffaZevfubW632WzFvm/mzJkaM2aMJKl+/fr66aef7LZPnz5d48ePN5d37dqlESNG6D//+Y/uvvtuvfDCCxo7dqzde1auXKlJkybpyJEjaty4sV5//XU98sgj5nbDMDRlyhS98847On36tB544AG99dZbaty48e1+DUCJ4s7nKA0ldbd27tSOsqxUQ1VOTo5at26tZ555Ro899liR7ceOHbNb/r//+z9FRETo8ccft1sfHR2tYcOGmcvVq1c3/5ydna1u3bopLCxMcXFx2r17t5555hnVqFFDw4cPlyRt3rxZ/fr10/Tp0/WXv/xFy5YtU+/evbVjxw61bNlS0pUgt2DBAi1ZskSBgYGaNGmSwsPDtW/fPnl4eFj2nQBWK6k7n3PXc9wM7taOiqRUQ1WPHj3Uo0ePa2739fW1W/7888/VpUsXNWjQwG599erVi9QWWrp0qfLy8rRo0SK5ubmpRYsWSk1NVUxMjBmq5s+fr+7du5tHv6ZNm6bExEQtXLhQcXFxMgxD8+bN08SJE9WrVy9J0vvvvy8fHx+tWrVKffv2veXvALhTrP6XG3c9BwB7TvPrv+PHj+vLL79UREREkW0zZsxQ7dq1dd9992nWrFm6dOmSuS05OVkPPvig3NzczHXh4eFKS0vTqVOnzJqwsDC7McPDw5WcnCxJSk9PV2Zmpl2Nl5eXgoODzZri5ObmKjs72+4FAADKJ6e5UH3JkiWqXr16kdOEL774otq2batatWpp8+bNmjBhgo4dO6aYmBhJUmZmpgIDA+3e4+PjY26rWbOmMjMzzXVX12RmZpp1V7+vuJriTJ8+Xa+++uotzBYAADgbpwlVixYt0oABA4pcvxQVFWX+OSgoSG5ubnr22Wc1ffp0ubu73+k27UyYMMGuv+zsbPn7W3dNCwAAKDuc4vTft99+q7S0NA0dOvSGtcHBwbp06ZKOHDki6cp1WcePH7erKVwuvA7rWjVXb7/6fcXVFMfd3V2enp52LwAAUD45Rah677331K5dO7Vu3fqGtampqXJxcVGdOnUkSSEhIdq4caPy8/PNmsTERDVp0kQ1a9Y0a5KSkuzGSUxMVEhIiCQpMDBQvr6+djXZ2dnaunWrWQMAACq2Uj39d+7cOR06dMhcTk9PV2pqqmrVqmXefyQ7O1srV67UnDlzirw/OTlZW7duVZcuXVS9enUlJycrMjJSAwcONANT//799eqrryoiIkLjxo3Tnj17NH/+fM2dO9ccZ9SoUXrooYc0Z84c9ezZU8uXL9f27dv19ttvS7pyv6zRo0frtddeU+PGjc1bKvj5+dndVwsAAFRcpRqqtm/fri5dupjLhdcfDR48WPHx8ZKk5cuXyzAM9evXr8j73d3dtXz5ck2dOlW5ubkKDAxUZGSk3XVMXl5eWrt2rUaMGKF27drJ29tbkydPNm+nIEkdO3bUsmXLNHHiRL3yyitq3LixVq1aZd6jSpLGjh2rnJwcDR8+XKdPn1anTp2UkJDAPaoAAICkUg5VoaGhMgzjujXDhw+3C0BXa9u2rbZs2XLDzwkKCtK333573ZonnnhCTzzxxDW322w2RUdHKzo6+oafB9yKkniUjMTjZFA+ldQ/1zxXELfDaX79B5RnPEoGuDkl+UxBiecK4vYQqoAyoKQeJSPxOBmULyX1TEGJ5wri9hGqgDKkJJ6TxuNkUB7xTEGURU5xSwUAAICyjlAFAABgAUIVAACABQhVAAAAFiBUAQAAWIBQBQAAYAFCFQAAgAUIVQAAABYgVAEAAFiAO6oDDiqJBx/z0GMAcH6EKsABPPgYAHAthCrAASX14GMeegwAzo9QBdwCqx/mykOPAcD5caE6AACABQhVAAAAFiBUAQAAWIBQBQAAYAEuVAcA4Colcd84b29v1atXz/JxUbYQqgAAkHT53CnJZtPAgQMtH9ujSlWlHdhPsCrnCFUAAEgqyD0nGYbl96HLP3FUJ1bPUVZWFqGqnCNUAQBwFavvQ4eKgwvVAQAALECoAgAAsAChCgAAwAKEKgAAAAsQqgAAACxAqAIAALAAoQoAAMAC3KcKAIA7oCQefyPxCJyyhFCFcikjI0NZWVmWj1tSfykCKL9K8vE3Eo/AKUsIVSh3MjIy1KRpM128cL60WwGAEnv8jcQjcMoaQhXKnaysLF28cL5E/gK78ON2nfn2Q0vHBFAx8Pib8o9QhXKrJP4Cyz9x1NLxAADlR6n++m/jxo169NFH5efnJ5vNplWrVtltf/rpp2Wz2exe3bt3t6s5efKkBgwYIE9PT9WoUUMRERE6d+6cXc2uXbvUuXNneXh4yN/fXzNnzizSy8qVK9W0aVN5eHioVatW+uqrr+y2G4ahyZMnq27duqpSpYrCwsJ08OBBa74IAADg9Eo1VOXk5Kh169aKjY29Zk337t117Ngx8/XRRx/ZbR8wYID27t2rxMRErV69Whs3btTw4cPN7dnZ2erWrZsCAgKUkpKiWbNmaerUqXr77bfNms2bN6tfv36KiIjQzp071bt3b/Xu3Vt79uwxa2bOnKkFCxYoLi5OW7duVbVq1RQeHq6LFy9a+I0AAABnVaqn/3r06KEePXpct8bd3V2+vr7Fbtu/f78SEhL0n//8R+3bt5ckvfHGG3rkkUc0e/Zs+fn5aenSpcrLy9OiRYvk5uamFi1aKDU1VTExMWb4mj9/vrp3764xY8ZIkqZNm6bExEQtXLhQcXFxMgxD8+bN08SJE9WrVy9J0vvvvy8fHx+tWrVKffv2Lba/3Nxc5ebmmsvZ2dmOfUEAAMBplPmbf65fv1516tRRkyZN9Pzzz+vEiRPmtuTkZNWoUcMMVJIUFhYmFxcXbd261ax58MEH5ebmZtaEh4crLS1Np06dMmvCwsLsPjc8PFzJycmSpPT0dGVmZtrVeHl5KTg42KwpzvTp0+Xl5WW+/P2tvWgaAACUHWU6VHXv3l3vv/++kpKS9Prrr2vDhg3q0aOHLl++LEnKzMxUnTp17N5TqVIl1apVS5mZmWaNj4+PXU3h8o1qrt5+9fuKqynOhAkTdObMGfN19CgXOQMAUF6V6V//XX1arVWrVgoKClLDhg21fv16de3atRQ7uznu7u5yd3cv7TYAAMAdUKaPVP1RgwYN5O3trUOHDkmSfH199dtvv9nVXLp0SSdPnjSvw/L19dXx48ftagqXb1Rz9far31dcDQAAqNicKlT9/PPPOnHihOrWrStJCgkJ0enTp5WSkmLWfP311yooKFBwcLBZs3HjRuXn55s1iYmJatKkiWrWrGnWJCUl2X1WYmKiQkJCJEmBgYHy9fW1q8nOztbWrVvNGgAAULGVaqg6d+6cUlNTlZqaKunKBeGpqanKyMjQuXPnNGbMGG3ZskVHjhxRUlKSevXqpUaNGik8PFyS1KxZM3Xv3l3Dhg3Ttm3btGnTJo0cOVJ9+/aVn5+fJKl///5yc3NTRESE9u7dq48//ljz589XVFSU2ceoUaOUkJCgOXPm6MCBA5o6daq2b9+ukSNHSpJsNptGjx6t1157Tf/+97+1e/duDRo0SH5+furdu/cd/c4AAEDZVKrXVG3fvl1dunQxlwuDzuDBg/XWW29p165dWrJkiU6fPi0/Pz9169ZN06ZNs7tOaenSpRo5cqS6du0qFxcXPf7441qwYIG53cvLS2vXrtWIESPUrl07eXt7a/LkyXb3surYsaOWLVumiRMn6pVXXlHjxo21atUqtWzZ0qwZO3ascnJyNHz4cJ0+fVqdOnVSQkKCPDw8SvIrAgAATqJUQ1VoaKgMw7jm9jVr1txwjFq1amnZsmXXrQkKCtK333573ZonnnhCTzzxxDW322w2RUdHKzo6+oY9AQCAiqdM//oPAADc2P79+y0f09vbW/Xq1bN83PKMUAUAgJO6fO6UZLNp4MCBlo/tUaWq0g7sJ1g5gFCFUpWRkaGsrCxLxyyJ/2IDgLKoIPecZBiq/ZeXVLm2dU/tyD9xVCdWz1FWVhahygGEKpSajIwMNWnaTBcvnC/tVgDAqVWu7S9330al3UaFR6hCqcnKytLFC+ct/y+sCz9u15lvP7RsPAAAbgahCqXO6v/Cyj/BMxYBAHeeU91RHQAAoKwiVAEAAFiAUAUAAGABQhUAAIAFCFUAAAAWIFQBAABYgFAFAABgAUIVAACABQhVAAAAFrjtUJWdna1Vq1bxEFsAAFChORyqnnzySS1cuFCSdOHCBbVv315PPvmkgoKC9Mknn1jeIAAAgDNwOFRt3LhRnTt3liR99tlnMgxDp0+f1oIFC/Taa69Z3iAAAIAzcDhUnTlzRrVq1ZIkJSQk6PHHH1fVqlXVs2dPHTx40PIGAQAAnIHDocrf31/JycnKyclRQkKCunXrJkk6deqUPDw8LG8QAADAGVRy9A2jR4/WgAEDdNddd6levXoKDQ2VdOW0YKtWrazuDwAAwCk4HKr+/ve/q0OHDjp69KgefvhhubhcOdjVoEEDrqkCAAAVlsOhSpLat2+voKAgpaenq2HDhqpUqZJ69uxpdW8AAABOw+Frqs6fP6+IiAhVrVpVLVq0UEZGhiTphRde0IwZMyxvEAAAwBk4HKomTJig77//XuvXr7e7MD0sLEwff/yxpc0BAAA4C4dP/61atUoff/yx7r//ftlsNnN9ixYtdPjwYUubAwAAcBYOh6rff/9dderUKbI+JyfHLmSh/MjIyFBWVpbl4/JoIwAo20rq72lvb2/Vq1evRMYuTQ6Hqvbt2+vLL7/UCy+8IElmkHr33XcVEhJibXcodRkZGWrStJkuXjhf2q0AAO6Qy+dOSTabBg4cWCLje1SpqrQD+8tdsHI4VP3zn/9Ujx49tG/fPl26dEnz58/Xvn37tHnzZm3YsKEkekQpysrK0sUL51X7Ly+pcm1/S8e+8ON2nfn2Q0vHBADcvoLcc5JhlMjf/fknjurE6jnKysoiVHXq1EmpqamaMWOGWrVqpbVr16pt27ZKTk7m5p/lWOXa/nL3bWTpmPknjlo6HgDAWiXxd395dkv3qWrYsKHeeecdq3sBAABwWg7fUmHHjh3avXu3ufz555+rd+/eeuWVV5SXl2dpcwAAAM7C4VD17LPP6ocffpAk/fjjj+rTp4+qVq2qlStXauzYsZY3CAAA4AwcDlU//PCD2rRpI0lauXKlHnroIS1btkzx8fH65JNPrO4PAADAKTgcqgzDUEFBgSRp3bp1euSRRyRJ/v7+JXIvIwAAAGfgcKhq3769XnvtNX3wwQfasGGD+SDl9PR0+fj4ODTWxo0b9eijj8rPz082m02rVq0yt+Xn52vcuHFq1aqVqlWrJj8/Pw0aNEi//vqr3Rj169eXzWaze/3xGYS7du1S586d5eHhIX9/f82cObNILytXrlTTpk3l4eGhVq1a6auvvrLbbhiGJk+erLp166pKlSoKCwvTwYMHHZovAAAovxwOVfPmzdOOHTs0cuRI/eMf/1CjRld+avmvf/1LHTt2dGisnJwctW7dWrGxsUW2nT9/Xjt27NCkSZO0Y8cOffrpp0pLS9Nf//rXIrXR0dE6duyY+Sq8MakkZWdnq1u3bgoICFBKSopmzZqlqVOn6u233zZrNm/erH79+ikiIkI7d+5U79691bt3b+3Zs8esmTlzphYsWKC4uDht3bpV1apVU3h4uC5evOjQnAEAQPnk8C0VgoKC7H79V2jWrFlydXV1aKwePXqoR48exW7z8vJSYmKi3bqFCxeqQ4cOysjIsLthWPXq1eXr61vsOEuXLlVeXp4WLVokNzc3tWjRQqmpqYqJidHw4cMlSfPnz1f37t01ZswYSdK0adOUmJiohQsXKi4uToZhaN68eZo4caJ69eolSXr//ffl4+OjVatWqW/fvsV+dm5urnJzc83l7Ozsm/xmAACAs3H4SFWh7du364MPPtAHH3yg7du3y8PDQ5UrV7aytyLOnDkjm82mGjVq2K2fMWOGateurfvuu0+zZs3SpUuXzG3Jycl68MEH5ebmZq4LDw9XWlqaTp06ZdaEhYXZjRkeHq7k5GRJV05tZmZm2tV4eXkpODjYrCnO9OnT5eXlZb78/a29Ky0AACg7HD5S9fPPP6tfv37atGmTGW5Onz6tjh07avny5brnnnus7lGSdPHiRY0bN079+vWTp6enuf7FF19U27ZtVatWLW3evFkTJkzQsWPHFBMTI0nKzMxUYGCg3ViF135lZmaqZs2ayszMLHI9mI+PjzIzM826q99XXE1xJkyYoKioKHM5OzubYAUAQDnlcKgaOnSo8vPztX//fjVp0kSSlJaWpiFDhmjo0KFKSEiwvMn8/Hw9+eSTMgxDb731lt22q0NLUFCQ3Nzc9Oyzz2r69Olyd3e3vBdHuLu7l3oPAADgznD49N+GDRv01ltvmYFKkpo0aaI33nhDGzdutLQ56f8Fqp9++kmJiYl2R6mKExwcrEuXLunIkSOSJF9fXx0/ftyupnC58Dqsa9Vcvf3q9xVXAwAAKjaHQ5W/v7/y8/OLrL98+bL8/PwsaapQYaA6ePCg1q1bp9q1a9/wPampqXJxcVGdOnUkSSEhIdq4caNdz4mJiWrSpIlq1qxp1iQlJdmNk5iYqJCQEElSYGCgfH197Wqys7O1detWswYAAFRsDoeqWbNm6YUXXtD27dvNddu3b9eoUaM0e/Zsh8Y6d+6cUlNTlZqaKunKBeGpqanKyMhQfn6+/va3v2n79u1aunSpLl++rMzMTGVmZprPGExOTta8efP0/fff68cff9TSpUsVGRmpgQMHmoGpf//+cnNzU0REhPbu3auPP/5Y8+fPtzttOGrUKCUkJGjOnDk6cOCApk6dqu3bt2vkyJGSJJvNptGjR+u1117Tv//9b+3evVuDBg2Sn5+fevfu7ehXCAAAyiGHr6l6+umndf78eQUHB6tSpStvv3TpkipVqqRnnnlGzzzzjFl78uTJ6461fft2denSxVwuDDqDBw/W1KlT9e9//1uSzMfiFPrmm28UGhoqd3d3LV++XFOnTlVubq4CAwMVGRlpF5i8vLy0du1ajRgxQu3atZO3t7cmT55s3k5Bkjp27Khly5Zp4sSJeuWVV9S4cWOtWrVKLVu2NGvGjh2rnJwcDR8+XKdPn1anTp2UkJAgDw8PB79BAACwf/9+y8f09va2u+XSneZwqJo3b55lHx4aGirDMK65/XrbJKlt27basmXLDT8nKChI33777XVrnnjiCT3xxBPX3G6z2RQdHa3o6Ogbfh4AACje5XOnJJtNAwcOtHxsjypVlXZgf6kFK4dD1eDBg0uiDwAAUAEU5J6TDEO1//KSKte27jZD+SeO6sTqOcrKynKeUHW1ixcvmtc3FbrRr/NQMjIyMkrkgdYlcXgWAIDKtf3l7tuotNuwlMOhKicnR+PGjdOKFSt04sSJItsvX75sSWO4eRkZGWrStJkuXjhf2q0AAFBhORyqxo4dq2+++UZvvfWWnnrqKcXGxuqXX37R//7v/2rGjBkl0SNuICsrSxcvnLf8UKokXfhxu858+6GlYwIAUB45HKq++OILvf/++woNDdWQIUPUuXNnNWrUSAEBAVq6dKkGDBhQEn3iJpTEodT8E0ctHQ8AgPLK4ftUnTx5Ug0aNJB05fqpwtsmdOrUqUTuqA4AAOAMHA5VDRo0UHp6uiSpadOmWrFihaQrR7AKH7AMAABQ0TgcqoYMGaLvv/9ekjR+/HjFxsbKw8NDkZGRGjNmjOUNAgAAOAOHr6mKjIw0/xwWFqYDBw4oJSVFjRo1UlBQkKXNAQAAOIvbuk+VJAUEBCggIMCKXgAAAJzWLYWqpKQkJSUl6bffflNBQYHdtkWLFlnSGAAAgDNxOFS9+uqrio6OVvv27VW3bl3ZbLaS6AsAAMCpOByq4uLiFB8fr6eeeqok+gEAAHBKDv/6Ly8vTx07diyJXgAAAJyWw6Fq6NChWrZsWUn0AgAA4LQcPv138eJFvf3221q3bp2CgoJUuXJlu+0xMTGWNQcAAOAsHA5Vu3btUps2bSRJe/bssdvGResAAKCicjhUffPNNyXRBwAAgFNz+JqqxYsX68KFCyXRCwAAgNNyOFSNHz9ePj4+ioiI0ObNm0uiJwAAAKfjcKj65ZdftGTJEmVlZSk0NFRNmzbV66+/rszMzJLoDwAAwCk4HKoqVaqk//7v/9bnn3+uo0ePatiwYVq6dKnq1aunv/71r/r888+LPLoGAACgvHM4VF3Nx8dHnTp1UkhIiFxcXLR7924NHjxYDRs21Pr16y1qEQAAoOy7pVB1/PhxzZ49Wy1atFBoaKiys7O1evVqpaen65dfftGTTz6pwYMHW90rAABAmeVwqHr00Ufl7++v+Ph4DRs2TL/88os++ugjhYWFSZKqVauml156SUePHrW8WQAAgLLK4ftU1alTRxs2bFBISMg1a+6++26lp6ffVmMAAADOxOFQ9d57792wxmazKSAg4JYaAgAAcEY3ffovOTlZq1evtlv3/vvvKzAwUHXq1NHw4cOVm5treYMAAADO4KZDVXR0tPbu3Wsu7969WxEREQoLC9P48eP1xRdfaPr06SXSJAAAQFl306EqNTVVXbt2NZeXL1+u4OBgvfPOO4qKitKCBQu0YsWKEmkSAACgrLvpUHXq1Cn5+PiYyxs2bFCPHj3M5T//+c/84g8AAFRYNx2qfHx8zF/05eXlaceOHbr//vvN7WfPnlXlypWt7xAAAMAJ3HSoeuSRRzR+/Hh9++23mjBhgqpWrarOnTub23ft2qWGDRuWSJMAAABl3U3fUmHatGl67LHH9NBDD+muu+7SkiVL5ObmZm5ftGiRunXrViJNAgAAlHU3Haq8vb21ceNGnTlzRnfddZdcXV3ttq9cuVJ33XWX5Q0CAAA4A4cfU+Pl5VUkUElSrVq17I5c3YyNGzfq0UcflZ+fn2w2m1atWmW33TAMTZ48WXXr1lWVKlUUFhamgwcP2tWcPHlSAwYMkKenp2rUqKGIiAidO3fOrmbXrl3q3LmzPDw85O/vr5kzZxbpZeXKlWratKk8PDzUqlUrffXVVw73AgAAKq5beqCyVXJyctS6dWvFxsYWu33mzJlasGCB4uLitHXrVlWrVk3h4eG6ePGiWTNgwADt3btXiYmJWr16tTZu3Kjhw4eb27Ozs9WtWzcFBAQoJSVFs2bN0tSpU/X222+bNZs3b1a/fv0UERGhnTt3qnfv3urdu7f27NnjUC8AAKDicvgxNVbq0aOH3W0ZrmYYhubNm6eJEyeqV69ekq7cwd3Hx0erVq1S3759tX//fiUkJOg///mP2rdvL0l644039Mgjj2j27Nny8/PT0qVLlZeXp0WLFsnNzU0tWrRQamqqYmJizPA1f/58de/eXWPGjJF05fqxxMRELVy4UHFxcTfVCwAAqNhK9UjV9aSnpyszM1NhYWHmOi8vLwUHBys5OVnSlUfn1KhRwwxUkhQWFiYXFxdt3brVrHnwwQftTk2Gh4crLS1Np06dMmuu/pzCmsLPuZleipObm6vs7Gy7FwAAKJ9uKlS1bdvWDCDR0dE6f/58iTYlSZmZmZJkd8PRwuXCbZmZmapTp47d9kqVKqlWrVp2NcWNcfVnXKvm6u036qU406dPl5eXl/ny9/e/wawBAICzuqlQtX//fuXk5EiSXn311SIXgqN4EyZM0JkzZ8wXd5wHAKD8uqlrqtq0aaMhQ4aoU6dOMgxDs2fPvubtEyZPnmxJY76+vpKk48ePq27duub648ePq02bNmbNb7/9Zve+S5cu6eTJk+b7fX19dfz4cbuawuUb1Vy9/Ua9FMfd3V3u7u43NV8AAODcbupIVXx8vGrXrq3Vq1fLZrPp//7v//TZZ58Vef3xlgi3IzAwUL6+vkpKSjLXZWdna+vWrQoJCZEkhYSE6PTp00pJSTFrvv76axUUFCg4ONis2bhxo/Lz882axMRENWnSRDVr1jRrrv6cwprCz7mZXgAAQMV2U0eqmjRpouXLl0uSXFxclJSUVORapltx7tw5HTp0yFxOT09XamqqatWqpXr16mn06NF67bXX1LhxYwUGBmrSpEny8/NT7969JUnNmjVT9+7dNWzYMMXFxSk/P18jR45U37595efnJ0nq37+/Xn31VUVERGjcuHHas2eP5s+fr7lz55qfO2rUKD300EOaM2eOevbsqeXLl2v79u3mbRdsNtsNewEAABWbw7dUKCgosOzDt2/fri5dupjLUVFRkqTBgwcrPj5eY8eOVU5OjoYPH67Tp0+rU6dOSkhIkIeHh/mepUuXauTIkeratatcXFz0+OOPa8GCBeZ2Ly8vrV27ViNGjFC7du3k7e2tyZMn293LqmPHjlq2bJkmTpyoV155RY0bN9aqVavUsmVLs+ZmegEAABXXLd2n6vDhw5o3b572798vSWrevLlGjRrl8AOVQ0NDZRjGNbfbbDZFR0crOjr6mjW1atXSsmXLrvs5QUFB+vbbb69b88QTT+iJJ564rV4AAEDF5fB9qtasWaPmzZtr27ZtCgoKUlBQkLZu3aoWLVooMTGxJHoEAAAo8xw+UjV+/HhFRkZqxowZRdaPGzdODz/8sGXNAQAAOAuHj1Tt379fERERRdY/88wz2rdvnyVNAQAAOBuHQ9Xdd9+t1NTUIutTU1Mt+UUgAACAM3L49N+wYcM0fPhw/fjjj+rYsaMkadOmTXr99dfNX+8BAABUNA6HqkmTJql69eqaM2eOJkyYIEny8/PT1KlT9eKLL1reIAAAgDNwOFTZbDZFRkYqMjJSZ8+elSRVr17d8sYAAACcyS3dp6oQYQoAAOAKhy9UBwAAQFGEKgAAAAsQqgAAACzgUKjKz89X165ddfDgwZLqBwAAwCk5FKoqV66sXbt2lVQvAAAATsvh038DBw7Ue++9VxK9AAAAOC2Hb6lw6dIlLVq0SOvWrVO7du1UrVo1u+0xMTGWNQcAAOAsHA5Ve/bsUdu2bSVJP/zwg902m81mTVcAAABOxuFQ9c0335REHwAAAE7tlm+pcOjQIa1Zs0YXLlyQJBmGYVlTAAAAzsbhUHXixAl17dpV9957rx555BEdO3ZMkhQREaGXXnrJ8gYBAACcgcOhKjIyUpUrV1ZGRoaqVq1qru/Tp48SEhIsbQ4AAMBZOHxN1dq1a7VmzRrdc889dusbN26sn376ybLGAAAAnInDR6pycnLsjlAVOnnypNzd3S1pCgAAwNk4HKo6d+6s999/31y22WwqKCjQzJkz1aVLF0ubAwAAcBYOn/6bOXOmunbtqu3btysvL09jx47V3r17dfLkSW3atKkkegQAACjzHD5S1bJlS/3www/q1KmTevXqpZycHD322GPauXOnGjZsWBI9AgAAlHkOH6mSJC8vL/3jH/+wuhcAAACndUuh6tSpU3rvvfe0f/9+SVLz5s01ZMgQ1apVy9LmAAAAnIXDp/82btyo+vXra8GCBTp16pROnTqlBQsWKDAwUBs3biyJHgEAAMo8h49UjRgxQn369NFbb70lV1dXSdLly5f197//XSNGjNDu3bstbxIAAKCsc/hI1aFDh/TSSy+ZgUqSXF1dFRUVpUOHDlnaHAAAgLNwOFS1bdvWvJbqavv371fr1q0taQoAAMDZ3NTpv127dpl/fvHFFzVq1CgdOnRI999/vyRpy5Ytio2N1YwZM0qmSwAAgDLupkJVmzZtZLPZZBiGuW7s2LFF6vr3768+ffpY1x0AAICTuKlQlZ6eXtJ9AAAAOLWbClUBAQEl3QcAAIBTc/hCdUn69ddftWLFCi1cuFALFiywe1mtfv36stlsRV4jRoyQJIWGhhbZ9txzz9mNkZGRoZ49e6pq1aqqU6eOxowZo0uXLtnVrF+/Xm3btpW7u7saNWqk+Pj4Ir3Exsaqfv368vDwUHBwsLZt22b5fAEAgHNy+D5V8fHxevbZZ+Xm5qbatWvLZrOZ22w2m1588UVLG/zPf/6jy5cvm8t79uzRww8/rCeeeMJcN2zYMEVHR5vLVatWNf98+fJl9ezZU76+vtq8ebOOHTumQYMGqXLlyvrnP/8p6crpzZ49e+q5557T0qVLlZSUpKFDh6pu3boKDw+XJH388ceKiopSXFycgoODNW/ePIWHhystLU116tSxdM4AAMD5OHykatKkSZo8ebLOnDmjI0eOKD093Xz9+OOPljd49913y9fX13ytXr1aDRs21EMPPWTWVK1a1a7G09PT3LZ27Vrt27dPH374odq0aaMePXpo2rRpio2NVV5eniQpLi5OgYGBmjNnjpo1a6aRI0fqb3/7m+bOnWuOExMTo2HDhmnIkCFq3ry54uLiVLVqVS1atMjyOQMAAOfjcKg6f/68+vbtKxeXWzpzeFvy8vL04Ycf6plnnrE7QrZ06VJ5e3urZcuWmjBhgs6fP29uS05OVqtWreTj42OuCw8PV3Z2tvbu3WvWhIWF2X1WeHi4kpOTzc9NSUmxq3FxcVFYWJhZU5zc3FxlZ2fbvQAAQPnkcDKKiIjQypUrS6KXG1q1apVOnz6tp59+2lzXv39/ffjhh/rmm280YcIEffDBBxo4cKC5PTMz0y5QSTKXMzMzr1uTnZ2tCxcuKCsrS5cvXy62pnCM4kyfPl1eXl7my9/f/5bmDQAAyj6Hr6maPn26/vKXvyghIUGtWrVS5cqV7bbHxMRY1twfvffee+rRo4f8/PzMdcOHDzf/3KpVK9WtW1ddu3bV4cOH1bBhwxLr5WZMmDBBUVFR5nJ2djbBCgCAcuqWQtWaNWvUpEkTSSpyoXpJ+emnn7Ru3Tp9+umn160LDg6WdOUZhQ0bNpSvr2+RX+kdP35ckuTr62v+b+G6q2s8PT1VpUoVubq6ytXVtdiawjGK4+7uLnd395ubIAAAcGoOh6o5c+Zo0aJFdqfg7oTFixerTp066tmz53XrUlNTJUl169aVJIWEhOh//ud/9Ntvv5m/0ktMTJSnp6eaN29u1nz11Vd24yQmJiokJESS5Obmpnbt2ikpKUm9e/eWJBUUFCgpKUkjR460aooAAMCJOXxNlbu7ux544IGS6OWaCgoKtHjxYg0ePFiVKv2/HHj48GFNmzZNKSkpOnLkiP79739r0KBBevDBBxUUFCRJ6tatm5o3b66nnnpK33//vdasWaOJEydqxIgR5lGk5557Tj/++KPGjh2rAwcO6M0339SKFSsUGRlpflZUVJTeeecdLVmyRPv379fzzz+vnJwcDRky5I5+FwAAoGxyOFSNGjVKb7zxRkn0ck3r1q1TRkaGnnnmGbv1bm5uWrdunbp166amTZvqpZde0uOPP64vvvjCrHF1ddXq1avl6uqqkJAQDRw4UIMGDbK7r1VgYKC+/PJLJSYmqnXr1pozZ47effdd8x5VktSnTx/Nnj1bkydPVps2bZSamqqEhIQiF68DAICKyeHTf9u2bdPXX3+t1atXq0WLFkUuVL/RNU+3olu3bnYPcy7k7++vDRs23PD9AQEBRU7v/VFoaKh27tx53ZqRI0dyug8AABTL4VBVo0YNPfbYYyXRCwAAgNNyOFQtXry4JPoAAABwanf+tugAAADlkMNHqgIDA697P6qSeP4fAABAWedwqBo9erTdcn5+vnbu3KmEhASNGTPGqr4AAACcisOhatSoUcWuj42N1fbt22+7IQAAAGdk2TVVPXr00CeffGLVcAAAAE7FslD1r3/9S7Vq1bJqOAAAAKfi8Om/++67z+5CdcMwlJmZqd9//11vvvmmpc0BAAA4C4dDVeEDhQu5uLjo7rvvVmhoqJo2bWpVXwAAAE7F4VA1ZcqUkugDAADAqXHzTwAAAAvc9JEqFxeX6970U5JsNpsuXbp0200BAAA4m5sOVZ999tk1tyUnJ2vBggUqKCiwpCkAAABnc9OhqlevXkXWpaWlafz48friiy80YMAARUdHW9ocAACAs7ila6p+/fVXDRs2TK1atdKlS5eUmpqqJUuWKCAgwOr+AAAAnIJDoerMmTMaN26cGjVqpL179yopKUlffPGFWrZsWVL9AQAAOIWbPv03c+ZMvf766/L19dVHH31U7OlAAACAiuqmQ9X48eNVpUoVNWrUSEuWLNGSJUuKrfv0008taw4AAMBZ3HSoGjRo0A1vqQAAAFBR3XSoio+PL8E2AAAAnBt3VAcAALAAoQoAAMAChCoAAAALEKoAAAAsQKgCAACwAKEKAADAAoQqAAAACxCqAAAALECoAgAAsAChCgAAwAKEKgAAAAsQqgAAACxAqAIAALAAoQoAAMAChCoAAAALlOlQNXXqVNlsNrtX06ZNze0XL17UiBEjVLt2bd111116/PHHdfz4cbsxMjIy1LNnT1WtWlV16tTRmDFjdOnSJbua9evXq23btnJ3d1ejRo0UHx9fpJfY2FjVr19fHh4eCg4O1rZt20pkzgAAwDmV6VAlSS1atNCxY8fM13fffWdui4yM1BdffKGVK1dqw4YN+vXXX/XYY4+Z2y9fvqyePXsqLy9Pmzdv1pIlSxQfH6/JkyebNenp6erZs6e6dOmi1NRUjR49WkOHDtWaNWvMmo8//lhRUVGaMmWKduzYodatWys8PFy//fbbnfkSAABAmVfmQ1WlSpXk6+trvry9vSVJZ86c0XvvvaeYmBj913/9l9q1a6fFixdr8+bN2rJliyRp7dq12rdvnz788EO1adNGPXr00LRp0xQbG6u8vDxJUlxcnAIDAzVnzhw1a9ZMI0eO1N/+9jfNnTvX7CEmJkbDhg3TkCFD1Lx5c8XFxalq1apatGjRdXvPzc1Vdna23QsAAJRPZT5UHTx4UH5+fmrQoIEGDBigjIwMSVJKSory8/MVFhZm1jZt2lT16tVTcnKyJCk5OVmtWrWSj4+PWRMeHq7s7Gzt3bvXrLl6jMKawjHy8vKUkpJiV+Pi4qKwsDCz5lqmT58uLy8v8+Xv738b3wQAACjLynSoCg4OVnx8vBISEvTWW28pPT1dnTt31tmzZ5WZmSk3NzfVqFHD7j0+Pj7KzMyUJGVmZtoFqsLthduuV5Odna0LFy4oKytLly9fLramcIxrmTBhgs6cOWO+jh496vB3AAAAnEOl0m7genr06GH+OSgoSMHBwQoICNCKFStUpUqVUuzs5ri7u8vd3b202wAAAHdAmT5S9Uc1atTQvffeq0OHDsnX11d5eXk6ffq0Xc3x48fl6+srSfL19S3ya8DC5RvVeHp6qkqVKvL29parq2uxNYVjAAAAOFWoOnfunA4fPqy6deuqXbt2qly5spKSksztaWlpysjIUEhIiCQpJCREu3fvtvuVXmJiojw9PdW8eXOz5uoxCmsKx3Bzc1O7du3sagoKCpSUlGTWAAAAlOlQ9fLLL2vDhg06cuSINm/erP/+7/+Wq6ur+vXrJy8vL0VERCgqKkrffPONUlJSNGTIEIWEhOj++++XJHXr1k3NmzfXU089pe+//15r1qzRxIkTNWLECPO03HPPPacff/xRY8eO1YEDB/Tmm29qxYoVioyMNPuIiorSO++8oyVLlmj//v16/vnnlZOToyFDhpTK9wIAAMqeMn1N1c8//6x+/frpxIkTuvvuu9WpUydt2bJFd999tyRp7ty5cnFx0eOPP67c3FyFh4frzTffNN/v6uqq1atX6/nnn1dISIiqVaumwYMHKzo62qwJDAzUl19+qcjISM2fP1/33HOP3n33XYWHh5s1ffr00e+//67JkycrMzNTbdq0UUJCQpGL1wEAQMVVpkPV8uXLr7vdw8NDsbGxio2NvWZNQECAvvrqq+uOExoaqp07d163ZuTIkRo5cuR1awAAQMVVpk//AQAAOAtCFQAAgAUIVQAAABYgVAEAAFiAUAUAAGABQhUAAIAFCFUAAAAWIFQBAABYgFAFAABgAUIVAACABQhVAAAAFiBUAQAAWIBQBQAAYAFCFQAAgAUIVQAAABYgVAEAAFiAUAUAAGABQhUAAIAFCFUAAAAWIFQBAABYgFAFAABgAUIVAACABQhVAAAAFiBUAQAAWIBQBQAAYAFCFQAAgAUIVQAAABYgVAEAAFiAUAUAAGABQhUAAIAFCFUAAAAWIFQBAABYgFAFAABgAUIVAACABQhVAAAAFijToWr69On685//rOrVq6tOnTrq3bu30tLS7GpCQ0Nls9nsXs8995xdTUZGhnr27KmqVauqTp06GjNmjC5dumRXs379erVt21bu7u5q1KiR4uPji/QTGxur+vXry8PDQ8HBwdq2bZvlcwYAAM6pTIeqDRs2aMSIEdqyZYsSExOVn5+vbt26KScnx65u2LBhOnbsmPmaOXOmue3y5cvq2bOn8vLytHnzZi1ZskTx8fGaPHmyWZOenq6ePXuqS5cuSk1N1ejRozV06FCtWbPGrPn4448VFRWlKVOmaMeOHWrdurXCw8P122+/lfwXAQAAyrxKpd3A9SQkJNgtx8fHq06dOkpJSdGDDz5orq9atap8fX2LHWPt2rXat2+f1q1bJx8fH7Vp00bTpk3TuHHjNHXqVLm5uSkuLk6BgYGaM2eOJKlZs2b67rvvNHfuXIWHh0uSYmJiNGzYMA0ZMkSSFBcXpy+//FKLFi3S+PHji/3s3Nxc5ebmmsvZ2dm3/mUAAIAyrUwfqfqjM2fOSJJq1aplt37p0qXy9vZWy5YtNWHCBJ0/f97clpycrFatWsnHx8dcFx4eruzsbO3du9esCQsLsxszPDxcycnJkqS8vDylpKTY1bi4uCgsLMysKc706dPl5eVlvvz9/W9x5gAAoKwr00eqrlZQUKDRo0frgQceUMuWLc31/fv3V0BAgPz8/LRr1y6NGzdOaWlp+vTTTyVJmZmZdoFKkrmcmZl53Zrs7GxduHBBp06d0uXLl4utOXDgwDV7njBhgqKioszl7OxsghUAAOWU04SqESNGaM+ePfruu+/s1g8fPtz8c6tWrVS3bl117dpVhw8fVsOGDe90m3bc3d3l7u5eqj0AAIA7wylO/40cOVKrV6/WN998o3vuuee6tcHBwZKkQ4cOSZJ8fX11/Phxu5rC5cLrsK5V4+npqSpVqsjb21uurq7F1lzrWi4AAFCxlOlQZRiGRo4cqc8++0xff/21AgMDb/ie1NRUSVLdunUlSSEhIdq9e7fdr/QSExPl6emp5s2bmzVJSUl24yQmJiokJESS5Obmpnbt2tnVFBQUKCkpyawBAAAVW5k+/TdixAgtW7ZMn3/+uapXr25eA+Xl5aUqVaro8OHDWrZsmR555BHVrl1bu3btUmRkpB588EEFBQVJkrp166bmzZvrqaee0syZM5WZmamJEydqxIgR5qm55557TgsXLtTYsWP1zDPP6Ouvv9aKFSv05Zdfmr1ERUVp8ODBat++vTp06KB58+YpJyfH/DUgAACo2Mp0qHrrrbckXbnB59UWL16sp59+Wm5ublq3bp0ZcPz9/fX4449r4sSJZq2rq6tWr16t559/XiEhIapWrZoGDx6s6OhosyYwMFBffvmlIiMjNX/+fN1zzz169913zdspSFKfPn30+++/a/LkycrMzFSbNm2UkJBQ5OJ1AABQMZXpUGUYxnW3+/v7a8OGDTccJyAgQF999dV1a0JDQ7Vz587r1owcOVIjR4684ecBAICKp0xfUwUAAOAsCFUAAAAWIFQBAABYgFAFAABgAUIVAACABQhVAAAAFiBUAQAAWIBQBQAAYAFCFQAAgAUIVQAAABYgVAEAAFiAUAUAAGABQhUAAIAFCFUAAAAWIFQBAABYgFAFAABgAUIVAACABQhVAAAAFiBUAQAAWIBQBQAAYAFCFQAAgAUIVQAAABYgVAEAAFiAUAUAAGABQhUAAIAFCFUAAAAWIFQBAABYgFAFAABgAUIVAACABQhVAAAAFiBUAQAAWIBQBQAAYAFCFQAAgAUIVQAAABYgVAEAAFiAUOWg2NhY1a9fXx4eHgoODta2bdtKuyUAAFAGEKoc8PHHHysqKkpTpkzRjh071Lp1a4WHh+u3334r7dYAAEApI1Q5ICYmRsOGDdOQIUPUvHlzxcXFqWrVqlq0aFFptwYAAEpZpdJuwFnk5eUpJSVFEyZMMNe5uLgoLCxMycnJxb4nNzdXubm55vKZM2ckSdnZ2Zb2du7cuSufl3lIBXkXLR07/8RRpxubnu/M2PR8Z8Z2xp5Lcmx6vjNjO2XPJ3+WdOXfiVb/e7ZwPMMwrl9o4Kb88ssvhiRj8+bNduvHjBljdOjQodj3TJkyxZDEixcvXrx48SoHr6NHj143K3CkqgRNmDBBUVFR5nJBQYFOnjyp2rVry2azlWJnV1K3v7+/jh49Kk9Pz1Lt5U6rqHOvqPOWKu7cK+q8JeZeEedekvM2DENnz56Vn5/fdesIVTfJ29tbrq6uOn78uN3648ePy9fXt9j3uLu7y93d3W5djRo1SqrFW+Lp6Vmh/k93tYo694o6b6nizr2izlti7hVx7iU1by8vrxvWcKH6TXJzc1O7du2UlJRkrisoKFBSUpJCQkJKsTMAAFAWcKTKAVFRURo8eLDat2+vDh06aN68ecrJydGQIUNKuzUAAFDKCFUO6NOnj37//XdNnjxZmZmZatOmjRISEuTj41ParTnM3d1dU6ZMKXJ6siKoqHOvqPOWKu7cK+q8JeZeEedeFuZtM4wb/T4QAAAAN8I1VQAAABYgVAEAAFiAUAUAAGABQhUAAIAFCFXlRGxsrOrXry8PDw8FBwdr27Zt16zdu3evHn/8cdWvX182m03z5s0rUjN16lTZbDa7V9OmTUtwBrfOkbm/88476ty5s2rWrKmaNWsqLCysSL1hGJo8ebLq1q2rKlWqKCwsTAcPHizpadwSq+f+9NNPF9nv3bt3L+lpOMyReX/66adq3769atSooWrVqqlNmzb64IMP7GrK6z6/mbk7yz6XHJv71ZYvXy6bzabevXvbrXeW/W71vMvrPo+Pjy8yLw8PD7uaEt/nt/9UPJS25cuXG25ubsaiRYuMvXv3GsOGDTNq1KhhHD9+vNj6bdu2GS+//LLx0UcfGb6+vsbcuXOL1EyZMsVo0aKFcezYMfP1+++/l/BMHOfo3Pv372/ExsYaO3fuNPbv3288/fTThpeXl/Hzzz+bNTNmzDC8vLyMVatWGd9//73x17/+1QgMDDQuXLhwp6Z1U0pi7oMHDza6d+9ut99Pnjx5p6Z0Uxyd9zfffGN8+umnxr59+4xDhw4Z8+bNM1xdXY2EhASzprzu85uZuzPsc8NwfO6F0tPTjT/96U9G586djV69etltc4b9XhLzLq/7fPHixYanp6fdvDIzM+1qSnqfE6rKgQ4dOhgjRowwly9fvmz4+fkZ06dPv+F7AwICrhmqWrdubWGXJeN25m4YhnHp0iWjevXqxpIlSwzDMIyCggLD19fXmDVrlllz+vRpw93d3fjoo4+sbf42WT13w7jyl+0f/wIua2533oZhGPfdd58xceJEwzAq1j43DPu5G4Zz7HPDuLW5X7p0yejYsaPx7rvvFpmns+x3q+dtGOV3ny9evNjw8vK65nh3Yp9z+s/J5eXlKSUlRWFhYeY6FxcXhYWFKTk5+bbGPnjwoPz8/NSgQQMNGDBAGRkZt9uupayY+/nz55Wfn69atWpJktLT05WZmWk3ppeXl4KDg2/7+7RSScy90Pr161WnTh01adJEzz//vE6cOGFp77fjdudtGIaSkpKUlpamBx98UFLF2efFzb1QWd7n0q3PPTo6WnXq1FFERESRbc6w30ti3oXK6z4/d+6cAgIC5O/vr169emnv3r3mtjuxz7mjupPLysrS5cuXi9zV3cfHRwcOHLjlcYODgxUfH68mTZro2LFjevXVV9W5c2ft2bNH1atXv922LWHF3MeNGyc/Pz/z/2SZmZnmGH8cs3BbWVASc5ek7t2767HHHlNgYKAOHz6sV155RT169FBycrJcXV0tncOtuNV5nzlzRn/605+Um5srV1dXvfnmm3r44Ycllf99fr25S2V/n0u3NvfvvvtO7733nlJTU4vd7gz7vSTmLZXffd6kSRMtWrRIQUFBOnPmjGbPnq2OHTtq7969uueee+7IPidUoVg9evQw/xwUFKTg4GAFBARoxYoV1/2vH2cyY8YMLV++XOvXry9yMWN5d6259+3b1/xzq1atFBQUpIYNG2r9+vXq2rVrabRqierVqys1NVXnzp1TUlKSoqKi1KBBA4WGhpZ2ayXuRnMvj/v87Nmzeuqpp/TOO+/I29u7tNu5Y2523uVxn0tSSEiIQkJCzOWOHTuqWbNm+t///V9NmzbtjvRAqHJy3t7ecnV11fHjx+3WHz9+XL6+vpZ9To0aNXTvvffq0KFDlo15u25n7rNnz9aMGTO0bt06BQUFmesL33f8+HHVrVvXbsw2bdpY1/xtKom5F6dBgwby9vbWoUOHysRftrc6bxcXFzVq1EiS1KZNG+3fv1/Tp09XaGhoud/n15t7ccraPpccn/vhw4d15MgRPfroo+a6goICSVKlSpWUlpbmFPu9JObdsGHDIu8rD/u8OJUrV9Z9991n/nvrTuxzrqlycm5ubmrXrp2SkpLMdQUFBUpKSrJL7Lfr3LlzOnz4sN0/iKXtVuc+c+ZMTZs2TQkJCWrfvr3dtsDAQPn6+tqNmZ2dra1bt1r6fd6ukph7cX7++WedOHGizOx3q/55LygoUG5urqTyv8//6Oq5F6es7XPJ8bk3bdpUu3fvVmpqqvn661//qi5duig1NVX+/v5Osd9LYt7FKQ/7vDiXL1/W7t27zXndkX1uyeXuKFXLly833N3djfj4eGPfvn3G8OHDjRo1apg/JX3qqaeM8ePHm/W5ubnGzp07jZ07dxp169Y1Xn75ZWPnzp3GwYMHzZqXXnrJWL9+vZGenm5s2rTJCAsLM7y9vY3ffvvtjs/vehyd+4wZMww3NzfjX//6l93Pbs+ePWtXU6NGDePzzz83du3aZfTq1avM/czaMKyf+9mzZ42XX37ZSE5ONtLT041169YZbdu2NRo3bmxcvHixVOZYHEfn/c9//tNYu3atcfjwYWPfvn3G7NmzjUqVKhnvvPOOWVNe9/mN5u4s+9wwHJ/7HxX3izdn2O9Wz7s87/NXX33VWLNmjXH48GEjJSXF6Nu3r+Hh4WHs3bvXrCnpfU6oKifeeOMNo169eoabm5vRoUMHY8uWLea2hx56yBg8eLC5nJ6ebkgq8nrooYfMmj59+hh169Y13NzcjD/96U9Gnz59jEOHDt3BGd08R+YeEBBQ7NynTJli1hQUFBiTJk0yfHx8DHd3d6Nr165GWlraHZzRzbNy7ufPnze6detm3H333UblypWNgIAAY9iwYUXu81IWODLvf/zjH0ajRo0MDw8Po2bNmkZISIixfPlyu/HK6z6/0dydaZ8bhmNz/6PiQpWz7Hcr512e9/no0aPNWh8fH+ORRx4xduzYYTdeSe9zm2EYhjXHvAAAACourqkCAACwAKEKAADAAoQqAAAACxCqAAAALECoAgAAsAChCgAAwAKEKgAAAAsQqgAAACxAqAJQLkyaNEnDhw+/I59Vv359zZs377bGiI+PV40aNa5bM3XqVLsHvT799NPq3bu3uRwaGqrRo0ffVh9ZWVmqU6eOfv7559saBwChCkAZca2AcDPhIzMzU/Pnz9c//vGPkmmulLz88st2D3/9o08//VTTpk0zl28l7Hl7e2vQoEGaMmXKrbYJ4P9HqALg9N5991117NhRAQEBtzVOXl6eRR1Z46677lLt2rWvub1WrVqqXr36bX/OkCFDtHTpUp08efK2xwIqMkIVAKe3fPlyPfroo3brQkNDNXLkSI0cOVJeXl7y9vbWpEmTdPXjTuvXr69p06Zp0KBB8vT0NE8ffvLJJ2rRooXc3d1Vv359zZkzp8hnnj17Vv369VO1atX0pz/9SbGxsXbbY2Ji1KpVK1WrVk3+/v76+9//rnPnzhUZZ9WqVWrcuLE8PDwUHh6uo0ePmtv+ePrvj64+uhcaGqqffvpJkZGRstlsstlsysnJkaenp/71r38V+cxq1arp7NmzkqQWLVrIz89Pn3322TU/C8CNEaoAOLWTJ09q3759at++fZFtS5YsUaVKlbRt2zbNnz9fMTExevfdd+1qZs+erdatW2vnzp2aNGmSUlJS9OSTT6pv377avXu3pk6dqkmTJik+Pt7ufbNmzTLfN378eI0aNUqJiYnmdhcXFy1YsEB79+7VkiVL9PXXX2vs2LF2Y5w/f17/8z//o/fff1+bNm3S6dOn1bdv31v6Hj799FPdc889io6O1rFjx3Ts2DFVq1ZNffv21eLFi+1qFy9erL/97W92R7k6dOigb7/99pY+G8AVlUq7AQC4HRkZGTIMQ35+fkW2+fv7a+7cubLZbGrSpIl2796tuXPnatiwYWbNf/3Xf+mll14ylwcMGKCuXbtq0qRJkqR7771X+/bt06xZs/T000+bdQ888IDGjx9v1mzatElz587Vww8/LEl214fVr19fr732mp577jm9+eab5vr8/HwtXLhQwcHBkq6EwGbNmmnbtm3q0KGDQ99DrVq15OrqqurVq8vX19dcP3ToUHXs2FHHjh1T3bp19dtvv+mrr77SunXr7N7v5+ennTt3OvSZAOxxpAqAU7tw4YIkycPDo8i2+++/XzabzVwOCQnRwYMHdfnyZXPdH49w7d+/Xw888IDdugceeKDI+0JCQuxqQkJCtH//fnN53bp16tq1q/70pz+pevXqeuqpp3TixAmdP3/erKlUqZL+/Oc/m8tNmzZVjRo17Ma5XR06dFCLFi20ZMkSSdKHH36ogIAAPfjgg3Z1VapUsesNgOMIVQDKBE9PT505c6bI+tOnT8vLy+ua7/P29pYknTp16pY+t1q1arf0vus5cuSI/vKXvygoKEiffPKJUlJSzGuuSuNi+KFDh5qnLxcvXqwhQ4bYhU3pymnUu++++473BpQnhCoAZUKTJk20Y8eOIut37Nihe++995rva9iwoTw9PbVv374i27Zu3Wq3vGXLFjVu3Fiurq7XHK9Zs2batGmT3bpNmzbp3nvvtXvfli1biozdrFkzSVJKSooKCgo0Z84c3X///br33nv166+/FvmsS5cuafv27eZyWlqaTp8+bY7jKDc3N7ujaYUGDhyon376SQsWLNC+ffs0ePDgIjV79uzRfffdd0ufC+AKQhWAMuH555/XDz/8oBdffFG7du1SWlqaYmJi9NFHH9ld8/RHLi4uCgsL03fffVdkW0ZGhqKiopSWlqaPPvpIb7zxhkaNGnXdPl566SUlJSVp2rRp+uGHH7RkyRItXLhQL7/8sl3dpk2bNHPmTP3www+KjY3VypUrzbEbNWqk/Px8vfHGG/rxxx/1wQcfKC4urshnVa5cWS+88IK2bt2qlJQUPf3007r//vsdvp6qUP369bVx40b98ssvysrKMtfXrFlTjz32mMaMGaNu3brpnnvusXvf+fPnlZKSom7dut3S5wK4glAFoExo0KCBNm7cqAMHDigsLEzBwcFasWKFVq5cqe7du1/3vUOHDtXy5ctVUFBgt37QoEG6cOGCOnTooBEjRmjUqFE3vOt627ZttWLFCi1fvlwtW7bU5MmTFR0dbXeRunQlfG3fvl333XefXnvtNcXExCg8PFyS1Lp1a8XExOj1119Xy5YttXTpUk2fPr3IZ1WtWlXjxo1T//799cADD+iuu+7Sxx9/fBPfVvGio6N15MgRNWzYsMipvIiICOXl5emZZ54p8r7PP/9c9erVU+fOnW/5swFINuPqm7YAgBMyDEPBwcGKjIxUv379JF25b1ObNm1u+3Ey5cUHH3ygyMhI/frrr3Jzc7Pbdv/99+vFF19U//79S6k7oHzgSBUAp2ez2fT222/r0qVLpd1KmXP+/HkdPnxYM2bM0LPPPlskUGVlZemxxx4zwyiAW0eoAlAutGnTRk899VRpt1HmzJw5U02bNpWvr68mTJhQZLu3t7fGjh1b5NeAABzH6T8AAAALcKQKAADAAoQqAAAACxCqAAAALECoAgAAsAChCgAAwAKEKgAAAAsQqgAAACxAqAIAALDA/wf9e3d3TYkmFAAAAABJRU5ErkJggg==\n", "text/plain": [ - "
" + "
" ] }, - "metadata": { - "needs_background": "light" - }, + "metadata": {}, "output_type": "display_data" } ], From 97c2a74163783619ebcc3fe7e05d37b854d787b0 Mon Sep 17 00:00:00 2001 From: Joni Herttuainen Date: Tue, 17 Oct 2023 11:20:30 +0200 Subject: [PATCH 8/8] bump up the major version --- CHANGELOG.rst | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b22cd74e..99c8470b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,21 +1,7 @@ Changelog ========= -Version v1.2.0 --------------- - -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`` - -Bug Fixes -~~~~~~~~~ -- Fixed the `Same property with different dtype` issue with ``nodes.get``, ``edges.get`` - -Version v1.1.0 +Version v2.0.0 -------------- New Features @@ -35,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