Skip to content

Commit

Permalink
Pull request #167: added items property to hyp_view and intersection …
Browse files Browse the repository at this point in the history
…and union to hypergraph

Merge in HYP/hypernetx from bpedits3 to develop

* commit 'b178da64e06a3bc4a2f5cad61ed4361b8f2bbf6b':
  Fix and refactor sum method; add tests
  added items property to hyp_view and intersection and union to hypergraph
  • Loading branch information
brendapraggastis authored and bonicim committed Apr 30, 2024
2 parents abd35cd + b178da6 commit c519d05
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 32 deletions.
5 changes: 4 additions & 1 deletion hypernetx/classes/hyp_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ def __init__(self, incidence_store, level, property_store=None):
### returns weight 1 on every call for a weight
### and empty properties otherwise.

@property
def items(self):
return set(self._items)

@property
def level(self):
return self._level
Expand Down Expand Up @@ -98,7 +102,6 @@ def to_dataframe(self):
non_user_defined_properties = pd.DataFrame(
index=non_user_defined_items, data=default_data
)
non_user_defined_properties.index.rename("uid", inplace=True)

# Combine user-defined and non-user-defined properties into one dataframe
return pd.concat([df, non_user_defined_properties])
Expand Down
90 changes: 61 additions & 29 deletions hypernetx/classes/hypergraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -1135,11 +1135,11 @@ def _construct_hyp_from_stores(
h._E = HypergraphView(incidence_store, 2, incidence_ps)

if edge_ps is None:
edge_ps = PropertyStore(incidence_df.edges.properties)
edge_ps = self.edges.property_store.copy()
h._edges = HypergraphView(incidence_store, 0, edge_ps)

if node_ps is None:
node_ps = PropertyStore(incidence_df.nodes.properties)
node_ps = self.nodes.property_store.copy()
h._nodes = HypergraphView(incidence_store, 1, node_ps)

h._set_default_state()
Expand Down Expand Up @@ -2539,8 +2539,8 @@ def __sub__(self, other):

def sum(self, other):
"""
Concatenate incidences from two hypergraphs, removing duplicates and
dropping duplicate property data in the order of addition.
Hypergraph obtained by joining incidences from self and other.
Removes duplicates and uses properties of self.
Parameters
----------
Expand All @@ -2551,33 +2551,29 @@ def sum(self, other):
Hypergraph
"""
df = self.incidences.to_dataframe
other_df = other.incidences.to_dataframe
combined_incidences_df = (
pd.concat([df, other_df]).groupby(["edges", "nodes"]).agg("first")
incidence_df = self._combine_properties_dataframes(
self.incidences.to_dataframe, other.incidences.to_dataframe
)

edges_df = self.edges.to_dataframe
other_edges_df = other.edges.to_dataframe
combined_edges_df = (
pd.concat([edges_df, other_edges_df]).groupby("uid").agg("first")
edges_data = self._combine_properties_dataframes(
self.edges.to_dataframe, other.edges.to_dataframe
)

nodes_df = self.nodes.to_dataframe
other_nodes_df = other.nodes.to_dataframe
combined_nodes_df = (
pd.concat([nodes_df, other_nodes_df]).groupby("uid").agg("first")
nodes_data = self._combine_properties_dataframes(
self.nodes.to_dataframe, other.nodes.to_dataframe
)

return self._construct_hyp_from_stores(
combined_incidences_df,
edge_ps=PropertyStore(combined_edges_df),
node_ps=PropertyStore(combined_nodes_df),
incidence_df,
edge_ps=PropertyStore(edges_data),
node_ps=PropertyStore(nodes_data),
)

def difference(self, other):
def _combine_properties_dataframes(self, df1, df2):
df = pd.concat([df1, df2])
return df[~df.index.duplicated(keep="first")]

def difference(self, other, name=None):
"""
Remove incidence pairs from self that belong to other.
Hypergraph obtained by restricting to incidences in self but not in other.
Parameters
----------
Expand All @@ -2588,12 +2584,48 @@ def difference(self, other):
: Hypergraph
"""
df = self.incidences.properties
odf = other.incidences.properties
ndf = df.loc[~df.index.isin(odf.index.tolist())]
return self._construct_hyp_from_stores(
ndf, edge_ps=self.edges._property_store, node_ps=self.nodes._property_store
)
ndx = list(self.incidences.items.difference(other.incidences.items))
ndf = self.incidences.to_dataframe.loc[ndx]
return self._construct_hyp_from_stores(ndf, name=name)

def intersection(self, other, name=None):
"""
The hypergraph gotten by restricting to incidence pairs contained in
both self and other. Properties inherited from self.
Parameters
----------
other : Hypergraph
name : str, optional
by default None
Returns
-------
: Hypergraph
"""
ndx = list(self.incidences.items.intersection(other.incidences.items))
ndf = self.incidences.to_dataframe.loc[ndx]
return self._construct_hyp_from_stores(ndf, name=name)

def union(self, other, name=None):
"""
The hypergraph gotten by joining incidence pairs contained in
self and other. Duplicates removed. Properties inherited from self.
Same as sum.
Parameters
----------
other : Hypergraph
name : str, optional
by default None
Returns
-------
: Hypergraph
"""
return self.sum(other)


def _agg_rows(df, groupby, rule_dict=None):
Expand Down
12 changes: 10 additions & 2 deletions tests/classes/test_hypergraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,7 @@ def test_sum_hypergraph_with_dupe_hypergraph(sevenbysix, sevenbysix_dupes):
hg = Hypergraph(sevenbysix.edgedict)
hg_dupes = Hypergraph(sevenbysix_dupes.edgedict)

# Case: HDuplicate + H
# add almost duplicate hypergraph to hypergraph
new_hg = hg.sum(hg_dupes)

Expand All @@ -1169,14 +1170,21 @@ def test_sum_hypergraph_with_dupe_hypergraph(sevenbysix, sevenbysix_dupes):
"R": ["A", "E", "F"],
"S": ["A", "K", "T2", "V"],
}
assert new_hg.incidences.incidence_dict == expected_incidences
actual_incidences = new_hg.incidence_dict
for expected_edge, expected_nodes in expected_incidences.items():
assert expected_edge in actual_incidences
assert all(node in actual_incidences[expected_edge] for node in expected_nodes)

# Case: H + HDuplicate
# add hypergraph to almost duplicate
new_hg = hg_dupes.sum(hg)

assert new_hg.shape == (len(sevenbysix.nodes) + 1, len(sevenbysix.edges) + 1)
# check for new incidences
assert new_hg.incidences.incidence_dict == expected_incidences
actual_incidences = new_hg.incidences.incidence_dict
for expected_edge, expected_nodes in expected_incidences.items():
assert expected_edge in actual_incidences
assert all(node in actual_incidences[expected_edge] for node in expected_nodes)


@pytest.mark.parametrize(
Expand Down

0 comments on commit c519d05

Please sign in to comment.