modernsn 0.1.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.
- modernsn-0.1.0/LICENSE +21 -0
- modernsn-0.1.0/PKG-INFO +353 -0
- modernsn-0.1.0/README.md +318 -0
- modernsn-0.1.0/modernsn/LeidenSigned.py +677 -0
- modernsn-0.1.0/modernsn/LouvainSigned.py +270 -0
- modernsn-0.1.0/modernsn/__init__.py +3 -0
- modernsn-0.1.0/modernsn/modern +1196 -0
- modernsn-0.1.0/modernsn/network_load.py +904 -0
- modernsn-0.1.0/modernsn/network_module.py +3289 -0
- modernsn-0.1.0/modernsn/network_score.py +270 -0
- modernsn-0.1.0/modernsn.egg-info/PKG-INFO +353 -0
- modernsn-0.1.0/modernsn.egg-info/SOURCES.txt +17 -0
- modernsn-0.1.0/modernsn.egg-info/dependency_links.txt +1 -0
- modernsn-0.1.0/modernsn.egg-info/requires.txt +8 -0
- modernsn-0.1.0/modernsn.egg-info/top_level.txt +1 -0
- modernsn-0.1.0/setup.cfg +4 -0
- modernsn-0.1.0/setup.py +43 -0
- modernsn-0.1.0/tests/test_check_signed_network.py +66 -0
- modernsn-0.1.0/tests/test_negative_edge_leverage.py +60 -0
modernsn-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ryuichiro Nakato
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
modernsn-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modernsn
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MODERN (MOdule DEtection and Refinement in signed Networks): community detection of a signed network.
|
|
5
|
+
Home-page: https://github.com/rnakato/MODERN
|
|
6
|
+
Author: Ryuichiro Nakato
|
|
7
|
+
Author-email: rnakato@iqb.u-tokyo.ac.jp
|
|
8
|
+
License: GPL3.0
|
|
9
|
+
Keywords: MODERN modernsn signed network community detection
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.6
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: numpy>=1.14.2
|
|
16
|
+
Requires-Dist: pandas>=0.22.0
|
|
17
|
+
Requires-Dist: leidenalg>=0.8.3
|
|
18
|
+
Requires-Dist: eeisp>=0.5.0
|
|
19
|
+
Requires-Dist: matplotlib
|
|
20
|
+
Requires-Dist: seaborn
|
|
21
|
+
Requires-Dist: networkx
|
|
22
|
+
Requires-Dist: igraph
|
|
23
|
+
Dynamic: author
|
|
24
|
+
Dynamic: author-email
|
|
25
|
+
Dynamic: classifier
|
|
26
|
+
Dynamic: description
|
|
27
|
+
Dynamic: description-content-type
|
|
28
|
+
Dynamic: home-page
|
|
29
|
+
Dynamic: keywords
|
|
30
|
+
Dynamic: license
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
Dynamic: requires-dist
|
|
33
|
+
Dynamic: requires-python
|
|
34
|
+
Dynamic: summary
|
|
35
|
+
|
|
36
|
+
# MODERN
|
|
37
|
+
|
|
38
|
+
**MO**dule **DE**tection and **R**efinement in signed **N**etworks (MODERN) detects community structure in signed networks — networks with both positive (attractive) and negative (repulsive) edges. It extends classical community detection algorithms (Louvain, Leiden) to leverage negative edge information, which is critical for accurate module detection in biological networks such as gene co-expression networks.
|
|
39
|
+
|
|
40
|
+
## Why signed networks?
|
|
41
|
+
|
|
42
|
+
Standard community detection methods use only positive edges and ignore negative correlations between genes. When inter-community positive edges are abundant (e.g., in correlation-based networks), unsigned methods fail to identify the correct community structure because they cannot distinguish "noise" positive edges from true intra-community edges.
|
|
43
|
+
|
|
44
|
+
Signed methods use negative edges as repulsive forces to push nodes apart, recovering the correct community structure even when the positive graph alone is ambiguous.
|
|
45
|
+
|
|
46
|
+
<p align="center">
|
|
47
|
+
<img src="docs/fig1_signed_vs_unsigned_matrix.png" width="90%">
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
*Signed adjacency matrix sorted by detected communities. As inter-community positive edges increase (top to bottom), unsigned modularity collapses (ARI=0.07) while signed methods maintain near-perfect recovery (ARI>0.93).*
|
|
51
|
+
|
|
52
|
+
## Methods
|
|
53
|
+
|
|
54
|
+
MODERN provides three community detection methods for signed networks:
|
|
55
|
+
|
|
56
|
+
| Method | Algorithm | Objective | Best for |
|
|
57
|
+
|--------|-----------|-----------|----------|
|
|
58
|
+
| `louvain` | LouvainSigned | α·Q⁺ − (1−α)·Q⁻ | Compatibility with existing Louvain workflows |
|
|
59
|
+
| `leiden-mod-alpha` | LeidenSigned (modularity) | α·Q⁺ − (1−α)·Q⁻ | General signed networks |
|
|
60
|
+
| `leiden-cpm-single` | LeidenSigned (CPM) | Σ(w_ij − γ) on signed graph | Correlation-based networks (co-expression) |
|
|
61
|
+
|
|
62
|
+
**When to use CPM over modularity:** Modularity's null model assumes a configuration-model random graph. In correlation-based networks (e.g., gene co-expression), the positive graph is inherently dense, distorting the null model. CPM uses an absolute density threshold (γ) instead and is not affected by this issue.
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install modernsn
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Dependencies
|
|
71
|
+
|
|
72
|
+
- Python ≥ 3.9
|
|
73
|
+
- numpy
|
|
74
|
+
- igraph (python-igraph)
|
|
75
|
+
- leidenalg
|
|
76
|
+
- networkx
|
|
77
|
+
- matplotlib (for plotting outputs)
|
|
78
|
+
- scipy (for MAT format support)
|
|
79
|
+
|
|
80
|
+
## Quick start
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Basic run with Leiden signed modularity
|
|
84
|
+
modern --pos positive_edges.tsv --neg negative_edges.tsv \
|
|
85
|
+
--method leiden-mod-alpha --alpha 0.6 --resolution 1.0 \
|
|
86
|
+
--out-prefix results/my_analysis
|
|
87
|
+
|
|
88
|
+
# Leiden signed CPM (recommended for co-expression networks)
|
|
89
|
+
modern --pos positive_edges.tsv --neg negative_edges.tsv \
|
|
90
|
+
--method leiden-cpm-single --gamma 0.05 --lambda-neg 1.0 \
|
|
91
|
+
--out-prefix results/my_analysis
|
|
92
|
+
|
|
93
|
+
# Louvain signed
|
|
94
|
+
modern --pos positive_edges.tsv --neg negative_edges.tsv \
|
|
95
|
+
--method louvain --alpha 0.6 --resolution 1.0 \
|
|
96
|
+
--out-prefix results/my_analysis
|
|
97
|
+
|
|
98
|
+
# Multi-seed negative-edge leverage screen
|
|
99
|
+
modern --pos positive_edges.tsv --neg negative_edges.tsv \
|
|
100
|
+
--method leiden-mod-alpha --negative-edge-leverage \
|
|
101
|
+
--leverage-seeds 1 2 3 10 42 \
|
|
102
|
+
--out-prefix results/my_analysis
|
|
103
|
+
|
|
104
|
+
# Signed-network diagnostic and analysis recommendations
|
|
105
|
+
modern --pos positive_edges.tsv --neg negative_edges.tsv \
|
|
106
|
+
--method leiden-mod-alpha --check-signed-network \
|
|
107
|
+
--leverage-seeds 1 2 3 10 42 \
|
|
108
|
+
--out-prefix results/my_analysis
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Input formats
|
|
112
|
+
|
|
113
|
+
### TSV (default)
|
|
114
|
+
|
|
115
|
+
Tab-separated file with five columns (no header):
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
gene_id1 gene_id2 gene_name1 gene_name2 weight
|
|
119
|
+
ENSG00001 ENSG00002 GeneA GeneB 15.3
|
|
120
|
+
ENSG00001 ENSG00003 GeneA GeneC 12.1
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Provide separate files for positive and negative edges via `--pos` and `--neg`. Use `--thre-pos` and `--thre-neg` to filter edges by weight.
|
|
124
|
+
|
|
125
|
+
### MAT (MATLAB sparse matrix)
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
modern --format mat --mat data/network.mat \
|
|
129
|
+
--pos-key pos --neg-key neg \
|
|
130
|
+
--method leiden-mod-alpha --out-prefix results/mat_run
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The MAT file should contain two sparse matrices (positive and negative adjacency).
|
|
134
|
+
|
|
135
|
+
## Output files
|
|
136
|
+
|
|
137
|
+
When `--out-prefix` is specified, MODERN generates the following outputs:
|
|
138
|
+
|
|
139
|
+
| File | Description |
|
|
140
|
+
|------|-------------|
|
|
141
|
+
| `<prefix>_partition.tsv` | Community assignment for each gene (gene_id, gene_name, community) |
|
|
142
|
+
| `<prefix>_summary.txt` | Parameters, graph statistics, community size distribution, entropy |
|
|
143
|
+
| `<prefix>_communities.gmt` | GMT format for direct use with GSEA, Enrichr, clusterProfiler |
|
|
144
|
+
| `<prefix>_community_sizes.pdf` | Rank-size plot and histogram of community sizes |
|
|
145
|
+
| `<prefix>_inter_community.pdf` | Heatmap of inter-community edge density (positive / negative / signed) |
|
|
146
|
+
| `<prefix>_module_<id>.pdf` | Subnetwork visualization for top modules (positive=red, negative=blue) |
|
|
147
|
+
|
|
148
|
+
With `--negative-edge-leverage`, MODERN instead writes one row per seed to
|
|
149
|
+
`<prefix>_negative_edge_leverage.tsv` and a multi-seed summary to
|
|
150
|
+
`<prefix>_negative_edge_leverage_summary.txt`.
|
|
151
|
+
|
|
152
|
+
With `--check-signed-network`, MODERN reports network size, low- and
|
|
153
|
+
high-resolution partition summaries, metanode enrichment, multi-seed
|
|
154
|
+
negative-edge leverage, and analysis recommendations. The full result is saved
|
|
155
|
+
to `<prefix>_signed_network_check.json`.
|
|
156
|
+
|
|
157
|
+
## Negative-edge leverage
|
|
158
|
+
|
|
159
|
+
Negative-edge leverage asks whether positive-only reintegration would merge
|
|
160
|
+
module boundaries supported by negative edges. MODERN first generates a
|
|
161
|
+
high-resolution positive-only Leiden partition, then performs reintegration
|
|
162
|
+
without applying the negative-edge veto. The score is
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
(negative edges newly placed within modules) / (negative edges evaluated)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The defaults reproduce the manuscript screen: resolution 4, positive-coupling
|
|
169
|
+
robust-z threshold 1, minimum module size 10, and seeds 1, 2, 3, 10, and 42.
|
|
170
|
+
The empirical candidate threshold of 0.02 is reported as a screen rather than a
|
|
171
|
+
general significance cutoff.
|
|
172
|
+
|
|
173
|
+
### Disabling outputs
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
--no-plot # Skip all PDF plots
|
|
177
|
+
--no-gmt # Skip GMT output
|
|
178
|
+
--quiet # Suppress progress messages
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Controlling module plots
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
--top-modules 10 # Number of top modules to plot (default: 10)
|
|
185
|
+
--max-nodes-per-module 200 # Max nodes per module plot (default: 200)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Parameters
|
|
189
|
+
|
|
190
|
+
### Method-specific parameters
|
|
191
|
+
|
|
192
|
+
**Modularity-based methods** (`louvain`, `leiden-mod-alpha`):
|
|
193
|
+
|
|
194
|
+
| Parameter | Description | Default |
|
|
195
|
+
|-----------|-------------|---------|
|
|
196
|
+
| `--alpha` | Balance between positive and negative modularity (0–1). Higher α emphasizes positive edges. | 0.5 |
|
|
197
|
+
| `--resolution` | Resolution parameter. Higher values produce more, smaller communities. | 1.0 |
|
|
198
|
+
|
|
199
|
+
**CPM method** (`leiden-cpm-single`):
|
|
200
|
+
|
|
201
|
+
| Parameter | Description | Default |
|
|
202
|
+
|-----------|-------------|---------|
|
|
203
|
+
| `--gamma` | CPM resolution. Minimum edge density within communities. | 0.5 |
|
|
204
|
+
| `--lambda-neg` | Weight multiplier for negative edges. Controls how strongly negative edges repel. | 0.0 |
|
|
205
|
+
| `--neg-weight-mode` | How to transform negative weights: `absolute` (−λ·\|w\|) or `signed` (λ·w). | absolute |
|
|
206
|
+
|
|
207
|
+
### Common parameters
|
|
208
|
+
|
|
209
|
+
| Parameter | Description | Default |
|
|
210
|
+
|-----------|-------------|---------|
|
|
211
|
+
| `--seed` | Random seed for reproducibility. | None |
|
|
212
|
+
| `--out-prefix` | Output file prefix. If omitted, only prints summary to stdout. | None |
|
|
213
|
+
|
|
214
|
+
## Python API
|
|
215
|
+
|
|
216
|
+
MODERN can also be used as a Python library:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
import modernsn.network_module as nr
|
|
220
|
+
import modernsn.LeidenSigned as les
|
|
221
|
+
|
|
222
|
+
# Load graphs
|
|
223
|
+
G_pos = nr.load_graph_from_TSV_igraph("positive.tsv", threshold=10)
|
|
224
|
+
G_neg = nr.load_graph_from_TSV_igraph("negative.tsv", threshold=5)
|
|
225
|
+
|
|
226
|
+
# Leiden signed modularity
|
|
227
|
+
partition = les.find_partition_signed_modularity_alpha(
|
|
228
|
+
G_pos, G_neg, alpha=0.6, resolution=1.0, seed=42
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Leiden signed CPM (single signed graph)
|
|
232
|
+
G_signed = nr.load_signed_graph_from_two_TSV_igraph(
|
|
233
|
+
"positive.tsv", "negative.tsv",
|
|
234
|
+
pos_threshold=10, neg_threshold=5, lambda_neg=1.0
|
|
235
|
+
)
|
|
236
|
+
partition = les.find_partition_signed_CPM_single_graph(
|
|
237
|
+
G_signed, gamma=0.05, seed=42
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Inspect results
|
|
241
|
+
print(partition.membership)
|
|
242
|
+
nr.display_communities_by_name(G_pos, partition)
|
|
243
|
+
nr.count_nodes_in_communities(partition)
|
|
244
|
+
|
|
245
|
+
# Multi-seed negative-edge leverage
|
|
246
|
+
leverage = nr.calculate_negative_edge_leverage_multiseed(
|
|
247
|
+
G_pos, G_neg, seeds=[1, 2, 3, 10, 42]
|
|
248
|
+
)
|
|
249
|
+
print(leverage["negative_edge_leverage_median"])
|
|
250
|
+
print(leverage["classification"])
|
|
251
|
+
|
|
252
|
+
# Combined signed-network diagnostic
|
|
253
|
+
diagnostic = nr.check_signed_network(G_pos, G_neg)
|
|
254
|
+
print(diagnostic["network"])
|
|
255
|
+
print(diagnostic["recommendations"])
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Visualization
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
# Visualize a specific module (positive=red, negative=blue)
|
|
262
|
+
nr.visualize_module_signed(G_pos, G_neg, partition, community_id=0)
|
|
263
|
+
|
|
264
|
+
# Visualize the module containing a specific gene
|
|
265
|
+
nr.visualize_module_of_gene_signed(G_pos, G_neg, partition, "TP53")
|
|
266
|
+
|
|
267
|
+
# Top-degree nodes within a module
|
|
268
|
+
nr.visualize_module_of_gene_top_degree_nodes_signed(
|
|
269
|
+
G_pos, G_neg, partition, "TP53", top_n=30
|
|
270
|
+
)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Benchmarking
|
|
274
|
+
|
|
275
|
+
MODERN includes a simulation framework for evaluating community detection methods on signed networks.
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
# Quick test (no modernsn package required)
|
|
279
|
+
python sim3_benchmark.py test
|
|
280
|
+
|
|
281
|
+
# Full benchmark with all methods and parameter grids
|
|
282
|
+
python sim3_benchmark.py run sim3_results/
|
|
283
|
+
|
|
284
|
+
# Generate example correlation matrix visualizations
|
|
285
|
+
python sim3_benchmark.py plot sim3_results/
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Simulation types
|
|
289
|
+
|
|
290
|
+
| Type | Model | Tests |
|
|
291
|
+
|------|-------|-------|
|
|
292
|
+
| **Type A** | Correlation-based co-expression | Modularity null model distortion on dense positive graphs |
|
|
293
|
+
| **Type B** | Planted partition (stochastic block model) | Control — balanced positive/negative structure |
|
|
294
|
+
| **Type C** | scRNA-seq exclusive expression | Multipartite negative graph where "enemy of enemy ≠ friend" |
|
|
295
|
+
|
|
296
|
+
### Example visualizations
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
# Generate concrete examples showing signed vs unsigned differences
|
|
300
|
+
python sim3_examples.py sim3_examples/
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
This produces network graphs and adjacency matrix heatmaps at three difficulty levels, clearly demonstrating when signed methods outperform unsigned methods.
|
|
304
|
+
|
|
305
|
+
## How it works
|
|
306
|
+
|
|
307
|
+
### Signed modularity (multiplex optimization)
|
|
308
|
+
|
|
309
|
+
The positive and negative graphs are treated as two layers of a multiplex network. The objective function is:
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
Q_signed = α · Q_modularity(G⁺) − (1 − α) · Q_modularity(G⁻)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
This is optimized using `leidenalg.optimise_partition_multiplex`, which simultaneously considers both layers with different weights.
|
|
316
|
+
|
|
317
|
+
### Signed CPM (single graph)
|
|
318
|
+
|
|
319
|
+
Positive and negative edges are combined into a single graph with signed weights:
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
w_signed(i,j) = w_pos(i,j) − λ_neg · w_neg(i,j)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Standard CPM is then applied, where the objective function naturally penalizes negative edges within the same community:
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
H_CPM = Σ_{i,j in same community} (w_signed(i,j) − γ)
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Choosing a method
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
Is your network correlation-based (co-expression, etc.)?
|
|
335
|
+
├── Yes → Use leiden-cpm-single
|
|
336
|
+
│ Start with --gamma 0.05 --lambda-neg 1.0
|
|
337
|
+
│ Increase gamma for smaller communities
|
|
338
|
+
│
|
|
339
|
+
└── No (e.g., social network, citation network)
|
|
340
|
+
├── Need reproducibility/stability? → Use leiden-mod-alpha
|
|
341
|
+
│ Start with --alpha 0.6 --resolution 1.0
|
|
342
|
+
│
|
|
343
|
+
└── Compatibility with existing Louvain pipeline? → Use louvain
|
|
344
|
+
Start with --alpha 0.6 --resolution 1.0
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Citation
|
|
348
|
+
|
|
349
|
+
If you use MODERN in your research, please cite:
|
|
350
|
+
|
|
351
|
+
```
|
|
352
|
+
[Citation information to be added]
|
|
353
|
+
```
|
modernsn-0.1.0/README.md
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# MODERN
|
|
2
|
+
|
|
3
|
+
**MO**dule **DE**tection and **R**efinement in signed **N**etworks (MODERN) detects community structure in signed networks — networks with both positive (attractive) and negative (repulsive) edges. It extends classical community detection algorithms (Louvain, Leiden) to leverage negative edge information, which is critical for accurate module detection in biological networks such as gene co-expression networks.
|
|
4
|
+
|
|
5
|
+
## Why signed networks?
|
|
6
|
+
|
|
7
|
+
Standard community detection methods use only positive edges and ignore negative correlations between genes. When inter-community positive edges are abundant (e.g., in correlation-based networks), unsigned methods fail to identify the correct community structure because they cannot distinguish "noise" positive edges from true intra-community edges.
|
|
8
|
+
|
|
9
|
+
Signed methods use negative edges as repulsive forces to push nodes apart, recovering the correct community structure even when the positive graph alone is ambiguous.
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<img src="docs/fig1_signed_vs_unsigned_matrix.png" width="90%">
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
*Signed adjacency matrix sorted by detected communities. As inter-community positive edges increase (top to bottom), unsigned modularity collapses (ARI=0.07) while signed methods maintain near-perfect recovery (ARI>0.93).*
|
|
16
|
+
|
|
17
|
+
## Methods
|
|
18
|
+
|
|
19
|
+
MODERN provides three community detection methods for signed networks:
|
|
20
|
+
|
|
21
|
+
| Method | Algorithm | Objective | Best for |
|
|
22
|
+
|--------|-----------|-----------|----------|
|
|
23
|
+
| `louvain` | LouvainSigned | α·Q⁺ − (1−α)·Q⁻ | Compatibility with existing Louvain workflows |
|
|
24
|
+
| `leiden-mod-alpha` | LeidenSigned (modularity) | α·Q⁺ − (1−α)·Q⁻ | General signed networks |
|
|
25
|
+
| `leiden-cpm-single` | LeidenSigned (CPM) | Σ(w_ij − γ) on signed graph | Correlation-based networks (co-expression) |
|
|
26
|
+
|
|
27
|
+
**When to use CPM over modularity:** Modularity's null model assumes a configuration-model random graph. In correlation-based networks (e.g., gene co-expression), the positive graph is inherently dense, distorting the null model. CPM uses an absolute density threshold (γ) instead and is not affected by this issue.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install modernsn
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Dependencies
|
|
36
|
+
|
|
37
|
+
- Python ≥ 3.9
|
|
38
|
+
- numpy
|
|
39
|
+
- igraph (python-igraph)
|
|
40
|
+
- leidenalg
|
|
41
|
+
- networkx
|
|
42
|
+
- matplotlib (for plotting outputs)
|
|
43
|
+
- scipy (for MAT format support)
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Basic run with Leiden signed modularity
|
|
49
|
+
modern --pos positive_edges.tsv --neg negative_edges.tsv \
|
|
50
|
+
--method leiden-mod-alpha --alpha 0.6 --resolution 1.0 \
|
|
51
|
+
--out-prefix results/my_analysis
|
|
52
|
+
|
|
53
|
+
# Leiden signed CPM (recommended for co-expression networks)
|
|
54
|
+
modern --pos positive_edges.tsv --neg negative_edges.tsv \
|
|
55
|
+
--method leiden-cpm-single --gamma 0.05 --lambda-neg 1.0 \
|
|
56
|
+
--out-prefix results/my_analysis
|
|
57
|
+
|
|
58
|
+
# Louvain signed
|
|
59
|
+
modern --pos positive_edges.tsv --neg negative_edges.tsv \
|
|
60
|
+
--method louvain --alpha 0.6 --resolution 1.0 \
|
|
61
|
+
--out-prefix results/my_analysis
|
|
62
|
+
|
|
63
|
+
# Multi-seed negative-edge leverage screen
|
|
64
|
+
modern --pos positive_edges.tsv --neg negative_edges.tsv \
|
|
65
|
+
--method leiden-mod-alpha --negative-edge-leverage \
|
|
66
|
+
--leverage-seeds 1 2 3 10 42 \
|
|
67
|
+
--out-prefix results/my_analysis
|
|
68
|
+
|
|
69
|
+
# Signed-network diagnostic and analysis recommendations
|
|
70
|
+
modern --pos positive_edges.tsv --neg negative_edges.tsv \
|
|
71
|
+
--method leiden-mod-alpha --check-signed-network \
|
|
72
|
+
--leverage-seeds 1 2 3 10 42 \
|
|
73
|
+
--out-prefix results/my_analysis
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Input formats
|
|
77
|
+
|
|
78
|
+
### TSV (default)
|
|
79
|
+
|
|
80
|
+
Tab-separated file with five columns (no header):
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
gene_id1 gene_id2 gene_name1 gene_name2 weight
|
|
84
|
+
ENSG00001 ENSG00002 GeneA GeneB 15.3
|
|
85
|
+
ENSG00001 ENSG00003 GeneA GeneC 12.1
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Provide separate files for positive and negative edges via `--pos` and `--neg`. Use `--thre-pos` and `--thre-neg` to filter edges by weight.
|
|
89
|
+
|
|
90
|
+
### MAT (MATLAB sparse matrix)
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
modern --format mat --mat data/network.mat \
|
|
94
|
+
--pos-key pos --neg-key neg \
|
|
95
|
+
--method leiden-mod-alpha --out-prefix results/mat_run
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The MAT file should contain two sparse matrices (positive and negative adjacency).
|
|
99
|
+
|
|
100
|
+
## Output files
|
|
101
|
+
|
|
102
|
+
When `--out-prefix` is specified, MODERN generates the following outputs:
|
|
103
|
+
|
|
104
|
+
| File | Description |
|
|
105
|
+
|------|-------------|
|
|
106
|
+
| `<prefix>_partition.tsv` | Community assignment for each gene (gene_id, gene_name, community) |
|
|
107
|
+
| `<prefix>_summary.txt` | Parameters, graph statistics, community size distribution, entropy |
|
|
108
|
+
| `<prefix>_communities.gmt` | GMT format for direct use with GSEA, Enrichr, clusterProfiler |
|
|
109
|
+
| `<prefix>_community_sizes.pdf` | Rank-size plot and histogram of community sizes |
|
|
110
|
+
| `<prefix>_inter_community.pdf` | Heatmap of inter-community edge density (positive / negative / signed) |
|
|
111
|
+
| `<prefix>_module_<id>.pdf` | Subnetwork visualization for top modules (positive=red, negative=blue) |
|
|
112
|
+
|
|
113
|
+
With `--negative-edge-leverage`, MODERN instead writes one row per seed to
|
|
114
|
+
`<prefix>_negative_edge_leverage.tsv` and a multi-seed summary to
|
|
115
|
+
`<prefix>_negative_edge_leverage_summary.txt`.
|
|
116
|
+
|
|
117
|
+
With `--check-signed-network`, MODERN reports network size, low- and
|
|
118
|
+
high-resolution partition summaries, metanode enrichment, multi-seed
|
|
119
|
+
negative-edge leverage, and analysis recommendations. The full result is saved
|
|
120
|
+
to `<prefix>_signed_network_check.json`.
|
|
121
|
+
|
|
122
|
+
## Negative-edge leverage
|
|
123
|
+
|
|
124
|
+
Negative-edge leverage asks whether positive-only reintegration would merge
|
|
125
|
+
module boundaries supported by negative edges. MODERN first generates a
|
|
126
|
+
high-resolution positive-only Leiden partition, then performs reintegration
|
|
127
|
+
without applying the negative-edge veto. The score is
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
(negative edges newly placed within modules) / (negative edges evaluated)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The defaults reproduce the manuscript screen: resolution 4, positive-coupling
|
|
134
|
+
robust-z threshold 1, minimum module size 10, and seeds 1, 2, 3, 10, and 42.
|
|
135
|
+
The empirical candidate threshold of 0.02 is reported as a screen rather than a
|
|
136
|
+
general significance cutoff.
|
|
137
|
+
|
|
138
|
+
### Disabling outputs
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
--no-plot # Skip all PDF plots
|
|
142
|
+
--no-gmt # Skip GMT output
|
|
143
|
+
--quiet # Suppress progress messages
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Controlling module plots
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
--top-modules 10 # Number of top modules to plot (default: 10)
|
|
150
|
+
--max-nodes-per-module 200 # Max nodes per module plot (default: 200)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Parameters
|
|
154
|
+
|
|
155
|
+
### Method-specific parameters
|
|
156
|
+
|
|
157
|
+
**Modularity-based methods** (`louvain`, `leiden-mod-alpha`):
|
|
158
|
+
|
|
159
|
+
| Parameter | Description | Default |
|
|
160
|
+
|-----------|-------------|---------|
|
|
161
|
+
| `--alpha` | Balance between positive and negative modularity (0–1). Higher α emphasizes positive edges. | 0.5 |
|
|
162
|
+
| `--resolution` | Resolution parameter. Higher values produce more, smaller communities. | 1.0 |
|
|
163
|
+
|
|
164
|
+
**CPM method** (`leiden-cpm-single`):
|
|
165
|
+
|
|
166
|
+
| Parameter | Description | Default |
|
|
167
|
+
|-----------|-------------|---------|
|
|
168
|
+
| `--gamma` | CPM resolution. Minimum edge density within communities. | 0.5 |
|
|
169
|
+
| `--lambda-neg` | Weight multiplier for negative edges. Controls how strongly negative edges repel. | 0.0 |
|
|
170
|
+
| `--neg-weight-mode` | How to transform negative weights: `absolute` (−λ·\|w\|) or `signed` (λ·w). | absolute |
|
|
171
|
+
|
|
172
|
+
### Common parameters
|
|
173
|
+
|
|
174
|
+
| Parameter | Description | Default |
|
|
175
|
+
|-----------|-------------|---------|
|
|
176
|
+
| `--seed` | Random seed for reproducibility. | None |
|
|
177
|
+
| `--out-prefix` | Output file prefix. If omitted, only prints summary to stdout. | None |
|
|
178
|
+
|
|
179
|
+
## Python API
|
|
180
|
+
|
|
181
|
+
MODERN can also be used as a Python library:
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
import modernsn.network_module as nr
|
|
185
|
+
import modernsn.LeidenSigned as les
|
|
186
|
+
|
|
187
|
+
# Load graphs
|
|
188
|
+
G_pos = nr.load_graph_from_TSV_igraph("positive.tsv", threshold=10)
|
|
189
|
+
G_neg = nr.load_graph_from_TSV_igraph("negative.tsv", threshold=5)
|
|
190
|
+
|
|
191
|
+
# Leiden signed modularity
|
|
192
|
+
partition = les.find_partition_signed_modularity_alpha(
|
|
193
|
+
G_pos, G_neg, alpha=0.6, resolution=1.0, seed=42
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Leiden signed CPM (single signed graph)
|
|
197
|
+
G_signed = nr.load_signed_graph_from_two_TSV_igraph(
|
|
198
|
+
"positive.tsv", "negative.tsv",
|
|
199
|
+
pos_threshold=10, neg_threshold=5, lambda_neg=1.0
|
|
200
|
+
)
|
|
201
|
+
partition = les.find_partition_signed_CPM_single_graph(
|
|
202
|
+
G_signed, gamma=0.05, seed=42
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# Inspect results
|
|
206
|
+
print(partition.membership)
|
|
207
|
+
nr.display_communities_by_name(G_pos, partition)
|
|
208
|
+
nr.count_nodes_in_communities(partition)
|
|
209
|
+
|
|
210
|
+
# Multi-seed negative-edge leverage
|
|
211
|
+
leverage = nr.calculate_negative_edge_leverage_multiseed(
|
|
212
|
+
G_pos, G_neg, seeds=[1, 2, 3, 10, 42]
|
|
213
|
+
)
|
|
214
|
+
print(leverage["negative_edge_leverage_median"])
|
|
215
|
+
print(leverage["classification"])
|
|
216
|
+
|
|
217
|
+
# Combined signed-network diagnostic
|
|
218
|
+
diagnostic = nr.check_signed_network(G_pos, G_neg)
|
|
219
|
+
print(diagnostic["network"])
|
|
220
|
+
print(diagnostic["recommendations"])
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Visualization
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
# Visualize a specific module (positive=red, negative=blue)
|
|
227
|
+
nr.visualize_module_signed(G_pos, G_neg, partition, community_id=0)
|
|
228
|
+
|
|
229
|
+
# Visualize the module containing a specific gene
|
|
230
|
+
nr.visualize_module_of_gene_signed(G_pos, G_neg, partition, "TP53")
|
|
231
|
+
|
|
232
|
+
# Top-degree nodes within a module
|
|
233
|
+
nr.visualize_module_of_gene_top_degree_nodes_signed(
|
|
234
|
+
G_pos, G_neg, partition, "TP53", top_n=30
|
|
235
|
+
)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Benchmarking
|
|
239
|
+
|
|
240
|
+
MODERN includes a simulation framework for evaluating community detection methods on signed networks.
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
# Quick test (no modernsn package required)
|
|
244
|
+
python sim3_benchmark.py test
|
|
245
|
+
|
|
246
|
+
# Full benchmark with all methods and parameter grids
|
|
247
|
+
python sim3_benchmark.py run sim3_results/
|
|
248
|
+
|
|
249
|
+
# Generate example correlation matrix visualizations
|
|
250
|
+
python sim3_benchmark.py plot sim3_results/
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Simulation types
|
|
254
|
+
|
|
255
|
+
| Type | Model | Tests |
|
|
256
|
+
|------|-------|-------|
|
|
257
|
+
| **Type A** | Correlation-based co-expression | Modularity null model distortion on dense positive graphs |
|
|
258
|
+
| **Type B** | Planted partition (stochastic block model) | Control — balanced positive/negative structure |
|
|
259
|
+
| **Type C** | scRNA-seq exclusive expression | Multipartite negative graph where "enemy of enemy ≠ friend" |
|
|
260
|
+
|
|
261
|
+
### Example visualizations
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# Generate concrete examples showing signed vs unsigned differences
|
|
265
|
+
python sim3_examples.py sim3_examples/
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
This produces network graphs and adjacency matrix heatmaps at three difficulty levels, clearly demonstrating when signed methods outperform unsigned methods.
|
|
269
|
+
|
|
270
|
+
## How it works
|
|
271
|
+
|
|
272
|
+
### Signed modularity (multiplex optimization)
|
|
273
|
+
|
|
274
|
+
The positive and negative graphs are treated as two layers of a multiplex network. The objective function is:
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
Q_signed = α · Q_modularity(G⁺) − (1 − α) · Q_modularity(G⁻)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
This is optimized using `leidenalg.optimise_partition_multiplex`, which simultaneously considers both layers with different weights.
|
|
281
|
+
|
|
282
|
+
### Signed CPM (single graph)
|
|
283
|
+
|
|
284
|
+
Positive and negative edges are combined into a single graph with signed weights:
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
w_signed(i,j) = w_pos(i,j) − λ_neg · w_neg(i,j)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Standard CPM is then applied, where the objective function naturally penalizes negative edges within the same community:
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
H_CPM = Σ_{i,j in same community} (w_signed(i,j) − γ)
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Choosing a method
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
Is your network correlation-based (co-expression, etc.)?
|
|
300
|
+
├── Yes → Use leiden-cpm-single
|
|
301
|
+
│ Start with --gamma 0.05 --lambda-neg 1.0
|
|
302
|
+
│ Increase gamma for smaller communities
|
|
303
|
+
│
|
|
304
|
+
└── No (e.g., social network, citation network)
|
|
305
|
+
├── Need reproducibility/stability? → Use leiden-mod-alpha
|
|
306
|
+
│ Start with --alpha 0.6 --resolution 1.0
|
|
307
|
+
│
|
|
308
|
+
└── Compatibility with existing Louvain pipeline? → Use louvain
|
|
309
|
+
Start with --alpha 0.6 --resolution 1.0
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Citation
|
|
313
|
+
|
|
314
|
+
If you use MODERN in your research, please cite:
|
|
315
|
+
|
|
316
|
+
```
|
|
317
|
+
[Citation information to be added]
|
|
318
|
+
```
|