Skip to content

Fix cbars for different norms #16286

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

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added doc/_static/gif_attachment_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 10 additions & 6 deletions doc/missing-references.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
"button": [
"doc/users/prev_whats_new/whats_new_3.1.0.rst:338"
],
"cbar_axes": [
"lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid:48",
"lib/mpl_toolkits/axisartist/axes_grid.py:docstring of mpl_toolkits.axisartist.axes_grid.ImageGrid:48"
],
"dividers": [
"lib/mpl_toolkits/axes_grid1/colorbar.py:docstring of mpl_toolkits.axes_grid1.colorbar.ColorbarBase:29"
],
Expand Down Expand Up @@ -792,8 +796,8 @@
"doc/api/prev_api_changes/api_changes_1.5.0.rst:44"
],
"Size.from_any": [
"lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid:73",
"lib/mpl_toolkits/axisartist/axes_grid.py:docstring of mpl_toolkits.axisartist.axes_grid.ImageGrid:48"
"lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid:60",
"lib/mpl_toolkits/axisartist/axes_grid.py:docstring of mpl_toolkits.axisartist.axes_grid.ImageGrid:60"
],
"Style": [
"doc/devel/MEP/MEP26.rst:68"
Expand Down Expand Up @@ -892,13 +896,13 @@
"doc/api/prev_api_changes/api_changes_3.1.0.rst:933"
],
"axes_grid1": [
"doc/index.rst:184"
"doc/index.rst:154"
],
"axis.Axis.get_ticks_position": [
"doc/api/prev_api_changes/api_changes_3.1.0.rst:821"
],
"axisartist": [
"doc/index.rst:184"
"doc/index.rst:154"
],
"backend_bases.RendererBase": [
"lib/matplotlib/backends/backend_template.py:docstring of matplotlib.backends.backend_template.RendererTemplate:4"
Expand Down Expand Up @@ -1506,7 +1510,7 @@
"lib/mpl_toolkits/axisartist/axis_artist.py:docstring of mpl_toolkits.axisartist.axis_artist:26"
],
"mplot3d": [
"doc/index.rst:184"
"doc/index.rst:154"
],
"new_figure_manager": [
"doc/devel/MEP/MEP23.rst:75"
Expand Down Expand Up @@ -1689,4 +1693,4 @@
"lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.get_ylim3d:24"
]
}
}
}
52 changes: 30 additions & 22 deletions doc/thirdpartypackages/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,36 @@ Yellowbrick
.. image:: /_static/yellowbrick.png
:height: 400px

Animations
**********

animatplot
==========
`animatplot <https://animatplot.readthedocs.io/>`_ is a library for
producing interactive animated plots with the goal of making production of
animated plots almost as easy as static ones.

.. image:: /_static/animatplot.png

For an animated version of the above picture and more examples, see the
`animatplot gallery. <https://animatplot.readthedocs.io/en/stable/gallery.html>`_

gif
===
`gif <https://github.com/maxhumber/gif/>`_ is an ultra lightweight animated gif API.

.. image:: /_static/gif_attachment_example.png

numpngw
=======

`numpngw <https://pypi.org/project/numpngw/>`_ provides functions for writing
NumPy arrays to PNG and animated PNG files. It also includes the class
``AnimatedPNGWriter`` that can be used to save a Matplotlib animation as an
animated PNG file. See the example on the PyPI page or at the ``numpngw``
`github repository <https://github.com/WarrenWeckesser/numpngw>`_.

.. image:: /_static/numpngw_animated_example.png

Interactivity
*************
Expand All @@ -209,17 +239,6 @@ MplDataCursor
written by Joe Kington to provide interactive "data cursors" (clickable
annotation boxes) for Matplotlib.

animatplot
==========
`animatplot <https://animatplot.readthedocs.io/>`_ is a library for
producing interactive animated plots with the goal of making production of
animated plots almost as easy as static ones.

.. image:: /_static/animatplot.png

For an animated version of the above picture and more examples, see the
`animatplot gallery. <https://animatplot.readthedocs.io/en/stable/gallery.html>`_

Rendering backends
******************

Expand Down Expand Up @@ -263,17 +282,6 @@ border, and logo.
.. image:: /_static/mpl_template_example.png
:height: 330px

numpngw
=======

`numpngw <https://pypi.org/project/numpngw/>`_ provides functions for writing
NumPy arrays to PNG and animated PNG files. It also includes the class
``AnimatedPNGWriter`` that can be used to save a Matplotlib animation as an
animated PNG file. See the example on the PyPI page or at the ``numpngw``
`github repository <https://github.com/WarrenWeckesser/numpngw>`_.

