Repository navigation
Enh: SymNorm for normalizing symmetrical data around a center #17709
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
Changes from all commits
a8de87b
2f9fd02
80019a8
4b2f105
3a475c3
8ac6dbe
58d6057
22d461c
1bc98c6
eb284e4
ff01e19
6f10076
5c0dadb
101d08d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ Classes | |
|
|
||
| BoundaryNorm | ||
| Colormap | ||
| CenteredNorm | ||
| LightSource | ||
| LinearSegmentedColormap | ||
| ListedColormap | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| New CenteredNorm for symmetrical data around a center | ||
| ----------------------------------------------------- | ||
| In cases where data is symmetrical around a center, for example, positive and | ||
| negative anomalies around a center zero, `~.matplotlib.colors.CenteredNorm` | ||
| is a new norm that automatically creates a symmetrical mapping around the | ||
| center. This norm is well suited to be combined with a divergent colormap which | ||
| uses an unsaturated color in its center. | ||
|
|
||
| .. plot:: | ||
|
|
||
| import matplotlib.pyplot as plt | ||
| import numpy as np | ||
| from matplotlib.colors import CenteredNorm | ||
|
|
||
| np.random.seed(20201004) | ||
| data = np.random.normal(size=(3, 4), loc=1) | ||
|
|
||
| fig, ax = plt.subplots() | ||
| pc = ax.pcolormesh(data, cmap=plt.get_cmap('RdGy'), norm=CenteredNorm()) | ||
| fig.colorbar(pc) | ||
| ax.set_title('data centered around zero') | ||
|
|
||
| # add text annotation | ||
| for irow, data_row in enumerate(data): | ||
| for icol, val in enumerate(data_row): | ||
| ax.text(icol + 0.5, irow + 0.5, f'{val:.2f}', color='C0', | ||
| size=16, va='center', ha='center') | ||
| plt.show() | ||
|
|
||
| If the center of symmetry is different from 0, it can be set with the *vcenter* | ||
| argument. To manually set the range of `~.matplotlib.colors.CenteredNorm`, use | ||
| the *halfrange* argument. | ||
|
|
||
| See :doc:`/tutorials/colors/colormapnorms` for an example and more details | ||
| about data normalization. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -359,6 +359,56 @@ def test_BoundaryNorm(): | |
| assert_array_equal(cmshould(mynorm(x)), cmref(refnorm(x))) | ||
|
|
||
|
|
||
| def test_CenteredNorm(): | ||
|
QuLogic marked this conversation as resolved.
Resolved
|
||
| np.random.seed(0) | ||
|
|
||
| # Assert equivalence to symmetrical Normalize. | ||
| x = np.random.normal(size=100) | ||
| x_maxabs = np.max(np.abs(x)) | ||
| norm_ref = mcolors.Normalize(vmin=-x_maxabs, vmax=x_maxabs) | ||
| norm = mcolors.CenteredNorm() | ||
| assert_array_almost_equal(norm_ref(x), norm(x)) | ||
|
|
||
| # Check that vcenter is in the center of vmin and vmax | ||
| # when vcenter is set. | ||
| vcenter = int(np.random.normal(scale=50)) | ||
| norm = mcolors.CenteredNorm(vcenter=vcenter) | ||
| norm.autoscale_None([1, 2]) | ||
| assert norm.vmax + norm.vmin == 2 * vcenter | ||
|
|
||
| # Check that halfrange input works correctly. | ||
| x = np.random.normal(size=10) | ||
| norm = mcolors.CenteredNorm(vcenter=0.5, halfrange=0.5) | ||
| assert_array_almost_equal(x, norm(x)) | ||
| norm = mcolors.CenteredNorm(vcenter=1, halfrange=1) | ||
| assert_array_almost_equal(x, 2 * norm(x)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you test the setters and getters in here? I know they are trivial, but.... |
||
|
|
||
| # Check that halfrange input works correctly and use setters. | ||
| norm = mcolors.CenteredNorm() | ||
| norm.vcenter = 2 | ||
| norm.halfrange = 2 | ||
| assert_array_almost_equal(x, 4 * norm(x)) | ||
|
|
||
| # Check that prior to adding data, setting halfrange first has same effect. | ||
| norm = mcolors.CenteredNorm() | ||
| norm.halfrange = 2 | ||
| norm.vcenter = 2 | ||
| assert_array_almost_equal(x, 4 * norm(x)) | ||
|
|
||
| # Check that manual change of vcenter adjusts halfrange accordingly. | ||
| norm = mcolors.CenteredNorm() | ||
| assert norm.vcenter == 0 | ||
| # add data | ||
| norm(np.linspace(-1.0, 0.0, 10)) | ||
| assert norm.vmax == 1.0 | ||
| assert norm.halfrange == 1.0 | ||
| # set vcenter to 1, which should double halfrange | ||
| norm.vcenter = 1 | ||
| assert norm.vmin == -1.0 | ||
| assert norm.vmax == 3.0 | ||
| assert norm.halfrange == 2.0 | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("vmin,vmax", [[-1, 2], [3, 1]]) | ||
| def test_lognorm_invalid(vmin, vmax): | ||
| # Check that invalid limits in LogNorm error | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,7 @@ | |
| import matplotlib.pyplot as plt | ||
| import matplotlib.colors as colors | ||
| import matplotlib.cbook as cbook | ||
| from matplotlib import cm | ||
|
|
||
| N = 100 | ||
| X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] | ||
|
|
@@ -69,6 +70,46 @@ | |
| fig.colorbar(pcm, ax=ax[1], extend='max') | ||
| plt.show() | ||
|
|
||
| ############################################################################### | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should definitely be moved under TwoSlopeNorm, please, as it is a specialization of that norm. |
||
| # Centered | ||
| # -------- | ||
| # | ||
| # In many cases, data is symmetrical around a center, for example, positive and | ||
| # negative anomalies around a center 0. In this case, we would like the center | ||
| # to be mapped to 0.5 and the datapoint with the largest deviation from the | ||
| # center to be mapped to 1.0, if its value is greater than the center, or 0.0 | ||
| # otherwise. The norm `.colors.CenteredNorm` creates such a mapping | ||
| # automatically. It is well suited to be combined with a divergent colormap | ||
| # which uses different colors edges that meet in the center at an unsaturated | ||
| # color. | ||
| # | ||
| # If the center of symmetry is different from 0, it can be set with the | ||
| # *vcenter* argument. For logarithmic scaling on both sides of the center, see | ||
| # `.colors.SymLogNorm` below; to apply a different mapping above and below the | ||
| # center, use `.colors.TwoSlopeNorm` below. | ||
|
|
||
| delta = 0.1 | ||
| x = np.arange(-3.0, 4.001, delta) | ||
| y = np.arange(-4.0, 3.001, delta) | ||
| X, Y = np.meshgrid(x, y) | ||
| Z1 = np.exp(-X**2 - Y**2) | ||
| Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) | ||
| Z = (0.9*Z1 - 0.5*Z2) * 2 | ||
|
|
||
| # select a divergent colormap | ||
| cmap = cm.coolwarm | ||
|
|
||
| fig, (ax1, ax2) = plt.subplots(ncols=2) | ||
| pc = ax1.pcolormesh(Z, cmap=cmap) | ||
| fig.colorbar(pc, ax=ax1) | ||
| ax1.set_title('Normalize()') | ||
|
|
||
| pc = ax2.pcolormesh(Z, norm=colors.CenteredNorm(), cmap=cmap) | ||
| fig.colorbar(pc, ax=ax2) | ||
| ax2.set_title('CenteredNorm()') | ||
|
|
||
| plt.show() | ||
|
|
||
| ############################################################################### | ||
| # Symmetric logarithmic | ||
| # --------------------- | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.