Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Add func norm #18653

Merged
merged 1 commit into from Jan 15, 2021
Merged

ENH: Add func norm #18653

merged 1 commit into from Jan 15, 2021

Conversation

jklymak
Copy link
Member

@jklymak jklymak commented Oct 4, 2020

PR Summary

Derives a func norm from func scale. From the example:

###############################################################################
# FuncNorm: Arbitrary function normalization
# ------------------------------------------
#
# If the above norms do not provide the normalization you want, and you
# can express your normalization in terms of function, you can use
# `~.colors.FuncNorm`.  Note that this example is the same as
# `PowerNorm` with a power of 0.5:

def _forward(x):
    return np.sqrt(x)


def _inverse(x):
    return x**2

N = 100
X, Y = np.mgrid[0:3:complex(0, N), 0:2:complex(0, N)]
Z1 = (1 + np.sin(Y * 10.)) * X**2
fig, ax = plt.subplots()

norm = colors.FuncNorm((_forward, _inverse), vmin=0, vmax=20)
pcm = ax.pcolormesh(X, Y, Z1, norm=norm, cmap='PuBu_r', shading='auto')
ax.set_title('FuncNorm(x)')
fig.colorbar(pcm, shrink=0.6)
plt.show()

FuncNorm

PR Checklist

  • Has pytest style unit tests (and pytest passes).
  • Is Flake 8 compliant (run flake8 on changed files to check).
  • New features are documented, with examples if plot related.
  • Documentation is sphinx and numpydoc compliant (the docs should build without error).
  • Conforms to Matplotlib style conventions (install flake8-docstrings and pydocstyle<4 and run flake8 --docstring-convention=all).
  • New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).
  • API changes documented in doc/api/next_api_changes/ (follow instructions in README.rst there).

@jklymak jklymak force-pushed the enh-add-func-norm branch 3 times, most recently from 989c966 to 15af903 Compare December 20, 2020 03:40
@jklymak jklymak marked this pull request as ready for review December 20, 2020 03:42
@jklymak jklymak added this to the v3.4.0 milestone Dec 20, 2020
lib/matplotlib/tests/test_colors.py Outdated Show resolved Hide resolved
tutorials/colors/colormapnorms.py Outdated Show resolved Hide resolved
Copy link
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor style comments.

lib/matplotlib/tests/test_colors.py Show resolved Hide resolved
lib/matplotlib/tests/test_colors.py Outdated Show resolved Hide resolved
return 10**x
norm = mcolors.FuncNorm((forward, inverse), vmin=0.1, vmax=10)
lognorm = mcolors.LogNorm(vmin=0.1, vmax=10)
assert_array_almost_equal(norm([0.2, 5, 10]), lognorm([0.2, 5, 10]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you not want to assert the inverse here as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I wasn't going to but it does drop our test coverage, so added...

Interestingly the _make_norm_from_scale inverse doesn't work on a list of floats (i.e. [1, 2, 3]), but only on a numpy array (np.array([1, 2, 3])) whereas the forward works on a bare list. Not sure if that is a bug. @anntzer ?

Copy link
Member

@QuLogic QuLogic Jan 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably because inverse doesn't pass value through process_value like __call__ does. Something like:

diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py
index e417b8178d..b37ec947fa 100644
--- a/lib/matplotlib/colors.py
+++ b/lib/matplotlib/colors.py
@@ -1449,12 +1449,14 @@ def _make_norm_from_scale(scale_cls, base_norm_cls=None, *, init=None):
             t_vmin, t_vmax = self._trf.transform([self.vmin, self.vmax])
             if not np.isfinite([t_vmin, t_vmax]).all():
                 raise ValueError("Invalid vmin or vmax")
+            value, is_scalar = self.process_value(value)
             rescaled = value * (t_vmax - t_vmin)
             rescaled += t_vmin
-            return (self._trf
-                    .inverted()
-                    .transform(rescaled)
-                    .reshape(np.shape(value)))
+            t_value = (self._trf
+                       .inverted()
+                       .transform(rescaled)
+                       .reshape(np.shape(value)))
+            return t_value[0] if is_scalar else t_value
 
     Norm.__name__ = base_norm_cls.__name__
     Norm.__qualname__ = base_norm_cls.__qualname__

Maybe it also needs the masking?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks right. But maybe orthogonal to this PR. I opened an issue in #19239 just to keep them separate...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since that is now fixed, do you want to remove the np.array from the inverse check?

@jklymak
Copy link
Member Author

jklymak commented Jan 12, 2021

@timhoffm I think I addressed your concern above? Thanks!

Copy link
Member

@QuLogic QuLogic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to merge after.

return 10**x
norm = mcolors.FuncNorm((forward, inverse), vmin=0.1, vmax=10)
lognorm = mcolors.LogNorm(vmin=0.1, vmax=10)
assert_array_almost_equal(norm([0.2, 5, 10]), lognorm([0.2, 5, 10]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since that is now fixed, do you want to remove the np.array from the inverse check?

@jklymak jklymak merged commit 6429e9d into matplotlib:master Jan 15, 2021
@jklymak jklymak deleted the enh-add-func-norm branch January 15, 2021 05:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants