# public matplotlib /matplotlib

### Subversion checkout URL

You can clone with HTTPS or Subversion.

# contour: map extended ranges to "under" and "over" values#1022

Merged
merged 4 commits into from over 1 year ago
 +79 38

### 3 participants

commented Owner

Contourf was handling the extend kwarg by mapping the extended
ranges directly into the 0-1 normal colormap range, so that
using set_under and set_over had no effect; after this change, the
extended ranges are mapped outside the 0-1 range (after applying
the norm) so that the colors established by the set_under and set_over
colormap methods are used. By default, these are the end values of
the colormap. The new behavior matches the existing contourf documentation.

Fixed a bug in which the legend support for extend options was using the
wrong names for the options.

 efiring contour: map extended ranges to "under" and "over" values Contourf was handling the extend kwarg by mapping the extended ranges directly into the 0-1 normal colormap range, so that using set_under and set_over had no effect. The legend support for extend options was also using the wrong names for the options. 44d7556
commented on 44d7556

I would like to see a test code or example.

 lib/matplotlib/contour.py 
 ((25 lines not shown)) 1046 1047  if self.extend in ('both', 'min'): 1047 - self.layers[0] = 0.5 * (self.vmin + self._levels[1]) 1048 + self.layers[0] = -1e300
 2 WeatherGod added a note July 20, 2012 Collaborator I hate magic numbers like this. Maybe we could use np.INF and np.NINF? efiring added a note July 21, 2012 Owner On 2012/07/20 5:20 PM, Benjamin Root wrote: > if self.extend in ('both', 'min'): > - self.layers[0] = 0.5 * (self.vmin + self._levels[1]) > + self.layers[0] = -1e300 I hate magic numbers like this. Maybe we could use np.INF and np.NINF? I thought of that, and may yet do so; but I wanted to get out a fix that would work, and could not take time to test whether potentially dangerous things like INF would cause trouble in practice. Note that mpl has other somewhat arbitrary special numbers, because they make it work most of the time. Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
 efiring contourf_demo: add a figure to illustrate extend options with cmap Also removed "from pylab import *". bca3321 efiring contourf: use np.inf instead of 1e300 for out-of-range color levels Note that trying to simplify the code further by using np.inf a few lines above, in self._levels, does not work; I'm not sure what it breaks, but it definitely breaks something, probably in the colorbar code. 72e1c6c efiring update CHANGELOG, api_changes.rst for new handling of extend kwarg 0c347f3
referenced this pull request from a commit
 efiring Merge pull request #1022 from efiring/contourf_extend contour: map extended ranges to "under" and "over" values 967c15a
merged commit 967c15a into from
closed this
referenced this pull request from a commit
 Commit has since been removed from the repository and is no longer available.
referenced this pull request from a commit in efiring/matplotlib
 efiring update contour_hatching test images for change caused by #1022 Filled contours now use more of the available colormap range than previously. 1bf4dde
referenced this pull request Merged

### Pull Request #1057: Contour norm scaling

Showing 4 unique commits by 1 author.

