Skip to content

Commit f199fd4

Browse files
committed
DOC: Clarify/simplify example of multiple images with one colorbar
1 parent 199c31f commit f199fd4

File tree

1 file changed

+54
-37
lines changed

1 file changed

+54
-37
lines changed
Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
"""
2-
===============
3-
Multiple images
4-
===============
2+
=================================
3+
Multiple images with one colorbar
4+
=================================
55
6-
Make a set of images with a single colormap, norm, and colorbar.
6+
Use a single colorbar for multiple images.
7+
8+
Currently, a colorbar can only be connected to one image. The connection
9+
guarantees that the data coloring is consistent with the colormap scale
10+
(i.e. the color of value *x* in the colormap is used for coloring a data
11+
value *x* in the image).
12+
13+
If we want one colorbar to be representative for multiple images, we have
14+
to explicitly ensure consistent data coloring. The most important aspect
15+
is the data normalization. By explicitly creating a norm and using that
16+
for all images, we ensure that all images are scaled consistently.
717
"""
818

919
import matplotlib.pyplot as plt
@@ -12,47 +22,56 @@
1222
from matplotlib import colors
1323

1424
np.random.seed(19680801)
15-
Nr = 3
16-
Nc = 2
1725

18-
fig, axs = plt.subplots(Nr, Nc)
26+
datasets = [
27+
(i+1)/10 * np.random.rand(10, 20)
28+
for i in range(4)
29+
]
30+
31+
fig, axs = plt.subplots(2, 2)
1932
fig.suptitle('Multiple images')
2033

21-
images = []
22-
for i in range(Nr):
23-
for j in range(Nc):
24-
# Generate data with a range that varies from one plot to the next.
25-
data = ((1 + i + j) / 10) * np.random.rand(10, 20)
26-
images.append(axs[i, j].imshow(data))
27-
axs[i, j].label_outer()
34+
# create a single norm to be shared across all images
35+
norm = colors.Normalize(vmin=np.min(datasets), vmax=np.max(datasets))
2836

29-
# Find the min and max of all colors for use in setting the color scale.
30-
vmin = min(image.get_array().min() for image in images)
31-
vmax = max(image.get_array().max() for image in images)
32-
norm = colors.Normalize(vmin=vmin, vmax=vmax)
33-
for im in images:
34-
im.set_norm(norm)
37+
images = []
38+
for ax, data in zip(axs.flat, datasets):
39+
images.append(ax.imshow(data, norm=norm))
3540

3641
fig.colorbar(images[0], ax=axs, orientation='horizontal', fraction=.1)
3742

38-
39-
# Make images respond to changes in the norm of other images (e.g. via the
40-
# "edit axis, curves and images parameters" GUI on Qt), but be careful not to
41-
# recurse infinitely!
42-
def update(changed_image):
43-
for im in images:
44-
if (changed_image.get_cmap() != im.get_cmap()
45-
or changed_image.get_clim() != im.get_clim()):
46-
im.set_cmap(changed_image.get_cmap())
47-
im.set_clim(changed_image.get_clim())
48-
49-
50-
for im in images:
51-
im.callbacks.connect('changed', update)
52-
5343
plt.show()
5444

5545
# %%
46+
# The colors are now kept consistent across all images when changing the
47+
# scaling, e.g. through zooming in the colorbar or via the "edit axis,
48+
# curves and images parameters" GUI of the Qt backend. This is sufficient
49+
# for most practical use cases.
50+
#
51+
# Advanced: Additionally sync the colormap
52+
# ----------------------------------------
53+
#
54+
# While the norm is shared in the above example, the colormaps are not:
55+
# Each image holds an independent reference to the colormap it wants to
56+
# use. We implicitly rely on all images using the same colormap. This is
57+
# often ok because colormaps are usually not changed dynamically. However,
58+
# a user could change the colormap of an individual image through the
59+
# "edit axis, curves and images parameters" GUI of the Qt backend.
60+
# Unlike with a norm, it does not help to share colormaps between images.
61+
# Changes to the norm limits modify the norm object in place and thus
62+
# propagate to all images. But changing a colormap sets a new colormap
63+
# object to the images and thus does not propagate to the other images.
64+
# To make all other images follow, you could additionally sync the
65+
# colormaps using the following code::
66+
#
67+
# def sync_cmaps(changed_image):
68+
# for im in images:
69+
# if changed_image.get_cmap() != im.get_cmap():
70+
# im.set_cmap(changed_image.get_cmap())
71+
#
72+
# for im in images:
73+
# im.callbacks.connect('changed', sync_cmaps)
74+
#
5675
#
5776
# .. admonition:: References
5877
#
@@ -63,6 +82,4 @@ def update(changed_image):
6382
# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
6483
# - `matplotlib.colors.Normalize`
6584
# - `matplotlib.cm.ScalarMappable.set_cmap`
66-
# - `matplotlib.cm.ScalarMappable.set_norm`
67-
# - `matplotlib.cm.ScalarMappable.set_clim`
6885
# - `matplotlib.cbook.CallbackRegistry.connect`

0 commit comments

Comments
 (0)