Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add include_empty keyword argument #250

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions bluepysnap/edges/edges.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ def ids(self, group=None, sample=None, limit=None):
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
def get(
self, edge_ids=None, properties=None, include_empty=False
): # pylint: disable=arguments-renamed
"""Edge properties by iterating populations.

Args:
Expand All @@ -102,7 +104,7 @@ def get(self, edge_ids=None, properties=None): # pylint: disable=arguments-rena
raise BluepySnapError("You need to set edge_ids in get.")
if properties is None:
return edge_ids
return super().get(edge_ids, properties)
return super().get(edge_ids, properties, include_empty=include_empty)

def afferent_nodes(self, target, unique=True):
"""Get afferent CircuitNodeIDs for given target ``node_id``.
Expand Down Expand Up @@ -148,13 +150,14 @@ def efferent_nodes(self, source, unique=True):
result.unique(inplace=True)
return result

def pathway_edges(self, source=None, target=None, properties=None):
def pathway_edges(self, source=None, target=None, properties=None, include_empty=False):
"""Get edges corresponding to ``source`` -> ``target`` connections.

Args:
source: source node group
target: target node group
properties: None / edge property name / list of edge property names
include_empty: whether to include populations for which the query is empty

Returns:
- CircuitEdgeIDs, if ``properties`` is None;
Expand All @@ -172,7 +175,7 @@ def pathway_edges(self, source=None, target=None, properties=None):
)

if properties:
return self.get(result, properties)
return self.get(result, properties, include_empty=include_empty)
return result

def afferent_edges(self, node_id, properties=None):
Expand Down
4 changes: 2 additions & 2 deletions bluepysnap/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def ids(self, group=None, sample=None, limit=None):
"""Resolves the ids of the NetworkObject."""

@abc.abstractmethod
def get(self, group=None, properties=None):
def get(self, group=None, properties=None, include_empty=False):
"""Yields the properties of the NetworkObject."""
ids = self.ids(group)
properties = utils.ensure_list(properties)
Expand All @@ -147,7 +147,7 @@ def get(self, group=None, properties=None):
# since ids is sorted, global_pop_ids should be sorted as well
global_pop_ids = ids.filter_population(name)
pop_ids = global_pop_ids.get_ids()
if len(pop_ids) > 0:
if len(pop_ids) > 0 or include_empty:
pop_properties = properties_set & pop.property_names
# Since the columns are passed as Series, index cannot be specified directly.
# However, it's a bit more performant than converting the Series to numpy arrays.
Expand Down
8 changes: 6 additions & 2 deletions bluepysnap/nodes/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ def ids(self, group=None, sample=None, limit=None):
fun = lambda x: (x.ids(group, raise_missing_property=False), x.name)
return self._get_ids_from_pop(fun, CircuitNodeIds, sample=sample, limit=limit)

def get(self, group=None, properties=None): # pylint: disable=arguments-differ
def get(
self, group=None, properties=None, include_empty=False
): # pylint: disable=arguments-differ
"""Node properties by iterating populations.

Args:
Expand All @@ -131,6 +133,8 @@ def get(self, group=None, properties=None): # pylint: disable=arguments-differ
properties (str/list): If specified, return only the properties in the list.
Otherwise return all properties.

include_empty: whether to include populations for which the query is empty

Returns:
generator: yields tuples of ``(<population_name>, pandas.DataFrame)``:
- DataFrame indexed by CircuitNodeIds containing the properties from ``properties``.
Expand All @@ -142,7 +146,7 @@ def get(self, group=None, properties=None): # pylint: disable=arguments-differ
if properties is None:
# not strictly needed, but ensure that the properties are always in the same order
properties = sorted(self.property_names)
return super().get(group, properties)
return super().get(group, properties, include_empty=include_empty)

def __getstate__(self):
"""Make Nodes pickle-able, without storing state of caches."""
Expand Down
13 changes: 13 additions & 0 deletions tests/test_edges.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,19 @@ def test_pathway_edges(self):
check_dtype=False,
)

# check that 'include_empty' kwarg works
tested = self.test_obj.pathway_edges(source=[1], properties=properties, include_empty=True)

expected = pd.DataFrame(
{prop: [] for prop in properties},
index=pd.MultiIndex.from_tuples(
[],
names=["population", "edge_ids"],
),
)
tested = pd.concat([df for _, df in tested])
pdt.assert_frame_equal(tested, expected, check_dtype=False, check_index_type=False)

# use global mapping for nodes
assert self.test_obj.pathway_edges(
source={"mtype": "L6_Y"}, target={"mtype": "L2_X"}
Expand Down
12 changes: 12 additions & 0 deletions tests/test_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,18 @@ def test_get(self):
with pytest.raises(BluepySnapError, match="Unknown properties required: {'unknown'}"):
next(self.test_obj.get(properties="unknown"))

tested = pd.concat([df for _, df in self.test_obj.get({"layer": 10}, include_empty=True)])
expected = pd.DataFrame(
{p: [] for p in list(self.test_obj.property_names)},
index=pd.MultiIndex.from_tuples([], names=["population", "node_ids"]),
)
pdt.assert_frame_equal(
tested.sort_index(axis=1),
expected.sort_index(axis=1),
check_dtype=False,
check_index_type=False,
)

def test_functionality_with_separate_node_set(self):
with pytest.raises(BluepySnapError, match="Undefined node set"):
self.test_obj.ids("ExtraLayer2")
Expand Down