From e36a113b298cd3ecd9c55113db69a99e9bf14d12 Mon Sep 17 00:00:00 2001 From: eli knaap Date: Mon, 13 May 2024 17:39:30 -0700 Subject: [PATCH 01/24] add classify rgb function --- mapclassify/__init__.py | 2 +- mapclassify/_classify_API.py | 61 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/mapclassify/__init__.py b/mapclassify/__init__.py index 55b9ccec..6c17f9fb 100644 --- a/mapclassify/__init__.py +++ b/mapclassify/__init__.py @@ -1,7 +1,7 @@ import contextlib from importlib.metadata import PackageNotFoundError, version -from ._classify_API import classify +from ._classify_API import classify, classify_to_rgba from .classifiers import ( CLASSIFIERS, BoxPlot, diff --git a/mapclassify/_classify_API.py b/mapclassify/_classify_API.py index c0c0fa8b..9002a406 100644 --- a/mapclassify/_classify_API.py +++ b/mapclassify/_classify_API.py @@ -1,3 +1,7 @@ +import pandas as pd +from matplotlib import cm +from matplotlib.colors import Normalize + from .classifiers import ( BoxPlot, EqualInterval, @@ -214,3 +218,60 @@ def classify( classifier = _classifiers[scheme](y, k) return classifier + + +def classify_to_rgba( + values, classifier="quantiles", k=6, cmap="viridis", nan_color=[255, 255, 255, 255] +): + """Convert array of values into RGBA colors using a colormap and classifier. + + Parameters + ---------- + values : list-like + array of input values + classifier : str, optional + string description of a mapclassify classifier, by default "quantiles" + k : int, optional + number of classes to form, by default 6 + cmap : str, optional + name of matplotlib colormap to use, by default "viridis" + nan_color : list, optional + RGBA color to fill NaN values, by default [255, 255, 255, 255] + + Returns + ------- + numpy.array + array of lists with each list containing four values that define a color using + RGBA specification. + """ + if not pd.api.types.is_list_like(nan_color) and not len(nan_color) == 4: + raise ValueError("`nan_color` must be list-like of 4 values: (R,G,B,A)") + + # only operate on non-NaN values + v = pd.Series(values) + legit_indices = v[~v.isna()].index.values + + # transform (non-NaN) values into class bins + bins = classify(v.dropna().values, scheme=classifier, k=k).yb + + # create a normalizer using the data's range (not strictly 1-k...) + norm = Normalize(min(bins), max(bins)) + + # map values to colors + n_cmap = cm.ScalarMappable(norm=norm, cmap=cmap) + + # create array of RGB values (lists of 4) of length n + vals = [n_cmap.to_rgba(i, alpha=None) for i in bins] + + # convert decimals to whole numbers + rgbas = [] + for val in vals: + # convert each value in the array of lists + rgbas.append([round(i * 255, 0) for i in val]) + + # replace non-nan values with colors + colors = pd.Series(rgbas, index=legit_indices) + v.update(colors) + v = v.fillna(f"{nan_color}").apply(list) + + return v.values From bec1919aa6c3118133995eb89d92ba59efa9a314 Mon Sep 17 00:00:00 2001 From: eli knaap Date: Tue, 18 Jun 2024 20:53:32 -0700 Subject: [PATCH 02/24] alpha --- mapclassify/_classify_API.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mapclassify/_classify_API.py b/mapclassify/_classify_API.py index 9002a406..678abddb 100644 --- a/mapclassify/_classify_API.py +++ b/mapclassify/_classify_API.py @@ -221,7 +221,12 @@ def classify( def classify_to_rgba( - values, classifier="quantiles", k=6, cmap="viridis", nan_color=[255, 255, 255, 255] + values, + classifier="quantiles", + k=6, + cmap="viridis", + alpha=1, + nan_color=[255, 255, 255, 255], ): """Convert array of values into RGBA colors using a colormap and classifier. @@ -235,6 +240,8 @@ def classify_to_rgba( number of classes to form, by default 6 cmap : str, optional name of matplotlib colormap to use, by default "viridis" + alpha : float + alpha parameter that defines transparency. Should be in the range [0,1] nan_color : list, optional RGBA color to fill NaN values, by default [255, 255, 255, 255] @@ -244,6 +251,8 @@ def classify_to_rgba( array of lists with each list containing four values that define a color using RGBA specification. """ + if not (alpha <= 1) and (alpha >= 0): + raise ValueError("alpha must be in the range [0,1]") if not pd.api.types.is_list_like(nan_color) and not len(nan_color) == 4: raise ValueError("`nan_color` must be list-like of 4 values: (R,G,B,A)") @@ -261,13 +270,13 @@ def classify_to_rgba( n_cmap = cm.ScalarMappable(norm=norm, cmap=cmap) # create array of RGB values (lists of 4) of length n - vals = [n_cmap.to_rgba(i, alpha=None) for i in bins] + vals = [n_cmap.to_rgba(i, alpha=alpha) for i in bins] # convert decimals to whole numbers rgbas = [] for val in vals: # convert each value in the array of lists - rgbas.append([round(i * 255, 0) for i in val]) + rgbas.append([i * 255 for i in val]) # replace non-nan values with colors colors = pd.Series(rgbas, index=legit_indices) From 6070e8c4303743d328fad1c71146215b77331862 Mon Sep 17 00:00:00 2001 From: eli knaap Date: Mon, 1 Jul 2024 20:43:24 -0700 Subject: [PATCH 03/24] move to util; add test --- mapclassify/__init__.py | 3 +- mapclassify/_classify_API.py | 69 -------------------------------- mapclassify/tests/test_rgba.py | 15 +++++++ mapclassify/util.py | 73 ++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 70 deletions(-) create mode 100644 mapclassify/tests/test_rgba.py create mode 100644 mapclassify/util.py diff --git a/mapclassify/__init__.py b/mapclassify/__init__.py index 6c17f9fb..ffdb75b8 100644 --- a/mapclassify/__init__.py +++ b/mapclassify/__init__.py @@ -1,7 +1,8 @@ import contextlib from importlib.metadata import PackageNotFoundError, version -from ._classify_API import classify, classify_to_rgba +from . import util +from ._classify_API import classify from .classifiers import ( CLASSIFIERS, BoxPlot, diff --git a/mapclassify/_classify_API.py b/mapclassify/_classify_API.py index 678abddb..9ea2e02a 100644 --- a/mapclassify/_classify_API.py +++ b/mapclassify/_classify_API.py @@ -1,6 +1,3 @@ -import pandas as pd -from matplotlib import cm -from matplotlib.colors import Normalize from .classifiers import ( BoxPlot, @@ -218,69 +215,3 @@ def classify( classifier = _classifiers[scheme](y, k) return classifier - - -def classify_to_rgba( - values, - classifier="quantiles", - k=6, - cmap="viridis", - alpha=1, - nan_color=[255, 255, 255, 255], -): - """Convert array of values into RGBA colors using a colormap and classifier. - - Parameters - ---------- - values : list-like - array of input values - classifier : str, optional - string description of a mapclassify classifier, by default "quantiles" - k : int, optional - number of classes to form, by default 6 - cmap : str, optional - name of matplotlib colormap to use, by default "viridis" - alpha : float - alpha parameter that defines transparency. Should be in the range [0,1] - nan_color : list, optional - RGBA color to fill NaN values, by default [255, 255, 255, 255] - - Returns - ------- - numpy.array - array of lists with each list containing four values that define a color using - RGBA specification. - """ - if not (alpha <= 1) and (alpha >= 0): - raise ValueError("alpha must be in the range [0,1]") - if not pd.api.types.is_list_like(nan_color) and not len(nan_color) == 4: - raise ValueError("`nan_color` must be list-like of 4 values: (R,G,B,A)") - - # only operate on non-NaN values - v = pd.Series(values) - legit_indices = v[~v.isna()].index.values - - # transform (non-NaN) values into class bins - bins = classify(v.dropna().values, scheme=classifier, k=k).yb - - # create a normalizer using the data's range (not strictly 1-k...) - norm = Normalize(min(bins), max(bins)) - - # map values to colors - n_cmap = cm.ScalarMappable(norm=norm, cmap=cmap) - - # create array of RGB values (lists of 4) of length n - vals = [n_cmap.to_rgba(i, alpha=alpha) for i in bins] - - # convert decimals to whole numbers - rgbas = [] - for val in vals: - # convert each value in the array of lists - rgbas.append([i * 255 for i in val]) - - # replace non-nan values with colors - colors = pd.Series(rgbas, index=legit_indices) - v.update(colors) - v = v.fillna(f"{nan_color}").apply(list) - - return v.values diff --git a/mapclassify/tests/test_rgba.py b/mapclassify/tests/test_rgba.py new file mode 100644 index 00000000..7066609b --- /dev/null +++ b/mapclassify/tests/test_rgba.py @@ -0,0 +1,15 @@ +import geopandas +import numpy as np +from mapclassify.util import get_rgba + +world = geopandas.read_file( + "https://naciscdn.org/naturalearth/110m/cultural/ne_110m_admin_0_countries.zip" +) + +def test_rgba(): + colors = get_rgba(world.area, + cmap='viridis')[0] + assert colors == [np.float64(68.08602), + np.float64(1.24287), + np.float64(84.000825), + np.float64(255.0)] diff --git a/mapclassify/util.py b/mapclassify/util.py new file mode 100644 index 00000000..ad3233f0 --- /dev/null +++ b/mapclassify/util.py @@ -0,0 +1,73 @@ +from ._classify_API import classify + + +def get_rgba( + values, + classifier="quantiles", + k=6, + cmap="viridis", + alpha=1, + nan_color=[255, 255, 255, 255], +): + """Convert array of values into RGBA colors using a colormap and classifier. + + Parameters + ---------- + values : list-like + array of input values + classifier : str, optional + string description of a mapclassify classifier, by default "quantiles" + k : int, optional + number of classes to form, by default 6 + cmap : str, optional + name of matplotlib colormap to use, by default "viridis" + alpha : float + alpha parameter that defines transparency. Should be in the range [0,1] + nan_color : list, optional + RGBA color to fill NaN values, by default [255, 255, 255, 255] + + Returns + ------- + numpy.array + array of lists with each list containing four values that define a color using + RGBA specification. + """ + try: + import pandas as pd + from matplotlib import cm + from matplotlib.colors import Normalize + except ImportError as e: + raise ImportError("This function requires pandas and matplotlib") from e + if not (alpha <= 1) and (alpha >= 0): + raise ValueError("alpha must be in the range [0,1]") + if not pd.api.types.is_list_like(nan_color) and not len(nan_color) == 4: + raise ValueError("`nan_color` must be list-like of 4 values: (R,G,B,A)") + + # only operate on non-NaN values + v = pd.Series(values, dtype=object) + legit_indices = v[~v.isna()].index.values + + # transform (non-NaN) values into class bins + bins = classify(v.dropna().values, scheme=classifier, k=k).yb + + # create a normalizer using the data's range (not strictly 1-k...) + norm = Normalize(min(bins), max(bins)) + + # map values to colors + n_cmap = cm.ScalarMappable(norm=norm, cmap=cmap) + + # create array of RGB values (lists of 4) of length n + vals = [n_cmap.to_rgba(i, alpha=alpha) for i in bins] + + # convert decimals to whole numbers + rgbas = [] + for val in vals: + # convert each value in the array of lists + rgbas.append([i * 255 for i in val]) + + # replace non-nan values with colors + colors = pd.Series(rgbas, index=legit_indices) + v.update(colors) + v = v.fillna(f"{nan_color}").apply(list) + + return v.values From 1f7434e3b12108fcc0725f14095f8139a93811d8 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Wed, 26 Jun 2024 22:09:05 +0200 Subject: [PATCH 04/24] CI: ensure 3.9 envs are compatible (#218) * CI: ensure 3.9 envs are compatible * pin geopandas --- ci/39-min.yaml | 2 +- ci/39-numba.yaml | 2 +- ci/39.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/39-min.yaml b/ci/39-min.yaml index 9a1b291a..4a4968b3 100644 --- a/ci/39-min.yaml +++ b/ci/39-min.yaml @@ -10,7 +10,7 @@ dependencies: - scikit-learn=1.0 - scipy=1.8 # testing - - geopandas + - geopandas<1 - libpysal - pytest - pytest-cov diff --git a/ci/39-numba.yaml b/ci/39-numba.yaml index 94b7a9be..6620edbf 100644 --- a/ci/39-numba.yaml +++ b/ci/39-numba.yaml @@ -10,7 +10,7 @@ dependencies: - scikit-learn - scipy # testing - - geopandas + - geopandas<1 - libpysal - pytest - pytest-cov diff --git a/ci/39.yaml b/ci/39.yaml index 4ec7f5bd..e4fe4def 100644 --- a/ci/39.yaml +++ b/ci/39.yaml @@ -10,7 +10,7 @@ dependencies: - scikit-learn - scipy # testing - - geopandas + - geopandas<1 - libpysal - pytest - pytest-cov From 4de91bb9c2ca5d8e0bdb3d18bdc856807e645304 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Wed, 26 Jun 2024 22:54:26 +0200 Subject: [PATCH 05/24] CI: doctest only on ubuntu latest (#219) * CI: doctest only on ubuntu latest * try including coverage * doctestplus * Apply suggestions from code review Co-authored-by: James Gaboardi --------- Co-authored-by: James Gaboardi --- .github/workflows/testing.yml | 13 ++++++++++++- ci/311-numba.yaml | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 519f7c33..895b6021 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -63,7 +63,18 @@ jobs: - name: Run pytest run: | - pytest mapclassify -r a -v -n auto --doctest-modules --cov mapclassify --cov-config .coveragerc --cov-report xml --color yes --cov-append --cov-report term-missing + pytest mapclassify -r a -v -n auto --cov mapclassify --cov-report xml --color yes --cov-append --cov-report term-missing + + - name: run docstring tests + if: contains(matrix.environment-file, '311-numba') && contains(matrix.os, 'ubuntu') + run: | + pytest \ + -v \ + -r a \ + -n auto \ + --color yes \ + --cov mapclassify --cov-report xml --cov-append \ + --doctest-only mapclassify - name: codecov (${{ matrix.os }}, ${{ matrix.environment-file }}) uses: codecov/codecov-action@v4 diff --git a/ci/311-numba.yaml b/ci/311-numba.yaml index 3c62a28e..8934a588 100644 --- a/ci/311-numba.yaml +++ b/ci/311-numba.yaml @@ -15,6 +15,7 @@ dependencies: - pytest - pytest-cov - pytest-xdist + - pytest-doctestplus - codecov - matplotlib # optional From 0fad7cda9861e8b5bc71e6e349124bac246d3bc0 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Wed, 26 Jun 2024 23:15:03 +0200 Subject: [PATCH 06/24] CI: test against Python 3.12 (#220) * CI: test against Python 3.12 * pytest-doctestplus --- .github/workflows/testing.yml | 14 ++++++++------ ci/311-numba.yaml | 1 - ci/{311-dev.yaml => 312-dev.yaml} | 2 +- ci/312-numba.yaml | 22 ++++++++++++++++++++++ ci/312.yaml | 26 ++++++++++++++++++++++++++ pyproject.toml | 3 ++- 6 files changed, 59 insertions(+), 9 deletions(-) rename ci/{311-dev.yaml => 312-dev.yaml} (97%) create mode 100644 ci/312-numba.yaml create mode 100644 ci/312.yaml diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 895b6021..a6b3ff39 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -29,16 +29,18 @@ jobs: ci/310-numba.yaml, ci/311.yaml, ci/311-numba.yaml, - ci/311-dev.yaml, + ci/312.yaml, + ci/312-numba.yaml, + ci/312-dev.yaml, ] include: - - environment-file: ci/311.yaml + - environment-file: ci/312.yaml os: macos-latest - - environment-file: ci/311-numba.yaml + - environment-file: ci/312-numba.yaml os: macos-latest - - environment-file: ci/311.yaml + - environment-file: ci/312.yaml os: windows-latest - - environment-file: ci/311-numba.yaml + - environment-file: ci/312-numba.yaml os: windows-latest fail-fast: false @@ -66,7 +68,7 @@ jobs: pytest mapclassify -r a -v -n auto --cov mapclassify --cov-report xml --color yes --cov-append --cov-report term-missing - name: run docstring tests - if: contains(matrix.environment-file, '311-numba') && contains(matrix.os, 'ubuntu') + if: contains(matrix.environment-file, '312-numba') && contains(matrix.os, 'ubuntu') run: | pytest \ -v \ diff --git a/ci/311-numba.yaml b/ci/311-numba.yaml index 8934a588..3c62a28e 100644 --- a/ci/311-numba.yaml +++ b/ci/311-numba.yaml @@ -15,7 +15,6 @@ dependencies: - pytest - pytest-cov - pytest-xdist - - pytest-doctestplus - codecov - matplotlib # optional diff --git a/ci/311-dev.yaml b/ci/312-dev.yaml similarity index 97% rename from ci/311-dev.yaml rename to ci/312-dev.yaml index ea50961d..273afe06 100644 --- a/ci/311-dev.yaml +++ b/ci/312-dev.yaml @@ -2,7 +2,7 @@ name: test channels: - conda-forge dependencies: - - python=3.11 + - python=3.12 # testing - pytest - pytest-cov diff --git a/ci/312-numba.yaml b/ci/312-numba.yaml new file mode 100644 index 00000000..dd107396 --- /dev/null +++ b/ci/312-numba.yaml @@ -0,0 +1,22 @@ +name: test +channels: + - conda-forge +dependencies: + - python=3.12 + # required + - networkx + - numpy + - pandas + - scikit-learn + - scipy + # testing + - geopandas + - libpysal + - pytest + - pytest-cov + - pytest-xdist + - pytest-doctestplus + - codecov + - matplotlib + # optional + - numba diff --git a/ci/312.yaml b/ci/312.yaml new file mode 100644 index 00000000..d1cd6ad9 --- /dev/null +++ b/ci/312.yaml @@ -0,0 +1,26 @@ +name: test +channels: + - conda-forge +dependencies: + - python=3.12 + # required + - networkx + - numpy + - pandas + - scikit-learn + - scipy + # testing + - geopandas + - libpysal + - pytest + - pytest-cov + - pytest-xdist + - codecov + - matplotlib + # docs + - nbsphinx + - numpydoc + - sphinx + - sphinx-gallery + - sphinxcontrib-bibtex + - sphinx_bootstrap_theme diff --git a/pyproject.toml b/pyproject.toml index 228dec2c..2534ba5e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Intended Audience :: Science/Research", @@ -74,6 +75,7 @@ tests = [ "pytest", "pytest-cov", "pytest-xdist", + "pytest-doctestplus", ] all = ["numba[speedups,dev,docs,tests]"] @@ -94,7 +96,6 @@ extend-exclude = ''' [tool.ruff] line-length = 88 select = ["E", "F", "W", "I", "UP", "N", "B", "A", "C4", "SIM", "ARG"] -target-version = "py39" ignore = [ "B006", "B008", From aab5ddd3ea29850d84e38526a76181e0319d1611 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 15:33:50 +0000 Subject: [PATCH 07/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mapclassify/_classify_API.py | 1 - mapclassify/tests/test_rgba.py | 14 ++++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/mapclassify/_classify_API.py b/mapclassify/_classify_API.py index 9ea2e02a..c0c0fa8b 100644 --- a/mapclassify/_classify_API.py +++ b/mapclassify/_classify_API.py @@ -1,4 +1,3 @@ - from .classifiers import ( BoxPlot, EqualInterval, diff --git a/mapclassify/tests/test_rgba.py b/mapclassify/tests/test_rgba.py index 7066609b..52863e1c 100644 --- a/mapclassify/tests/test_rgba.py +++ b/mapclassify/tests/test_rgba.py @@ -6,10 +6,12 @@ "https://naciscdn.org/naturalearth/110m/cultural/ne_110m_admin_0_countries.zip" ) + def test_rgba(): - colors = get_rgba(world.area, - cmap='viridis')[0] - assert colors == [np.float64(68.08602), - np.float64(1.24287), - np.float64(84.000825), - np.float64(255.0)] + colors = get_rgba(world.area, cmap="viridis")[0] + assert colors == [ + np.float64(68.08602), + np.float64(1.24287), + np.float64(84.000825), + np.float64(255.0), + ] From 8e9ac989c39cb44069b5a0a402a44edcb2636e85 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 08:52:05 +0200 Subject: [PATCH 08/24] [pre-commit.ci] pre-commit autoupdate (#221) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 24.3.0 → 24.4.2](https://github.com/psf/black/compare/24.3.0...24.4.2) - [github.com/astral-sh/ruff-pre-commit: v0.3.5 → v0.5.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.5...v0.5.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b4285758..ea9b0372 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ files: 'mapclassify\/' repos: - repo: https://github.com/psf/black - rev: "24.3.0" + rev: "24.4.2" hooks: - id: black language_version: python3 - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.3.5" + rev: "v0.5.0" hooks: - id: ruff From f1f4add09560c0f48cc0e60cd3dbe49f173ddb14 Mon Sep 17 00:00:00 2001 From: eli knaap Date: Tue, 18 Jun 2024 21:20:19 -0700 Subject: [PATCH 09/24] plot histogram --- mapclassify/classifiers.py | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/mapclassify/classifiers.py b/mapclassify/classifiers.py index 515a0dc0..0d28c757 100644 --- a/mapclassify/classifiers.py +++ b/mapclassify/classifiers.py @@ -1123,6 +1123,52 @@ def plot( plt.savefig(file_name, dpi=dpi) return f, ax + def plot_histogram( + self, hist_kwargs=None, hist_color="dodgerblue", linecolor="black", ax=None + ): + """Plot histogram of `y` with bin values superimposed + + Parameters + ---------- + hist_kwargs : dict, optional + additional keyword arguments passed to pandas.Series.histogram, by default None + hist_color : str, optional + hue to color bars of the histogram, by default "dodgerblue" + linecolor : str, optional + color of the lines demarcating each class bin, by default "black" + ax : matplotlib.Axes, optional + axes object to plot onto, by default None + + Returns + ------- + matplotlib.Axes + an Axes object with histogram and class bins + + Raises + ------ + ImportError + depends on pandas and seaborn and rasies if not packages not present + """ + try: + import pandas as pd + import seaborn as sns + except ImportError as e: + raise ImportError from e( + "You must have pandas ans seaborn available to use this function" + ) + if hist_kwargs is None: + hist_kwargs = dict() + if 'color' not in hist_kwargs: + hist_kwargs['color'] = hist_color + series = pd.Series(self.y) + ax = series.plot(kind="hist", ax=ax, **hist_kwargs) + + lim = ax.get_ylim()[1] + for i in self.bins: + ax.vlines(i, 0, lim, color=linecolor) + sns.despine() + return ax + class HeadTailBreaks(MapClassifier): """ From d780b2f68ac31c8b63742719c3dc99eda0b56086 Mon Sep 17 00:00:00 2001 From: eli knaap Date: Tue, 18 Jun 2024 22:01:59 -0700 Subject: [PATCH 10/24] reorder args --- mapclassify/classifiers.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mapclassify/classifiers.py b/mapclassify/classifiers.py index 0d28c757..5e918b92 100644 --- a/mapclassify/classifiers.py +++ b/mapclassify/classifiers.py @@ -1124,20 +1124,21 @@ def plot( return f, ax def plot_histogram( - self, hist_kwargs=None, hist_color="dodgerblue", linecolor="black", ax=None + self, hist_color="dodgerblue", linecolor="black", ax=None, hist_kwargs=None ): """Plot histogram of `y` with bin values superimposed Parameters ---------- - hist_kwargs : dict, optional - additional keyword arguments passed to pandas.Series.histogram, by default None hist_color : str, optional hue to color bars of the histogram, by default "dodgerblue" linecolor : str, optional color of the lines demarcating each class bin, by default "black" ax : matplotlib.Axes, optional axes object to plot onto, by default None + hist_kwargs : dict, optional + additional keyword arguments passed to pandas.Series.histogram, by default + None Returns ------- From ce219a0563160979b99a7ce1287f7a39306a9231 Mon Sep 17 00:00:00 2001 From: eli knaap Date: Tue, 18 Jun 2024 22:14:20 -0700 Subject: [PATCH 11/24] despine optional; add comments --- mapclassify/classifiers.py | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/mapclassify/classifiers.py b/mapclassify/classifiers.py index 5e918b92..3bfec636 100644 --- a/mapclassify/classifiers.py +++ b/mapclassify/classifiers.py @@ -1124,18 +1124,27 @@ def plot( return f, ax def plot_histogram( - self, hist_color="dodgerblue", linecolor="black", ax=None, hist_kwargs=None + self, + hist_color="dodgerblue", + linecolor="black", + ax=None, + despine=True, + hist_kwargs=None, ): """Plot histogram of `y` with bin values superimposed Parameters ---------- hist_color : str, optional - hue to color bars of the histogram, by default "dodgerblue" + hue to color bars of the histogram, by default "dodgerblue". This option + overrides the 'color' entry in `hist_kwargs` if specified. linecolor : str, optional color of the lines demarcating each class bin, by default "black" ax : matplotlib.Axes, optional axes object to plot onto, by default None + despine : bool, optional + If True, to use seaborn's despine function to remove top and right axes, + default is True hist_kwargs : dict, optional additional keyword arguments passed to pandas.Series.histogram, by default None @@ -1148,26 +1157,37 @@ def plot_histogram( Raises ------ ImportError - depends on pandas and seaborn and rasies if not packages not present + depends on pandas (and seaborn if despine=True) and rasies if not packages + not present """ try: import pandas as pd - import seaborn as sns except ImportError as e: raise ImportError from e( - "You must have pandas ans seaborn available to use this function" + "You must have pandas available to use this function" ) if hist_kwargs is None: hist_kwargs = dict() - if 'color' not in hist_kwargs: - hist_kwargs['color'] = hist_color + # override color in case specified explicitly and inside the dict + if "color" not in hist_kwargs: + hist_kwargs["color"] = hist_color + # plot `y` as a histogram series = pd.Series(self.y) ax = series.plot(kind="hist", ax=ax, **hist_kwargs) - + # get the top of the ax so we know how high to raise each class bar lim = ax.get_ylim()[1] + # plot upper limit of each bin for i in self.bins: ax.vlines(i, 0, lim, color=linecolor) - sns.despine() + # despine if specified + if despine: + try: + import seaborn as sns + sns.despine(ax=ax) + except ImportError as e: + raise ImportError from e( + "The seaborn package is required to use the despine option" + ) return ax From 171a566594b5aa67cb617537a7ebdc2d1cfd1ed3 Mon Sep 17 00:00:00 2001 From: eli knaap Date: Tue, 18 Jun 2024 22:16:43 -0700 Subject: [PATCH 12/24] warn not raise if sns missing --- mapclassify/classifiers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mapclassify/classifiers.py b/mapclassify/classifiers.py index 3bfec636..4eb73190 100644 --- a/mapclassify/classifiers.py +++ b/mapclassify/classifiers.py @@ -1185,8 +1185,9 @@ def plot_histogram( import seaborn as sns sns.despine(ax=ax) except ImportError as e: - raise ImportError from e( - "The seaborn package is required to use the despine option" + warnings.warn( + "The seaborn package is required to use the despine option", + stacklevel=2, ) return ax From 394bd67cec0a2c8d6158a924b52fb0ea2d6ead80 Mon Sep 17 00:00:00 2001 From: eli knaap Date: Tue, 25 Jun 2024 23:22:56 -0700 Subject: [PATCH 13/24] martins improvements --- mapclassify/classifiers.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/mapclassify/classifiers.py b/mapclassify/classifiers.py index 4eb73190..17ada897 100644 --- a/mapclassify/classifiers.py +++ b/mapclassify/classifiers.py @@ -1157,14 +1157,15 @@ def plot_histogram( Raises ------ ImportError - depends on pandas (and seaborn if despine=True) and rasies if not packages - not present + depends matplotlib and rasies if not installed """ try: - import pandas as pd + import matplotlib.pyplot as plt + if ax is None: + _, ax = plt.subplots() except ImportError as e: raise ImportError from e( - "You must have pandas available to use this function" + "You must have matplotlib available to use this function" ) if hist_kwargs is None: hist_kwargs = dict() @@ -1172,8 +1173,7 @@ def plot_histogram( if "color" not in hist_kwargs: hist_kwargs["color"] = hist_color # plot `y` as a histogram - series = pd.Series(self.y) - ax = series.plot(kind="hist", ax=ax, **hist_kwargs) + ax.hist(self.y, **hist_kwargs) # get the top of the ax so we know how high to raise each class bar lim = ax.get_ylim()[1] # plot upper limit of each bin @@ -1181,14 +1181,8 @@ def plot_histogram( ax.vlines(i, 0, lim, color=linecolor) # despine if specified if despine: - try: - import seaborn as sns - sns.despine(ax=ax) - except ImportError as e: - warnings.warn( - "The seaborn package is required to use the despine option", - stacklevel=2, - ) + ax.spines['right'].set_visible(False) + ax.spines['top'].set_visible(False) return ax From 918ff1ccae7c0dedfafda212ea928b4023a51003 Mon Sep 17 00:00:00 2001 From: eli knaap Date: Wed, 26 Jun 2024 08:49:19 -0700 Subject: [PATCH 14/24] simplify args --- mapclassify/classifiers.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mapclassify/classifiers.py b/mapclassify/classifiers.py index 17ada897..3e4276a2 100644 --- a/mapclassify/classifiers.py +++ b/mapclassify/classifiers.py @@ -1125,17 +1125,17 @@ def plot( def plot_histogram( self, - hist_color="dodgerblue", + color="dodgerblue", linecolor="black", ax=None, despine=True, - hist_kwargs=None, + **kwargs, ): """Plot histogram of `y` with bin values superimposed Parameters ---------- - hist_color : str, optional + color : str, optional hue to color bars of the histogram, by default "dodgerblue". This option overrides the 'color' entry in `hist_kwargs` if specified. linecolor : str, optional @@ -1145,7 +1145,7 @@ def plot_histogram( despine : bool, optional If True, to use seaborn's despine function to remove top and right axes, default is True - hist_kwargs : dict, optional + kwargs : dict, optional additional keyword arguments passed to pandas.Series.histogram, by default None @@ -1167,13 +1167,13 @@ def plot_histogram( raise ImportError from e( "You must have matplotlib available to use this function" ) - if hist_kwargs is None: - hist_kwargs = dict() + if kwargs is None: + kwargs = dict() # override color in case specified explicitly and inside the dict - if "color" not in hist_kwargs: - hist_kwargs["color"] = hist_color + if "color" not in kwargs: + kwargs["color"] = color # plot `y` as a histogram - ax.hist(self.y, **hist_kwargs) + ax.hist(self.y, **kwargs) # get the top of the ax so we know how high to raise each class bar lim = ax.get_ylim()[1] # plot upper limit of each bin From 815a75f22ac685bee331d836ae99040e9bbd76ec Mon Sep 17 00:00:00 2001 From: eli knaap Date: Wed, 26 Jun 2024 09:12:58 -0700 Subject: [PATCH 15/24] mpl not pandas in docstring --- mapclassify/classifiers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapclassify/classifiers.py b/mapclassify/classifiers.py index 3e4276a2..38f2d665 100644 --- a/mapclassify/classifiers.py +++ b/mapclassify/classifiers.py @@ -1146,7 +1146,7 @@ def plot_histogram( If True, to use seaborn's despine function to remove top and right axes, default is True kwargs : dict, optional - additional keyword arguments passed to pandas.Series.histogram, by default + additional keyword arguments passed to matplotlib.axes.Axes.hist, by default None Returns From e668c8e37afcafb307579da003c52e72bc3f37c7 Mon Sep 17 00:00:00 2001 From: eli knaap Date: Wed, 26 Jun 2024 12:01:25 -0700 Subject: [PATCH 16/24] linewidth --- mapclassify/classifiers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mapclassify/classifiers.py b/mapclassify/classifiers.py index 38f2d665..e517b0bb 100644 --- a/mapclassify/classifiers.py +++ b/mapclassify/classifiers.py @@ -1127,6 +1127,7 @@ def plot_histogram( self, color="dodgerblue", linecolor="black", + linewidth=None, ax=None, despine=True, **kwargs, @@ -1140,6 +1141,8 @@ def plot_histogram( overrides the 'color' entry in `hist_kwargs` if specified. linecolor : str, optional color of the lines demarcating each class bin, by default "black" + linewidth : int, optional + change the linewidth demarcating each class bin ax : matplotlib.Axes, optional axes object to plot onto, by default None despine : bool, optional @@ -1178,7 +1181,7 @@ def plot_histogram( lim = ax.get_ylim()[1] # plot upper limit of each bin for i in self.bins: - ax.vlines(i, 0, lim, color=linecolor) + ax.vlines(i, 0, lim, color=linecolor, linewidth=linewidth) # despine if specified if despine: ax.spines['right'].set_visible(False) From 447df21c1f7cbd8bc2e9db70f2ce2d3594ddcb5b Mon Sep 17 00:00:00 2001 From: eli knaap Date: Wed, 26 Jun 2024 15:08:56 -0700 Subject: [PATCH 17/24] add tests --- .../tests/baseline/test_histogram_plot.png | Bin 0 -> 8726 bytes .../baseline/test_histogram_plot_despine.png | Bin 0 -> 8787 bytes .../test_histogram_plot_linewidth.png | Bin 0 -> 8660 bytes mapclassify/tests/test_mapclassify.py | 20 ++++++++++++++++++ 4 files changed, 20 insertions(+) create mode 100644 mapclassify/tests/baseline/test_histogram_plot.png create mode 100644 mapclassify/tests/baseline/test_histogram_plot_despine.png create mode 100644 mapclassify/tests/baseline/test_histogram_plot_linewidth.png diff --git a/mapclassify/tests/baseline/test_histogram_plot.png b/mapclassify/tests/baseline/test_histogram_plot.png new file mode 100644 index 0000000000000000000000000000000000000000..1c1274bcc278218fbcec6e6b7aa01bee3f063fbc GIT binary patch literal 8726 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 GXa5Ichld6L literal 0 HcmV?d00001 diff --git a/mapclassify/tests/baseline/test_histogram_plot_despine.png b/mapclassify/tests/baseline/test_histogram_plot_despine.png new file mode 100644 index 0000000000000000000000000000000000000000..e46eea1c0b0972fc4ab5c4bcdbe1f16537ec27f2 GIT binary patch literal 8787 zcmeHMc|4Ts+kd1^r_&-LvW;kwNRp6cT5KIjlIRFUl0A`aY}Gk65!0zo)L@FDB$C}& z5@RAuw(RR_}{n!Kh|6^IqQocLKo0~0xz+tt_UK(>X^ad(}D4m zJ-BA^7>y{8kwzaiY?fm+u-=0#yyiqHE2!Skg%RMN)EFS{hwV z0Fg_;9LbD;MNw?MOT@BW8e$%+yBHZ%jy5LKrYnR9W6i15P3F47BtCb%IGa= zY-$p5qQ*ooEiJvHF4~C5iS6}he;c?oma3Cb#&*m1>S6JdH7D9K3A;0k-z8-WAk&R1 zwt31s%$}TPNnlJ@JYTVgOn+zd?&7g@+x$I7cO@FbO8qB0@puUhx0W!BX5e%`9{*Cu z_h)IJ4;br^`i%Zb73|#}{#A!LSxwb64#t6{g}Lm~{9#mMc$mQL>zL)T9T~H)>5GWY zNsjhO`eFcX%8jgjPk#!xyqCPF#VZYc`Y?B7Uzz^~ie4F8yMd^w3JBnrvP%{QqkM+` zu+HO`MDuk7P6>>V!o!ZoYpG3@a;Dnzy&djKYA7WJ4L^?43Gg}|w|A>PyRc!B?ZYUc zkK;EjYq^>0_(cY@%euuij#8-u)P!GHikQm{!(yv1sxnHMKhs0Q{?NmmU+son9jV9n zYOwRvT-`(sZNT_zVq*L9g&w|Rony7tKg%1z_l4C{G2tQc!Q=d8ZCoMu_K0WT?u(R2 zF}2X8Idrq`N@?zyA~Q-CYOG5|UY~iRgxilGiqS5Il%A)g?AWYqvvKWaWksAQf{gCd z6`az1?Z_0?}!sKR zRyuvpz$OANydk%A>nL4Jww!Va_$1$Lh%<$itK(y1<4@>|UJr-7V@V z6&<@$<4-lg*`CxFICXeug5N0sA(S|RJpD8+qWrC!zk2%0GS%qXwQFGYlYlrB@WMk^m{KO|&Aq4rE2EaLA zi5FILti-b%NY}oc^{L(-Tj`=d`?z2-ugj6%p0)i1w4x#AI+OeQp-Yv&uGuuWI6@+n z;MkA!Ho$X#P?x(CnS{sVVLd{{-cXS3*+GW4vlCMf7)*Y=6zJ3I=ZpFy?r1( zH#hgW>jv2s+p1ZUT~2jRG}J;CrlC1eSNC?g*Vb9!I-ftUMv!*Wty{M)Umjgp-#&?5 zt@Toycb`eea~NeBszWl`1^JGjphd?*}bNTn|gHnItNMh7l)#ANBtd|tk(9meibSTrJ-Bow&c<1a) z0ITsL4TWc8jEv4;0WBs98l%>5oKq4g%15dWvZz4K$;qiq2Gp8cIpZyI4&&u>U$9uLUZY=I+4xb2PbxJ%vF$5(vwFJ{)_)rDBIlJ z+#_d<*Ec_mHO=2R!PZ&VS)vPh1y0j(r23tR^dJn%$YoINk4C74bT=^k7Em()c?n)X*Mk*{?v6puqm2k-vdPzTZOZ z?S+b41;bC=9Y5aNYGG?DMV0nFe$KDakV(&Oc=Qd8Kf|GhvU|i;R8-VFJ9BKv{+`Oe ziY8X{q+D{}9dJOSsREfW;Qe@A0>d4_{vNf%au& zWo1?SSUr`V1xo1jiswgq18LEy(Jbb%x_p=v@mN&l`>knp7P|6wsdA^ja96%J0rU~7 za+K|ga_GOq7yaOi-!v0sO-0hG-^ImWBPYxe#*M#%Q_)o(B!Eb|dBZW0A52ifU(g3EJWduR0qTpuDICDZ~98@S&@KOgC58T{t+8(No21H~(XS z|6c!p`Xl69PD^uRYFGN-gCR6thtb!S#nH3gQCEOAu!_}tUq0Sssv3<(Us>nNZ!EN= z>WdNdmoD$$zrV6HlrtFRz;*1PK|kJw_G9<9jA%$wbT_NoOSd_Mhk~izQOb<=t_0aG zp5Ghn&K(I~cIi-<*_xx*WyAV01mTPo(NoZPq%KU$20GosR6v;&I1-=5q1XVevZe9Y z-*P|hs4-T_b)SBuUfr_XXw6>|6{UXY^RC=-4EOM9>wu8ox`2&3GX;_N&rE=HKu8IL z51sH^?(Kl<-}S&T_^tU~JNhje2%;4iB%Y4uar`jSP~dExrh+_+LpL#~>@C45$PYpS zmfn5Et`gyACHrPVYVv&&v@_48(VQ2 zl|Z1%;*>KA1|wOsKC~*zYy%bpK1!ALwqnD?T!Cj-9tAeUX}Z7M!qW1AHqcJlZO9gk zOifMi%IF*buk%TE#OYYX>5q^)U>bC1k8@iccvkU&gLfcNkmy^e(rke*-QF*7YAb5g zbfyXvO6T8rQ;6QBTaMmZcjs*OC5m|I^l$G_jrnH|o<9Jbi@G1F10HzyjKUqa)@>1i zXu_vfjaq3aZYSEH&+$mbftRAqlK3Glt*wS6{m?-aevn~4+&lkl;6JGLgsEKOjEY^+ ze&~ov85tSP8l37}So_Pgv`z-~8(rFycTp|P^~BKq0qh!q$BXxCI;Q5`TfBkYZkB=N zWMgrCnMD3#u9N0advTEOxezAhTWNG;yR6%?Z`-$MGTX)cHite^AuM+X%>*yIy54bZ zPN8(>x;r%{8D?FZ_yS?-X~<=t@k-I4*@z%3!zi9T5i()h7wH`~OJrjBOpMQ$vn~|$ zHxca3n{EH)GjNO&%`X{tQbJhnm36YZS{X&-X>r)_FixnyWoer|PQ|Nm=n-m%Q{7pX ziRh;fcy_i7&>mWza4qxF915IkG^WM-*53i>QY(b8(r86)QNL6f9zLJj^OZc2K|UB- z;?bG23(lAp=M;0NA&iwRd_mJW2vo&jt$M@VM~@yY5X%;cHrbcVLI&14;9-qA;B-3~ z*(AbKxw?tWvfeQd==~H6V;QS>+*IMDORPg!kcESTLk`F*Qf^L;Xb(9+)r<_welE z8|+dtRtbd&%c09`amVqvlaTUe#pwoJ1)m^ z2Ib){OZ{nm@|ffSuWlOy1D}zEfIL!%nf4S&IC=R<5|8d8Q=dtmDuQ1$`$Do6f8s1E z>I&Y!{60b;q5j`=A!PBe3Muz5>32TuAu9)iR0`|csn5J4p?>Lj2%FqzMvTQnsLl#M z-sDx2@6qutaHe{v6HF8mGCIuLoHd7Riu_!2;E|xC^|EEjV+UnJrpp0aktmSm=_$uy z#RNPawmm?L(VS}TH}OSf0~{h}5x>%;`WIWD1(oGB=f5me5w~WDX`&Xl`HgN1ktmDG;fQ6l%4CraRlvlx4|EcaR z!v4)xa9mYD-Gkk}4be0LRv%r*gPrCvXygcc@A&DFN)`YJMS|}1dqK5p<1H`WyvZdI zOd=k7jJLdM{PJaUK(~)E&^oh9N>~`Z-w8HrqD>XD$;qz#)cQchLU<<`$DTsxEZ^dD z2To_(;L=1U;YaeWu78V#oXcQ2($Ixs&}tGC(%lI4nRU4_DR92k+G%dI5uJ$O-xA*y zvpU_gTpAJ!V0c1{+?|0BUrG&+gvpDBY}mqp1U4*0)bN6)odL)jdJkE1ncKiG=GA)D zsDtci&vCWn&ZT0b&BJCMG`_aZjMMUQL=Ev~m0v`KS+f=>2@=kz&gE|&5py{Y4^ubpOx>-ZCVCr-5nVAzA_Mp5UF(lfJiles}dqMpJB zuNqeWO(7%xGLLrObkCSyo!1Q1TV>^)O|+NEF_BiU&rp6hmfeC~Y9u%=a~HE<=L4t9 zS9dXTNkYaE4=?`3)3{+7ForVVW~B4UP(kWw^E4;vpLLSGiY6mBe^?)h$1`bEKrKwc z9A~~(`&-@zs8d^BS<*f^EMHwGt+f}HAS^75wm1VWOQyCcfZ>I;GlAai_~F+zK71aV z3S}{wvW>@K6QENR9$jfBe8Fu8(hVaEt4n~%imgj=UUOcAB&cL8F2{Im^4{LU|n;nbMUmCsa<~BZuA_tXh z=#+uGI6IlTwxcu@2dA_HoovGL-V*Ph^Y3sL7S4i@X2S@|1bDnfvQdXwbuXA!n>>%R z@nK72GRr;jKZauzo8Mm-r1kN6eNODD9+`Sg&rVTcVUQ8&ypYq*C49!S(Mtqh*1VUj)CA}pPjznxbI1=w0 zi4W`}@TVTTB<`x$V|OP(%Q4KfFNus&EiGuq;w!Gt)_I3zYfYqQM*?*uK|D1Ijo+tU zdqk9=J7baxkaur)h!Fjacabx0UG@uT>*7(U9iR3Kx5bQQgD#>&A5qv-e&?7yxjG;I zzGKzhEfgQBjP+?4dPDIkU8;LxRXqR2R%R-jcr_k`rXeJJS-vCn>=LS1O%{YMgWTB$ zRTLCSDJdzaJh$o&nXRMbd3GJ_cEMs!SXpq}q7H*YRH|)=_W_ZFN7P{Y^-0S81t zLZa3?wBw*dH99L%xQDT^y!)48*Edrchu$&V*Xy@$h&|NLNS1TYX@5jr8*oU5_~SAa z(<2XcN^_PR$@}m}w2j%;{mR;LxoZz(j@&UcHNvf9yrFK;+?3-HMy5EH^=|5tH=hk! z)L|5~{>5x~)0>1@?is?p;;Ne&8)9K#d*5YoWe_BK@l^yqT9*BUAesr^FaLWTax-<) z!n7FiYvrOuf`swjDkhGpCKojHMvt@!K{nVPVK3$uXhe+sSX)YnU1&-^foJi7}T~DBZGHjUa|A{p%lH zqQ@y&J3BicNZFRpwMUTqe=bWh6-s97u=8JM;*{LNe!e1$)v&d-#hrb3zkDd!XYeh) ze-p_Z#*t8VvaSBzpSw7>>%^WH$GVKpyt#wPzIS1tc*L3zd-qo_MvQKnvQ*$NRlK<` zi;I&%G*kU|7F_DB3S~O`*C_nz)f7M4nr&^>kYk@Y_~DXlqTZ2*4IVTLMWXPz(!R7c z2r?PI;<)-Wub-f$wTGtBJ0R`}VkJOb z#*?h2Kaec9c250sV0&MXZm%Dlzptk&5!K=p zH^;W+IWVah?JkzHlarQ~`^T4mV84n@f?(rvzff0YX6X!;c5B_*6k|MIUX(o-&_TrV zLn&pmEdQ=Gn7XN^DrT(`->*MP<%{f2M%rvJ0joe7uO8mRerxSbVNdmOhh#^m1hx7k zmM593HbNp3OPmtUm2{1YSrRVmv)Oy{K^P3%D_xz#NQmq9ud@i@^{H!HC&p)|Md+Vl zrKx)`6BAez$rKEWMlm03kh^|mGkc0vH(SS4?+{Xk`)Xzb83(1+YjD1f)w3*bQANB3 zZ;EWGFX-`Rttw8TwUCR3YuInGEyH6`$0yTXi#tu;%^rxF(?~KsQR;i*z};VT6gNA` z52um*dZK1~1LtQtn6U~QzZD-xA<1;KuZHv{1#d9)HpM}(0YTnvtzNVUK^|Z@#Oek!+n4Y!HBT^610{|*xWVT4RF zbu9C9J*t1x!ZIy#@>LOYF%sQkc+=vUgxh4si~qIBIP(R2x$eC~Ertkk$WrF8HB&9< zK)jks5f=x*aVl(uB7!9CiGJVwz~*{JZmwR$%x8s6Yg!_pSqAqHc;x7!2dg7y$JMXv z+9i^BHFzgyXOH^d{=uJoZNLN!aDSU@sxr>qN|bJAG4sBB;`s{B zujz9KdM>uw;v%L;)?~x*Cd2-*&L-V5%AMO;$OmkoKXI*U)POha8&F8H+HG%TWtDY! z?wx3eMRP2D%sEl=wGaMFt;A{bxe}-BsdFVN$B1{vjAX8zetm0m;kmX)%9fE~7r(TR z?c$}H8o7&w59$%5Q~OVQER4K%I?I5b^z*Jknp8;hcor?gAB;0#i07s_Z4Sf)5wEU} z%ZEuC!lPGKsVc%7<4EHbx*^<742q>_C-MR17+wSs``h9I)ZVd-ia3yNS+lT&142DV z1lg`4^Q}j|U;Yj2aP2gIkZzxQG^3)TVu1h+7p<#D`4Zm~uxq7;dHKT4qj1xx!$mGT zHN@#&aQg{Q8VoIKjb?D^=~vgn#C)d5!1K%BZpvu}KlN^Xc;^?-eJJ#;CWt4Bm@?2x z%acqi1zkRzP7hi_jGXQ*3(#(czdtNm+DWvSe5qY{;ZtMS&|R;L!?`yW=y{3E;iYCF zT&m6W^>l~2uWicaVFA9ql}1`xec{8YRBC5sun$VR5d!Xs!5h$ecEAUKvWGWq!Tx z?{n-^n|O6niSfw&OM)nf!Z5ipUuCk>=t76yp=dx4`2_;;>mq{bQ$kSgs3=W!ny5U%K+J>DegKW=DZqztQc^dqs-FT3yo#B+vICWKB z@%;Jog%?L#P?6~l5+m?Se%UPxEznszq0UgahHY1GVkD}o)(Pnb?#&sy7cBuvf2-`I zj>^`P+YODpEuj!xYE_DLa<=e&X~;rbvnX-IBnihr%ilj_OF&Zpi-9=lZh@}z3K@~n?foT^{> zpWJ4EUHL^160fXWyJpvwl`&K*&VylP)eyh-SNG??FUJLZc=n4m0RQ1-Nmjb)kr#j| zEch+^4WiCQze&dy`kr7GpPQ)x+AslH0NHnNxb2+LquQ4e|vfAWhQfGB|&?aK7`K`^)iuk^;%rc@3 z67US5BA`((yMZv@N}v;@Qv_yvzH??pg^6T@CfT47IZ-d1yj|xa2m@+WfyTd0B!|0@ z=RPfT%_^qjNbHmJ5JA$j=ENqlM zY*c7}RH$!HIc~jOn%d^gn+s3Y|Ay8m$f94`pSU_NY-lnH^4NI}LE^De#W3du+hq8j zoHaJ;Vh;d0Kb!J3TgMh``=`kt_o|EiBCV_p@pvYmiNzY!i7zadgC6ntNzrNv(f9y$4a~1M_aN2J6uEQHoB=t1;%Ju z6-2*hbJ@in<2@CDO!d%);0{-Zg>UrXn`ZQz_Qxl1KJAao=Y~=!yy3V1M{hhhIP~VO zT=AK=v0&$n&NSVdqeP6q&&6<<9o}0Jglw=g(-M07O*(-f`C1?~ZGveV^Qxx?U+JcE z1ERn%eVaD)#$P~wRH|@1v`)2uemvC7)gV@jv<^YeE(0|Mo#G%^2JRtAEDr|wZ!CiE zxdf7XeXP5TX^Zcb_ezfnS*d2cRsZI`H_1kM+`?~){-7IWe#Y_XS^MgFL3d#NKa{?y zmv6#GJ^bSzg`Tf(z3PNT>gLZB<0&Hq;Q@@+>}ot22G zp07>Qj^*(z5agwOUR%cTykOIU<9YTom)UM|nyb!!eoy6dUZ*WWK0I5$fXL0C+wwB= z^Ea3J_iJDG zM{KaSRzKEq8d_#GJf({F+eBIdrMTf=k_G}S?5pxmnxe@A4KWD^kiN(&3SmXTrs)g^fEfr)frVky>YwN5bg+`9< zC+aW6RhJ#Mf4-lUrK1rw-RxK%aLOfWrY(@M)~f+u%TF-=^0;`Aq3R#sFAi?wpr9a^3$Jw4n*gCFg>U`Ias=^!?Ed8VsDU6`kK%NO zCmu~NQsxb}$Q1bl2#P=I5@I-sW?PbzO>1AA@BHi3SceSqVf#7|+%p|S8hQR}uqj2# zg=?0^as*NN4LWaMR*^#of}ErbWuI)@A(hO}3iX+){?!;vA=ANHODklo3+kF6+%bQ~ z$B-v=b#~H9qty1Ckl^bi!3`~0B&qn}7=|fsV#m>}szp+P_hZ7cwWPj|`N*T(im5bJ zPdqkj-jhWtP9Z#Cb&DQxEpE>h2HoeGq>;8{`1i|3HUa6l4>zSk+t4bTO6wlU@3y~T z(I6Rml=mdoDPPgK)OU0uN>i2~4uAz0*2|KAN*DDEn_Bn-{qR2-P)vf3B8cEQP^LP> z0PbuL@eUO3AsSHr^~?ct@x+V3(X890>FHpV7aV*6c_t6F5%3D|ABAV$CL4dw$;=?l zj^EW@j7(6rUK}}85Y5$*IjoBUlKIU0BYZq%3LiM#Y;B!?JkCFoqgn{2OqoEAWTTy~ z2T%j?fVN-7r(44IvRVcIYW|*;>ZXQhTuBrUD!pFFO0~BG6d96N8Z7knI;a1I{Ch|H z(A-g}cH;6`KfgrFsILvXm0aZwYNih_KY~?GixRL=nsa}d^mB6!X?CQefOzjhqaHe{ zgR@<>bBYFTYB%@kB&Vd9xfZ9pd}+^*gPmqFK|!(y+^Y1La(3nz4@P`UZIo!w?07Hd zjz?mQNzl;6a;D#fc{)){UrO!~k24c{Z*cB$Bc*e(9*E6936gN5kl(Ox7>>t|JjII)&ZXRjivJxuh`4ICo zl-17U3y1J^Yo&AP(zOsJo~s7asv}t?AUc=%C1@uaYmHY=HJ$bLj#;V^rkQB^IcLEk zfT$RoW!uiVg@3wPK5#|Ved0rX>JzqIIQq0>9XaYM6SAXs7U;cbm-YYHqjEJps+vV^| z<*oNSOV2xt*zkrph>H~Qy%&!(KCT|NlYF#`h3RF?vqFy47J4uh!Srlgf8frcX^=)Q zj6|FT1$_Hbqf2I5q41s}`7KCuU1J!ovmd#oFZyO6gTpIJjE>F64 zzPohuHs8*l(k0W`+FFCd1Zg<8t7IdjS?!~Jr(Rs4v&e$tFz|szO!Jf6z8bSWczf7b zNryY6A84xzq*J9S7vQ(`9v-;62F(v%$P%hsgGG8B|0WZhPlf^(^-Ia9A$1EIxQwQ6 zhDJv8`#UZTCi^^z!S_f0s>OY1?+fEP{@4+NZeygFgQYF2(rf*Fxm*U!02ese5C#z9 zE)tBP310bZAIYQTflYI+5Y}OG``3KizWH(i%j;mP$q8wz7;^N4A&W z?aMCG31%#QRnp<6>RJ_&0J}nmr?CGrp^5No-RPI-2@33WAgyC8TMezmsnL8w-Pmo60|4+dl|)Gl3VyBL#92@QlT>=0Bkgp0R=Z za0eL9&JrI7@h76hQ*^iY#&Aw%V(iS94yGM>^12XsV=Zbg133&jOxYdsQp_oW=us|L zX7%Z^-VoXLUy>jy_bjh`1;+ zs(MudK#W#^J4k#*US z2a*l-YQMG>Fh0~@@&KU4U{H{=Y9JBnANzDX5wvcH*T|se zQ`g$6HHaQKp~q6t1B2oz)s9cXVJ=)5Fv$1$!rEP!; zYcUu!ECjsKM0-!z=uo@nsz$_Ft4>zmq~hZ>Z_+HxpiUG}`vl=e#fuVjyHUOn@@bTw z(QtxnwJQE_GnazE?&gl*9Pq^(C@yc&s5yAq>Iw9oeT7FX^=nQCdlsDoR$=hdJpJytQxQWZkJ zNK#}qZyrk%OPq_jUcBW=r{TOY=0x!sdVX1-3LMpOFV;Y$51^@pN+b{Hwy|LdB?~f2 zN{qHtj%2(ElnsMEKmkSAplf*wuVXU(ZdYbWNlB~6^hk4azH`~x^XC&Pf`+^77PE^y z_dj+j(MQh~dn<#BQ?9v0^64OJKo_hY1{)1({ldq+ReEeV*HYgPhiJf!6)$*iVsJnm5LF0knQJ$hdv`yJ3R^`@7J{L$H-7yV5R|ufs4kH8OurYSOLCsSt z6_tm+bu56?G_ovp{$t+pJURruv7cVwR)UGsGj_+t!r^7<*>mUgQH?1OaUZIo6oQtB z0(yFj-W~KIik#5JK#wfOW;<=eQ8f#su{%z2j9&?WNR-YHK@S{84^|xEl#LI0u|jB1 zpmlRkkBb^b%%{VexXNFi=dJ*5pKi4?&>CEfm9`$Svp9AST6s53!y?wLCPG2UC16K3 zk@~`VIHg~FCh&7^wj!js8+=UYZ zyLo*zahFz`<|rEnoJ2cV5x7>`H|kdPstgbfWotUdTFoBNS^*L_Jf^;J z7tnxwH!IB|4)A+wF+k0Q$F5bS#v*QU+%8#*r)UJp&$WyCw$M<linzfLt o-EWGN{m+_;|9{gqBwajzY-{P`XJ)TqFo@0WgH}&=p8V~<0KXNh`2YX_ literal 0 HcmV?d00001 diff --git a/mapclassify/tests/test_mapclassify.py b/mapclassify/tests/test_mapclassify.py index dc06e190..c042eb09 100644 --- a/mapclassify/tests/test_mapclassify.py +++ b/mapclassify/tests/test_mapclassify.py @@ -736,3 +736,23 @@ def test_pooled_bad_classifier(self): message = f"'{classifier}' not a valid classifier." with pytest.raises(ValueError, match=message): Pooled(self.data, classifier=classifier, k=4) + +class TestPlots: + def setup_method(self): + n = 20 + self.data = numpy.array([numpy.arange(n) + i * n for i in range(1, 4)]).T + + @pytest.mark.mpl_image_compare + def test_histogram_plot(self): + ax = Quantiles(self.data).plot_histogram() + return ax.get_figure() + + @pytest.mark.mpl_image_compare + def test_histogram_plot_despine(self): + ax = Quantiles(self.data).plot_histogram(despine=False) + return ax.get_figure() + + @pytest.mark.mpl_image_compare + def test_histogram_plot_linewidth(self): + ax = Quantiles(self.data).plot_histogram(linewidth=3, linecolor='red', color='yellow') + return ax.get_figure() \ No newline at end of file From c89ef62a889dc1df963ca006e4d169d9f9c39a1f Mon Sep 17 00:00:00 2001 From: eli knaap Date: Wed, 26 Jun 2024 15:12:45 -0700 Subject: [PATCH 18/24] pytestmpl --- .github/workflows/testing.yml | 3 ++- ci/310-numba.yaml | 1 + ci/310.yaml | 1 + ci/311-numba.yaml | 1 + ci/311.yaml | 1 + ci/312-dev.yaml | 1 + ci/312-numba.yaml | 1 + ci/312.yaml | 1 + pyproject.toml | 26 +++++++++----------------- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index a6b3ff39..56a044b3 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -76,7 +76,8 @@ jobs: -n auto \ --color yes \ --cov mapclassify --cov-report xml --cov-append \ - --doctest-only mapclassify + --doctest-only \ + --mpl mapclassify - name: codecov (${{ matrix.os }}, ${{ matrix.environment-file }}) uses: codecov/codecov-action@v4 diff --git a/ci/310-numba.yaml b/ci/310-numba.yaml index 20cd22f1..10a9e532 100644 --- a/ci/310-numba.yaml +++ b/ci/310-numba.yaml @@ -15,6 +15,7 @@ dependencies: - pytest - pytest-cov - pytest-xdist + - pytest-mpl - codecov - matplotlib # optional diff --git a/ci/310.yaml b/ci/310.yaml index d1e7f5ef..42a7cb20 100644 --- a/ci/310.yaml +++ b/ci/310.yaml @@ -15,5 +15,6 @@ dependencies: - pytest - pytest-cov - pytest-xdist + - pytest-mpl - codecov - matplotlib diff --git a/ci/311-numba.yaml b/ci/311-numba.yaml index 3c62a28e..4639be60 100644 --- a/ci/311-numba.yaml +++ b/ci/311-numba.yaml @@ -15,6 +15,7 @@ dependencies: - pytest - pytest-cov - pytest-xdist + - pytest-mpl - codecov - matplotlib # optional diff --git a/ci/311.yaml b/ci/311.yaml index c8eb4299..2f5d309b 100644 --- a/ci/311.yaml +++ b/ci/311.yaml @@ -15,6 +15,7 @@ dependencies: - pytest - pytest-cov - pytest-xdist + - pytest-mpl - codecov - matplotlib # docs diff --git a/ci/312-dev.yaml b/ci/312-dev.yaml index 273afe06..e94b49b7 100644 --- a/ci/312-dev.yaml +++ b/ci/312-dev.yaml @@ -7,6 +7,7 @@ dependencies: - pytest - pytest-cov - pytest-xdist + - pytest-mpl - codecov # optional - pyproj diff --git a/ci/312-numba.yaml b/ci/312-numba.yaml index dd107396..54934992 100644 --- a/ci/312-numba.yaml +++ b/ci/312-numba.yaml @@ -16,6 +16,7 @@ dependencies: - pytest-cov - pytest-xdist - pytest-doctestplus + - pytest-mpl - codecov - matplotlib # optional diff --git a/ci/312.yaml b/ci/312.yaml index d1cd6ad9..5f83b27c 100644 --- a/ci/312.yaml +++ b/ci/312.yaml @@ -15,6 +15,7 @@ dependencies: - pytest - pytest-cov - pytest-xdist + - pytest-mpl - codecov - matplotlib # docs diff --git a/pyproject.toml b/pyproject.toml index 2534ba5e..5aba00a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,13 +8,13 @@ build-backend = "setuptools.build_meta" name = "mapclassify" dynamic = ["version"] maintainers = [ - {name = "Serge Rey", email = "sjsrey@gmail.com"}, - {name = "Wei Kang", email = "weikang9009@gmail.com"}, + { name = "Serge Rey", email = "sjsrey@gmail.com" }, + { name = "Wei Kang", email = "weikang9009@gmail.com" }, ] -license = {text = "BSD 3-Clause"} +license = { text = "BSD 3-Clause" } description = "Classification Schemes for Choropleth Maps." keywords = ["spatial statistics", "geovisualization"] -readme = {text = """\ +readme = { text = """\ `mapclassify` implements a family of classification schemes for choropleth maps. Its focus is on the determination of the number of classes, and the assignment of observations to those classes. It is intended for use with upstream mapping @@ -26,7 +26,7 @@ For further theoretical background see "`Choropleth Mapping`_" in Rey, S.J., D. .. _geopandas: https://geopandas.org/mapping.html .. _geoplot: https://residentmario.github.io/geoplot/user_guide/Customizing_Plots.html .. _Choropleth Mapping: https://geographicdata.science/book/notebooks/05_choropleth.html -""", content-type = "text/x-rst"} +""", content-type = "text/x-rst" } classifiers = [ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", @@ -52,14 +52,8 @@ Home = "https://pysal.org/mapclassify/" Repository = "https://github.com/pysal/mapclassify" [project.optional-dependencies] -speedups = [ - "numba>=0.54", -] -dev = [ - "black", - "ruff", - "pre-commit", -] +speedups = ["numba>=0.54"] +dev = ["black", "ruff", "pre-commit"] docs = [ "nbsphinx", "numpydoc", @@ -76,14 +70,12 @@ tests = [ "pytest-cov", "pytest-xdist", "pytest-doctestplus", + "pytest-mpl" ] all = ["numba[speedups,dev,docs,tests]"] [tool.setuptools.packages.find] -include = [ - "mapclassify", - "mapclassify.*", -] +include = ["mapclassify", "mapclassify.*"] [tool.black] line-length = 88 From 5ff49daac72d89d5cbe93a35d5b92bb483ed2cb2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 26 Jun 2024 22:16:56 +0000 Subject: [PATCH 19/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mapclassify/classifiers.py | 5 +++-- mapclassify/tests/test_mapclassify.py | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/mapclassify/classifiers.py b/mapclassify/classifiers.py index e517b0bb..b0114fda 100644 --- a/mapclassify/classifiers.py +++ b/mapclassify/classifiers.py @@ -1164,6 +1164,7 @@ def plot_histogram( """ try: import matplotlib.pyplot as plt + if ax is None: _, ax = plt.subplots() except ImportError as e: @@ -1184,8 +1185,8 @@ def plot_histogram( ax.vlines(i, 0, lim, color=linecolor, linewidth=linewidth) # despine if specified if despine: - ax.spines['right'].set_visible(False) - ax.spines['top'].set_visible(False) + ax.spines["right"].set_visible(False) + ax.spines["top"].set_visible(False) return ax diff --git a/mapclassify/tests/test_mapclassify.py b/mapclassify/tests/test_mapclassify.py index c042eb09..d90ea226 100644 --- a/mapclassify/tests/test_mapclassify.py +++ b/mapclassify/tests/test_mapclassify.py @@ -737,11 +737,12 @@ def test_pooled_bad_classifier(self): with pytest.raises(ValueError, match=message): Pooled(self.data, classifier=classifier, k=4) + class TestPlots: def setup_method(self): n = 20 self.data = numpy.array([numpy.arange(n) + i * n for i in range(1, 4)]).T - + @pytest.mark.mpl_image_compare def test_histogram_plot(self): ax = Quantiles(self.data).plot_histogram() @@ -754,5 +755,7 @@ def test_histogram_plot_despine(self): @pytest.mark.mpl_image_compare def test_histogram_plot_linewidth(self): - ax = Quantiles(self.data).plot_histogram(linewidth=3, linecolor='red', color='yellow') - return ax.get_figure() \ No newline at end of file + ax = Quantiles(self.data).plot_histogram( + linewidth=3, linecolor="red", color="yellow" + ) + return ax.get_figure() From 46fee1e255a38a266dcf147621d83393a4613fec Mon Sep 17 00:00:00 2001 From: eli knaap Date: Wed, 26 Jun 2024 15:30:16 -0700 Subject: [PATCH 20/24] rm unused kwarg logic --- mapclassify/classifiers.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/mapclassify/classifiers.py b/mapclassify/classifiers.py index b0114fda..853fdb3a 100644 --- a/mapclassify/classifiers.py +++ b/mapclassify/classifiers.py @@ -1137,8 +1137,7 @@ def plot_histogram( Parameters ---------- color : str, optional - hue to color bars of the histogram, by default "dodgerblue". This option - overrides the 'color' entry in `hist_kwargs` if specified. + hue to color bars of the histogram, by default "dodgerblue". linecolor : str, optional color of the lines demarcating each class bin, by default "black" linewidth : int, optional @@ -1171,11 +1170,6 @@ def plot_histogram( raise ImportError from e( "You must have matplotlib available to use this function" ) - if kwargs is None: - kwargs = dict() - # override color in case specified explicitly and inside the dict - if "color" not in kwargs: - kwargs["color"] = color # plot `y` as a histogram ax.hist(self.y, **kwargs) # get the top of the ax so we know how high to raise each class bar From bfefddf0405681fb7c2539c4b23e5d7875feb455 Mon Sep 17 00:00:00 2001 From: eli knaap Date: Wed, 26 Jun 2024 15:31:27 -0700 Subject: [PATCH 21/24] forgot color --- mapclassify/classifiers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapclassify/classifiers.py b/mapclassify/classifiers.py index 853fdb3a..8ceae3ac 100644 --- a/mapclassify/classifiers.py +++ b/mapclassify/classifiers.py @@ -1171,7 +1171,7 @@ def plot_histogram( "You must have matplotlib available to use this function" ) # plot `y` as a histogram - ax.hist(self.y, **kwargs) + ax.hist(self.y, color=color, **kwargs) # get the top of the ax so we know how high to raise each class bar lim = ax.get_ylim()[1] # plot upper limit of each bin From d8805db88c09f5b174e34c319e44850f1e57bcf1 Mon Sep 17 00:00:00 2001 From: eli knaap Date: Wed, 3 Jul 2024 08:17:50 -0700 Subject: [PATCH 22/24] kwargs no k --- mapclassify/util.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mapclassify/util.py b/mapclassify/util.py index ad3233f0..69b22df8 100644 --- a/mapclassify/util.py +++ b/mapclassify/util.py @@ -4,10 +4,10 @@ def get_rgba( values, classifier="quantiles", - k=6, cmap="viridis", alpha=1, nan_color=[255, 255, 255, 255], + **kwargs ): """Convert array of values into RGBA colors using a colormap and classifier. @@ -17,8 +17,6 @@ def get_rgba( array of input values classifier : str, optional string description of a mapclassify classifier, by default "quantiles" - k : int, optional - number of classes to form, by default 6 cmap : str, optional name of matplotlib colormap to use, by default "viridis" alpha : float @@ -48,7 +46,7 @@ def get_rgba( legit_indices = v[~v.isna()].index.values # transform (non-NaN) values into class bins - bins = classify(v.dropna().values, scheme=classifier, k=k).yb + bins = classify(v.dropna().values, scheme=classifier, **kwargs).yb # create a normalizer using the data's range (not strictly 1-k...) norm = Normalize(min(bins), max(bins)) From cb9d01404c85474a362b561820ff87c76f9bf78a Mon Sep 17 00:00:00 2001 From: eli knaap Date: Wed, 3 Jul 2024 08:39:14 -0700 Subject: [PATCH 23/24] document kwargs --- mapclassify/util.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mapclassify/util.py b/mapclassify/util.py index 69b22df8..29cbc927 100644 --- a/mapclassify/util.py +++ b/mapclassify/util.py @@ -1,4 +1,4 @@ -from ._classify_API import classify +from ._classify_API import classify as _classify def get_rgba( @@ -23,6 +23,8 @@ def get_rgba( alpha parameter that defines transparency. Should be in the range [0,1] nan_color : list, optional RGBA color to fill NaN values, by default [255, 255, 255, 255] + kwargs : dict + additional keyword arguments are passed to `mapclassify.classify` Returns ------- @@ -46,7 +48,7 @@ def get_rgba( legit_indices = v[~v.isna()].index.values # transform (non-NaN) values into class bins - bins = classify(v.dropna().values, scheme=classifier, **kwargs).yb + bins = _classify(v.dropna().values, scheme=classifier, **kwargs).yb # create a normalizer using the data's range (not strictly 1-k...) norm = Normalize(min(bins), max(bins)) From 0f64631dad08278fdf2bff403246b15d4f3be00f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 15:40:12 +0000 Subject: [PATCH 24/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mapclassify/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapclassify/util.py b/mapclassify/util.py index 29cbc927..33d917c6 100644 --- a/mapclassify/util.py +++ b/mapclassify/util.py @@ -7,7 +7,7 @@ def get_rgba( cmap="viridis", alpha=1, nan_color=[255, 255, 255, 255], - **kwargs + **kwargs, ): """Convert array of values into RGBA colors using a colormap and classifier.