How to Compute Network Statistics

Goal: Calculate metrics that describe the structure of your multilayer network—quick counts, per-layer comparisons, node-level rankings, and multilayer-specific measures.

Prerequisites: A loaded network (see How to Load and Build Networks). Examples reuse the same small network unless noted.

Note

Imports used below

Most snippets rely on the DSL helpers Q and L:

from py3plex.dsl import Q, L

Note

Where to find this data

Examples in this guide use:

  • Programmatically created networks (recommended for self-contained examples)

  • Built-in generators: from py3plex.algorithms import random_generators

  • Example files: datasets/multiedgelist.txt in the repository

For reproducibility, we’ll create networks from scratch in most examples.

Quick Statistics

Get a fast overview (total nodes, edges, layers):

from py3plex.core import multinet

# Create a multilayer network
network = multinet.multi_layer_network()
network.add_edges([
    ['A', '1', 'B', '1', 1],
    ['B', '1', 'C', '1', 1],
    ['A', '2', 'C', '2', 1],
    ['C', '2', 'D', '2', 1],
], input_type="list")

# Display comprehensive stats
network.basic_stats()

Example output:

Number of nodes: 6
Number of edges: 4
Number of unique node IDs (across all layers): 4
Nodes per layer:
  Layer '1': 3 nodes
  Layer '2': 3 nodes

Layer-Specific Statistics

Understand how structure varies per layer before aggregating the network.

Compute Per-Layer Density

Density compares observed edges m to the maximum possible edges among n nodes in one layer.

layers = network.get_layers()

for layer in layers:
    # Get nodes and edges in this layer
    nodes = Q.nodes().from_layers(L[layer]).execute(network)
    edges = Q.edges().from_layers(L[layer]).execute(network)

    n = len(nodes)
    m = len(edges)

    # Density = actual edges / possible edges
    max_edges = n * (n - 1) / 2  # undirected
    density = m / max_edges if max_edges > 0 else 0

    print(f"Layer {layer}: density = {density:.4f}")

For directed layers, use max_edges = n * (n - 1) instead (ordered pairs).

Compare Layers

Use the DSL for efficient comparison:

for layer in network.get_layers():
    result = (
        Q.nodes()
         .from_layers(L[layer])
         .compute("degree")
         .execute(network)
    )
    df = result.to_pandas()
    print(f"{layer}: avg degree = {df['degree'].mean():.2f}")

Example output (values depend on your data):

layer1: avg degree = 3.45
layer2: avg degree = 2.87
layer3: avg degree = 4.12

Node-Level Statistics

Once you know layer-level trends, drill down to node importance and activity.

Node Activity (Layer Count)

How many layers does each node participate in? layer_count counts unique layers where the node appears.

# Get nodes present in multiple layers
active_nodes = (
    Q.nodes()
     .compute("layer_count")
     .where(layer_count__gt=1)
     .order_by("-layer_count")
     .execute(network)
)

df = active_nodes.to_pandas()
print(df.head(10))

Example output:

     node  layer_count
0   Alice           3
1     Bob           3
2   Carol           2
3    Dave           1

Degree Centrality

Compute degree for all nodes and sort in descending order:

from py3plex.dsl import Q

result = (
    Q.nodes()
     .compute("degree")
     .order_by("-degree")
     .limit(10)
     .execute(network)
)

df = result.to_pandas()
print("Top 10 by degree:")
print(df)

Betweenness Centrality

Find nodes that bridge different parts of the network (higher values indicate more shortest paths go through the node):

result = (
    Q.nodes()
     .compute("betweenness_centrality")
     .order_by("-betweenness_centrality")
     .limit(10)
     .execute(network)
)

df = result.to_pandas()
print("Top 10 by betweenness:")
print(df)

Multiple Metrics at Once

Compute several measures in one pass to avoid recomputing them separately:

result = (
    Q.nodes()
     .compute("degree", "betweenness_centrality", "clustering")
     .execute(network)
)

df = result.to_pandas()
print(df.describe())

Multilayer-Specific Statistics

Use these measures when the interaction between layers matters, not just within-layer structure.

Node Versatility

Versatility measures how evenly a node distributes its connections across layers; higher values mean more balanced participation across layers.

from py3plex.algorithms.statistics import calculate_versatility

versatility_scores = calculate_versatility(network)

# Sort by versatility
sorted_scores = sorted(
    versatility_scores.items(),
    key=lambda x: x[1],
    reverse=True
)

print("Top 5 most versatile nodes:")
for node, score in sorted_scores[:5]:
    print(f"{node}: {score:.3f}")

Edge Overlap Between Layers

How many connections exist in multiple layers?

from py3plex.algorithms.statistics import calculate_edge_overlap

overlap = calculate_edge_overlap(network, 'layer1', 'layer2')

print(f"Edge overlap between layer1 and layer2: {overlap:.2%}")

overlap is a ratio (0–1) of edges shared by both layers.

Inter-Layer Degree Correlation

Are high-degree nodes in layer 1 also high-degree in layer 2?

from py3plex.algorithms.statistics import inter_layer_correlation

correlation = inter_layer_correlation(
    network,
    'layer1',
    'layer2',
    metric='degree'
)

print(f"Degree correlation: {correlation:.3f}")
# Values near 1.0 indicate that high-degree nodes stay high across layers.

Network-Wide Statistics

Aggregate everything into a single view of the whole multilayer graph.

Global Clustering Coefficient

from py3plex.algorithms.statistics import global_clustering_coefficient

gcc = global_clustering_coefficient(network)
print(f"Global clustering: {gcc:.3f}")

Average Path Length

from py3plex.algorithms.statistics import average_path_length

# Note: computationally expensive for large networks; ensure the graph
# is connected or run on the largest connected component.
apl = average_path_length(network)
print(f"Average path length: {apl:.2f}")

Exporting Statistics

Persist results so you can compare runs or feed them into other tools.

Save to CSV

from py3plex.dsl import Q

result = (
    Q.nodes()
     .compute("degree", "betweenness_centrality", "layer_count")
     .execute(network)
)

df = result.to_pandas()
df.to_csv("network_statistics.csv", index=False)

Save to JSON

import json
from py3plex.dsl import Q, L

stats = {
    'num_nodes': len(list(network.get_nodes())),
    'num_edges': len(list(network.get_edges())),
    'num_layers': len(network.get_layers()),
    'density_by_layer': {}
}

for layer in network.get_layers():
    layer_nodes = Q.nodes().from_layers(L[layer]).execute(network)
    layer_edges = Q.edges().from_layers(L[layer]).execute(network)
    n = len(layer_nodes)
    m = len(layer_edges)
    max_edges = n * (n - 1) / 2  # undirected density
    stats['density_by_layer'][layer] = m / max_edges if max_edges > 0 else 0

with open('stats.json', 'w') as f:
    json.dump(stats, f, indent=2)

Common Patterns

Pattern: Compare Node Importance Across Layers

import pandas as pd

layers = network.get_layers()
all_stats = []

for layer in layers:
    result = (
        Q.nodes()
         .from_layers(L[layer])
         .compute("degree", "betweenness_centrality")
         .execute(network)
    )
    df = result.to_pandas()
    df['layer'] = layer
    all_stats.append(df)

combined = pd.concat(all_stats, ignore_index=True)

# Find nodes that are important in all layers
pivot = combined.pivot_table(
    values='degree',
    index='node',
    columns='layer',
    aggfunc='mean'
)

print(pivot)

Pattern: Identify Layer-Specific Hubs

for layer in network.get_layers():
    result = (
        Q.nodes()
         .from_layers(L[layer])
         .compute("degree")
         .order_by("-degree")
         .limit(5)
         .execute(network)
    )
    df = result.to_pandas()
    print(f"\nTop 5 hubs in {layer}:")
    print(df)

Next Steps