Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a2d44d5

Browse filesBrowse files
committed
Merge pull request #766 from ajdawson/colorbar-extensions
NF - control the length of colorbar extensions
2 parents 18cf273 + c33a09f commit a2d44d5
Copy full SHA for a2d44d5

File tree

Expand file treeCollapse file tree

8 files changed

+200
-14
lines changed
Filter options
Expand file treeCollapse file tree

8 files changed

+200
-14
lines changed

‎doc/api/api_changes.rst

Copy file name to clipboardExpand all lines: doc/api/api_changes.rst
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ Changes in 1.2.x
5151
matplotlib axes by providing a ``_as_mpl_axes`` method. See
5252
:ref:`adding-new-scales` for more detail.
5353

54+
* A new keyword *extendfrac* in :meth:`~matplotlib.pyplot.colorbar` and
55+
:class:`~matplotlib.colorbar.ColorbarBase` allows one to control the size of
56+
the triangular minimum and maximum extensions on colorbars.
5457

5558
Changes in 1.1.x
5659
================

‎doc/users/whats_new.rst

Copy file name to clipboardExpand all lines: doc/users/whats_new.rst
+31Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,37 @@ Damon McDougall added a new plotting method for the
2626

2727
.. plot:: mpl_examples/mplot3d/trisurf3d_demo.py
2828

29+
Control the lengths of colorbar extensions
30+
------------------------------------------
31+
32+
Andrew Dawson added a new keyword argument *extendfrac* to
33+
:meth:`~matplotlib.pyplot.colorbar` to control the length of
34+
minimum and maximum colorbar extensions.
35+
36+
.. plot::
37+
38+
import matplotlib.pyplot as plt
39+
import numpy as np
40+
41+
x = y = np.linspace(0., 2*np.pi, 100)
42+
X, Y = np.meshgrid(x, y)
43+
Z = np.cos(X) * np.sin(0.5*Y)
44+
45+
clevs = [-.75, -.5, -.25, 0., .25, .5, .75]
46+
cmap = plt.cm.get_cmap(name='jet', lut=8)
47+
48+
ax1 = plt.subplot(211)
49+
cs1 = plt.contourf(x, y, Z, clevs, cmap=cmap, extend='both')
50+
cb1 = plt.colorbar(orientation='horizontal', extendfrac=None)
51+
cb1.set_label('Default length colorbar extensions')
52+
53+
ax2 = plt.subplot(212)
54+
cs2 = plt.contourf(x, y, Z, clevs, cmap=cmap, extend='both')
55+
cb2 = plt.colorbar(orientation='horizontal', extendfrac='auto')
56+
cb2.set_label('Custom length colorbar extensions')
57+
58+
plt.show()
59+
2960
.. _whats-new-1-1:
3061

3162
new in matplotlib-1.1

‎examples/api/colorbar_only.py

Copy file name to clipboardExpand all lines: examples/api/colorbar_only.py
+25-2Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66

77
# Make a figure and axes with dimensions as desired.
88
fig = pyplot.figure(figsize=(8,3))
9-
ax1 = fig.add_axes([0.05, 0.65, 0.9, 0.15])
10-
ax2 = fig.add_axes([0.05, 0.25, 0.9, 0.15])
9+
ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
10+
ax2 = fig.add_axes([0.05, 0.475, 0.9, 0.15])
11+
ax3 = fig.add_axes([0.05, 0.15, 0.9, 0.15])
1112

1213
# Set the colormap and norm to correspond to the data for which
1314
# the colorbar will be used.
@@ -47,5 +48,27 @@
4748
orientation='horizontal')
4849
cb2.set_label('Discrete intervals, some other units')
4950

51+
# The third example illustrates the use of custom length colorbar
52+
# extensions, used on a colorbar with discrete intervals.
53+
cmap = mpl.colors.ListedColormap([[0., .4, 1.], [0., .8, 1.],
54+
[1., .8, 0.], [1., .4, 0.]])
55+
cmap.set_over((1., 0., 0.))
56+
cmap.set_under((0., 0., 1.))
57+
58+
bounds = [-1., -.5, 0., .5, 1.]
59+
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
60+
cb3 = mpl.colorbar.ColorbarBase(ax3, cmap=cmap,
61+
norm=norm,
62+
boundaries=[-10]+bounds+[10],
63+
extend='both',
64+
# Make the length of each extension
65+
# the same as the length of the
66+
# interior colors:
67+
extendfrac='auto',
68+
ticks=bounds,
69+
spacing='uniform',
70+
orientation='horizontal')
71+
cb3.set_label('Custom extension lengths, some other units')
72+
5073
pyplot.show()
5174

‎lib/matplotlib/__init__.py

