API reference¶
The package is used through Network: load a graph, decompose(), then visualize()
(the layout is computed on the way, or explicitly with compute_layout()). Every setting
is a field of LaNetConfig, the same model the CLI fills from its flags. The
usage guide walks through a complete example.
Network, LaNetConfig and its sub-models, DecompositionType, ColorScheme,
BackgroundColor, load_config_from_yaml and save_config_to_yaml are re-exported
from the top-level lanet_vi package; everything else is imported from its module.
Network¶
network ¶
Main Network class for LaNet-vi.
Network ¶
Network(graph: Graph, config: LaNetConfig | None = None)
Main class for network analysis and visualization.
This class provides a high-level API for:
- loading network data,
- computing the k-core, k-dense or d-core decomposition,
- computing the layout and rendering the picture.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
NetworkX graph |
required |
config
|
LaNetConfig
|
Complete configuration |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
graph |
Graph
|
The network graph; after |
input_graph |
Graph
|
The graph as given, which every |
config |
LaNetConfig
|
Configuration settings |
decomposition |
Optional[DecompositionResult]
|
Decomposition results (None until decompose() is called) |
communities |
Optional[CommunityResult]
|
Communities of |
kconnectivity |
Optional[Dict[int, int]]
|
K-connectivity of every node, 0 for the nodes that are not k-connected (None
until |
node_names |
Dict[int, str]
|
Node name mappings |
custom_names |
bool
|
A names file was loaded (even an empty one): only the nodes it names are
labeled, never the node numbers (the C++ |
node_colors |
Dict[int, Tuple[float, float, float]]
|
Custom node colors |
Examples:
>>> config = LaNetConfig()
>>> G = nx.Graph(nx.karate_club_graph().edges()) # drop the weights
>>> net = Network(G, config)
>>> net.decompose()
>>> net.visualize("output.png")
Initialize Network with graph and configuration.
colors_by_community
property
¶
colors_by_community: bool
True when the nodes take their community's color instead of the shell color.
Communities were detected and config.community.color_by_community is set; a
colors file (load_node_colors) takes precedence, as it names each node's color.
from_edge_list
classmethod
¶
from_edge_list(
file_path: Path | str, config: LaNetConfig | None = None
) -> Network
Create Network from edge list file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file_path
|
Union[Path, str]
|
Path to edge list file |
required |
config
|
Optional[LaNetConfig]
|
Configuration (uses defaults if None) |
None
|
Returns:
| Type | Description |
|---|---|
Network
|
Network instance |
Examples:
>>> net = Network.from_edge_list("network.txt")
load_node_names ¶
load_node_names(file_path: Path | str) -> None
Load node names from file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file_path
|
Union[Path, str]
|
Path to node names file |
required |
load_node_colors ¶
load_node_colors(file_path: Path | str) -> None
Load custom node colors from file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file_path
|
Union[Path, str]
|
Path to node colors file |
required |
decompose ¶
decompose(
decomp_type: DecompositionType | None = None,
) -> DecompositionResult
Compute network decomposition.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
decomp_type
|
Optional[DecompositionType]
|
Type of decomposition (uses config if None) |
None
|
Returns:
| Type | Description |
|---|---|
DecompositionResult
|
Decomposition results |
Examples:
>>> result = net.decompose(DecompositionType.KCORES)
detect_communities ¶
detect_communities() -> CommunityResult
Detect the communities of the graph with config.community.
decompose() calls this when config.community.detect_communities is set;
calling it directly detects them regardless of that flag. The result is kept in
communities and, from then on, compute_layout() colors the nodes by
community (config.community.color_by_community) and visualize() draws the
community boundaries or circles (draw_boundaries / draw_circles).
Returns:
| Type | Description |
|---|---|
CommunityResult
|
Communities of |
Examples:
>>> net.decompose()
>>> communities = net.detect_communities()
>>> net.visualize("communities.png")
compute_kconnectivity ¶
compute_kconnectivity() -> dict[int, int]
Compute the k-connectivity of the shells (the C++ -kconn).
decompose() calls this when config.decomposition.kconn is set; calling it
directly computes it regardless of that flag, with config.decomposition.kconn_type
(wide or strict). The result is kept in kconnectivity and, from then
on, compute_layout() paints the nodes that are not k-connected black on white
/ white on black (squares in the grayscale schemes), as the C++ did.
The clusters are those of the component tree the layout draws, in the same order
(the tree depends on config.layout.seed), so the walk sees what the picture
shows.
Returns:
| Type | Description |
|---|---|
Dict[int, int]
|
K-connectivity of every node, 0 when not k-connected |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> net.decompose()
>>> kconn = net.compute_kconnectivity()
>>> net.visualize("kconn.png")
compute_layout ¶
compute_layout() -> VisualizationLayout
Compute visualization layout.
Returns:
| Type | Description |
|---|---|
VisualizationLayout
|
Layout with node positions and visual properties |
Raises:
| Type | Description |
|---|---|
ValueError
|
If decompose() hasn't been called yet |
visualize ¶
visualize(
output_path: Path | str,
layout: VisualizationLayout | None = None,
) -> None
Generate and save visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
output_path
|
Union[Path, str]
|
Output file path |
required |
layout
|
Optional[VisualizationLayout]
|
Pre-computed layout (computes if None) |
None
|
Examples:
>>> net.visualize("network.png")
get_metadata ¶
get_metadata() -> dict
Get network metadata.
Returns:
| Type | Description |
|---|---|
Dict
|
Dictionary with network statistics |
Configuration¶
config ¶
Configuration models for LaNet-vi using Pydantic.
BackgroundColor ¶
Bases: str, Enum
Background color options.
ColorScheme ¶
Bases: str, Enum
Color scheme options for visualization.
DecompositionType ¶
Bases: str, Enum
Type of network decomposition.
StrengthIntervalMethod ¶
Bases: str, Enum
Method for building strength intervals in weighted graphs.
CoordDistributionAlgorithm ¶
Bases: str, Enum
Algorithm for distributing components.
MeasureType ¶
Bases: str, Enum
Centrality measure type.
KConnectivityType ¶
Bases: str, Enum
How the k-connectivity walk treats the clusters it skips (-kconntype).
CommunityConfig ¶
Bases: BaseModel
Configuration for community detection.
Attributes:
| Name | Type | Description |
|---|---|---|
detect_communities |
bool
|
Whether to detect and visualize communities |
algorithm |
Literal['louvain', 'greedy_modularity']
|
Community detection algorithm to use |
resolution |
float
|
Resolution parameter for Louvain algorithm (higher = more communities) |
color_by_community |
bool
|
Color nodes by community membership instead of k-core |
draw_boundaries |
bool
|
Draw convex hull boundaries around communities |
draw_circles |
bool
|
Draw circles around communities |
boundary_alpha |
float
|
Transparency of community boundaries |
colormap |
str
|
Matplotlib colormap for community colors |
colormap_is_known
classmethod
¶
colormap_is_known(value: str) -> str
Require a matplotlib colormap name (a short error, not the list of all names).
VisualizationConfig ¶
Bases: BaseModel
Configuration for network visualization.
Attributes:
| Name | Type | Description |
|---|---|---|
background |
BackgroundColor
|
Background color for the visualization |
color_scheme |
ColorScheme
|
Color scheme for nodes and edges |
width |
int
|
Image width in pixels |
height |
int
|
Image height in pixels |
window |
tuple of four floats
|
|
epsilon |
float
|
Ring thickness as a fraction of its radius (formula (1) of NIPS 2005) |
delta |
float
|
Shrink factor of sibling components (formula (5)) |
gamma |
float
|
Component diameter; scales the whole picture (formulas (6)-(7)) |
font_zoom |
float
|
Font size multiplier for node labels |
legend_fontsize |
Optional[float]
|
Legend font size (auto-scales with diagram size if not set) |
edges_percent |
float
|
Percentage of visible edges (0.0-1.0) |
min_edges |
int
|
Minimum number of visible edges |
opacity |
float
|
Edge opacity (0.0-1.0), the C++ |
unit_length |
float
|
Base unit length for scaling |
draw_circles |
bool
|
Whether to draw component border circles |
show_degree_scale |
bool
|
Whether to show the degree (node size) legend, as the C++ |
show_color_legend |
bool
|
Whether to show the shell/dense index color legend |
color_scale_max_value |
Optional[int]
|
Maximum value for color scale normalization |
gradient_edges |
bool
|
Whether to use gradient edge coloring |
show_node_labels |
bool
|
Whether to show node labels |
label_all_nodes |
bool
|
If True, label all nodes; if False, use label_kcore_range or label_node_list |
label_kcore_min |
Optional[int]
|
Minimum k-core for labeling (only if label_all_nodes=False) |
label_kcore_max |
Optional[int]
|
Maximum k-core for labeling (only if label_all_nodes=False) |
node_edge_color |
Optional[str]
|
Border color of the nodes (the C++ drew none: border = node color) |
node_size_scale |
float
|
Multiplier on the node radius (1.0 = the C++ size) |
edge_alpha |
float
|
Deprecated alias of |
show_size_legend |
bool
|
Deprecated alias of |
window_is_a_sub_rectangle
classmethod
¶
window_is_a_sub_rectangle(
value: tuple[float, float, float, float],
) -> tuple[float, float, float, float]
Require 0 <= hstart < hend <= 1 and the same vertically.
fold_deprecated_aliases
classmethod
¶
fold_deprecated_aliases(data: Any) -> Any
Map the deprecated aliases onto their current fields.
An alias only applies when the current field itself is absent, so a file that sets the current field is never overridden by the old one.
DecompositionConfig ¶
Bases: BaseModel
Configuration for network decomposition.
Attributes:
| Name | Type | Description |
|---|---|---|
decomp_type |
DecompositionType
|
Type of decomposition to apply |
measure |
MeasureType
|
Name of the k-dense measure in the legend: |
from_layer |
int
|
Consider graph induced from this layer upward |
granularity |
int
|
Number of groups in weighted graphs (-1 for max degree) |
strength_intervals |
StrengthIntervalMethod
|
Method for building strength intervals |
maximum_strength |
Optional[float]
|
Upper limit for strength intervals |
strength_intervals_file |
Optional[Path]
|
Boundaries, one per line, for |
no_cliques |
bool
|
Whether to omit cliques in central core |
kconn |
bool
|
Compute the k-connectivity of the shells (the C++ |
kconn_type |
KConnectivityType
|
|
granularity_is_sentinel_or_positive
classmethod
¶
granularity_is_sentinel_or_positive(value: int) -> int
-1 means "maximum degree"; any other value must be at least 1.
custom_intervals_need_a_file ¶
custom_intervals_need_a_file() -> DecompositionConfig
strength_intervals = custom is only meaningful with a boundaries file.
kconn_needs_kcores ¶
kconn_needs_kcores() -> DecompositionConfig
Refuse kconn outside the k-cores: it is defined on their clusters.
GraphConfig ¶
Bases: BaseModel
Configuration for graph construction.
Attributes:
| Name | Type | Description |
|---|---|---|
multigraph |
bool
|
Allow repeated edges |
weighted |
bool
|
Support edge weights |
directed |
bool
|
Whether graph is directed |
LayoutConfig ¶
Bases: BaseModel
Configuration for layout algorithm.
Attributes:
| Name | Type | Description |
|---|---|---|
coord_distribution |
CoordDistributionAlgorithm
|
Placement of the components: |
alpha |
float
|
Constant of the disc area law of the circle packing ( |
beta |
float
|
Exponent of the disc area law ( |
seed |
int
|
Random seed for reproducibility |
ratio_constant |
Optional[float]
|
Node radius factor of the |
min_component_size |
int
|
Minimum component size to visualize (filters small components for performance) |
LaNetConfig ¶
Bases: BaseModel
Complete LaNet-vi configuration.
Attributes:
| Name | Type | Description |
|---|---|---|
graph |
GraphConfig
|
Graph construction settings |
decomposition |
DecompositionConfig
|
Decomposition algorithm settings |
visualization |
VisualizationConfig
|
Visualization appearance settings |
layout |
LayoutConfig
|
Layout algorithm settings |
community |
CommunityConfig
|
Community detection settings |
kconn_needs_a_simple_unweighted_graph ¶
kconn_needs_a_simple_unweighted_graph() -> LaNetConfig
Refuse kconn on weighted, directed or multigraph inputs, as the C++ did.
Results¶
graph ¶
Graph data models for LaNet-vi.
NodeData ¶
Bases: BaseModel
Node attributes and metadata.
Attributes:
| Name | Type | Description |
|---|---|---|
node_id |
int
|
Unique node identifier |
name |
Optional[str]
|
Human-readable node name |
color |
Optional[Tuple[float, float, float]]
|
RGB color values (0.0-1.0 range) |
shell_index |
Optional[int]
|
k-core shell index |
dense_index |
Optional[int]
|
k-dense index |
degree |
Optional[int]
|
Node degree |
strength |
Optional[float]
|
Node strength (sum of edge weights) |
coordinates |
Optional[Tuple[float, float]]
|
2D visualization coordinates (x, y) |
EdgeData ¶
Bases: BaseModel
Edge attributes and metadata.
Attributes:
| Name | Type | Description |
|---|---|---|
source |
int
|
Source node ID |
target |
int
|
Target node ID |
weight |
float
|
Edge weight |
visible |
bool
|
Whether edge should be rendered |
Component ¶
Component(**data: Any)
Bases: BaseModel
Connected component in the network.
Attributes:
| Name | Type | Description |
|---|---|---|
component_id |
int
|
Unique component identifier |
nodes |
List[int]
|
List of node IDs in this component |
shell_index |
Optional[int]
|
k-core shell index for this component |
dense_index |
Optional[int]
|
k-dense index for this component |
size |
int
|
Number of nodes in component |
center |
Optional[Tuple[float, float]]
|
Component center coordinates |
radius |
Optional[float]
|
Component radius for visualization |
Initialize component and compute size.
DecompositionResult ¶
Bases: BaseModel
Results from k-core or k-dense decomposition.
Attributes:
| Name | Type | Description |
|---|---|---|
decomp_type |
str
|
Type of decomposition ('kcores' or 'kdenses') |
node_indices |
Dict[int, int]
|
Mapping from node ID to shell/dense index |
max_index |
int
|
Maximum shell/dense index |
min_index |
int
|
Minimum shell/dense index |
components |
List[Component]
|
List of connected components at each level |
p_function |
Optional[List[float]]
|
Strength interval boundaries for weighted graphs |
metadata |
Dict[str, Any]
|
Extra, decomposition-specific data (e.g. |
VisualizationLayout ¶
Bases: BaseModel
Complete layout information for visualization.
Attributes:
| Name | Type | Description |
|---|---|---|
node_positions |
Dict[int, Tuple[float, float]]
|
Mapping from node ID to (x, y) coordinates |
node_colors |
Dict[int, Tuple[float, float, float]]
|
Mapping from node ID to RGB color |
node_sizes |
Dict[int, float]
|
Mapping from node ID to visual size/radius |
visible_edges |
List[Tuple[int, int]]
|
List of edges to render (source, target) |
edge_colors |
Dict[Tuple[int, int], Tuple[Tuple[float, ...], Tuple[float, ...]]]
|
Mapping from edge (u,v) to pair of RGB colors (color1, color2) for gradient rendering |
edge_widths |
Dict[Tuple[int, int], float]
|
Mapping from edge (u,v) to line width |
components |
List[Component]
|
Component layout information |
bounds |
Tuple[float, float, float, float]
|
Layout bounds (xmin, xmax, ymin, ymax) |
frame |
float
|
Network radius in layout units ( |
weighted |
bool
|
Node radii are strength-based (the degree legend then shows strengths) |
radius_law |
(RadiusLaw, optional)
|
The node radius law of the picture (mode, |
square_nodes |
Set[int]
|
Nodes drawn as squares instead of circles: the nodes that are not k-connected in
the grayscale schemes (the C++ |
NetworkMetadata ¶
Bases: BaseModel
Metadata about the network.
Attributes:
| Name | Type | Description |
|---|---|---|
num_nodes |
int
|
Total number of nodes |
num_edges |
int
|
Total number of edges |
is_weighted |
bool
|
Whether graph has edge weights |
is_multigraph |
bool
|
Whether graph allows parallel edges |
is_directed |
bool
|
Whether graph is directed |
max_degree |
int
|
Maximum node degree |
min_degree |
int
|
Minimum node degree |
avg_degree |
float
|
Average node degree |
density |
float
|
Graph density |
Decomposition¶
kcores ¶
K-core decomposition (degree-based and, for weighted graphs, strength-based).
compute_kcores ¶
compute_kcores(
graph: Graph,
config: DecompositionConfig | None = None,
weighted: bool | None = None,
p_function: list[float] | None = None,
) -> DecompositionResult
Compute k-core decomposition of a graph.
For unweighted graphs, uses NetworkX's built-in k-core algorithm. For weighted graphs, computes strength-based decomposition using p-function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
Input graph |
required |
config
|
Optional[DecompositionConfig]
|
Decomposition configuration |
None
|
weighted
|
Optional[bool]
|
Force the weighted (strength-based) or unweighted algorithm. |
None
|
p_function
|
Optional[list[float]]
|
Strength interval boundaries to reuse instead of building them from this
graph (the C++ |
None
|
Returns:
| Type | Description |
|---|---|
DecompositionResult
|
K-core decomposition results with shell indices |
Examples:
>>> G = nx.karate_club_graph()
>>> result = compute_kcores(G)
>>> print(f"Max core: {result.max_index}")
read_custom_intervals ¶
read_custom_intervals(path: Path | None) -> list[float]
Read the p-function boundaries of strength_intervals = custom from a file.
One boundary per line, as the C++ -strengthsIntervalsFile. A positive strength
in (0, p1] gets index 1, (p(i-1), pi] index i, and anything above pn
the last index n; a strength of exactly 0 (isolated node) gets index 0, like
every other interval method.
find_components_by_shell ¶
find_components_by_shell(
graph: Graph, decomposition: DecompositionResult
) -> DecompositionResult
Find connected components for each shell index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
Original graph |
required |
decomposition
|
DecompositionResult
|
K-core decomposition result |
required |
Returns:
| Type | Description |
|---|---|
DecompositionResult
|
Updated result with component information |
kdenses ¶
K-dense (m-core) decomposition by triangle-pair peeling.
This ports graph_kdenses.cpp / graph_triangled_kcores.cpp from the C++ LaNet-vi.
The C++ builds a dual graph whose vertices are the edges of the input graph and whose
edges join the three sides of every triangle, then peels it: whenever a dual vertex is
removed, the two other sides of each triangle it belonged to lose one triangle. That is
the k-truss decomposition of the input graph. The k-dense index of an edge is its
trussness (2 for edges in no triangle, 3 for edges in a triangle whose sides are
in no other triangle, ...) and the k-dense index of a vertex is the maximum over its
incident edges.
compute_kdenses ¶
compute_kdenses(graph: Graph) -> DecompositionResult
Compute the k-dense decomposition of a graph.
Parallel edges and self-loops are ignored, as in the C++ reader.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
Input graph (must be undirected) |
required |
Returns:
| Type | Description |
|---|---|
DecompositionResult
|
|
Notes
The k-dense index of an edge is 2 + s where s is its truss support number:
the largest s such that the edge belongs to a subgraph in which every edge closes
at least s triangles. The k-dense index of a vertex is the maximum k-dense index
of its incident edges, or 2 if it has none.
Examples:
>>> G = nx.karate_club_graph()
>>> result = compute_kdenses(G)
>>> print(f"Max dense index: {result.max_index}")
Max dense index: 5
find_components_by_dense ¶
find_components_by_dense(
graph: Graph, decomposition: DecompositionResult
) -> DecompositionResult
Find connected components for each dense index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
Original graph |
required |
decomposition
|
DecompositionResult
|
K-dense decomposition result |
required |
Returns:
| Type | Description |
|---|---|
DecompositionResult
|
Updated result with component information |
dcores ¶
D-core decomposition for directed graphs.
D-cores extend k-cores to directed graphs by considering both in-degree and out-degree. Each node is assigned a (k_in, k_out) pair indicating its core membership based on incoming and outgoing edges separately.
Each direction is peeled independently: k_in is the largest k such that the node
belongs to the subgraph where every node has in-degree >= k, and k_out likewise for
out-degree. compute_dcore_table gives the full (k, l)-core table of Giatsidis et al.
(for every out-degree threshold l, the largest k with the node in the (k, l)-core), the
dcores_list.txt of the C++ 4.0.0 driver.
compute_dcores ¶
compute_dcores(
graph: DiGraph,
config: DecompositionConfig | None = None,
) -> DecompositionResult
Compute d-core decomposition for directed graphs.
Each node receives a (k_in, k_out) core number pair based on: - k_in: maximum k such that node has in-degree >= k in the k-in-core - k_out: maximum k such that node has out-degree >= k in the k-out-core
Self-loops are ignored (they count for neither degree).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
DiGraph
|
Directed input graph |
required |
config
|
Optional[DecompositionConfig]
|
Decomposition configuration |
None
|
Returns:
| Type | Description |
|---|---|
DecompositionResult
|
D-core decomposition results with (k_in, k_out) pairs |
Raises:
| Type | Description |
|---|---|
ValueError
|
If graph is not directed |
Examples:
>>> G = nx.DiGraph()
>>> G.add_edges_from([(0, 1), (1, 2), (2, 0)])
>>> result = compute_dcores(G)
>>> result.node_indices[0] # max(k_in, k_out), used for layout/coloring
1
>>> result.metadata["d_cores"][0] # full (k_in, k_out) pair for node 0
(1, 1)
Notes
For undirected graphs, use compute_kcores instead.
compute_dcore_table ¶
compute_dcore_table(
graph: DiGraph,
) -> dict[int, dict[int, int]]
Compute the (k, l)-core table of a directed graph.
The (k, l)-core (Giatsidis, Thilikos & Vazirgiannis, 2011) is the largest subgraph
in which every node has in-degree >= k and out-degree >= l. For every l from 0 up to
the last non-empty (0, l)-core, the table gives each node of that core the largest k
such that the node belongs to the (k, l)-core. Row 0 is the in-core number
(k_in of compute_dcores); a node absent from a row is not in the (0, l)-core.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
DiGraph
|
Directed input graph |
required |
Returns:
| Type | Description |
|---|---|
Dict[int, Dict[int, int]]
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the graph is not directed |
Examples:
>>> G = nx.DiGraph([(0, 1), (1, 2), (2, 0), (2, 3)])
>>> table = compute_dcore_table(G)
>>> sorted(table[0].items()) # in-core numbers
[(0, 1), (1, 1), (2, 1), (3, 1)]
>>> sorted(table[1].items()) # node 3 has out-degree 0: not in the (0, 1)-core
[(0, 1), (1, 1), (2, 1)]
Notes
This is what the C++ 4.0.0 driver computed for a directed graph (-directed): it
wrote the table to dcores_list.txt as node k l lines and drew nothing. That
code kept a node in the in-degree peeling after its out-degree had dropped below l,
so it reported a larger k than the definition for such nodes; this implementation
follows the definition (a node leaves the (k, l)-core as soon as either degree
fails), which is checked against a brute-force one in the tests.
find_components_by_dcore ¶
find_components_by_dcore(
graph: DiGraph, decomposition: DecompositionResult
) -> DecompositionResult
Find weakly connected components within each d-core level.
Nodes are grouped by max(k_in, k_out), the same value stored in
decomposition.node_indices and used by the layout, so the resulting
components line up with the rings drawn for the other decompositions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
DiGraph
|
Directed input graph |
required |
decomposition
|
DecompositionResult
|
D-core decomposition result |
required |
Returns:
| Type | Description |
|---|---|
DecompositionResult
|
Updated decomposition with component information |
kconnectivity ¶
K-connectivity of the k-core clusters (the C++ -kconn).
Port of computeKConnectivityWide / computeKConnectivityStrict and their
conditions from graph_kcores_components.cpp, the method of Beiró, Alvarez-Hamelin
& Busch, A low complexity visualization tool that helps to perform complex systems
analysis, New J. Phys. 10 (2008) 125003: a lower bound of the edge connectivity of
the nodes of each shell, built from the clusters of the nested component tree.
A cluster is a connected piece of a shell inside a component (the ones the layout
draws, in the same order). The walk grows a k-connected set C from the top
shell down:
- Seed. From the top shell down, the first cluster
Qwhose induced subgraph has diameter at most 2 (wide: or a minimum edge cut of at least the shell index; strict: and a minimum degree of at least the shell index) joinsCwith k-connectivity equal to its shell index. - Growth. For every remaining shell
kfrom the top down, a clusterQjoinsCwith k-connectivitykwhen the graph ofQwithCcontracted to a single vertex has diameter at most 2 (skipped fork = 1) and either at leastknodes ofQtouchC, or all of them do, orphi(Q) >= kwithphithe sum over the nodes ofQofmin(max(1, |N(v) ∩ NotB2|), |N(v) ∩ C|), whereNotB2holds the nodes ofQwith fewer than two neighbors inC. Each shell's clusters are examined once, in order, withCgrowing as clusters are accepted.
The two types differ in what happens to the clusters that are skipped: wide (the
C++ default) keeps them pending and tries them again at every lower k (so a node
can end up with a k-connectivity below its shell index); strict drops them.
Nodes that never join C have k-connectivity 0 and are drawn black on white / white
on black (as squares in the bw and bwi schemes), as the C++ did.
Deliberate differences from the C++: shells with no clusters are skipped (the C++
dereferenced an empty list there); the shell == 77 escape in the strict seed is a
debugging leftover and is not reproduced; self-loops are ignored.
diameter_at_most_two ¶
diameter_at_most_two(
graph: Graph, nodes: Iterable[int]
) -> bool
Whether the subgraph induced by nodes has diameter at most 2 (connected).
cluster_conditions ¶
cluster_conditions(
graph: Graph,
cluster: list[int],
k: int,
kconnectivity: Mapping[int, int],
) -> bool
Test whether cluster may join the k-connected set at k (conditions).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
The network |
required |
cluster
|
List[int]
|
Nodes of the cluster |
required |
k
|
int
|
The shell index being processed ( |
required |
kconnectivity
|
Mapping[int, int]
|
Current k-connectivity of every node; non-zero means "in |
required |
Returns:
| Type | Description |
|---|---|
bool
|
Whether the contracted diameter and the frontier / |
compute_kconnectivity ¶
compute_kconnectivity(
graph: Graph,
clusters_by_shell: Mapping[int, list[list[int]]],
kind: str = "wide",
) -> dict[int, int]
K-connectivity of every node from the clusters of the k-core component tree.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
The network (undirected, simple, unweighted: the C++ refused anything else) |
required |
clusters_by_shell
|
Mapping[int, List[List[int]]]
|
The clusters of each shell index in the order of the component tree walk
(:func: |
required |
kind
|
str
|
|
'wide'
|
Returns:
| Type | Description |
|---|---|
Dict[int, int]
|
K-connectivity of every node of the graph; 0 for the nodes that are not k-connected |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> root = build_component_tree(G, cores, edge_index, rng)
>>> kconn = compute_kconnectivity(G, clusters_by_index(root))
Layout¶
lanet_layout ¶
The LaNet-vi placement: nested components, neighbor-based rho, clique sectors.
Port of kcores_component.cpp / graph_kcores_components.cpp from the C++ LaNet-vi
(-coordDistributionAlgorithm classic, the default), following the formulas of
Alvarez-Hamelin, Dall'Asta, Barrat & Vespignani, k-core decomposition: a tool for the
visualization of large scale networks (NIPS 2005):
- The graph is split recursively into nested components: the connected components of
the subgraph with index
> kinside a component of indexk. A component of indexkowns the clusters of its nodes with index exactlyk(connected inside the shell) and its children (indexk + 1). - Each component gets a center, a radius (
ratio) and a scale (u): the top core of a branch has a radius proportional to the root of the sum of its squared log-degrees, every enclosing shell adds one unit (part 1); children are placed inside their parent atrho = 1 - size / siblings,phi = 2 pi (previous / siblings)^2and scaled bysqrt(size / siblings) / delta(formulas (3)-(5), part 2). - A node of index
ksits atrho = ratio (1 - eps) + eps ratio * average, whereaveragemeasures how deep its higher-index neighbors are (formula (1)), and at the circular average of the angles of those neighbors (already placed). Top cores are split into cliques laid along U-shaped paths in angular sectors (formula (2)).
K-dense and d-core results go through the same placement with their own edge index
for the component tree (an edge belongs to the inner component when its index is above
the component's: for k-cores the minimum of its endpoints' indices, for k-dense the
edge's own dense index, as kdenses_component.cpp walks it).
The pow and log coordinate distributions (findCoordinatesModern) replace step
2: the whole network is a disc of radius 1; inside a component of radius ratio the
children share a disc of radius R ((sqrt((T - S) / T))^0.25 ratio capped at
0.96 ratio for pow, sqrt((size - shell) / size) ratio for log, with T
and S the sums of squared log-degrees of the component and of its own shell), packed
as non-overlapping discs whose areas follow their sum(log(1 + d)^(2 / beta)) weight
(:func:lanet_vi.visualization.layout.distribute_components); the nodes of the shell
sit on the ring between R and ratio by formula (1). The k-dense variant of
kdenses_component.cpp (the only placement the C++ had for k-dense) shrinks by a fixed
0.92, counts the neighbors of the same index in formula (1), scales epsilon by
tau = (ratio - R) / ratio and auto-adjusts the ratioConstant of the node radii.
LayoutComponent
dataclass
¶
LayoutComponent(
index: int,
parent: LayoutComponent | None = None,
clusters: list[list[int]] = list(),
children: list[LayoutComponent] = list(),
size: int = 0,
shell_cardinal: int = 0,
rho: float = -1.0,
phi: float = 0.0,
x: float = 0.0,
y: float = 0.0,
u: float = 1.0,
ratio: float = 1.0,
central_core_k: int = 0,
central_core_ratio: float = 1.0,
end_ratio: float = 0.0,
t_log2_degree: float = 0.0,
shell_t_log2_degree: float = 0.0,
t_log_beta_degree: float = 0.0,
)
A component of the nested decomposition with its placement (KCores_Component).
walk ¶
walk() -> list[LayoutComponent]
Return this component and all its descendants, parents first.
LayoutParameters
dataclass
¶
LayoutParameters(
epsilon: float = 0.18,
delta: float = 1.3,
gamma: float = 1.5,
u: float = 1.0,
no_cliques: bool = False,
weighted: bool = False,
coord_distribution: str = "classic",
alpha: float = 0.3,
beta: float = 1.0,
dense: bool = False,
ratio_constant: float | None = None,
)
The C++ parameters that shape the picture.
LanetLayout
dataclass
¶
LanetLayout(
positions: dict[int, tuple[float, float]],
root: LayoutComponent,
frame: float,
ratio_constant: float = 1.0,
)
Result of :func:compute_lanet_layout.
RadiusLaw
dataclass
¶
RadiusLaw(
max_degree: int,
max_strength: float = 0.0,
weighted: bool = False,
modern: bool = False,
dense: bool = False,
ratio_constant: float = 1.0,
)
Node radius in layout units for the active placement (computeHostRatio).
classic uses :func:node_radius. The pow / log modes use
0.007 ratioConstant log(1 + d)^1.5 (graphics_kcores.cpp), or
ratioConstant sqrt(log(1 + d)) for k-dense (graphics_kdenses.cpp, whose
radii only ever went with that placement); on weighted graphs both use
ratioConstant log(1 + s) / log(s_max) (times 0.007 for k-cores), with the same
fallback to the degree law as :func:strength_radii.
circular_average ¶
circular_average(
a: float, weight_a: float, b: float, weight_b: float
) -> float
Weighted average of two angles along the shorter arc (Circular_average).
place_in_circular_sector ¶
place_in_circular_sector(
ratio: float,
alfa: float,
n: int,
total: int,
random: float,
unique: bool,
) -> tuple[float, float]
Position n of total along a U-shaped path in a sector (placeInCircularSector).
The path goes out along the radius, along the arc of aperture alfa and back in;
with unique (the clique is the whole core) only the arc is used.
build_component_tree ¶
build_component_tree(
graph: Graph,
node_index: dict[int, int],
edge_index: Callable[[int, int], int],
rng: Generator,
) -> LayoutComponent
Nested components and clusters of a decomposition (computeComponents).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
The network |
required |
node_index
|
Dict[int, int]
|
Shell / dense index of every node |
required |
edge_index
|
Callable[[int, int], int]
|
Index of an edge: the component of index |
required |
rng
|
Generator
|
Source of the random cluster order |
required |
Returns:
| Type | Description |
|---|---|
LayoutComponent
|
The root (index 0, all nodes) |
clusters_by_index ¶
clusters_by_index(
root: LayoutComponent,
) -> dict[int, list[list[int]]]
Collect the clusters of every index in tree-walk order (buildClustersMap).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root
|
LayoutComponent
|
The component tree of :func: |
required |
Returns:
| Type | Description |
|---|---|
Dict[int, List[List[int]]]
|
Clusters keyed by their component's index, parents' clusters before children's; indices without clusters are absent |
component_tree ¶
component_tree(
graph: Graph,
node_index: dict[int, int],
edge_index: Callable[[int, int], int] | None = None,
seed: int = 0,
) -> tuple[LayoutComponent, Generator]
Build the component tree exactly as :func:compute_lanet_layout starts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
The network |
required |
node_index
|
Dict[int, int]
|
Shell / dense index of every node |
required |
edge_index
|
Callable[[int, int], int]
|
Index of an edge; defaults to the minimum of its endpoints' indices (k-cores) |
None
|
seed
|
int
|
Seed of the random generator (the C++ |
0
|
Returns:
| Type | Description |
|---|---|
Tuple[LayoutComponent, Generator]
|
The root and the generator after the tree's draws, to hand both to
:func: |
compute_lanet_layout ¶
compute_lanet_layout(
graph: Graph,
node_index: dict[int, int],
params: LayoutParameters,
seed: int = 0,
edge_index: Callable[[int, int], int] | None = None,
tree: tuple[LayoutComponent, Generator] | None = None,
) -> LanetLayout
Place every node with the LaNet-vi algorithm.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
The network (undirected view is used for neighborhoods) |
required |
node_index
|
Dict[int, int]
|
Shell / dense index of every node |
required |
params
|
LayoutParameters
|
|
required |
seed
|
int
|
Seed of the random generator (the C++ |
0
|
edge_index
|
Callable[[int, int], int]
|
Index of an edge; defaults to the minimum of its endpoints' indices (k-cores) |
None
|
tree
|
Tuple[LayoutComponent, Generator]
|
A tree already built by :func: |
None
|
Returns:
| Type | Description |
|---|---|
LanetLayout
|
Node positions, the component tree (centers, radii, scales) and the frame size |
strength_radii ¶
strength_radii(weighted: bool, max_strength: float) -> bool
Whether node radii follow the strength law (weighted and a usable maximum).
node_radius ¶
node_radius(
degree: int,
max_degree: int,
strength: float = 0.0,
max_strength: float = 0.0,
weighted: bool = False,
) -> float
Node radius in layout units (computeHostRatio, classic mode).
The strength law needs log(max_strength) > 0; when every strength is at most 1
(the C++ would divide by zero or a negative number) the degree law is used instead,
see :func:strength_radii.
layout ¶
Circle packing of sibling components (the C++ distribute_components.cpp).
Used by the pow / log coordinate distributions of :mod:lanet_layout: the child
components of a component are packed as non-overlapping discs inside its disc, each with
an area that grows with its weight, and the discs are inflated step by step until they no
longer fit.
packing_radii ¶
packing_radii(
radius: float,
weights: ndarray,
alpha: float,
beta: float,
log_mode: bool,
) -> ndarray
Radii of the discs for normalized weights: R sqrt(alpha w^beta).
In log mode the weight enters as log(1 + w).
distribute_components ¶
distribute_components(
x0: float,
y0: float,
radius: float,
weights: ndarray,
alpha: float,
beta: float,
log_mode: bool,
seed: int,
) -> tuple[ndarray, ndarray, ndarray]
Pack len(weights) discs inside the disc of radius radius at (x0, y0).
Port of distribute_components: every disc starts with radius
R sqrt(alpha w^beta) (w the normalized weight) at a random spot; discs
outside the container or overlapping another are moved to a random free spot (up to
10 N tries each, the smaller of an overlapping pair first); while every disc could
be settled, alpha grows by 1 % and the discs are inflated in place (pulled
towards the center by the radius increase); when a disc cannot be settled the last
round is undone by dividing alpha by 1.1.
The C++ re-seeded its generator with -seed on every call, so every packing of the
same weights is the same; that is reproduced with a fresh generator per call.
Deliberate deviations in the inflation step: the C++ pulled the discs towards the
origin of the picture, not the container center (harmless for the root, wrong for
nested components, which the next round pushed back at random), and it overwrote
x before computing y from it, so the pull skewed the angle; here the discs
keep their angle and are pulled towards (x0, y0).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x0
|
float
|
Center of the container |
required |
y0
|
float
|
Center of the container |
required |
radius
|
float
|
Radius of the container |
required |
weights
|
ndarray
|
One positive weight per disc (normalized here) |
required |
alpha
|
float
|
Constant and exponent of the disc area law |
required |
beta
|
float
|
Constant and exponent of the disc area law |
required |
log_mode
|
bool
|
|
required |
seed
|
int
|
Seed of the random generator |
required |
Returns:
| Type | Description |
|---|---|
(x, y, r) : tuple of np.ndarray
|
Centers and radii of the discs |
Rendering¶
matplotlib_renderer ¶
Matplotlib-based renderer for network visualization.
The picture is laid out as the C++ SVG writer did (svg.cpp, graphics_kcores.cpp
generateNetworkFile): the viewport is the layout's frame, scaled uniformly to the
requested pixel size and centered; edges are drawn under the nodes in increasing index
order; the color and degree legends are drawn in layout units in the margins of the
frame so they scale with the picture.
render_network ¶
render_network(
graph: Graph,
layout: VisualizationLayout,
decomposition: DecompositionResult,
config: VisualizationConfig,
output_path: Path | str,
node_names: dict[int, str] | None = None,
*,
custom_colors: bool = False,
measure: MeasureType = MCORE,
communities: CommunityResult | None = None,
community_config: CommunityConfig | None = None,
) -> None
Render network visualization using matplotlib.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
Network graph |
required |
layout
|
VisualizationLayout
|
Layout with node positions and visual properties |
required |
decomposition
|
DecompositionResult
|
Decomposition results |
required |
config
|
VisualizationConfig
|
Visualization configuration |
required |
output_path
|
Union[Path, str]
|
Output file path (.png, .pdf, .svg) |
required |
node_names
|
Optional[Dict[int, str]]
|
Node names for the labels ( |
None
|
custom_colors
|
bool
|
Nodes were colored from a colors file: the color legend is not drawn (the C++
hid it with |
False
|
measure
|
MeasureType
|
Labels of the k-dense legend: |
MCORE
|
communities
|
Optional[CommunityResult]
|
Communities to outline under the network: a translucent convex hull
( |
None
|
community_config
|
Optional[CommunityConfig]
|
Which overlays to draw and how (defaults when None) |
None
|
Examples:
>>> render_network(G, layout, decomp, config, "output.png")
select_visible_edges ¶
select_visible_edges(
graph: Graph,
config: VisualizationConfig,
seed: int | None = None,
) -> list[tuple[int, int]]
Select the edges to draw: an independent Bernoulli draw per edge, as the C++ did.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
Network graph |
required |
config
|
VisualizationConfig
|
|
required |
seed
|
int | None
|
Seed of the draw, so the same seed gives the same picture |
None
|
Returns:
| Type | Description |
|---|---|
List[Tuple[int, int]]
|
Edges to render, in the graph's order |
Notes
Every edge is kept with probability max(edges_percent, min_edges / E)
(graphics_kcores.cpp addCluster), so about that fraction of the edges is
drawn, spread over all shells in proportion to their edge counts.
colors ¶
Color scales of the C++ LaNet-vi (types.cpp, graphics_kcores.cpp).
The rainbow runs magenta -> blue -> cyan -> green -> yellow -> red, so the maximum index
is red; the black-and-white scale runs white -> gray -> black, so the maximum index is
black. The stop lists are the C++ ones verbatim: some stops lie outside [0, 1]
(white is 2.0, the blackwhite positions are -0.75 and 1.23) so that
the extreme indices saturate; the results are clamped as the SVG writer did.
get_color_scale ¶
get_color_scale(
scheme: ColorScheme,
) -> list[tuple[RGB, float]]
Get the color stops of a color scheme.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scheme
|
ColorScheme
|
Color scheme to use |
required |
Returns:
| Type | Description |
|---|---|
list[tuple[RGB, float]]
|
|
interpolate_color ¶
interpolate_color(
color1: RGB, color2: RGB, alpha: float
) -> RGB
Linearly interpolate between two colors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
color1
|
RGB
|
First color (R, G, B) |
required |
color2
|
RGB
|
Second color (R, G, B) |
required |
alpha
|
float
|
Interpolation factor (0.0 = color1, 1.0 = color2) |
required |
Returns:
| Type | Description |
|---|---|
RGB
|
Interpolated color |
clamp_color ¶
clamp_color(color: RGB) -> RGB
Clamp every channel to [0, 1] (the SVG writer saturated at 255).
scale_color ¶
scale_color(color: RGB, factor: float) -> RGB
Multiply every channel by factor and clamp (edge and luminosity shading).
default_node_color ¶
default_node_color(background: BackgroundColor) -> RGB
Color of a node absent from the colors file: white on black, black on white.
compute_shell_color ¶
compute_shell_color(
shell_index: int,
max_shell_index: int,
color_scheme: ColorScheme,
color_scale_max: int | None = None,
*,
background: BackgroundColor = BLACK,
dense: bool = False,
) -> RGB
Compute the color of a shell or dense index (computeHostColorByShellIndex).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
shell_index
|
int
|
Shell or dense index of the node |
required |
max_shell_index
|
int
|
Maximum shell/dense index in the network |
required |
color_scheme
|
ColorScheme
|
Color scheme to use |
required |
color_scale_max
|
int | None
|
Index shown with the last color of the scale ( |
None
|
background
|
BackgroundColor
|
Background of the picture; only k-dense colors depend on it |
BLACK
|
dense
|
bool
|
Apply the k-dense rules of |
False
|
Returns:
| Type | Description |
|---|---|
RGB
|
RGB color, every channel in |
Notes
Position on the scale is (min(i, max) - 1) / (max - 1) for col and bw;
bwi interlaces even and odd indices over the two halves of the scale. A single
shell (max == 1) is red. With col, consecutive shells alternate a luminosity
of 0.7 and 1.2 so neighboring rings stay distinguishable.
Examples:
>>> compute_shell_color(5, 5, ColorScheme.COLOR) # top shell: red, luminosity 1.2
(1.0, 0.24, 0.24)
create_matplotlib_colormap ¶
create_matplotlib_colormap(
color_scheme: ColorScheme, n_colors: int = 256
) -> LinearSegmentedColormap
Create a matplotlib colormap from a color scheme.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
color_scheme
|
ColorScheme
|
Color scheme to use |
required |
n_colors
|
int
|
Number of discrete colors in the colormap |
256
|
Returns:
| Type | Description |
|---|---|
LinearSegmentedColormap
|
Matplotlib colormap over |
Input and output¶
readers ¶
Input/output functions for reading network data.
read_edge_list ¶
read_edge_list(
file_path: Path | str,
weighted: bool = False,
directed: bool = False,
multigraph: bool = False,
delimiter: str | None = None,
comment: str = "#",
) -> Graph
Read an edge list file and create a NetworkX graph.
Lines hold source target [weight] separated by whitespace (or by
delimiter if given). Behavior follows the C++ LaNet-vi reader: an
unused third column is ignored, a missing weight on a weighted graph counts
as 1.0, and self-loops are dropped (with a warning) because the
decompositions do not accept them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file_path
|
Union[Path, str]
|
Path to the edge list file (supports .txt, .gz, .bz2) |
required |
weighted
|
bool
|
Whether edges have weights (third column) |
False
|
directed
|
bool
|
Whether to create a directed graph |
False
|
multigraph
|
bool
|
Whether to allow parallel edges |
False
|
delimiter
|
Optional[str]
|
Column delimiter; |
None
|
comment
|
str
|
Comment character to skip lines |
'#'
|
Returns:
| Type | Description |
|---|---|
Graph
|
NetworkX graph constructed from the edge list |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the file has fewer than two columns or non-integer node ids |
Examples:
>>> g = read_edge_list("network.txt", weighted=True)
>>> g = read_edge_list("network.txt.bz2", weighted=False, directed=True)
read_caida_snapshot ¶
read_caida_snapshot(
url: str, timeout: int = 30
) -> tuple[Graph, DataFrame]
Fetch and parse CAIDA AS-Relationships data.
This function downloads a CAIDA AS-relationships snapshot in bz2 format, decompresses it, and creates both a NetworkX graph and a pandas DataFrame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
URL to the CAIDA .as-rel.txt.bz2 file |
required |
timeout
|
int
|
Request timeout in seconds |
30
|
Returns:
| Name | Type | Description |
|---|---|---|
graph |
Graph
|
NetworkX graph with AS relationships |
dataframe |
DataFrame
|
DataFrame with columns: provider, customer, relationship_type |
Raises:
| Type | Description |
|---|---|
RequestException
|
If the download fails |
ValueError
|
If decompression or parsing fails |
Examples:
>>> url = "https://publicdata.caida.org/.../20251001.as-rel.txt.bz2"
>>> graph, df = read_caida_snapshot(url)
read_node_names ¶
read_node_names(
file_path: Path | str,
delimiter: str | None = None,
comment: str = "#",
) -> dict[int, str]
Read node names from a file.
Each line is node_id name; the name is everything after the first
separator, so it may contain spaces. Surrounding quotes are removed, as in
the C++ reader.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file_path
|
Union[Path, str]
|
Path to file with node names (format: node_id name) |
required |
delimiter
|
Optional[str]
|
Separator between the id and the name; |
None
|
comment
|
str
|
Comment character |
'#'
|
Returns:
| Type | Description |
|---|---|
Dict[int, str]
|
Mapping from node ID to node name |
Examples:
>>> names = read_node_names("nodes.txt")
>>> names[42]
'node_name_42'
read_node_colors ¶
read_node_colors(
file_path: Path | str,
delimiter: str | None = None,
comment: str = "#",
) -> dict[int, tuple[float, float, float]]
Read node colors from a file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file_path
|
Union[Path, str]
|
Path to file with node colors (format: node_id r g b) RGB values should be in range [0.0, 1.0] |
required |
delimiter
|
Optional[str]
|
Column delimiter; |
None
|
comment
|
str
|
Comment character |
'#'
|
Returns:
| Type | Description |
|---|---|
Dict[int, Tuple[float, float, float]]
|
Mapping from node ID to (r, g, b) tuple |
Examples:
>>> colors = read_node_colors("colors.txt")
>>> colors[42]
(1.0, 0.0, 0.0) # Red
writers ¶
Output functions for writing decomposition results and visualizations.
write_decomposition_csv ¶
write_decomposition_csv(
result: DecompositionResult, output_path: Path | str
) -> None
Export decomposition results to CSV.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
DecompositionResult
|
Decomposition result to export |
required |
output_path
|
Union[Path, str]
|
Output file path |
required |
Examples:
>>> write_decomposition_csv(decomp_result, "cores.csv")
write_dcore_table ¶
write_dcore_table(
table: dict[int, dict[int, int]], file_path: Path | str
) -> None
Write a (k, l)-core table as the C++ dcores_list.txt.
One node k l line per node and out-degree threshold l, after a # node k l
header, sorted by l then node.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
Dict[int, Dict[int, int]]
|
|
required |
file_path
|
Union[Path, str]
|
Output path |
required |
Examples:
>>> write_dcore_table(compute_dcore_table(G), "dcores_list.txt")
write_kconnectivity ¶
write_kconnectivity(
kconnectivity: dict[int, int],
node_indices: dict[int, int],
file_path: Path | str,
) -> None
Write the k-connectivity of every node as the C++ log/kconn.log.
One node shell_index k_connectivity line per node after a
# node shell_index k_connectivity header, sorted by shell index then node, as
the C++ walked its cores list; 0 marks a node that is not k-connected.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kconnectivity
|
Dict[int, int]
|
|
required |
node_indices
|
Dict[int, int]
|
Shell index of every node ( |
required |
file_path
|
Union[Path, str]
|
Output path |
required |
Examples:
>>> write_kconnectivity(net.kconnectivity, net.decomposition.node_indices, "kconn.txt")
write_decomposition_json ¶
write_decomposition_json(
result: DecompositionResult,
output_path: Path | str,
include_components: bool = True,
include_metadata: bool = True,
) -> None
Export decomposition results to JSON with enhanced details.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
DecompositionResult
|
Decomposition result to export |
required |
output_path
|
Union[Path, str]
|
Output file path |
required |
include_components
|
bool
|
Include component information (default: True) |
True
|
include_metadata
|
bool
|
Include metadata (default: True) |
True
|
Examples:
>>> write_decomposition_json(decomp_result, "cores.json")
>>> write_decomposition_json(decomp_result, "cores_full.json", include_components=True)
write_node_attributes ¶
write_node_attributes(
node_data: dict[int, dict], output_path: Path | str
) -> None
Export node attributes to CSV.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_data
|
Dict[int, Dict]
|
Mapping from node ID to attribute dictionary |
required |
output_path
|
Union[Path, str]
|
Output file path |
required |
Examples:
>>> attrs = {1: {"shell": 3, "x": 0.5, "y": 0.3}, 2: {"shell": 2, "x": 0.2, "y": 0.1}}
>>> write_node_attributes(attrs, "nodes.csv")
write_graph_json ¶
write_graph_json(
graph: Graph,
output_path: Path | str,
include_node_attrs: bool = True,
include_edge_attrs: bool = True,
) -> None
Export NetworkX graph to JSON using node-link format.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
NetworkX graph to export |
required |
output_path
|
Union[Path, str]
|
Output file path |
required |
include_node_attrs
|
bool
|
Include node attributes (default: True) |
True
|
include_edge_attrs
|
bool
|
Include edge attributes (default: True) |
True
|
Examples:
>>> import networkx as nx
>>> G = nx.karate_club_graph()
>>> write_graph_json(G, "karate.json")
Notes
Uses NetworkX's node-link JSON format, which is compatible with D3.js and other visualization libraries.
write_community_json ¶
write_community_json(
community_result: CommunityResult,
output_path: Path | str,
) -> None
Export community detection results to JSON.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
community_result
|
CommunityResult
|
Community detection result |
required |
output_path
|
Union[Path, str]
|
Output file path |
required |
Examples:
>>> from lanet_vi.community import detect_communities_louvain
>>> communities = detect_communities_louvain(G)
>>> write_community_json(communities, "communities.json")
write_edge_list ¶
write_edge_list(
graph: Graph,
output_path: Path | str,
include_weights: bool = True,
delimiter: str = " ",
) -> None
Write graph to edge list file using pandas.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
NetworkX graph |
required |
output_path
|
Union[Path, str]
|
Output file path |
required |
include_weights
|
bool
|
Include edge weights if available (default: True) |
True
|
delimiter
|
str
|
Column delimiter (default: space, the format |
' '
|
Examples:
>>> write_edge_list(G, "network.txt")
config_loader ¶
Configuration loading from YAML files.
load_config_from_yaml ¶
load_config_from_yaml(file_path: Path | str) -> LaNetConfig
Load LaNet-vi configuration from a YAML file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file_path
|
Union[Path, str]
|
Path to YAML configuration file |
required |
Returns:
| Type | Description |
|---|---|
LaNetConfig
|
Validated configuration object |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If the configuration file doesn't exist |
YAMLError
|
If the YAML file is malformed |
ValidationError
|
If the configuration values are invalid |
Examples:
>>> config = load_config_from_yaml("config.yaml")
>>> net = Network(graph, config)
>>> net.decompose()
>>> net.visualize("output.png")
read_config_yaml ¶
read_config_yaml(file_path: Path | str) -> dict[str, Any]
Read a YAML configuration file into a plain dictionary without validating it.
Used by the CLI to merge the file with explicit command-line flags before a single validation pass.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file_path
|
Union[Path, str]
|
Path to the YAML file |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Nested dictionary as written in the file (empty if the file is empty) |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If the file does not exist |
ValueError
|
If the top level of the file is not a mapping |
save_config_to_yaml ¶
save_config_to_yaml(
config: LaNetConfig, file_path: Path | str
) -> None
Save LaNet-vi configuration to a YAML file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
LaNetConfig
|
Configuration object to save |
required |
file_path
|
Union[Path, str]
|
Path where YAML file should be saved |
required |
Examples:
>>> config = LaNetConfig()
>>> config.visualization.width = 1920
>>> save_config_to_yaml(config, "my_config.yaml")
Generators¶
generators ¶
Random graph generation utilities.
This module provides functions for generating various types of random graphs for testing, benchmarking, and demonstration purposes.
generate_barabasi_albert ¶
generate_barabasi_albert(
n: int, m: int, seed: int | None = None
) -> Graph
Generate Barabási-Albert scale-free network.
Creates a random graph using preferential attachment. The graph exhibits a power-law degree distribution, common in real-world networks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Number of nodes |
required |
m
|
int
|
Number of edges to attach from a new node to existing nodes |
required |
seed
|
Optional[int]
|
Random seed for reproducibility |
None
|
Returns:
| Type | Description |
|---|---|
Graph
|
Generated scale-free graph |
Examples:
>>> G = generate_barabasi_albert(n=1000, m=3, seed=42)
>>> G.number_of_nodes()
1000
Notes
The Barabási-Albert model produces graphs with: - Power-law degree distribution P(k) ~ k^(-γ) where γ ≈ 3 - High clustering coefficient - Short average path length (small-world property)
This model is useful for simulating social networks, the internet, and citation networks.
generate_erdos_renyi ¶
generate_erdos_renyi(
n: int,
p: float | None = None,
m: int | None = None,
seed: int | None = None,
directed: bool = False,
) -> Graph
Generate Erdős-Rényi random graph.
Creates a random graph using either G(n,p) or G(n,m) model: - G(n,p): n nodes, each edge exists with probability p - G(n,m): n nodes, exactly m edges
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Number of nodes |
required |
p
|
Optional[float]
|
Probability of edge creation (for G(n,p) model). Must be in [0,1]. Either p or m must be specified, but not both. |
None
|
m
|
Optional[int]
|
Number of edges (for G(n,m) model). Either p or m must be specified, but not both. |
None
|
seed
|
Optional[int]
|
Random seed for reproducibility |
None
|
directed
|
bool
|
If True, generate directed graph (default: False) |
False
|
Returns:
| Type | Description |
|---|---|
Graph or DiGraph
|
Generated random graph |
Raises:
| Type | Description |
|---|---|
ValueError
|
If neither p nor m is specified, or if both are specified |
Examples:
>>> # G(n,p) model: 100 nodes, 10% edge probability
>>> G = generate_erdos_renyi(n=100, p=0.1, seed=42)
>>> G.number_of_nodes()
100
>>> # G(n,m) model: 100 nodes, exactly 500 edges
>>> G = generate_erdos_renyi(n=100, m=500, seed=42)
>>> G.number_of_edges()
500
Notes
Uses NetworkX's fast_gnp_random_graph and gnm_random_graph functions. For large dense graphs, G(n,m) model may be faster.
generate_powerlaw_cluster ¶
generate_powerlaw_cluster(
n: int, m: int, p: float, seed: int | None = None
) -> Graph
Generate Holme-Kim powerlaw cluster graph.
Creates a scale-free graph with higher clustering than Barabási-Albert, using triangle formation mechanism.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Number of nodes |
required |
m
|
int
|
Number of random edges to add for each new node |
required |
p
|
float
|
Probability of adding a triangle after adding a random edge (0 ≤ p ≤ 1) |
required |
seed
|
Optional[int]
|
Random seed for reproducibility |
None
|
Returns:
| Type | Description |
|---|---|
Graph
|
Generated powerlaw cluster graph |
Examples:
>>> G = generate_powerlaw_cluster(n=1000, m=3, p=0.5, seed=42)
>>> G.number_of_nodes()
1000
Notes
The Holme-Kim model extends Barabási-Albert to include: - Power-law degree distribution (like BA) - Higher clustering coefficient (via triangle formation)
This model better represents real networks where clustering matters, such as social networks and collaboration networks.
generate_watts_strogatz ¶
generate_watts_strogatz(
n: int, k: int, p: float, seed: int | None = None
) -> Graph
Generate Watts-Strogatz small-world network.
Creates a random graph with small-world properties: high clustering and short average path length.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Number of nodes |
required |
k
|
int
|
Each node is connected to k nearest neighbors in ring topology |
required |
p
|
float
|
Probability of rewiring each edge (0 ≤ p ≤ 1) - p=0: regular ring lattice - p=1: random graph - 0<p<1: small-world network |
required |
seed
|
Optional[int]
|
Random seed for reproducibility |
None
|
Returns:
| Type | Description |
|---|---|
Graph
|
Generated small-world graph |
Examples:
>>> G = generate_watts_strogatz(n=1000, k=6, p=0.3, seed=42)
>>> G.number_of_nodes()
1000
Notes
The Watts-Strogatz model interpolates between: - Regular lattice (p=0): high clustering, long path length - Random graph (p=1): low clustering, short path length - Small-world (intermediate p): high clustering AND short path length
This model is useful for modeling social networks and neural networks.
Metrics¶
metrics ¶
Network metrics and information theory measures.
This module provides various metrics for analyzing network structure, including mutual information, entropy, and similarity measures.
compute_mutual_information ¶
compute_mutual_information(
partition1: dict[int, int], partition2: dict[int, int]
) -> float
Compute mutual information between two partitions.
Mutual information I(X;Y) measures how much knowing one partition tells us about the other: I(X;Y) = Σᵢ Σⱼ p(i,j) log₂(p(i,j) / (p(i)p(j)))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition1
|
Dict[int, int]
|
First partition (node ID -> cluster ID) |
required |
partition2
|
Dict[int, int]
|
Second partition (node ID -> cluster ID) |
required |
Returns:
| Type | Description |
|---|---|
float
|
Mutual information in bits |
Raises:
| Type | Description |
|---|---|
ValueError
|
If partitions have different node sets |
Examples:
>>> p1 = {0: 0, 1: 0, 2: 1, 3: 1}
>>> p2 = {0: 0, 1: 0, 2: 1, 3: 1} # Identical partition
>>> mi = compute_mutual_information(p1, p2)
>>> mi # Should equal entropy of either partition
1.0
>>> p2 = {0: 1, 1: 0, 2: 1, 3: 0} # Completely different
>>> mi = compute_mutual_information(p1, p2)
>>> mi # Should be close to 0
0.0
Notes
- MI = 0 when partitions are independent
- MI = H(X) = H(Y) when partitions are identical
- MI ≤ min(H(X), H(Y))
compute_normalized_mutual_information ¶
compute_normalized_mutual_information(
partition1: dict[int, int],
partition2: dict[int, int],
method: str = "arithmetic",
) -> float
Compute normalized mutual information (NMI) between two partitions.
NMI normalizes MI to [0, 1] range by dividing by entropy: - NMI = 0: partitions are independent - NMI = 1: partitions are identical
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition1
|
Dict[int, int]
|
First partition |
required |
partition2
|
Dict[int, int]
|
Second partition |
required |
method
|
str
|
Normalization method (default: "arithmetic") - "arithmetic": NMI = 2*I(X;Y) / (H(X) + H(Y)) - "geometric": NMI = I(X;Y) / sqrt(H(X) * H(Y)) - "min": NMI = I(X;Y) / min(H(X), H(Y)) - "max": NMI = I(X;Y) / max(H(X), H(Y)) |
'arithmetic'
|
Returns:
| Type | Description |
|---|---|
float
|
Normalized mutual information in [0, 1] |
Examples:
>>> p1 = {0: 0, 1: 0, 2: 1, 3: 1}
>>> p2 = {0: 0, 1: 0, 2: 1, 3: 1} # Identical
>>> nmi = compute_normalized_mutual_information(p1, p2)
>>> nmi
1.0
>>> p2 = {0: 0, 1: 1, 2: 2, 3: 3} # Different
>>> nmi = compute_normalized_mutual_information(p1, p2)
>>> nmi < 0.5
True
Notes
The choice of normalization affects the score: - Arithmetic mean is most common in literature - Geometric mean is symmetric - Min/max provide bounds on the score
compute_partition_entropy ¶
compute_partition_entropy(
partition: dict[int, int],
) -> float
Compute Shannon entropy of a partition.
The entropy H(X) measures the uncertainty in the partition: H(X) = -Σ p(x) log₂ p(x)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition
|
Dict[int, int]
|
Mapping from node ID to cluster/community ID |
required |
Returns:
| Type | Description |
|---|---|
float
|
Entropy in bits |
Examples:
>>> partition = {0: 0, 1: 0, 2: 1, 3: 1} # 2 clusters, balanced
>>> entropy = compute_partition_entropy(partition)
>>> entropy # Should be close to 1.0 bit
1.0
Notes
- Maximum entropy occurs when all clusters have equal size
- Minimum entropy (0) occurs when all nodes in one cluster
- Uses log base 2, so entropy is measured in bits
compare_decompositions ¶
compare_decompositions(
decomp1: DecompositionResult,
decomp2: DecompositionResult,
) -> dict[str, float]
Compare two network decompositions.
Compares decompositions as partitions where each k-core/k-dense level is treated as a cluster.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
decomp1
|
DecompositionResult
|
First decomposition |
required |
decomp2
|
DecompositionResult
|
Second decomposition |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, float]
|
Similarity metrics (see compare_partitions) |
Examples:
>>> from lanet_vi.decomposition import compute_kcores
>>> decomp1 = compute_kcores(G1)
>>> decomp2 = compute_kcores(G2)
>>> metrics = compare_decompositions(decomp1, decomp2)
Notes
Useful for comparing: - k-core vs k-dense on same graph - Same decomposition on different graphs - Community detection vs structural decomposition
compare_partitions ¶
compare_partitions(
partition1: dict[int, int], partition2: dict[int, int]
) -> dict[str, float]
Compare two partitions using multiple similarity metrics.
Computes several metrics for comprehensive comparison: - Normalized Mutual Information (NMI) - Adjusted Rand Index (ARI) - Variation of Information (VI)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition1
|
Dict[int, int]
|
First partition |
required |
partition2
|
Dict[int, int]
|
Second partition |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, float]
|
Dictionary with similarity metrics: - "nmi": Normalized Mutual Information [0, 1] - "ari": Adjusted Rand Index [-1, 1] - "vi": Variation of Information (lower is better) - "num_clusters_1": Number of clusters in partition 1 - "num_clusters_2": Number of clusters in partition 2 |
Examples:
>>> p1 = {0: 0, 1: 0, 2: 1, 3: 1}
>>> p2 = {0: 0, 1: 0, 2: 1, 3: 1}
>>> metrics = compare_partitions(p1, p2)
>>> metrics["nmi"]
1.0
>>> metrics["ari"]
1.0
compute_adjusted_rand_index ¶
compute_adjusted_rand_index(
partition1: dict[int, int], partition2: dict[int, int]
) -> float
Compute Adjusted Rand Index (ARI) between two partitions.
ARI measures similarity between two clusterings, adjusted for chance: - ARI = 1: partitions are identical - ARI = 0: partitions are independent (random) - ARI < 0: worse than random
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition1
|
Dict[int, int]
|
First partition (node ID -> cluster ID) |
required |
partition2
|
Dict[int, int]
|
Second partition (node ID -> cluster ID) |
required |
Returns:
| Type | Description |
|---|---|
float
|
Adjusted Rand Index in [-1, 1] |
Raises:
| Type | Description |
|---|---|
ValueError
|
If partitions have different node sets |
Examples:
>>> p1 = {0: 0, 1: 0, 2: 1, 3: 1}
>>> p2 = {0: 0, 1: 0, 2: 1, 3: 1} # Identical
>>> ari = compute_adjusted_rand_index(p1, p2)
>>> ari
1.0
>>> p2 = {0: 0, 1: 1, 2: 0, 3: 1} # Different
>>> ari = compute_adjusted_rand_index(p1, p2)
>>> ari < 1.0
True
Notes
Uses sklearn's implementation. ARI is widely used for comparing clustering algorithms and community detection methods.
Community detection¶
community ¶
Community detection algorithms for network analysis.
This module provides community detection functionality including:
- Louvain algorithm for modularity optimization
- Greedy modularity maximization
- detect_communities, which runs the algorithm named in a CommunityConfig
The overlays that draw a CommunityResult on a picture live in
lanet_vi.visualization.community_viz.
Community ¶
Community(**data: Any)
Bases: BaseModel
Represents a single community in a network.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
int
|
Unique community identifier |
nodes |
List[int]
|
List of node IDs in this community |
size |
int
|
Number of nodes in the community |
Initialize community and compute size if not provided.
CommunityResult ¶
CommunityResult(**data: Any)
Bases: BaseModel
Results from community detection algorithm.
Attributes:
| Name | Type | Description |
|---|---|---|
algorithm |
str
|
Name of the community detection algorithm used |
communities |
List[Community]
|
List of detected communities |
node_to_community |
Dict[int, int]
|
Mapping from node ID to community ID |
num_communities |
int
|
Total number of communities detected |
modularity |
float
|
Modularity score of the partition (if applicable) |
Initialize community result and compute derived fields.
get_community ¶
get_community(community_id: int) -> Community | None
Get a community by its ID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
community_id
|
int
|
Community identifier |
required |
Returns:
| Type | Description |
|---|---|
Community | None
|
The community if found, None otherwise |
get_node_community ¶
get_node_community(node: int) -> int | None
Get the community ID for a given node.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node
|
int
|
Node identifier |
required |
Returns:
| Type | Description |
|---|---|
int | None
|
Community ID if node is found, None otherwise |
get_community_sizes ¶
get_community_sizes() -> dict[int, int]
Get the size of each community.
Returns:
| Type | Description |
|---|---|
Dict[int, int]
|
Mapping from community ID to size |
detect_communities_greedy_modularity ¶
detect_communities_greedy_modularity(
graph: Graph,
weight: str | None = "weight",
resolution: float = 1.0,
) -> CommunityResult
Detect communities using greedy modularity maximization.
This is an alternative to Louvain that may be faster on some graphs but typically produces lower modularity scores.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
Input graph (undirected) |
required |
weight
|
Optional[str]
|
Edge attribute to use as weight (default: "weight") |
'weight'
|
resolution
|
float
|
Resolution parameter for modularity (default: 1.0). Higher values lead to more communities. |
1.0
|
Returns:
| Type | Description |
|---|---|
CommunityResult
|
Detected communities with modularity score |
Notes
Uses NetworkX's community.greedy_modularity_communities.
detect_communities_louvain ¶
detect_communities_louvain(
graph: Graph,
weight: str | None = "weight",
resolution: float = 1.0,
seed: int | None = None,
) -> CommunityResult
Detect communities using the Louvain algorithm.
The Louvain method is a greedy optimization algorithm that attempts to maximize the modularity of a partition of the network. It works on both weighted and unweighted graphs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
Input graph (undirected). For directed graphs, the graph will be converted to undirected first. |
required |
weight
|
Optional[str]
|
Edge attribute to use as weight (default: "weight"). Set to None for unweighted graphs. |
'weight'
|
resolution
|
float
|
Resolution parameter for modularity (default: 1.0). Higher values lead to more communities. |
1.0
|
seed
|
Optional[int]
|
Random seed for reproducibility (default: None) |
None
|
Returns:
| Type | Description |
|---|---|
CommunityResult
|
Detected communities with modularity score |
Examples:
>>> G = nx.karate_club_graph()
>>> result = detect_communities_louvain(G)
>>> result.num_communities
4
>>> result.modularity > 0.4
True
Notes
This function uses NetworkX's community.louvain_communities which implements
the algorithm from:
Blondel, V.D. et al. "Fast unfolding of communities in large networks."
Journal of Statistical Mechanics (2008).
detect_communities ¶
detect_communities(
graph: Graph,
config: CommunityConfig,
seed: int | None = None,
) -> CommunityResult
Run the community detection algorithm named in config.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Graph
|
Input graph; directed graphs are analyzed as their undirected version |
required |
config
|
CommunityConfig
|
|
required |
seed
|
Optional[int]
|
Random seed of the Louvain algorithm (greedy modularity is deterministic) |
None
|
Returns:
| Type | Description |
|---|---|
CommunityResult
|
Detected communities with modularity score |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
community_viz ¶
Community visualization utilities.
This module provides functions for visualizing network communities, including: - Coloring nodes by community membership - Drawing community boundaries - Creating community-based color palettes
get_community_colors ¶
get_community_colors(
num_communities: int, colormap: str = "tab20"
) -> list[tuple[float, float, float]]
Generate distinct colors for communities.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_communities
|
int
|
Number of communities to generate colors for |
required |
colormap
|
str
|
Matplotlib colormap name (default: "tab20", 20 distinct colors) |
'tab20'
|
Returns:
| Type | Description |
|---|---|
List[Tuple[float, float, float]]
|
List of RGB color tuples, one per community id (0-based) |
Notes
A qualitative colormap (tab10, tab20, Set3, ...) is used entry by entry,
so up to its size no two communities share a color; with more communities than
entries the colors are spread evenly over hsv instead. A continuous colormap
(viridis, hsv, ...) is sampled evenly over its range. The default tab20
alternates a dark and a light shade of each hue, so up to 10 communities take the
dark shades (tab10) and stay telling apart.
assign_node_colors_by_community ¶
assign_node_colors_by_community(
community_result: CommunityResult,
node_positions: dict[int, tuple[float, float]],
colormap: str = "tab20",
) -> dict[int, tuple[float, float, float]]
Assign colors to nodes based on their community membership.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
community_result
|
CommunityResult
|
Community detection result |
required |
node_positions
|
Dict[int, Tuple[float, float]]
|
Node positions (used to determine which nodes to color) |
required |
colormap
|
str
|
Matplotlib colormap name |
'tab20'
|
Returns:
| Type | Description |
|---|---|
Dict[int, Tuple[float, float, float]]
|
Mapping from node ID to RGB color tuple |
Examples:
>>> from lanet_vi.community import detect_communities_louvain
>>> import networkx as nx
>>> G = nx.karate_club_graph()
>>> communities = detect_communities_louvain(G)
>>> positions = nx.spring_layout(G)
>>> colors = assign_node_colors_by_community(communities, positions)
draw_community_boundaries ¶
draw_community_boundaries(
ax: Axes,
community_result: CommunityResult,
node_positions: dict[int, tuple[float, float]],
alpha: float = 0.2,
linewidth: float = 2.0,
colormap: str = "tab20",
zorder: float = 0.0,
) -> None
Draw convex hull boundaries around communities.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ax
|
Axes
|
Matplotlib axes to draw on |
required |
community_result
|
CommunityResult
|
Community detection result |
required |
node_positions
|
Dict[int, Tuple[float, float]]
|
Node positions in 2D space |
required |
alpha
|
float
|
Transparency of the boundary fill (default: 0.2) |
0.2
|
linewidth
|
float
|
Width of the boundary line (default: 2.0) |
2.0
|
colormap
|
str
|
Matplotlib colormap name |
'tab20'
|
zorder
|
float
|
Drawing order of the hulls (default 0: behind the edges and nodes) |
0.0
|
Notes
Only draws boundaries for communities with 3 or more nodes (required for convex hull).
draw_community_circles ¶
draw_community_circles(
ax: Axes,
community_result: CommunityResult,
node_positions: dict[int, tuple[float, float]],
padding: float = 0.1,
alpha: float = 0.15,
linewidth: float = 2.0,
colormap: str = "tab20",
zorder: float = 0.0,
) -> None
Draw circles around communities based on their bounding box.
This is an alternative to convex hulls that works better for small communities.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ax
|
Axes
|
Matplotlib axes to draw on |
required |
community_result
|
CommunityResult
|
Community detection result |
required |
node_positions
|
Dict[int, Tuple[float, float]]
|
Node positions in 2D space |
required |
padding
|
float
|
Extra padding around community as fraction of radius (default: 0.1) |
0.1
|
alpha
|
float
|
Transparency of the circle fill (default: 0.15) |
0.15
|
linewidth
|
float
|
Width of the circle line (default: 2.0) |
2.0
|
colormap
|
str
|
Matplotlib colormap name |
'tab20'
|
zorder
|
float
|
Drawing order of the circles (default 0: behind the edges and nodes) |
0.0
|