Skip to content

Implement extend color bar for contourf #8806

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 4 commits into from
Closed

Implement extend color bar for contourf #8806

wants to merge 4 commits into from

Conversation

ghost
Copy link

@ghost ghost commented Jun 26, 2017

PR Summary

Implement extend keyword for lognorm color bar on contourf. Travis ci might sometimes timeout during installation (before testing starts) and fails.

PR Checklist

  • Has Pytest style unit tests
  • Code is PEP 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/whats_new.rst if major new feature
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

@tacaswell tacaswell added this to the 2.1 (next point release) milestone Jun 26, 2017
@tacaswell tacaswell requested a review from efiring June 26, 2017 18:57
Copy link
Member

@efiring efiring left a comment

Choose a reason for hiding this comment

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

Thank you for filling in this gap in colorbar.

if self.logscale:
tempLocator = LogLocator()
if self.locator is not None:
tempLocator = self.locator
Copy link
Member

Choose a reason for hiding this comment

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

Instead of using a temporary Locator and then requiring that all Locators have 3 new private methods, I think it would be better to keep everything within the ContourBase class. This will be more readable than using Locator private methods that modify one of their arguments. It will likely require fewer LOC as well.

Copy link
Author

@ghost ghost Jun 28, 2017

Choose a reason for hiding this comment

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

@efiring Thank you for the input. I took sometimes to think it over more carefully, I will fix it your way.

@@ -308,3 +310,34 @@ def test_colorbar_lognorm_extension():
cb = ColorbarBase(ax, norm=LogNorm(vmin=0.1, vmax=1000.0),
orientation='vertical', extend='both')
assert cb._values[0] >= 0.0


@image_comparison(baseline_images=['extended_cbar_with_contourf_min',
Copy link
Member

Choose a reason for hiding this comment

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

Please condense the test down to a single image with 3 subplots, saving time and space.

'extended_cbar_with_contourf_both'],
extensions=['png'])
def test_extended_colorbar_on_contourf():
np.random.seed(1)
Copy link
Member

Choose a reason for hiding this comment

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

Not used.

x = np.linspace(-3.0, 3.0, N)
y = np.linspace(-2.0, 2.0, N)
X, Y = np.meshgrid(x, y)
z = (bivariate_normal(X, Y, 0.1, 0.2, 1.0, 1.0)
Copy link
Member

Choose a reason for hiding this comment

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

For the purpose of a test you can use a simpler pattern with far fewer points. For example, you could use

z = np.logspace(0.1, 10, 24).reshape((4, 6))

and omit the X and Y entirely.

plt.subplot(111)
plt.contourf(X, Y, z, cmap=cm.PuBu_r, locator=ticker.LogLocator(),
extend='min')
plt.colorbar()
Copy link
Member

Choose a reason for hiding this comment

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

For condensing the example you can use fig, axs = plt.subplots(ncols=3) and then use Axes and Figure method calls.

Copy link
Author

Choose a reason for hiding this comment

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

Thanks. Will fix this test up.

@efiring
Copy link
Member

efiring commented Jun 28, 2017

Here is what I came up with:

diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py
index 2b3c21b6d..e72a10c81 100644
--- a/lib/matplotlib/contour.py
+++ b/lib/matplotlib/contour.py
@@ -831,9 +831,6 @@ class ContourSet(cm.ScalarMappable, ContourLabeler):
             self.logscale = True
             if norm is None:
                 norm = colors.LogNorm()
-            if self.extend is not 'neither':
-                raise ValueError('extend kwarg does not work yet with log '
-                                 ' scale')
         else:
             self.logscale = False

@@ -1213,10 +1210,19 @@ class ContourSet(cm.ScalarMappable, ContourLabeler):
         # (Colorbar needs this even for line contours.)
         self._levels = list(self.levels)

+        if self.logscale:
+            raised = lambda x: x * 1.1
+            lowered = lambda x: x / 1.1
+        else:
+            raised = lambda x: x + 1
+            lowered = lambda x: x - 1
+
         if self.extend in ('both', 'min'):
-            self._levels.insert(0, min(self.levels[0], self.zmin) - 1)
+            lower = lowered(min(self.levels[0], self.zmin))
+            self._levels.insert(0, lower)
         if self.extend in ('both', 'max'):
-            self._levels.append(max(self.levels[-1], self.zmax) + 1)
+            upper = raised(max(self.levels[-1], self.zmax))
+            self._levels.append(upper)
         self._levels = np.asarray(self._levels)

         if not self.filled:
@@ -1228,7 +1234,7 @@ class ContourSet(cm.ScalarMappable, ContourLabeler):
         # ...except that extended layers must be outside the
         # normed range:
         if self.extend in ('both', 'min'):
-            self.layers[0] = -1e150
+            self.layers[0] = 1e-150 if self.logscale else -1e150
         if self.extend in ('both', 'max'):
             self.layers[-1] = 1e150

@ghost
Copy link
Author

ghost commented Jun 28, 2017

Yes. That can work. But shouldn't self._levels be scaled with whatever base of LogNorm? I.e lamda x: x*some_base ? LogNorm takes more than 1 bases and self._levels is used in calculating rgba. I mean when firing up debugger, I see values of self._levels to be like [10^-6, 10^-5,..., 0.1, 1, 10,100] so I think the extended version should be like [10^-7, 10^-6,..., 0.1, 1, 10,100,1000] and LogNorm can take different bases as well. Hence fixed factor like 1.1 might not work.

@efiring
Copy link
Member

efiring commented Jun 28, 2017

No, just as the value added or subtracted in the linear case (1) is arbitrary, so long as it is positive, the multiplier or divisor in the log case is also arbitrary, so long as it is greater than 1. It merely needs to push the level outside the normed range so that the "under" and "over" colors of the colormap are used.

@ghost
Copy link
Author

ghost commented Jun 28, 2017

Ah. I see. Thanks for clarifying that. I think your implementation is better. Please commit it.

@ghost ghost closed this Jun 28, 2017
@ghost ghost deleted the new branch June 29, 2017 09:23
@tacaswell
Copy link
Member

tacaswell commented Jun 29, 2017

@Tuan333 did you mean to close this and delete your branch?

@efiring
Copy link
Member

efiring commented Jun 29, 2017

I haven't made an alternative PR yet, but I have committed the implementation suggested above. I find that specifying a LogLocator really doesn't do anything sensible at present, regardless of the extend kwarg. I think I have a fix for that, and to be consistent it will also make an improvement in the case with no locator specified and with the extend kwarg. I think this is a rare use case, so I'm not too worried about the behavior change.

@ghost
Copy link
Author

ghost commented Jun 30, 2017

@efiring @tacaswell oops... I thought this is not needed anymore and moved on to some other bugs I'm interested in. Should I have left this open? (genuine mistake/question)

@efiring
Copy link
Member

efiring commented Jun 30, 2017

I think closing it is fine. If you don't see a PR from me on this within a week, ping me.

This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants