"""Marker gene visualizations: rank_genes_groups dot/matrix plots and
matrix/violin plots for a user-supplied marker set."""
from __future__ import annotations
from pathlib import Path
import matplotlib.pyplot as plt
import scanpy as sc
from ._config import PlotConfig
from ._style import save_figure
from ._utils import require_columns
__all__ = [
"rank_genes_matrix_and_dot",
"annotation_marker_matrixplot",
"annotation_marker_stacked_violin",
]
[docs]
def rank_genes_matrix_and_dot(
adata,
groupby_column: str,
n_markers: int = 5,
method: str = "wilcoxon",
layer: str | None = None,
standard_scale: str | None = "var",
use_expression: bool = False,
figsize: tuple = (12, 8),
# title: str | None = None,
save_name: str | None = None,
config: PlotConfig | None = None,
output_dir: str | Path | None = "figures",
**kwargs,
):
"""Compute marker genes with ``sc.tl.rank_genes_groups`` and plot them as
a dotplot and a matrixplot, both ordered by a dendrogram over ``groupby_column``.
By default, dot/matrix color encodes log fold change (``config.rank_genes``
defaults: ``values_to_plot="logfoldchanges"``, diverging ``cmap``/``vmin``/
``vmax``). Set ``use_expression=True`` to instead color by mean expression,
scaled per gene via ``standard_scale`` (``"var"`` by default).
``**kwargs`` are forwarded to both the ``sc.pl.rank_genes_groups_dotplot``
and ``sc.pl.rank_genes_groups_matrixplot`` calls, overriding this
function's own defaults -- anything the underlying scanpy functions accept.
Returns ``(fig_dot, fig_matrix)``.
"""
config = PlotConfig.load(config)
require_columns(adata, groupby_column)
general = config.general
rank_cfg = config["rank_genes"]
print(f"Computing marker genes for '{groupby_column}' using {method} method...")
sc.tl.dendrogram(adata, groupby=groupby_column)
sc.tl.rank_genes_groups(
adata, groupby=groupby_column, method=method, layer=layer, use_raw=False,
min_logfoldchange=rank_cfg["min_logfoldchange"],
)
plt.rcParams["font.family"] = general["font_family"]
if use_expression:
values_to_plot = None
plot_standard_scale = standard_scale
vmin = None
vmax = None
cmap = config["continuous"]["cmap"]
else:
values_to_plot = rank_cfg["values_to_plot"]
plot_standard_scale = None
vmin = rank_cfg["vmin"]
vmax = rank_cfg["vmax"]
cmap = rank_cfg["cmap"]
print("Generating dotplot...")
fig_dot = plt.figure(figsize=figsize, dpi=general["dpi"])
sc.pl.rank_genes_groups_dotplot(
adata,
**{
"groupby": groupby_column,
"standard_scale": plot_standard_scale,
"n_genes": n_markers,
"use_raw": False,
"layer": layer,
"ax": fig_dot.gca(),
"var_group_rotation": rank_cfg["var_group_rotation"],
"values_to_plot": values_to_plot,
"vmin": vmin,
"vmax": vmax,
"cmap": cmap,
"show": False,
**kwargs,
},
)
ax_dot = fig_dot.gca()
legend = ax_dot.get_legend()
if legend:
for text in legend.get_texts():
text.set_fontsize(general["legend_fontsize"])
text.set_fontweight(general["legend_fontweight"])
plt.tight_layout()
if save_name is not None:
save_figure(fig_dot, output_dir, "marker_analysis", f"{save_name}_dotplot.png", config)
print("Generating matrixplot...")
fig_matrix = plt.figure(figsize=figsize, dpi=general["dpi"])
sc.pl.rank_genes_groups_matrixplot(
adata,
**{
"groupby": groupby_column,
"standard_scale": plot_standard_scale,
"n_genes": n_markers,
"use_raw": False,
"layer": layer,
"ax": fig_matrix.gca(),
"var_group_rotation": rank_cfg["var_group_rotation"],
"values_to_plot": values_to_plot,
"vmin": vmin,
"vmax": vmax,
"cmap": cmap,
"show": False,
**kwargs,
},
)
plt.tight_layout()
if save_name is not None:
save_figure(fig_matrix, output_dir, "marker_analysis", f"{save_name}_matrixplot.png", config)
return fig_dot, fig_matrix
def _flatten_markers(markers: dict[str, list[str]]) -> tuple[list[str], list[str]]:
cell_types = sorted(markers.keys())
flat = [gene for ct in cell_types for gene in markers[ct]]
return cell_types, flat
[docs]
def annotation_marker_matrixplot(
adata,
markers: dict[str, list[str]],
groupby_column: str,
layer: str | None = None,
standard_scale: str | None = "var",
cmap: str = "coolwarm",
figsize: tuple = (12, 8),
save_name: str | None = None,
config: PlotConfig | None = None,
output_dir: str | Path | None = "figures",
**kwargs,
):
"""Matrixplot of a user-supplied ``{cell_type: [marker_genes]}`` set,
restricted to cells whose ``groupby_column`` value is one of ``markers``' keys.
``**kwargs`` are forwarded to ``sc.pl.matrixplot``, overriding this
function's own defaults -- anything the underlying scanpy function accepts.
"""
config = PlotConfig.load(config)
require_columns(adata, groupby_column)
cell_types, flat_markers = _flatten_markers(markers)
fig = plt.figure(figsize=figsize, dpi=config.general["dpi"])
sc.pl.matrixplot(
adata[adata.obs[groupby_column].isin(cell_types)],
**{
"standard_scale": standard_scale,
"layer": layer,
"var_names": flat_markers,
"groupby": groupby_column,
"cmap": cmap,
"ax": fig.gca(),
"show": False,
**kwargs,
},
)
plt.tight_layout()
if save_name is not None:
save_figure(fig, output_dir, "marker_analysis", f"{save_name}_matrixplot_markers.png", config)
return fig
[docs]
def annotation_marker_stacked_violin(
adata,
markers: dict[str, list[str]],
groupby_column: str,
layer: str | None = None,
standard_scale: str | None = "var",
cmap: str = "coolwarm",
figsize: tuple = (12, 8),
save_name: str | None = None,
config: PlotConfig | None = None,
output_dir: str | Path | None = "figures",
**kwargs,
):
"""Stacked violin plot of a user-supplied ``{cell_type: [marker_genes]}`` set,
restricted to cells whose ``groupby_column`` value is one of ``markers``' keys.
``**kwargs`` are forwarded to ``sc.pl.stacked_violin``, overriding this
function's own defaults -- anything the underlying scanpy function accepts.
"""
config = PlotConfig.load(config)
require_columns(adata, groupby_column)
cell_types, flat_markers = _flatten_markers(markers)
fig = plt.figure(figsize=figsize, dpi=config.general["dpi"])
sc.pl.stacked_violin(
adata[adata.obs[groupby_column].isin(cell_types)],
**{
"standard_scale": standard_scale,
"layer": layer,
"var_names": flat_markers,
"groupby": groupby_column,
"cmap": cmap,
"ax": fig.gca(),
"show": False,
**kwargs,
},
)
plt.tight_layout()
if save_name is not None:
save_figure(fig, output_dir, "marker_analysis", f"{save_name}_stacked_violin_markers.png", config)
return fig