Jul 15, 2012
contour: map extended ranges to "under" and "over" values
Contourf was handling the extend kwarg by mapping the extended
ranges directly into the 0-1 normal colormap range, so that
using set_under and set_over had no effect.
The legend support for extend options was also using the
wrong names for the options.
44d7556
Jul 21, 2012
contourf_demo: add a figure to illustrate extend options with cmap
Also removed "from pylab import *".
bca3321
contourf: use np.inf instead of 1e300 for out-of-range color levels
Note that trying to simplify the code further by using np.inf a
few lines above, in self._levels, does not work; I'm not sure what
it breaks, but it definitely breaks something, probably in the
colorbar code.
72e1c6c
Jul 24, 2012
update CHANGELOG, api_changes.rst for new handling of extend kwarg 0c347f3
 ... ... @@ -1,3 +1,10 @@ 1 +2012-07-24 Contourf handles the extend kwarg by mapping the extended 2 + ranges outside the normed 0-1 range so that they are 3 + handled by colormap colors determined by the set_under 4 + and set_over methods. Previously the extended ranges 5 + were mapped to 0 or 1 so that the "under" and "over" 6 + colormap colors were ignored. - EF 7 + 1 8  2012-06-24 Make use of mathtext in tick labels configurable - DSD 2 9   3 10  2012-06-05 Images loaded through PIL are now ordered correctly - CG
 @@ -14,6 +14,16 @@ For new features that were added to matplotlib, please see 14 14  Changes in 1.2.x 15 15  ================ 16 16   17 +* In :meth:~matplotlib.axes.Axes.contourf, the handling of the *extend* 18 + kwarg has changed. Formerly, the extended ranges were mapped 19 + after to 0, 1 after being normed, so that they always corresponded 20 + to the extreme values of the colormap. Now they are mapped 21 + outside this range so that they correspond to the special 22 + colormap values determined by the 23 + :meth:~matplotlib.colors.Colormap.set_under and 24 + :meth:~matplotlib.colors.Colormap.set_over methods, which 25 + default to the colormap end points. 26 + 17 27  * The new rc parameter savefig.format replaces cairo.format and 18 28  savefig.extension, and sets the default file format used by 19 29  :meth:matplotlib.figure.Figure.savefig.
 ... ... @@ -1,39 +1,41 @@ 1 1  #!/usr/bin/env python 2 -from pylab import * 2 +import numpy as np 3 +import matplotlib.pyplot as plt 4 + 3 5  origin = 'lower' 4 6  #origin = 'upper' 5 7   6 8  delta = 0.025 7 9   8 -x = y = arange(-3.0, 3.01, delta) 9 -X, Y = meshgrid(x, y) 10 -Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) 11 -Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1) 10 +x = y = np.arange(-3.0, 3.01, delta) 11 +X, Y = np.meshgrid(x, y) 12 +Z1 = plt.mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) 13 +Z2 = plt.mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) 12 14  Z = 10 * (Z1 - Z2) 13 15   14 16  nr, nc = Z.shape 15 17   16 18  # put NaNs in one corner: 17 -Z[-nr//6:, -nc//6:] = nan 19 +Z[-nr//6:, -nc//6:] = np.nan 18 20  # contourf will convert these to masked 19 21   20 22   21 -Z = ma.array(Z) 23 +Z = np.ma.array(Z) 22 24  # mask another corner: 23 -Z[:nr//6, :nc//6] = ma.masked 25 +Z[:nr//6, :nc//6] = np.ma.masked 24 26   25 27  # mask a circle in the middle: 26 -interior = sqrt((X**2) + (Y**2)) < 0.5 27 -Z[interior] = ma.masked 28 +interior = np.sqrt((X**2) + (Y**2)) < 0.5 29 +Z[interior] = np.ma.masked 28 30   29 31   30 32  # We are using automatic selection of contour levels; 31 33  # this is usually not such a good idea, because they don't 32 34  # occur on nice boundaries, but we do it here for purposes 33 35  # of illustration. 34 -CS = contourf(X, Y, Z, 10, # [-1, -0.1, 0, 0.1], 36 +CS = plt.contourf(X, Y, Z, 10, # [-1, -0.1, 0, 0.1], 35 37  #alpha=0.5, 36 - cmap=cm.bone, 38 + cmap=plt.cm.bone, 37 39  origin=origin) 38 40   39 41  # Note that in the following, we explicitly pass in a subset of @@ -41,28 +43,28 @@ 41 43  # We could pass in additional levels to provide extra resolution, 42 44  # or leave out the levels kwarg to use all of the original levels. 43 45   44 -CS2 = contour(CS, levels=CS.levels[::2], 46 +CS2 = plt.contour(CS, levels=CS.levels[::2], 45 47  colors = 'r', 46 48  origin=origin, 47 49  hold='on') 48 50   49 -title('Nonsense (3 masked regions)') 50 -xlabel('word length anomaly') 51 -ylabel('sentence length anomaly') 51 +plt.title('Nonsense (3 masked regions)') 52 +plt.xlabel('word length anomaly') 53 +plt.ylabel('sentence length anomaly') 52 54   53 55  # Make a colorbar for the ContourSet returned by the contourf call. 54 -cbar = colorbar(CS) 56 +cbar = plt.colorbar(CS) 55 57  cbar.ax.set_ylabel('verbosity coefficient') 56 58  # Add the contour line levels to the colorbar 57 59  cbar.add_lines(CS2) 58 60   59 -figure() 61 +plt.figure() 60 62   61 63  # Now make a contour plot with the levels specified, 62 64  # and with the colormap generated automatically from a list 63 65  # of colors. 64 66  levels = [-1.5, -1, -0.5, 0, 0.5, 1] 65 -CS3 = contourf(X, Y, Z, levels, 67 +CS3 = plt.contourf(X, Y, Z, levels, 66 68  colors = ('r', 'g', 'b'), 67 69  origin=origin, 68 70  extend='both') @@ -72,16 +74,34 @@ 72 74  CS3.cmap.set_under('yellow') 73 75  CS3.cmap.set_over('cyan') 74 76   75 -CS4 = contour(X, Y, Z, levels, 77 +CS4 = plt.contour(X, Y, Z, levels, 76 78  colors = ('k',), 77 79  linewidths = (3,), 78 80  origin = origin) 79 -title('Listed colors (3 masked regions)') 80 -clabel(CS4, fmt = '%2.1f', colors = 'w', fontsize=14) 81 +plt.title('Listed colors (3 masked regions)') 82 +plt.clabel(CS4, fmt = '%2.1f', colors = 'w', fontsize=14) 81 83   82 84  # Notice that the colorbar command gets all the information it 83 85  # needs from the ContourSet object, CS3. 84 -colorbar(CS3) 85 - 86 -show() 86 +plt.colorbar(CS3) 87 + 88 +# Illustrate all 4 possible "extend" settings: 89 +extends = ["neither", "both", "min", "max"] 90 +cmap = plt.cm.get_cmap("winter") 91 +cmap.set_under("magenta") 92 +cmap.set_over("yellow") 93 +# Note: contouring simply excludes masked or nan regions, so 94 +# instead of using the "bad" colormap value for them, it draws 95 +# nothing at all in them. Therefore the following would have 96 +# no effect: 97 +#cmap.set_bad("red") 98 + 99 +fig, axs = plt.subplots(2,2) 100 +for ax, extend in zip(axs.ravel(), extends): 101 + cs = ax.contourf(X, Y, Z, levels, cmap=cmap, extend=extend, origin=origin) 102 + fig.colorbar(cs, ax=ax, shrink=0.9) 103 + ax.set_title("extend = %s" % extend) 104 + ax.locator_params(nbins=4) 105 + 106 +plt.show() 87 107 
 @@ -870,10 +870,10 @@ def legend_elements(self, variable_name='x', str_format=str): 870 870  lower = str_format(lower) 871 871  upper = str_format(upper) 872 872   873 - if i == 0 and self.extend in ('lower', 'both'): 874 - labels.append(r'$%s \leq %s$' % (variable_name, upper, )) 875 - elif i == n_levels-1 and self.extend in ('upper', 'both'): 876 - labels.append(r'$%s > %s$' % (variable_name, lower, )) 873 + if i == 0 and self.extend in ('min', 'both'): 874 + labels.append(r'$%s \leq %s$' % (variable_name, lower, )) 875 + elif i == n_levels-1 and self.extend in ('max', 'both'): 876 + labels.append(r'$%s > %s$' % (variable_name, upper, )) 877 877  else: 878 878  labels.append(r'$%s < %s \leq %s$' % (lower, variable_name, upper)) 879 879  else: @@ -1029,24 +1029,25 @@ def _contour_level_args(self, z, args): 1029 1029  raise ValueError("Filled contours require at least 2 levels.") 1030 1030   1031 1031  def _process_levels(self): 1032 + # Color mapping range (norm vmin, vmax) is based on levels. 1033 + self.vmin = np.amin(self.levels) 1034 + self.vmax = np.amax(self.levels) 1035 + # Make a private _levels to include extended regions. 1032 1036  self._levels = list(self.levels) 1033 1037  if self.extend in ('both', 'min'): 1034 1038  self._levels.insert(0, min(self.levels[0],self.zmin) - 1) 1035 1039  if self.extend in ('both', 'max'): 1036 1040  self._levels.append(max(self.levels[-1],self.zmax) + 1) 1037 1041  self._levels = np.asarray(self._levels) 1038 - self.vmin = np.amin(self.levels) # alternative would be self.layers 1039 - self.vmax = np.amax(self.levels) 1040 - if self.extend in ('both', 'min'): 1041 - self.vmin = 2 * self.levels[0] - self.levels[1] 1042 - if self.extend in ('both', 'max'): 1043 - self.vmax = 2 * self.levels[-1] - self.levels[-2] 1044 1042  if self.filled: 1043 + # layer values are mid-way between levels 1045 1044  self.layers = 0.5 * (self._levels[:-1] + self._levels[1:]) 1045 + # ...except that extended layers must be outside the 1046 + # normed range: 1046 1047  if self.extend in ('both', 'min'): 1047 - self.layers[0] = 0.5 * (self.vmin + self._levels[1]) 1048 + self.layers[0] = -np.inf 1048 1049  if self.extend in ('both', 'max'): 1049 - self.layers[-1] = 0.5 * (self.vmax + self._levels[-2]) 1050 + self.layers[-1] = np.inf 1050 1051  else: 1051 1052  self.layers = self.levels # contour: a line is a thin layer 1052 1053  # Use only original levels--no extended levels @@ -1065,9 +1066,11 @@ def _process_colors(self): 1065 1066  """ 1066 1067  self.monochrome = self.cmap.monochrome 1067 1068  if self.colors is not None: 1069 + # Generate integers for direct indexing. 1068 1070  i0, i1 = 0, len(self.levels) 1069 1071  if self.filled: 1070 1072  i1 -= 1 1073 + # Out of range indices for over and under: 1071 1074  if self.extend in ('both', 'min'): 1072 1075  i0 = -1 1073 1076  if self.extend in ('both', 'max'): @@ -1080,7 +1083,8 @@ def _process_colors(self): 1080 1083  self.set_clim(self.vmin, self.vmax) 1081 1084  if self.extend in ('both', 'max', 'min'): 1082 1085  self.norm.clip = False 1083 - self.set_array(self.layers) 1086 + self.set_array(self.layers) # Required by colorbar, but not 1087 + # actually used. 1084 1088  # self.tcolors are set by the "changed" method 1085 1089   1086 1090  def _process_linewidths(self):