Copy file name to clipboardExpand all lines: lib/matplotlib/__init__.py
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,8 @@ def tk_window_focus():
992992
'matplotlib.tests.test_text',
993993
'matplotlib.tests.test_tightlayout',
994994
'matplotlib.tests.test_delaunay',
995-
'matplotlib.tests.test_legend'
995+
'matplotlib.tests.test_legend',
996+
'matplotlib.tests.test_colorbar',
996997
]
997998

998999
def test(verbosity=1):

‎lib/matplotlib/colorbar.py

Copy file name to clipboardExpand all lines: lib/matplotlib/colorbar.py
+78-11Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,29 @@
5959

6060
colormap_kw_doc = '''
6161
62-
=========== ====================================================
62+
============ ====================================================
6363
Property Description
64-
=========== ====================================================
64+
============ ====================================================
6565
*extend* [ 'neither' | 'both' | 'min' | 'max' ]
6666
If not 'neither', make pointed end(s) for out-of-
6767
range values. These are set for a given colormap
6868
using the colormap set_under and set_over methods.
69+
*extendfrac* [ *None* | 'auto' | length | lengths ]
70+
If set to *None*, both the minimum and maximum
71+
triangular colorbar extensions with have a length of
72+
5% of the interior colorbar length (this is the
73+
default setting). If set to 'auto', makes the
74+
triangular colorbar extensions the same lengths as
75+
the interior boxes (when *spacing* is set to
76+
'uniform') or the same lengths as the respective
77+
adjacent interior boxes (when *spacing* is set to
78+
'proportional'). If a scalar, indicates the length
79+
of both the minimum and maximum triangular colorbar
80+
extensions as a fraction of the interior colorbar
81+
length. A two-element sequence of fractions may also
82+
be given, indicating the lengths of the minimum and
83+
maximum colorbar extensions respectively as a
84+
fraction of the interior colorbar length.
6985
*spacing* [ 'uniform' | 'proportional' ]
7086
Uniform spacing gives each discrete color the same
7187
space; proportional makes the space proportional to
@@ -82,7 +98,7 @@
8298
given instead.
8399
*drawedges* [ False | True ] If true, draw lines at color
84100
boundaries.
85-
=========== ====================================================
101+
============ ====================================================
86102
87103
The following will probably be useful only in the context of
88104
indexed colors (that is, when the mappable has norm=NoNorm()),
@@ -221,6 +237,7 @@ def __init__(self, ax, cmap=None,
221237
format=None,
222238
drawedges=False,
223239
filled=True,
240+
extendfrac=None,
224241
):
225242
self.ax = ax
226243
self._patch_ax()
@@ -236,6 +253,7 @@ def __init__(self, ax, cmap=None,
236253
self.orientation = orientation
237254
self.drawedges = drawedges
238255
self.filled = filled
256+
self.extendfrac = extendfrac
239257
self.solids = None
240258
self.lines = None
241259
self.outline = None
@@ -616,6 +634,35 @@ def _extended_N(self):
616634
N += 1
617635
return N
618636

637+
def _get_extension_lengths(self, frac, automin, automax, default=0.05):
638+
'''
639+
Get the lengths of colorbar extensions.
640+
641+
A helper method for _uniform_y and _proportional_y.
642+
'''
643+
# Set the default value.
644+
extendlength = np.array([default, default])
645+
if isinstance(frac, str):
646+
if frac.lower() == 'auto':
647+
# Use the provided values when 'auto' is required.
648+
extendlength[0] = automin
649+
extendlength[1] = automax
650+
else:
651+
# Any other string is invalid.
652+
raise ValueError('invalid value for extendfrac')
653+
elif frac is not None:
654+
try:
655+
# Try to set min and max extension fractions directly.
656+
extendlength[:] = frac
657+
# If frac is a sequence contaning None then NaN may
658+
# be encountered. This is an error.
659+
if np.isnan(extendlength).any():
660+
raise ValueError()
661+
except (TypeError, ValueError):
662+
# Raise an error on encountering an invalid value for frac.
663+
raise ValueError('invalid value for extendfrac')
664+
return extendlength
665+
619666
def _uniform_y(self, N):
620667
'''
621668
Return colorbar data coordinates for *N* uniformly
@@ -624,16 +671,19 @@ def _uniform_y(self, N):
624671
if self.extend == 'neither':
625672
y = np.linspace(0, 1, N)
626673
else:
674+
automin = automax = 1. / (N - 1.)
675+
extendlength = self._get_extension_lengths(self.extendfrac,
676+
automin, automax, default=0.05)
627677
if self.extend == 'both':
628678
y = np.zeros(N + 2, 'd')
629-
y[0] = -0.05
630-
y[-1] = 1.05
679+
y[0] = 0. - extendlength[0]
680+
y[-1] = 1. + extendlength[1]
631681
elif self.extend == 'min':
632682
y = np.zeros(N + 1, 'd')
633-
y[0] = -0.05
683+
y[0] = 0. - extendlength[0]
634684
else:
635685
y = np.zeros(N + 1, 'd')
636-
y[-1] = 1.05
686+
y[-1] = 1. + extendlength[1]
637687
y[self._inside] = np.linspace(0, 1, N)
638688
return y
639689