.. image:: /_static/numpngw_animated_example.png

blume
=====

Expand Down
7 changes: 4 additions & 3 deletions lib/matplotlib/cbook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1382,9 +1382,10 @@ def _check_1d(x):
# This code should correctly identify and coerce to a
# numpy array all pandas versions.
with warnings.catch_warnings(record=True) as w:
warnings.filterwarnings("always",
category=DeprecationWarning,
module='pandas[.*]')
warnings.filterwarnings(
"always",
category=DeprecationWarning,
message='Support for multi-dimensional indexing')

ndim = x[:, None].ndim
# we have definitely hit a pandas index or series object
Expand Down
20 changes: 14 additions & 6 deletions lib/matplotlib/colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ def __init__(self, ax, cmap=None,
self.locator = None
self.formatter = None
self._manual_tick_data_values = None
self._scale = None # linear, log10 for now. Hopefully more?

if ticklocation == 'auto':
ticklocation = 'bottom' if orientation == 'horizontal' else 'right'
Expand Down Expand Up @@ -585,8 +586,8 @@ def _use_auto_colorbar_locator(self):
one. (check is used twice so factored out here...)
"""
contouring = self.boundaries is not None and self.spacing == 'uniform'
return (type(self.norm) in [colors.Normalize, colors.LogNorm]
and not contouring)
return (type(self.norm) in [colors.Normalize, colors.LogNorm] and
not contouring)

def _reset_locator_formatter_scale(self):
"""
Expand All @@ -602,9 +603,16 @@ def _reset_locator_formatter_scale(self):
self.ax.set_xscale('log')
self.ax.set_yscale('log')
self.minorticks_on()
self._scale = 'log'
else:
self.ax.set_xscale('linear')
self.ax.set_yscale('linear')
if (isintance(self.norm, colors.Normalize) and
not issubclass(type(self.norm), colors.Normalize):
self._scale = 'linear'
else:
self._scale = 'manual'


def update_ticks(self):
"""
Expand Down Expand Up @@ -1119,13 +1127,13 @@ def _mesh(self):
else:
y = self._proportional_y()
xmid = np.array([0.5])
try:
if not self._scale == 'manual'
y = norm.inverse(y)
x = norm.inverse(x)
xmid = norm.inverse(xmid)
except ValueError:
# occurs for norms that don't have an inverse, in
# which case manually scale:
else:
# if a norm doesn't have a named scale, or
# we are not using a norm
dv = self.vmax - self.vmin
x = x * dv + self.vmin
y = y * dv + self.vmin
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 15 additions & 1 deletion lib/matplotlib/tests/test_colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,6 @@ def test_colorbar_get_ticks():
data = np.arange(1200).reshape(30, 40)
levels = [0, 200, 400, 600, 800, 1000, 1200]

plt.subplot()
plt.contourf(data, levels=levels)

# testing getter for user set ticks
Expand Down Expand Up @@ -581,3 +580,18 @@ def test_colorbar_int(clim):
im = ax.imshow([[*map(np.int16, clim)]])
fig.colorbar(im)
assert (im.norm.vmin, im.norm.vmax) == clim


@image_comparison(
baseline_images=['colorbar_norms'], extensions=['png'])
Copy link
Contributor

@anntzer anntzer Jan 21, 2020

Choose a reason for hiding this comment

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

Image test looks reasonable (no need to overthink into avoiding them either -- unless someone has a better idea...); perhaps you can use style="default"? (ticks inwards look weird to me now :-), and then you don't need to specify viridis below explicitly)

def test_cbar_norms():
# Test colormaps with different norms
norms = [mcolors.Normalize(vmin=-1, vmax=2),
mcolors.BoundaryNorm(boundaries=[-1, 0, 1], ncolors=4),
mcolors.LogNorm(vmin=1, vmax=1e4),
mcolors.PowerNorm(gamma=0.2, vmin=0.1, vmax=0.6),
mcolors.SymLogNorm(1, vmin=-10, vmax=10),
mcolors.TwoSlopeNorm(1, vmin=0, vmax=2)]
fig, axs = plt.subplots(ncols=len(norms), constrained_layout=True)
for ax, norm in zip(axs, norms):
fig.colorbar(cm.ScalarMappable(norm=norm, cmap='viridis'), cax=ax, extend='both')
56 changes: 40 additions & 16 deletions lib/mpl_toolkits/axes_grid1/axes_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,13 @@ def cla(self):

class Grid:
"""
A class that creates a grid of Axes. In matplotlib, the axes
location (and size) is specified in the normalized figure
coordinates. This may not be ideal for images that needs to be
displayed with a given aspect ratio. For example, displaying
images of a same size with some fixed padding between them cannot
be easily done in matplotlib. AxesGrid is used in such case.
A grid of Axes.

In Matplotlib, the axes location (and size) is specified in normalized
figure coordinates. This may not be ideal for images that needs to be
displayed with a given aspect ratio; for example, it is difficult to
display multiple images of a same size with some fixed padding between
them. AxesGrid can be used in such case.
"""

_defaultAxesClass = Axes
Expand Down Expand Up @@ -115,14 +116,25 @@ def __init__(self, fig,
rect : (float, float, float, float) or int
The axes position, as a ``(left, bottom, width, height)`` tuple or
as a three-digit subplot position code (e.g., "121").
nrows_ncols : (int, int)
Number of rows and columns in the grid.
ngrids : int or None, default: None
If not None, only the first *ngrids* axes in the grid are created.
direction : {"row", "column"}, default: "row"
Whether axes are created in row-major ("row by row") or
column-major order ("column by column").
axes_pad : float or (float, float), default: 0.02
Padding or (horizontal padding, vertical padding) between axes, in
inches.
add_all : bool, default: True
Whether to add the axes to the figure using `.Figure.add_axes`.
share_all : bool, default: False
Whether all axes share their x- and y-axis. Overrides *share_x*
and *share_y*.
share_x : bool, default: True
Whether all axes of a column share their x-axis.
share_y : bool, default: True
Whether all axes of a row share their y-axis.
label_mode : {"L", "1", "all"}, default: "L"
Determines which axes will get tick labels:

Expand All @@ -133,6 +145,8 @@ def __init__(self, fig,

axes_class : subclass of `matplotlib.axes.Axes`, default: None
aspect : bool, default: False
Whether the axes aspect ratio follows the aspect ratio of the data
limits.
"""
self._nrows, self._ncols = nrows_ncols

Expand Down Expand Up @@ -332,14 +346,7 @@ def get_vsize_hsize(self):


class ImageGrid(Grid):
"""
A class that creates a grid of Axes. In matplotlib, the axes
location (and size) is specified in the normalized figure
coordinates. This may not be ideal for images that needs to be
displayed with a given aspect ratio. For example, displaying
images of a same size with some fixed padding between them cannot
be easily done in matplotlib. ImageGrid is used in such case.
"""
# docstring inherited

_defaultCbarAxesClass = CbarAxes

Expand Down Expand Up @@ -368,13 +375,24 @@ def __init__(self, fig,
rect : (float, float, float, float) or int
The axes position, as a ``(left, bottom, width, height)`` tuple or
as a three-digit subplot position code (e.g., "121").
nrows_ncols : (int, int)
Number of rows and columns in the grid.
ngrids : int or None, default: None
If not None, only the first *ngrids* axes in the grid are created.
direction : {"row", "column"}, default: "row"
axes_pad : float or (float, float), default: 0.02
Whether axes are created in row-major ("row by row") or
column-major order ("column by column"). This also affects the
order in which axes are accessed using indexing (``grid[index]``).
axes_pad : float or (float, float), default: 0.02in
Padding or (horizontal padding, vertical padding) between axes, in
inches.
add_all : bool, default: True
Whether to add the axes to the figure using `.Figure.add_axes`.
share_all : bool, default: False
Whether all axes share their x- and y-axis.
aspect : bool, default: True
Whether the axes aspect ratio follows the aspect ratio of the data
limits.
label_mode : {"L", "1", "all"}, default: "L"
Determines which axes will get tick labels:

Expand All @@ -383,10 +401,16 @@ def __init__(self, fig,
- "1": Only the bottom left axes is labelled.
- "all": all axes are labelled.

cbar_mode : {"each", "single", "edge", None }, default: None
cbar_mode : {"each", "single", "edge", None}, default: None
Whether to create a colorbar for "each" axes, a "single" colorbar
for the entire grid, colorbars only for axes on the "edge"
determined by *cbar_location*, or no colorbars. The colorbars are
stored in the :attr:`cbar_axes` attribute.
cbar_location : {"left", "right", "bottom", "top"}, default: "right"
cbar_pad : float, default: None
Padding between the image axes and the colorbar axes.
cbar_size : size specification (see `.Size.from_any`), default: "5%"
Colorbar size.
cbar_set_cax : bool, default: True
If True, each axes in the grid has a *cax* attribute that is bound
to associated *cbar_axes*.
Expand Down
Loading