Metadata-Version: 2.4
Name: signet-gene
Version: 0.1.0
Summary: SIGNET: Estimation and community detection of a signed network.
Home-page: https://github.com/rnakato/SIGNET
Author: Ryuichiro Nakato
Author-email: rnakato@iqb.u-tokyo.ac.jp
License: GPL3.0
Keywords: SIGNET signed network community detection
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.6
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.14.2
Requires-Dist: pandas>=0.22.0
Requires-Dist: leidenalg>=0.8.3
Requires-Dist: eeisp>=0.5.0
Requires-Dist: matplotlib
Requires-Dist: seaborn
Requires-Dist: networkx
Requires-Dist: igraph
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: license
Dynamic: license-file
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# SIGNET

**Si**gned **Net**work Community Detection (SIGNET) 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.

## Why signed networks?

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.

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.

<p align="center">
<img src="docs/fig1_signed_vs_unsigned_matrix.png" width="90%">
</p>

*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).*

## Methods

SIGNET provides three community detection methods for signed networks:

| Method | Algorithm | Objective | Best for |
|--------|-----------|-----------|----------|
| `louvain` | LouvainSigned | α·Q⁺ − (1−α)·Q⁻ | Compatibility with existing Louvain workflows |
| `leiden-mod-alpha` | LeidenSigned (modularity) | α·Q⁺ − (1−α)·Q⁻ | General signed networks |
| `leiden-cpm-single` | LeidenSigned (CPM) | Σ(w_ij − γ) on signed graph | Correlation-based networks (co-expression) |

**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.

## Installation

```bash
pip install signet
```

pip install -i https://test.pypi.org/simple/ signet-gene==0.0.8

### Dependencies

- Python ≥ 3.9
- numpy
- igraph (python-igraph)
- leidenalg
- networkx
- matplotlib (for plotting outputs)
- scipy (for MAT format support)

## Quick start

```bash
# Basic run with Leiden signed modularity
signet --pos positive_edges.tsv --neg negative_edges.tsv \
  --method leiden-mod-alpha --alpha 0.6 --resolution 1.0 \
  --out-prefix results/my_analysis

# Leiden signed CPM (recommended for co-expression networks)
signet --pos positive_edges.tsv --neg negative_edges.tsv \
  --method leiden-cpm-single --gamma 0.05 --lambda-neg 1.0 \
  --out-prefix results/my_analysis

# Louvain signed
signet --pos positive_edges.tsv --neg negative_edges.tsv \
  --method louvain --alpha 0.6 --resolution 1.0 \
  --out-prefix results/my_analysis
```

## Input formats

### TSV (default)

Tab-separated file with five columns (no header):

```
gene_id1    gene_id2    gene_name1    gene_name2    weight
ENSG00001   ENSG00002   GeneA         GeneB         15.3
ENSG00001   ENSG00003   GeneA         GeneC         12.1
```

Provide separate files for positive and negative edges via `--pos` and `--neg`. Use `--thre-pos` and `--thre-neg` to filter edges by weight.

### MAT (MATLAB sparse matrix)

```bash
signet --format mat --mat data/network.mat \
  --pos-key pos --neg-key neg \
  --method leiden-mod-alpha --out-prefix results/mat_run
```

The MAT file should contain two sparse matrices (positive and negative adjacency).

## Output files

When `--out-prefix` is specified, SIGNET generates the following outputs:

| File | Description |
|------|-------------|
| `<prefix>_partition.tsv` | Community assignment for each gene (gene_id, gene_name, community) |
| `<prefix>_summary.txt` | Parameters, graph statistics, community size distribution, entropy |
| `<prefix>_communities.gmt` | GMT format for direct use with GSEA, Enrichr, clusterProfiler |
| `<prefix>_community_sizes.pdf` | Rank-size plot and histogram of community sizes |
| `<prefix>_inter_community.pdf` | Heatmap of inter-community edge density (positive / negative / signed) |
| `<prefix>_module_<id>.pdf` | Subnetwork visualization for top modules (positive=red, negative=blue) |

### Disabling outputs

```bash
--no-plot     # Skip all PDF plots
--no-gmt      # Skip GMT output
--quiet       # Suppress progress messages
```

### Controlling module plots

```bash
--top-modules 10             # Number of top modules to plot (default: 10)
--max-nodes-per-module 200   # Max nodes per module plot (default: 200)
```

## Parameters

### Method-specific parameters

**Modularity-based methods** (`louvain`, `leiden-mod-alpha`):

| Parameter | Description | Default |
|-----------|-------------|---------|
| `--alpha` | Balance between positive and negative modularity (0–1). Higher α emphasizes positive edges. | 0.5 |
| `--resolution` | Resolution parameter. Higher values produce more, smaller communities. | 1.0 |

**CPM method** (`leiden-cpm-single`):

| Parameter | Description | Default |
|-----------|-------------|---------|
| `--gamma` | CPM resolution. Minimum edge density within communities. | 0.5 |
| `--lambda-neg` | Weight multiplier for negative edges. Controls how strongly negative edges repel. | 0.0 |
| `--neg-weight-mode` | How to transform negative weights: `absolute` (−λ·\|w\|) or `signed` (λ·w). | absolute |

### Common parameters

| Parameter | Description | Default |
|-----------|-------------|---------|
| `--seed` | Random seed for reproducibility. | None |
| `--out-prefix` | Output file prefix. If omitted, only prints summary to stdout. | None |

## Python API

SIGNET can also be used as a Python library:

```python
import signet.network_module as nr
import signet.LeidenSigned as les

# Load graphs
G_pos = nr.load_graph_from_TSV_igraph("positive.tsv", threshold=10)
G_neg = nr.load_graph_from_TSV_igraph("negative.tsv", threshold=5)

# Leiden signed modularity
partition = les.find_partition_signed_modularity_alpha(
    G_pos, G_neg, alpha=0.6, resolution=1.0, seed=42
)

# Leiden signed CPM (single signed graph)
G_signed = nr.load_signed_graph_from_two_TSV_igraph(
    "positive.tsv", "negative.tsv",
    pos_threshold=10, neg_threshold=5, lambda_neg=1.0
)
partition = les.find_partition_signed_CPM_single_graph(
    G_signed, gamma=0.05, seed=42
)

# Inspect results
print(partition.membership)
nr.display_communities_by_name(G_pos, partition)
nr.count_nodes_in_communities(partition)
```

### Visualization

```python
# Visualize a specific module (positive=red, negative=blue)
nr.visualize_module_signed(G_pos, G_neg, partition, community_id=0)

# Visualize the module containing a specific gene
nr.visualize_module_of_gene_signed(G_pos, G_neg, partition, "TP53")

# Top-degree nodes within a module
nr.visualize_module_of_gene_top_degree_nodes_signed(
    G_pos, G_neg, partition, "TP53", top_n=30
)
```

## Benchmarking

SIGNET includes a simulation framework for evaluating community detection methods on signed networks.

```bash
# Quick test (no signet package required)
python sim3_benchmark.py test

# Full benchmark with all methods and parameter grids
python sim3_benchmark.py run sim3_results/

# Generate example correlation matrix visualizations
python sim3_benchmark.py plot sim3_results/
```

### Simulation types

| Type | Model | Tests |
|------|-------|-------|
| **Type A** | Correlation-based co-expression | Modularity null model distortion on dense positive graphs |
| **Type B** | Planted partition (stochastic block model) | Control — balanced positive/negative structure |
| **Type C** | scRNA-seq exclusive expression | Multipartite negative graph where "enemy of enemy ≠ friend" |

### Example visualizations

```bash
# Generate concrete examples showing signed vs unsigned differences
python sim3_examples.py sim3_examples/
```

This produces network graphs and adjacency matrix heatmaps at three difficulty levels, clearly demonstrating when signed methods outperform unsigned methods.

## How it works

### Signed modularity (multiplex optimization)

The positive and negative graphs are treated as two layers of a multiplex network. The objective function is:

```
Q_signed = α · Q_modularity(G⁺) − (1 − α) · Q_modularity(G⁻)
```

This is optimized using `leidenalg.optimise_partition_multiplex`, which simultaneously considers both layers with different weights.

### Signed CPM (single graph)

Positive and negative edges are combined into a single graph with signed weights:

```
w_signed(i,j) = w_pos(i,j) − λ_neg · w_neg(i,j)
```

Standard CPM is then applied, where the objective function naturally penalizes negative edges within the same community:

```
H_CPM = Σ_{i,j in same community} (w_signed(i,j) − γ)
```

## Choosing a method

```
Is your network correlation-based (co-expression, etc.)?
├── Yes → Use leiden-cpm-single
│         Start with --gamma 0.05 --lambda-neg 1.0
│         Increase gamma for smaller communities
│
└── No (e.g., social network, citation network)
    ├── Need reproducibility/stability? → Use leiden-mod-alpha
    │   Start with --alpha 0.6 --resolution 1.0
    │
    └── Compatibility with existing Louvain pipeline? → Use louvain
        Start with --alpha 0.6 --resolution 1.0
```

## Citation

If you use SIGNET in your research, please cite:

```
[Citation information to be added]
```

