mlnlib 0.2.39__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- ### MIT License
1
+ MIT License
2
2
 
3
3
  Copyright (c) 2024 Eszter Bokányi
4
4
 
@@ -12,25 +12,6 @@ furnished to do so, subject to the following conditions:
12
12
  The above copyright notice and this permission notice shall be included in all
13
13
  copies or substantial portions of the Software.
14
14
 
15
- In any academic work, the package should be cited as follows:
16
-
17
- Bokányi, E., Kazmina, Y., & de Jong, R. (2023). POPNET MultiLayerNetwork Python Class (Version v2.0) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.10838865
18
-
19
- The BibTeX entry for the citation is:
20
- ```
21
- @software{bokanyi2023popnet,
22
- title = {{{POPNET MultiLayerNetwork Python Class}}},
23
- author = {Bok\'anyi, Eszter and Kazmina, Yuliia and family=Jong, given=Rachel, prefix=de, useprefix=true},
24
- date = {2023},
25
- doi = {10.5281/zenodo.10838865},
26
- url = {https://zenodo.org/doi/10.5281/zenodo.10838865},
27
- urldate = {2024-11-11},
28
- abstract = {Library for working with large multilayer networks by the POPNET group.},
29
- organization = {Zenodo},
30
- version = {v2.0}
31
- }
32
- ```
33
-
34
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
mlnlib-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,282 @@
1
+ Metadata-Version: 2.4
2
+ Name: mlnlib
3
+ Version: 0.3.0
4
+ Summary: Memory-efficient library for loading and working with large unweighted multilayer networks.
5
+ Author: Rachel G. de Jong, Yuliia Kazmina, Frank W. Takes
6
+ Author-email: Eszter Bokányi <e.bokanyi@liacs.leidenuniv.nl>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/planet-nl/mlnlib
9
+ Project-URL: Issues, https://github.com/planet-nl/mlnlib/issues
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE.md
15
+ Requires-Dist: pandas>=1.4.4
16
+ Requires-Dist: polars>=0.20.6
17
+ Requires-Dist: numpy>=1.20.3
18
+ Requires-Dist: python-igraph>=0.9.9
19
+ Requires-Dist: networkx>=3.0
20
+ Requires-Dist: scipy>=1.10.1
21
+ Dynamic: license-file
22
+
23
+ # mlnlib
24
+
25
+ Memory-efficient Python tools by the [PLANET-NL](https://planetnl.org) team (formerly POPNET) for working with large multilayer networks on machines with limited memory/cores.
26
+
27
+ Authors:
28
+ * Eszter Bokányi
29
+ * Rachel de Jong
30
+ * Yuliia Kazmina
31
+ * Frank W. Takes
32
+
33
+ Contact: `e.bokanyi@liacs.leidenuniv.nl`
34
+
35
+ License: MIT (see `LICENSE.md`). Please attribute appropriately when reusing.
36
+
37
+ ## Overview
38
+
39
+ `mlnlib` provides the `MultiLayerNetwork` class for building, loading, manipulating, and exporting multilayer networks where edges can exist on multiple layers. Layers are encoded efficiently in a single sparse adjacency matrix using a binary encoding (powers of two).
40
+
41
+ Key components stored in a `MultiLayerNetwork` instance:
42
+ - `nodes`: pandas DataFrame (or polars when enabled) with at least columns `label` and `id`
43
+ - `A`: scipy.sparse.csr_matrix with binary layer encoding in its data
44
+ - `layers`: pandas DataFrame with at least columns `layer`, `label`, and `binary`; optionally `group`
45
+
46
+ ## Install
47
+
48
+ From PyPI:
49
+
50
+ ```bash
51
+ pip install mlnlib
52
+ ```
53
+
54
+ ## Loading data
55
+
56
+ There are three supported ways to initialize a `MultiLayerNetwork` (class defined in `src/mlnlib/mln.py`).
57
+
58
+ 1) Load a prepared library from disk
59
+
60
+ Folder layout must contain at least:
61
+ - `edges.npz` — NxN csr_matrix with binary encoded layers
62
+ - `nodes.csv.gz` or `nodes_{pd.__version__}.pkl` — nodes DataFrame with `label` and `id`
63
+ - `layers.csv` — layer definitions with `layer`, `label`, `binary` (and optional `group`)
64
+
65
+ ```python
66
+ from mlnlib import MultiLayerNetwork
67
+
68
+ mln = MultiLayerNetwork(
69
+ load_from_library=True,
70
+ library_path="/path/to/library"
71
+ )
72
+ ```
73
+
74
+ 2) Build from raw CSVs via a config
75
+
76
+ Uses `RawCSVtoMLN` behind the scenes (see `src/mlnlib/preparation.py` and `test/config.json` for an example schema).
77
+
78
+ ```python
79
+ from mlnlib import MultiLayerNetwork
80
+
81
+ mln = MultiLayerNetwork(
82
+ load_from_config=True,
83
+ config_path="config.json"
84
+ )
85
+ ```
86
+
87
+ 3) Provide in-memory objects
88
+
89
+ ```python
90
+ from mlnlib import MultiLayerNetwork
91
+ from scipy.sparse import csr_matrix
92
+ import pandas as pd
93
+
94
+ # nodes must have columns: label, id (IDs are 0..N-1)
95
+ nodes = pd.DataFrame({
96
+ "label": ["a","b","c"],
97
+ "id": [0,1,2]
98
+ })
99
+
100
+ # layers dataframe: at least layer, label, binary
101
+ layers = pd.DataFrame({
102
+ "layer": [0,1],
103
+ "label": ["friendship","kinship"],
104
+ "binary": [1,2]
105
+ })
106
+
107
+ # A is NxN csr_matrix whose data encodes layers as sums of powers of two
108
+ A = csr_matrix((3,3), dtype="int64")
109
+
110
+ mln = MultiLayerNetwork(nodes=nodes, edges=A, layers=layers)
111
+ ```
112
+
113
+ ## Binary layer encoding
114
+
115
+ Each layer is assigned a power-of-two value: 1, 2, 4, 8, ... When an edge exists on multiple layers, the corresponding entry in `A` stores the sum, enabling fast bitwise checks. For example, if `A[i,j] = 7`, then layers 0, 1, and 2 are present because `7 = 1 + 2 + 4`. Checking for layer 1 is a cheap mask: `7 & 2 == 2`.
116
+
117
+ Binary encoding is stored in `self.layers`.
118
+
119
+ ## Common tasks
120
+
121
+ - Convert node identifiers
122
+ - `mln.to_id("person_123")` or `mln.to_label(42)`
123
+ - Filter by nodes/layers/groups
124
+ - `mln.get_filtered_network(nodes_selected=[...], layers_selected=[...], groups_selected=[...], node_type="label", layer_type="label")`
125
+ - Per-layer adjacency
126
+ - `mln.get_layer_adjacency_matrix(layer="friendship", layer_type="label", store=True, dtype="int8")`
127
+ - Supra-adjacency matrix
128
+ - `mln.get_supra_adjacency_matrix(dtype="int8")`
129
+ - Edgelist export
130
+ - `mln.get_edgelist(edge_attribute="label")` # options: "binary", "layer", "label", "weight", or None
131
+ - Degrees and clustering
132
+ - `mln.get_degrees([...])`, `mln.get_clustering_coefficient([...])`
133
+ - Excess closure
134
+ - `mln.get_excess_closure(selected_nodes=[...], selected_layers=[...], layer_type="label")`
135
+ - Convert to graph libraries
136
+ - `mln.to_igraph(directed=True, edge_attributes=True, node_attributes=False, edge_attribute_type="binary")`
137
+ - `mln.to_networkx(directed=True, edge_attributes=True, node_attributes=False, edge_attribute_type="binary")`
138
+
139
+ ## Saving and exporting
140
+
141
+ Save a prepared library for fast future loads:
142
+
143
+ ```python
144
+ mln.save(path="/path/to/library", overwrite=False)
145
+ # creates edges.npz, nodes.csv.gz (or pickle), layers.csv
146
+ ```
147
+
148
+ Export individual pieces:
149
+ - Edges: `mln.export_edges("edges.npz" | "edges.csv" | "edges.csv.gz")`
150
+ - Nodes: `mln.export_nodes("nodes.csv" | "nodes.csv.gz")`
151
+ - Layers: `mln.export_layers("layers.csv")`
152
+ - GraphML: `mln.save_to_graphml("network.graphml", edge_attribute_type="binary")`
153
+
154
+ ## Data structures
155
+
156
+ - Nodes (`mln.nodes`): DataFrame with at least `label` and `id`. You may add arbitrary node attributes. Preferred mapping helpers are `to_id()` and `to_label()`.
157
+ - Layers (`mln.layers`): DataFrame with columns:
158
+ - `layer`: arbitrary ID, can be of any type
159
+ - `label`: human-readable name
160
+ - `label_long` : longer description
161
+ - `binary`: power-of-two encoding
162
+ - `group` (optional): used for grouped aggregations
163
+ - Adjacency (`mln.A`): csr_matrix whose data contains the sum of layer powers-of-two. Use `mln.get_binary_adjacency()` to collapse to 0/1.
164
+
165
+ ## API Reference
166
+
167
+ ### Core Methods
168
+
169
+ #### Node ID mapping (preferred interface)
170
+ - `to_id(labels: Any | List[Any]) -> int | List[int]`
171
+ Convert node label(s) to integer ID(s).
172
+
173
+ - `to_label(ids: int | List[int]) -> Any | List[Any]`
174
+ Convert integer node ID(s) to label(s).
175
+
176
+ #### Filtering and subgraphs
177
+ - `get_filtered_network(nodes_selected=None, layers_selected=[], groups_selected=[], node_type="label", layer_type="label", keep_node_alignment=False) -> MultiLayerNetwork`
178
+ Return a filtered subnetwork based on node and/or layer selection.
179
+
180
+ - `get_egonetwork(ego_label, depth=1, return_list=False, ignore_limit=False) -> MultiLayerNetwork | List`
181
+ Extract ego network centered on a node at specified depth.
182
+
183
+ #### Layer-specific adjacency
184
+ - `get_layer_adjacency_matrix(layer, layer_type="layer", store=False, dtype="int64") -> csr_matrix`
185
+ Create binary adjacency matrix for a single layer or group.
186
+
187
+ - `get_binary_adjacency(dtype="int64") -> csr_matrix`
188
+ Convert multilayer adjacency to binary (0/1) format, collapsing all layer information.
189
+
190
+ - `get_supra_adjacency_matrix(dtype="int64") -> csr_matrix`
191
+ Build supra-adjacency matrix (L*N × L*N) with inter-layer couplings.
192
+
193
+ - `get_grouped_mln() -> MultiLayerNetwork`
194
+ Return network with layers aggregated by group (requires `group` column in `layers`).
195
+
196
+ - `clear_layer_adjacency_matrices()`, `clear_group_adjacency_matrices()`, `clear_all_adjacency_matrices()`
197
+ Clear cached adjacency matrices.
198
+
199
+ #### Edge lists and conversions
200
+ - `get_edgelist(edge_attribute="binary") -> DataFrame`
201
+ Convert sparse adjacency matrix to edgelist. Options: "binary", "layer", "label", "weight", or None.
202
+
203
+ - `convert_layer_representation(layer, input_type="layer", output_type="binary") -> Any | List[Any]`
204
+ Convert between layer representations: "layer", "label", "binary".
205
+
206
+ - `convert_layer_binary_to_list(num, output_type="layer") -> List`
207
+ Decode binary integer to list of active layers.
208
+
209
+ #### Graph library exports
210
+ - `to_igraph(directed=True, edge_attributes=True, node_attributes=False, replace_igraph=False, edge_attribute_type="binary") -> ig.Graph`
211
+ Create igraph object. Options for `edge_attribute_type`: "binary", "weight".
212
+
213
+ - `to_networkx(directed=True, edge_attributes=True, node_attributes=False, edge_attribute_type="binary", layer_type="layer", ignore_limit=False) -> nx.Graph | nx.DiGraph | None`
214
+ Create NetworkX object (limited by `nx_node_limit` unless `ignore_limit=True`).
215
+
216
+ - `save_to_graphml(file_name, directed=True, edge_attributes=True, node_attributes=False, overwrite=False, edge_attribute_type="binary")`
217
+ Save as GraphML for external tools (Gephi, etc.).
218
+
219
+ #### Metrics and analysis
220
+ - `get_degrees(selected_nodes=[]) -> Dict[Any, int]`
221
+ Calculate degree for selected nodes across all layers.
222
+
223
+ - `get_clustering_coefficient(selected_nodes=[], batchsize=100000) -> Dict[Any, float]`
224
+ Compute clustering coefficient for selected nodes.
225
+
226
+ - `get_excess_closure(selected_nodes=[], node_type="label", selected_layers=[], layer_type="layer", batchsize=100000) -> Dict[str, Dict]`
227
+ Calculate excess closure metric (returns both clustering coefficient and excess closure).
228
+
229
+ #### Node operations
230
+ - `add_nodes(nodes_df: pd.DataFrame)`
231
+ Add new nodes without connections (expands adjacency matrix).
232
+
233
+ - `remove_nodes(nodes: List[Any])`
234
+ Remove nodes and their edges; reindexes remaining nodes.
235
+
236
+ #### Aggregation
237
+ - `create_affiliation_matrix(key, affil_edgelist: List[Tuple])`
238
+ Create bipartite affiliation matrix for node-to-entity relationships.
239
+
240
+ - `get_aggregated_network(aggregation_column=None, keep_layers=False, convert_A="none") -> MultiLayerNetwork`
241
+ Aggregate network by node attribute (e.g., region, age group).
242
+
243
+ #### Persistence
244
+ - `save(path="", overwrite=False, **kwargs)`
245
+ Save MLN library (edges.npz, nodes.csv.gz, layers.csv, optional codebook).
246
+
247
+ - `export_edges(file_name)`, `export_nodes(file_name)`, `export_layers(file_name)`, `export_codebook(file_name)`
248
+ Export individual components.
249
+
250
+ - `load(path) -> Tuple[DataFrame, csr_matrix, DataFrame]`
251
+ Load MLN components from disk.
252
+
253
+ #### Utilities
254
+ - `report_time(message="", init=False)`
255
+ Helper for timing measurements (requires `verbose=True`).
256
+
257
+ ### RawCSVtoMLN (preparation.py)
258
+
259
+ Utility class for converting raw CSV node/edge files into MLN library format.
260
+
261
+ - `__init__(node_conf, edge_conf, layer_conf, output_folder, **kwargs)`
262
+ Configure input/output paths and column mappings.
263
+
264
+ - `init_all()`
265
+ Run full pipeline: init_layers() → init_nodes() → init_edges() → read_all_edges() → save_all() (if configured).
266
+
267
+ - `save_all(overwrite=False)`
268
+ Save nodes.csv.gz, edges.npz, and layers.csv to output folder.
269
+
270
+ See `test/config.json` for configuration schema.
271
+
272
+ ## Notes
273
+
274
+ - NetworkX conversion limits very large graphs (`nx_node_limit` in code). Use igraph for larger networks.
275
+ - The package supports pandas by default and optional polars interop for CSV loading and some operations.
276
+ - The network can be directed; layer information is encoded in the adjacency matrix when `adjacency_element="binary"`.
277
+
278
+ ## Citation
279
+
280
+ If you use this library in academic work, please cite this repository and acknowledge the PLANET-NL project (https://planetnl.org). Citation metadata is available in `CITATION.cff`, and GitHub shows it under "Cite this repository" in the sidebar.
281
+
282
+ A software paper describing `mlnlib` is in preparation (see [`paper/`](paper/)). We will update the citation information once it is published.
mlnlib-0.3.0/README.md ADDED
@@ -0,0 +1,260 @@
1
+ # mlnlib
2
+
3
+ Memory-efficient Python tools by the [PLANET-NL](https://planetnl.org) team (formerly POPNET) for working with large multilayer networks on machines with limited memory/cores.
4
+
5
+ Authors:
6
+ * Eszter Bokányi
7
+ * Rachel de Jong
8
+ * Yuliia Kazmina
9
+ * Frank W. Takes
10
+
11
+ Contact: `e.bokanyi@liacs.leidenuniv.nl`
12
+
13
+ License: MIT (see `LICENSE.md`). Please attribute appropriately when reusing.
14
+
15
+ ## Overview
16
+
17
+ `mlnlib` provides the `MultiLayerNetwork` class for building, loading, manipulating, and exporting multilayer networks where edges can exist on multiple layers. Layers are encoded efficiently in a single sparse adjacency matrix using a binary encoding (powers of two).
18
+
19
+ Key components stored in a `MultiLayerNetwork` instance:
20
+ - `nodes`: pandas DataFrame (or polars when enabled) with at least columns `label` and `id`
21
+ - `A`: scipy.sparse.csr_matrix with binary layer encoding in its data
22
+ - `layers`: pandas DataFrame with at least columns `layer`, `label`, and `binary`; optionally `group`
23
+
24
+ ## Install
25
+
26
+ From PyPI:
27
+
28
+ ```bash
29
+ pip install mlnlib
30
+ ```
31
+
32
+ ## Loading data
33
+
34
+ There are three supported ways to initialize a `MultiLayerNetwork` (class defined in `src/mlnlib/mln.py`).
35
+
36
+ 1) Load a prepared library from disk
37
+
38
+ Folder layout must contain at least:
39
+ - `edges.npz` — NxN csr_matrix with binary encoded layers
40
+ - `nodes.csv.gz` or `nodes_{pd.__version__}.pkl` — nodes DataFrame with `label` and `id`
41
+ - `layers.csv` — layer definitions with `layer`, `label`, `binary` (and optional `group`)
42
+
43
+ ```python
44
+ from mlnlib import MultiLayerNetwork
45
+
46
+ mln = MultiLayerNetwork(
47
+ load_from_library=True,
48
+ library_path="/path/to/library"
49
+ )
50
+ ```
51
+
52
+ 2) Build from raw CSVs via a config
53
+
54
+ Uses `RawCSVtoMLN` behind the scenes (see `src/mlnlib/preparation.py` and `test/config.json` for an example schema).
55
+
56
+ ```python
57
+ from mlnlib import MultiLayerNetwork
58
+
59
+ mln = MultiLayerNetwork(
60
+ load_from_config=True,
61
+ config_path="config.json"
62
+ )
63
+ ```
64
+
65
+ 3) Provide in-memory objects
66
+
67
+ ```python
68
+ from mlnlib import MultiLayerNetwork
69
+ from scipy.sparse import csr_matrix
70
+ import pandas as pd
71
+
72
+ # nodes must have columns: label, id (IDs are 0..N-1)
73
+ nodes = pd.DataFrame({
74
+ "label": ["a","b","c"],
75
+ "id": [0,1,2]
76
+ })
77
+
78
+ # layers dataframe: at least layer, label, binary
79
+ layers = pd.DataFrame({
80
+ "layer": [0,1],
81
+ "label": ["friendship","kinship"],
82
+ "binary": [1,2]
83
+ })
84
+
85
+ # A is NxN csr_matrix whose data encodes layers as sums of powers of two
86
+ A = csr_matrix((3,3), dtype="int64")
87
+
88
+ mln = MultiLayerNetwork(nodes=nodes, edges=A, layers=layers)
89
+ ```
90
+
91
+ ## Binary layer encoding
92
+
93
+ Each layer is assigned a power-of-two value: 1, 2, 4, 8, ... When an edge exists on multiple layers, the corresponding entry in `A` stores the sum, enabling fast bitwise checks. For example, if `A[i,j] = 7`, then layers 0, 1, and 2 are present because `7 = 1 + 2 + 4`. Checking for layer 1 is a cheap mask: `7 & 2 == 2`.
94
+
95
+ Binary encoding is stored in `self.layers`.
96
+
97
+ ## Common tasks
98
+
99
+ - Convert node identifiers
100
+ - `mln.to_id("person_123")` or `mln.to_label(42)`
101
+ - Filter by nodes/layers/groups
102
+ - `mln.get_filtered_network(nodes_selected=[...], layers_selected=[...], groups_selected=[...], node_type="label", layer_type="label")`
103
+ - Per-layer adjacency
104
+ - `mln.get_layer_adjacency_matrix(layer="friendship", layer_type="label", store=True, dtype="int8")`
105
+ - Supra-adjacency matrix
106
+ - `mln.get_supra_adjacency_matrix(dtype="int8")`
107
+ - Edgelist export
108
+ - `mln.get_edgelist(edge_attribute="label")` # options: "binary", "layer", "label", "weight", or None
109
+ - Degrees and clustering
110
+ - `mln.get_degrees([...])`, `mln.get_clustering_coefficient([...])`
111
+ - Excess closure
112
+ - `mln.get_excess_closure(selected_nodes=[...], selected_layers=[...], layer_type="label")`
113
+ - Convert to graph libraries
114
+ - `mln.to_igraph(directed=True, edge_attributes=True, node_attributes=False, edge_attribute_type="binary")`
115
+ - `mln.to_networkx(directed=True, edge_attributes=True, node_attributes=False, edge_attribute_type="binary")`
116
+
117
+ ## Saving and exporting
118
+
119
+ Save a prepared library for fast future loads:
120
+
121
+ ```python
122
+ mln.save(path="/path/to/library", overwrite=False)
123
+ # creates edges.npz, nodes.csv.gz (or pickle), layers.csv
124
+ ```
125
+
126
+ Export individual pieces:
127
+ - Edges: `mln.export_edges("edges.npz" | "edges.csv" | "edges.csv.gz")`
128
+ - Nodes: `mln.export_nodes("nodes.csv" | "nodes.csv.gz")`
129
+ - Layers: `mln.export_layers("layers.csv")`
130
+ - GraphML: `mln.save_to_graphml("network.graphml", edge_attribute_type="binary")`
131
+
132
+ ## Data structures
133
+
134
+ - Nodes (`mln.nodes`): DataFrame with at least `label` and `id`. You may add arbitrary node attributes. Preferred mapping helpers are `to_id()` and `to_label()`.
135
+ - Layers (`mln.layers`): DataFrame with columns:
136
+ - `layer`: arbitrary ID, can be of any type
137
+ - `label`: human-readable name
138
+ - `label_long` : longer description
139
+ - `binary`: power-of-two encoding
140
+ - `group` (optional): used for grouped aggregations
141
+ - Adjacency (`mln.A`): csr_matrix whose data contains the sum of layer powers-of-two. Use `mln.get_binary_adjacency()` to collapse to 0/1.
142
+
143
+ ## API Reference
144
+
145
+ ### Core Methods
146
+
147
+ #### Node ID mapping (preferred interface)
148
+ - `to_id(labels: Any | List[Any]) -> int | List[int]`
149
+ Convert node label(s) to integer ID(s).
150
+
151
+ - `to_label(ids: int | List[int]) -> Any | List[Any]`
152
+ Convert integer node ID(s) to label(s).
153
+
154
+ #### Filtering and subgraphs
155
+ - `get_filtered_network(nodes_selected=None, layers_selected=[], groups_selected=[], node_type="label", layer_type="label", keep_node_alignment=False) -> MultiLayerNetwork`
156
+ Return a filtered subnetwork based on node and/or layer selection.
157
+
158
+ - `get_egonetwork(ego_label, depth=1, return_list=False, ignore_limit=False) -> MultiLayerNetwork | List`
159
+ Extract ego network centered on a node at specified depth.
160
+
161
+ #### Layer-specific adjacency
162
+ - `get_layer_adjacency_matrix(layer, layer_type="layer", store=False, dtype="int64") -> csr_matrix`
163
+ Create binary adjacency matrix for a single layer or group.
164
+
165
+ - `get_binary_adjacency(dtype="int64") -> csr_matrix`
166
+ Convert multilayer adjacency to binary (0/1) format, collapsing all layer information.
167
+
168
+ - `get_supra_adjacency_matrix(dtype="int64") -> csr_matrix`
169
+ Build supra-adjacency matrix (L*N × L*N) with inter-layer couplings.
170
+
171
+ - `get_grouped_mln() -> MultiLayerNetwork`
172
+ Return network with layers aggregated by group (requires `group` column in `layers`).
173
+
174
+ - `clear_layer_adjacency_matrices()`, `clear_group_adjacency_matrices()`, `clear_all_adjacency_matrices()`
175
+ Clear cached adjacency matrices.
176
+
177
+ #### Edge lists and conversions
178
+ - `get_edgelist(edge_attribute="binary") -> DataFrame`
179
+ Convert sparse adjacency matrix to edgelist. Options: "binary", "layer", "label", "weight", or None.
180
+
181
+ - `convert_layer_representation(layer, input_type="layer", output_type="binary") -> Any | List[Any]`
182
+ Convert between layer representations: "layer", "label", "binary".
183
+
184
+ - `convert_layer_binary_to_list(num, output_type="layer") -> List`
185
+ Decode binary integer to list of active layers.
186
+
187
+ #### Graph library exports
188
+ - `to_igraph(directed=True, edge_attributes=True, node_attributes=False, replace_igraph=False, edge_attribute_type="binary") -> ig.Graph`
189
+ Create igraph object. Options for `edge_attribute_type`: "binary", "weight".
190
+
191
+ - `to_networkx(directed=True, edge_attributes=True, node_attributes=False, edge_attribute_type="binary", layer_type="layer", ignore_limit=False) -> nx.Graph | nx.DiGraph | None`
192
+ Create NetworkX object (limited by `nx_node_limit` unless `ignore_limit=True`).
193
+
194
+ - `save_to_graphml(file_name, directed=True, edge_attributes=True, node_attributes=False, overwrite=False, edge_attribute_type="binary")`
195
+ Save as GraphML for external tools (Gephi, etc.).
196
+
197
+ #### Metrics and analysis
198
+ - `get_degrees(selected_nodes=[]) -> Dict[Any, int]`
199
+ Calculate degree for selected nodes across all layers.
200
+
201
+ - `get_clustering_coefficient(selected_nodes=[], batchsize=100000) -> Dict[Any, float]`
202
+ Compute clustering coefficient for selected nodes.
203
+
204
+ - `get_excess_closure(selected_nodes=[], node_type="label", selected_layers=[], layer_type="layer", batchsize=100000) -> Dict[str, Dict]`
205
+ Calculate excess closure metric (returns both clustering coefficient and excess closure).
206
+
207
+ #### Node operations
208
+ - `add_nodes(nodes_df: pd.DataFrame)`
209
+ Add new nodes without connections (expands adjacency matrix).
210
+
211
+ - `remove_nodes(nodes: List[Any])`
212
+ Remove nodes and their edges; reindexes remaining nodes.
213
+
214
+ #### Aggregation
215
+ - `create_affiliation_matrix(key, affil_edgelist: List[Tuple])`
216
+ Create bipartite affiliation matrix for node-to-entity relationships.
217
+
218
+ - `get_aggregated_network(aggregation_column=None, keep_layers=False, convert_A="none") -> MultiLayerNetwork`
219
+ Aggregate network by node attribute (e.g., region, age group).
220
+
221
+ #### Persistence
222
+ - `save(path="", overwrite=False, **kwargs)`
223
+ Save MLN library (edges.npz, nodes.csv.gz, layers.csv, optional codebook).
224
+
225
+ - `export_edges(file_name)`, `export_nodes(file_name)`, `export_layers(file_name)`, `export_codebook(file_name)`
226
+ Export individual components.
227
+
228
+ - `load(path) -> Tuple[DataFrame, csr_matrix, DataFrame]`
229
+ Load MLN components from disk.
230
+
231
+ #### Utilities
232
+ - `report_time(message="", init=False)`
233
+ Helper for timing measurements (requires `verbose=True`).
234
+
235
+ ### RawCSVtoMLN (preparation.py)
236
+
237
+ Utility class for converting raw CSV node/edge files into MLN library format.
238
+
239
+ - `__init__(node_conf, edge_conf, layer_conf, output_folder, **kwargs)`
240
+ Configure input/output paths and column mappings.
241
+
242
+ - `init_all()`
243
+ Run full pipeline: init_layers() → init_nodes() → init_edges() → read_all_edges() → save_all() (if configured).
244
+
245
+ - `save_all(overwrite=False)`
246
+ Save nodes.csv.gz, edges.npz, and layers.csv to output folder.
247
+
248
+ See `test/config.json` for configuration schema.
249
+
250
+ ## Notes
251
+
252
+ - NetworkX conversion limits very large graphs (`nx_node_limit` in code). Use igraph for larger networks.
253
+ - The package supports pandas by default and optional polars interop for CSV loading and some operations.
254
+ - The network can be directed; layer information is encoded in the adjacency matrix when `adjacency_element="binary"`.
255
+
256
+ ## Citation
257
+
258
+ If you use this library in academic work, please cite this repository and acknowledge the PLANET-NL project (https://planetnl.org). Citation metadata is available in `CITATION.cff`, and GitHub shows it under "Cite this repository" in the sidebar.
259
+
260
+ A software paper describing `mlnlib` is in preparation (see [`paper/`](paper/)). We will update the citation information once it is published.
@@ -1,19 +1,23 @@
1
1
  [build-system]
2
- requires = ["setuptools>=61.0"]
2
+ requires = ["setuptools>=77.0"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mlnlib"
7
- version = "0.2.39"
7
+ version = "0.3.0"
8
8
  authors = [
9
- { name="Eszter Bokányi", email="e.bokanyi@uva.nl" },
9
+ { name="Eszter Bokányi", email="e.bokanyi@liacs.leidenuniv.nl" },
10
+ { name="Rachel G. de Jong" },
11
+ { name="Yuliia Kazmina" },
12
+ { name="Frank W. Takes" },
10
13
  ]
11
14
  description = "Memory-efficient library for loading and working with large unweighted multilayer networks."
12
15
  readme = "README.md"
16
+ license = "MIT"
17
+ license-files = ["LICENSE.md"]
13
18
  requires-python = ">=3.8"
14
19
  classifiers = [
15
20
  "Programming Language :: Python :: 3",
16
- "License :: OSI Approved :: MIT License",
17
21
  "Operating System :: OS Independent",
18
22
  ]
19
23
  dependencies = [
@@ -26,5 +30,5 @@ dependencies = [
26
30
  ]
27
31
 
28
32
  [project.urls]
29
- Homepage = "https://github.com/popnet-io/popnet_mln"
30
- Issues = "https://github.com/popnet-io/popnet_mln/issues"
33
+ Homepage = "https://github.com/planet-nl/mlnlib"
34
+ Issues = "https://github.com/planet-nl/mlnlib/issues"
@@ -0,0 +1,21 @@
1
+ """
2
+ mlnlib - Memory-efficient Python library for multilayer networks
3
+
4
+ This package provides tools for working with large multilayer networks
5
+ efficiently on computers with limited resources.
6
+
7
+ Main Classes:
8
+ -------------
9
+ - MultiLayerNetwork: Main class for multilayer network analysis
10
+ - RawCSVtoMLN: Utility class for converting raw CSV files to MLN format
11
+
12
+ Authors: Eszter Bokányi, Rachel G. de Jong, Yuliia Kazmina, Frank W. Takes
13
+ Contact: e.bokanyi@liacs.leidenuniv.nl
14
+ License: MIT
15
+ """
16
+
17
+ from .mln import MultiLayerNetwork
18
+ from .preparation import RawCSVtoMLN
19
+
20
+ __version__ = "0.3.0"
21
+ __all__ = ["MultiLayerNetwork", "RawCSVtoMLN"]