@@ -648,10 +698,27 @@ def _proportional_y(self):
648698
y = y / (self._boundaries[-1] - self._boundaries[0])
649699
else:
650700
y = self.norm(self._boundaries.copy())
651-
if self._extend_lower():
652-
y[0] = -0.05
653-
if self._extend_upper():
654-
y[-1] = 1.05
701+
if self.extend == 'min':
702+
# Exclude leftmost interval of y.
703+
clen = y[-1] - y[1]
704+
automin = (y[2] - y[1]) / clen
705+
automax = (y[-1] - y[-2]) / clen
706+
elif self.extend == 'max':
707+
# Exclude rightmost interval in y.
708+
clen = y[-2] - y[0]
709+
automin = (y[1] - y[0]) / clen
710+
automax = (y[-2] - y[-3]) / clen
711+
else:
712+
# Exclude leftmost and rightmost intervals in y.
713+
clen = y[-2] - y[1]
714+
automin = (y[2] - y[1]) / clen
715+
automax = (y[-2] - y[-3]) / clen
716+
extendlength = self._get_extension_lengths(self.extendfrac,
717+
automin, automax, default=0.05)
718+
if self.extend in ('both', 'min'):
719+
y[0] = 0. - extendlength[0]
720+
if self.extend in ('both', 'max'):
721+
y[-1] = 1. + extendlength[1]
655722
yi = y[self._inside]
656723
norm = colors.Normalize(yi[0], yi[-1])
657724
y[self._inside] = norm(yi)
Loading
Loading

‎lib/matplotlib/tests/test_colorbar.py

Copy file name to clipboard
+61Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from matplotlib import rcParams, rcParamsDefault
2+
from matplotlib.testing.decorators import image_comparison
3+
import matplotlib.pyplot as plt
4+
from matplotlib.colors import BoundaryNorm
5+
from matplotlib.cm import get_cmap
6+
from matplotlib.colorbar import ColorbarBase
7+
8+
9+
def _colorbar_extensions(spacing):
10+
11+
# Create a color map and specify the levels it represents.
12+
cmap = get_cmap("RdBu", lut=5)
13+
clevs = [-5., -2.5, -.5, .5, 1.5, 3.5]
14+
15+
# Define norms for the color maps.
16+
norms = dict()
17+
norms['neither'] = BoundaryNorm(clevs, len(clevs)-1)
18+
norms['min'] = BoundaryNorm([-10]+clevs[1:], len(clevs)-1)
19+
norms['max'] = BoundaryNorm(clevs[:-1]+[10], len(clevs)-1)
20+
norms['both'] = BoundaryNorm([-10]+clevs[1:-1]+[10], len(clevs)-1)
21+
22+
# Create a figure and adjust whitespace for subplots.
23+
fig = plt.figure()
24+
fig.subplots_adjust(hspace=.6)
25+
26+
for i, extension_type in enumerate(('neither', 'min', 'max', 'both')):
27+
# Get the appropriate norm and use it to get colorbar boundaries.
28+
norm = norms[extension_type]
29+
boundaries = values = norm.boundaries
30+
for j, extendfrac in enumerate((None, 'auto', 0.1)):
31+
# Create a subplot.
32+
cax = fig.add_subplot(12, 1, i*3+j+1)
33+
# Turn off text and ticks.
34+
for item in cax.get_xticklabels() + cax.get_yticklabels() +\
35+
cax.get_xticklines() + cax.get_yticklines():
36+
item.set_visible(False)
37+
# Generate the colorbar.
38+
cb = ColorbarBase(cax, cmap=cmap, norm=norm,
39+
boundaries=boundaries, values=values,
40+
extend=extension_type, extendfrac=extendfrac,
41+
orientation='horizontal', spacing=spacing)
42+
43+
# Return the figure to the caller.
44+
return fig
45+
46+
47+
@image_comparison(
48+
baseline_images=['colorbar_extensions_uniform', 'colorbar_extensions_proportional'],
49+
extensions=['png'])
50+
def test_colorbar_extensions():
51+
# Use default params so .matplotlibrc doesn't cause the test to fail.
52+
rcParams.update(rcParamsDefault)
53+
# Create figures for uniform and proportionally spaced colorbars.
54+
fig1 = _colorbar_extensions('uniform')
55+
fig2 = _colorbar_extensions('proportional')
56+
57+
58+
if __name__ == '__main__':
59+
import nose
60+
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)
61+

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.