In [1]:
import relationalai as rai
from relationalai.std.graphs import Graph
from relationalai.std import alias
import matplotlib.pyplot as plt
import pandas as pd

🔧 Utility Functions for Graph Construction

In [ ]:
# =======================
# ⚠️ Demo-Only Functions ⚠️
# =======================
# The following functions are designed for demonstration purposes only,
# specifically for use in `graphlib-egonetwork.ipynb`.
#
# ⚠️ WARNING: These functions include hardcoded values
# (e.g., node ID is assumed to be under the 'id' property).
#
# ✅ Please modify them accordingly for your production or custom use cases.

def compute_ego_network(graph, node_id, hops=1):
    """
    Computes the ego network of a specified node within a given number of hops.

    Parameters:
    - graph (Graph): The graph to compute the ego network from.
    - node (str): The node to center the ego network on.
    - hops (int): The number of hops to include in the ego network.

    Returns:
    - pd.DataFrame: A DataFrame containing the edges of the ego network.
    """
    with graph.model.query() as select:
        node = graph.Node()
        node.id == node_id
        a,b = graph.compute.ego_network(node, hops=hops)
        ego_response = select(alias(graph.Node(a).id, "u"), alias(graph.Node(b).id, "v"))

    ego_df =  ego_response.results
    return ego_df

def create_graph(model, node_set, edge_list, undirected=False):
    graph = Graph(model, undirected=undirected)
    with model.rule(dynamic=True):
        nodes = []
        for id in node_set:
            node = graph.Node.add(id=id)
            nodes.append(node)
        for e in edge_list:
            u, v = e[0]-1, e[1]-1
            graph.Edge.add(nodes[u], nodes[v])
    return graph

📎 Egonet extraction

Let's create a directed graph with a simple community structure: a 4-node clique connected by an edge to a 3-node triangle, plus two isolated nodes. The ego_network function assumes the input graph is directed; for undirected graphs, each undirected edge is represented as two directed edges.

In [3]:
model = rai.Model("Demo_Egonets", keep_model=False)
In [4]:
# Graph structure:
#
# K4 (complete graph on 4 nodes) ----- 5           8,  9 (isolated nodes)
#                                     / \
#                                    6---7
#
# Edge list:
num_nodes = 9
edge_list = [(1,2), (1,3), (1,4), (2,3), (2,4), (3,4),  # 4-clique
             (4,5), # edge connecting K4 to node 5
             (5,6), (5,7), (6,7)               # triangle connected via node 5
]

graph = create_graph(model, range(1,num_nodes+1), edge_list, undirected=True)

graph_style = {
    "node": {
        "label": lambda n: str(n.get("id")),  # or any other key, e.g., "name"
        "color": "#92979c",
        "size": 10,
    },
    "edge": {
        "color": "#cccccc",
    }
}

# let's visualize it
graph.visualize(
    three=True,
    node_label_size_factor=1.9,
    use_links_force=True,
    node_hover_neighborhood=True,
    style=graph_style
)

▰▰▰▰ Setup complete


Out[4]:
Details for selected element
General
App state
Display mode
Export
Data selection
Graph
Node label text
Node size
Minimum
Maximum
Edge size
Minimum
Maximum
Nodes
Visibility
Size
Scaling factor
Position
Drag behavior
Hover behavior
Node images
Visibility
Size
Scaling factor
Node labels
Visibility
Size
Scaling factor
Edges
Visibility
Size
Scaling factor
Form
Curvature
Hover behavior
Layout algorithm
Simulation
Many-body force
Strength
Theta
Min
Max
Links force
Distance
Strength
x-positioning force
Strength
y-positioning force
Strength
z-positioning force
Strength
Centering force

We now demonstrate how to compute the egonetwork of a specific node.

  • Here, we choose node 1 (i.e., node_id=1) with a radius of 2 hops.
  • This is done using the compute_ego_network function, which takes the model, graph, node ID, and hop count, and returns a DataFrame containing the edge list of the induced subgraph. We then pass this output to visualize_ego_network, which provides a basic visualization. This step can be customized to suit your preferred graph visualization tool.
In [ ]:
ego_df = compute_ego_network(graph, node_id=1, hops=2)

Using the create_graph function, we create a graphlib object from the pandas dataframe output.

In [6]:
node_set = set(pd.concat([ego_df['v'], ego_df['u']]))
induced_edge_list = list(ego_df[['u', 'v']].itertuples(index=False, name=None))
ego_graph_of_node_1 = create_graph(model, node_set, induced_edge_list, graph.undirected)

# let's visualize it
ego_graph_of_node_1.visualize(
    three=True,
    node_label_size_factor=1.9,
    use_links_force=True,
    node_hover_neighborhood=True,
    style=graph_style
)
Out[6]:
Details for selected element
General
App state
Display mode
Export
Data selection
Graph
Node label text
Node size
Minimum
Maximum
Edge size
Minimum
Maximum
Nodes
Visibility
Size
Scaling factor
Position
Drag behavior
Hover behavior
Node images
Visibility
Size
Scaling factor
Node labels
Visibility
Size
Scaling factor
Edges
Visibility
Size
Scaling factor
Form
Curvature
Hover behavior
Layout algorithm
Simulation
Many-body force
Strength
Theta
Min
Max
Links force
Distance
Strength
x-positioning force
Strength
y-positioning force
Strength
z-positioning force
Strength
Centering force

📈 Local Community Analysis

Finally, we apply community detection to the ego-network we have extracted. We use the Louvain algorithm with the default parameter values, which outputs two communities, one consisting of the 3-clique consisting of ndoes 1,2,3 and the other community of nodes 4 and 5.

In [8]:
# Find the community label for each person in the graph.
with model.query() as select:
    node = ego_graph_of_node_1.Node()
    community = ego_graph_of_node_1.compute.louvain(node)
    node.set(community = community)
    louvain_response = select(alias(node.id,"node"), alias(community, "community_label"))

louvain_comms = louvain_response.results
print(louvain_comms)
   node  community_label
0     1                2
1     2                2
2     3                2
3     4                1
4     5                1
In [ ]: