Skip to content

Commit ec8c78f

Browse files
PauBadiaMclaude
andcommitted
Allow volcano() top parameter to accept gene names
The `top` parameter now accepts a string or list of strings to annotate specific genes on volcano plots, in addition to the existing int behavior. Missing gene names are reported in the error message. Bump version to 2.1.5. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent df101b4 commit ec8c78f

4 files changed

Lines changed: 40 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning][].
88
[keep a changelog]: https://keepachangelog.com/en/1.0.0/
99
[semantic versioning]: https://semver.org/spec/v2.0.0.html
1010

11+
## 2.1.5
12+
13+
### Added
14+
- `pl.volcano` now accepts a gene name (`str`) or list of gene names (`list[str]`) for the `top` parameter to annotate specific features on volcano plots
15+
1116
## 2.1.4
1217

1318
### Changes

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ requires = [ "hatchling" ]
44

55
[project]
66
name = "decoupler"
7-
version = "2.1.4"
7+
version = "2.1.5"
88
description = "Python package to perform enrichment analysis from omics data."
99
readme = "README.md"
1010
license = { file = "LICENSE" }

src/decoupler/pl/_volcano.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def volcano(
1515
y: str,
1616
net: pd.DataFrame | None = None,
1717
name: str | None = None,
18-
top: int = 5,
18+
top: int | str | list[str] = 5,
1919
thr_stat: float = 0.5,
2020
thr_sign: float = 0.05,
2121
max_stat: float | None = None,
@@ -40,7 +40,8 @@ def volcano(
4040
name
4141
Name of the source to subset ``net``.
4242
top
43-
Number of top differentially abundant features to show.
43+
Number of top differentially abundant features to show. Can also be a gene name
44+
(``str``) or a list of gene names (``list[str]``) to annotate specific features.
4445
thr_stat
4546
Significance threshold for change statitsics.
4647
thr_sign
@@ -76,7 +77,14 @@ def volcano(
7677
assert isinstance(data, pd.DataFrame), m
7778
assert {x, y}.issubset(data.columns), m
7879
assert (net is None) == (name is None), "net and name must be both defined or both None"
79-
assert isinstance(top, int) and top > 0, "top must be int and > 0"
80+
if isinstance(top, str):
81+
top = [top]
82+
if isinstance(top, list):
83+
assert all(isinstance(g, str) for g in top), "top must contain only str gene names"
84+
missing = [g for g in top if g not in data.index]
85+
assert not missing, f"gene names not found in data.index: {missing}"
86+
else:
87+
assert isinstance(top, int) and top > 0, "top must be int, str, list[str], and int must be > 0"
8088
assert isinstance(thr_stat, int | float) and thr_stat > 0, "thr_stat must be numeric and > 0"
8189
assert isinstance(thr_sign, int | float) and thr_sign > 0, "thr_sign must be numeric and > 0"
8290
if max_stat is None:
@@ -127,8 +135,11 @@ def volcano(
127135
bp.ax.set_xlabel(x)
128136
bp.ax.set_ylabel(rf"$-\log_{{10}}({y})$")
129137
# Show top sign features
130-
signs = df[up_msk | dw_msk].sort_values("pval", ascending=False)
131-
signs = signs.iloc[:top]
138+
if isinstance(top, list):
139+
signs = df[df.index.isin(top)]
140+
else:
141+
signs = df[up_msk | dw_msk].sort_values("pval", ascending=False)
142+
signs = signs.iloc[:top]
132143
texts = []
133144
for x, y, s in zip(signs["stat"], signs["pval"], signs.index, strict=False):
134145
texts.append(bp.ax.text(x, y, s))

tests/pl/test_volcano.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,21 @@ def test_volcano(
3232
else:
3333
with pytest.raises(AssertionError):
3434
dc.pl.volcano(data=deg, x="stat", y="padj", net=net, name=name, return_fig=True)
35+
36+
37+
@pytest.mark.parametrize(
38+
"top,a_err",
39+
[
40+
["G03", False],
41+
[["G01", "G03"], False],
42+
[["G01", "NONEXISTENT"], True],
43+
],
44+
)
45+
def test_volcano_top_genes(deg, top, a_err):
46+
if not a_err:
47+
fig = dc.pl.volcano(data=deg, x="stat", y="padj", top=top, return_fig=True)
48+
assert isinstance(fig, Figure)
49+
plt.close(fig)
50+
else:
51+
with pytest.raises(AssertionError):
52+
dc.pl.volcano(data=deg, x="stat", y="padj", top=top, return_fig=True)

0 commit comments

Comments
 (0)