Skip to content

Commit e1c6998

Browse files
authored
Merge pull request #290 from scverse/feature/volcano-top-genes-v2
Allow volcano() top to accept gene names
2 parents df101b4 + ec8c78f commit e1c6998

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)