From 935d578b8b5ec90f0768d440b35143d659f0bc78 Mon Sep 17 00:00:00 2001 From: Blampey Quentin Date: Fri, 17 May 2024 10:53:59 +0200 Subject: [PATCH 1/5] use numpy histogram in scatter_density --- CHANGELOG.md | 9 ++++ pyproject.toml | 1 - pytometry/plotting/_scatter_density.py | 70 +++++++++++++------------- 3 files changed, 45 insertions(+), 35 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9ed76db --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [0.1.6] - 2024-xx-xx + +### Changed +- `pm.pl.scatter_density` now uses `np.histogram2d` +- `datashader` dependency has been removed diff --git a/pyproject.toml b/pyproject.toml index 8620d21..2d67011 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,6 @@ dependencies = [ "matplotlib", "readfcs >=1.1.0", "flowutils", - "datashader", "consensusclustering", "minisom" ] diff --git a/pytometry/plotting/_scatter_density.py b/pytometry/plotting/_scatter_density.py index 4e03cc4..730ebe3 100644 --- a/pytometry/plotting/_scatter_density.py +++ b/pytometry/plotting/_scatter_density.py @@ -1,13 +1,10 @@ from typing import Literal # noqa: TYP001 from typing import List, Optional, Tuple, Union -import datashader as ds +import matplotlib.colors as mcolors import matplotlib.pyplot as plt import numpy as np -import pandas as pd -import scanpy as sc from anndata import AnnData -from datashader.mpl_ext import dsshow from matplotlib.axes import Axes from matplotlib.colors import Colormap from matplotlib.scale import ScaleBase @@ -25,6 +22,7 @@ def scatter_density( y_lim: Optional[Tuple[float, float]] = None, ax: Optional[Axes] = None, figsize: Optional[tuple[int, int]] = None, + bins: Union[int, tuple[int, int]] = 500, cmap: Union[str, List, Colormap] = "jet", vmin: Optional[float] = None, vmax: Optional[float] = None, @@ -54,6 +52,8 @@ def scatter_density( draw into an existing figure. figsize (tuple), optional: Figure size (width, height) if ``ax`` not provided. Defaults to (10, 10). + bins (int or tuple), optional: + Number of bins for the `np.histogram2d` function cmap (str or list or :class:`matplotlib.colors.Colormap`), optional: For scalar aggregates, a matplotlib colormap name or instance. Alternatively, an iterable of colors can be passed and will be converted @@ -69,41 +69,43 @@ def scatter_density( Returns: Scatter plot that displays cell density """ - figsize = figsize if figsize is not None else (10, 10) ax = plt.subplots(figsize=figsize)[1] if ax is None else ax - if x_label is None: - x_label = x - if y_label is None: - y_label = y - # Create df from anndata object - markers = [x, y] - joined = sc.get.obs_df(adata, keys=[*markers], layer=layer) - # Convert variables to np.array - x = np.array(joined[x]) - y = np.array(joined[y]) + if isinstance(bins, int): + bins = (bins, bins) - # Plot density with datashader - df = pd.DataFrame(dict(x=x, y=y)) - dsartist = dsshow( - df, - ds.Point("x", "y"), - ds.count(), - vmin=vmin, - vmax=vmax, - norm=None, - # aspect="auto", - ax=ax, - cmap=cmap, + hist, xedges, yedges = np.histogram2d( + adata.obs_vector(x, layer=layer), adata.obs_vector(y, layer=layer), bins=bins ) - plt.colorbar(dsartist) + vmin = hist.min() if vmin is None else vmin + vmax = hist.max() if vmax is None else vmax - plt.xlim(x_lim) - plt.ylim(y_lim) - plt.yscale(x_scale) - plt.xscale(y_scale) - plt.xlabel(x_label) - plt.ylabel(y_label) + image = ax.imshow( + hist.T, + extent=[xedges[0], xedges[-1], yedges[0], yedges[-1]], + norm=mcolors.Normalize(vmin=vmin, vmax=vmax), + cmap=_get_cmap_white_background(cmap), + aspect="auto", + origin="lower", + ) + plt.colorbar(image, ax=ax) + + ax.set_xlim(x_lim) + ax.set_ylim(y_lim) + ax.set_yscale(x_scale) + ax.set_xscale(y_scale) + ax.set_xlabel(x if x_label is None else x_label) + ax.set_ylabel(y if y_label is None else y_label) plt.show() + + +def _get_cmap_white_background(cmap: Union[str, List, Colormap]) -> Colormap: + if isinstance(cmap, str): + cmap = plt.cm.get_cmap(cmap) + + colors = cmap(np.arange(cmap.N)) + colors[0] = np.array([1, 1, 1, 1]) + + return mcolors.ListedColormap(colors) From 5de533cd79f5a3364cf7733482927a5bfafd56ad Mon Sep 17 00:00:00 2001 From: Blampey Quentin Date: Fri, 17 May 2024 11:06:09 +0200 Subject: [PATCH 2/5] pre-commit changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ed76db..d46ed93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,5 +5,6 @@ All notable changes to this project will be documented in this file. ## [0.1.6] - 2024-xx-xx ### Changed + - `pm.pl.scatter_density` now uses `np.histogram2d` - `datashader` dependency has been removed From 5be3deac76a5b4266255b7db63cf6ee69745a15f Mon Sep 17 00:00:00 2001 From: Blampey Quentin Date: Fri, 17 May 2024 11:28:30 +0200 Subject: [PATCH 3/5] fix tests: use colormap from mpl --- pyproject.toml | 2 +- pytometry/plotting/_scatter_density.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2d67011..de4dbab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,6 @@ dynamic = ["version"] description = "Pytometry is a Python package for flow and mass cytometry analysis." requires-python = '>= 3.9' dependencies = [ - "nbproject", "numpy>=1.20.0", "numba>=0.57", "pandas<2.0.0,>=1.5.3", @@ -40,6 +39,7 @@ dev = [ test = [ "pytest>=6.0", "pytest-cov", + "nbproject", "nbproject_test >= 0.2.0", ] diff --git a/pytometry/plotting/_scatter_density.py b/pytometry/plotting/_scatter_density.py index 730ebe3..1707430 100644 --- a/pytometry/plotting/_scatter_density.py +++ b/pytometry/plotting/_scatter_density.py @@ -5,6 +5,7 @@ import matplotlib.pyplot as plt import numpy as np from anndata import AnnData +from matplotlib import colormaps from matplotlib.axes import Axes from matplotlib.colors import Colormap from matplotlib.scale import ScaleBase @@ -23,7 +24,7 @@ def scatter_density( ax: Optional[Axes] = None, figsize: Optional[tuple[int, int]] = None, bins: Union[int, tuple[int, int]] = 500, - cmap: Union[str, List, Colormap] = "jet", + cmap: Union[str, Colormap] = "jet", vmin: Optional[float] = None, vmax: Optional[float] = None, *, @@ -101,9 +102,9 @@ def scatter_density( plt.show() -def _get_cmap_white_background(cmap: Union[str, List, Colormap]) -> Colormap: +def _get_cmap_white_background(cmap: Union[str, Colormap]) -> Colormap: if isinstance(cmap, str): - cmap = plt.cm.get_cmap(cmap) + cmap = colormaps.get_cmap(cmap) colors = cmap(np.arange(cmap.N)) colors[0] = np.array([1, 1, 1, 1]) From d0383e546dece3c19b52b6f44d3e73e874bf6b2b Mon Sep 17 00:00:00 2001 From: Blampey Quentin Date: Fri, 17 May 2024 11:32:40 +0200 Subject: [PATCH 4/5] remove unused List type --- pytometry/plotting/_scatter_density.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytometry/plotting/_scatter_density.py b/pytometry/plotting/_scatter_density.py index 1707430..ef0a6ff 100644 --- a/pytometry/plotting/_scatter_density.py +++ b/pytometry/plotting/_scatter_density.py @@ -1,5 +1,5 @@ from typing import Literal # noqa: TYP001 -from typing import List, Optional, Tuple, Union +from typing import Optional, Tuple, Union import matplotlib.colors as mcolors import matplotlib.pyplot as plt From 58dc03d685601492b2ddda52ca750b4510c2c852 Mon Sep 17 00:00:00 2001 From: Blampey Quentin Date: Fri, 17 May 2024 14:49:31 +0200 Subject: [PATCH 5/5] remove changelog file --- CHANGELOG.md | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index d46ed93..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,10 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -## [0.1.6] - 2024-xx-xx - -### Changed - -- `pm.pl.scatter_density` now uses `np.histogram2d` -- `datashader` dependency has been removed