Skip to content

BUG: contour plot levels #868

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

Merged
merged 7 commits into from
Jul 20, 2016
Merged
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
4 changes: 4 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Breaking changes
- Indexing on multi-index now drop levels, which is consitent with pandas.
It also changes the name of the dimension / coordinate when the multi-index is
reduced to a single index.
- Contour plots no longer add a colorbar per default (:issue:`866`).

Enhancements
~~~~~~~~~~~~
Expand Down Expand Up @@ -100,6 +101,9 @@ Bug fixes
- Fixed incorrect test for dask version :issue:`891`. By
`Stephan Hoyer <https://github.com/shoyer>`_.

- :py:func:`~xarray.plot.contour` now plots the correct number of contours
(:issue:`866`). By `Fabien Maussion <https://github.com/fmaussion>`_.

.. _whats-new.0.7.2:

v0.7.2 (13 March 2016)
Expand Down
8 changes: 6 additions & 2 deletions xarray/plot/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,13 +339,17 @@ def _plot2d(plotfunc):
@functools.wraps(plotfunc)
def newplotfunc(darray, x=None, y=None, ax=None, row=None, col=None,
col_wrap=None, xincrease=True, yincrease=True,
add_colorbar=True, add_labels=True, vmin=None, vmax=None,
add_colorbar=None, add_labels=True, vmin=None, vmax=None,
cmap=None, center=None, robust=False, extend=None,
levels=None, colors=None, subplot_kws=None,
cbar_ax=None, cbar_kwargs=None, **kwargs):
# All 2d plots in xarray share this function signature.
# Method signature below should be consistent.

# Decide on a default for the colorbar before facetgrids
if add_colorbar is None:
add_colorbar = plotfunc.__name__ != 'contour'

# Handle facetgrids first
if row or col:
allargs = locals().copy()
Expand Down Expand Up @@ -450,7 +454,7 @@ def newplotfunc(darray, x=None, y=None, ax=None, row=None, col=None,
@functools.wraps(newplotfunc)
def plotmethod(_PlotMethods_obj, x=None, y=None, ax=None, row=None,
col=None, col_wrap=None, xincrease=True, yincrease=True,
add_colorbar=True, add_labels=True, vmin=None, vmax=None,
add_colorbar=None, add_labels=True, vmin=None, vmax=None,
cmap=None, colors=None, center=None, robust=False,
extend=None, levels=None, subplot_kws=None,
cbar_ax=None, cbar_kwargs=None, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion xarray/plot/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def _build_discrete_cmap(cmap, levels, extend, filled):

if not filled:
# non-filled contour plots
extend = 'neither'
extend = 'max'
Copy link
Member

Choose a reason for hiding this comment

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

I think I missed the rationale for this one -- why should this change?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the actual bug fix for #866 which motivated this PR ;-).

xarray wasn't plotting contours correctly before this change, see the following code:

import numpy as np
import matplotlib.pyplot as plt
import xarray as xr

x, y = np.meshgrid(np.arange(12), np.arange(12))
z = xr.DataArray(np.sqrt(x**2 + y**2))
z.plot.contour(levels=[2, 4, 6, 8], add_colorbar=False, colors='k')
plt.show()

figure_1

With this fix everything is fine now:

figure_1-1

Intuitively, it also makes sense: the last contour does mark all data beyond the last level, hence extend = 'max'

Copy link
Member

Choose a reason for hiding this comment

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

Got it, thanks!


if extend == 'both':
ext_n = 2
Expand Down
37 changes: 29 additions & 8 deletions xarray/test/test_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ def test_build_discrete_cmap(self):
if filled:
self.assertEqual(ncmap.colorbar_extend, extend)
else:
self.assertEqual(ncmap.colorbar_extend, 'neither')
self.assertEqual(ncmap.colorbar_extend, 'max')

def test_discrete_colormap_list_of_levels(self):
for extend, levels in [('max', [-1, 2, 4, 8, 10]),
Expand All @@ -429,7 +429,7 @@ def test_discrete_colormap_list_of_levels(self):
if kind != 'contour':
self.assertEqual(extend, primitive.cmap.colorbar_extend)
else:
self.assertEqual('neither', primitive.cmap.colorbar_extend)
self.assertEqual('max', primitive.cmap.colorbar_extend)
self.assertEqual(len(levels) - 1, len(primitive.cmap.colors))

def test_discrete_colormap_int_levels(self):
Expand All @@ -454,7 +454,7 @@ def test_discrete_colormap_int_levels(self):
if kind != 'contour':
self.assertEqual(extend, primitive.cmap.colorbar_extend)
else:
self.assertEqual('neither', primitive.cmap.colorbar_extend)
self.assertEqual('max', primitive.cmap.colorbar_extend)
self.assertGreaterEqual(levels, len(primitive.cmap.colors))

def test_discrete_colormap_list_levels_and_vmin_or_vmax(self):
Expand Down Expand Up @@ -620,7 +620,7 @@ def test_default_title(self):

def test_colorbar_default_label(self):
self.darray.name = 'testvar'
self.plotmethod()
self.plotmethod(add_colorbar=True)
self.assertIn(self.darray.name, text_in_fig())

def test_no_labels(self):
Expand All @@ -633,26 +633,28 @@ def test_no_labels(self):
def test_colorbar_kwargs(self):
# replace label
self.darray.name = 'testvar'
self.plotmethod(cbar_kwargs={'label':'MyLabel'})
self.plotmethod(add_colorbar=True, cbar_kwargs={'label':'MyLabel'})
alltxt = text_in_fig()
self.assertIn('MyLabel', alltxt)
self.assertNotIn('testvar', alltxt)
# you can use mapping types as well
self.plotmethod(cbar_kwargs=(('label', 'MyLabel'),))
self.plotmethod(add_colorbar=True, cbar_kwargs=(('label', 'MyLabel'),))
alltxt = text_in_fig()
self.assertIn('MyLabel', alltxt)
self.assertNotIn('testvar', alltxt)
# change cbar ax
fig, (ax, cax) = plt.subplots(1, 2)
self.plotmethod(ax=ax, cbar_ax=cax, cbar_kwargs={'label':'MyBar'})
self.plotmethod(ax=ax, cbar_ax=cax, add_colorbar=True,
cbar_kwargs={'label':'MyBar'})
self.assertTrue(ax.has_data())
self.assertTrue(cax.has_data())
alltxt = text_in_fig()
self.assertIn('MyBar', alltxt)
self.assertNotIn('testvar', alltxt)
# note that there are two ways to achieve this
fig, (ax, cax) = plt.subplots(1, 2)
self.plotmethod(ax=ax, cbar_kwargs={'label':'MyBar', 'cax':cax})
self.plotmethod(ax=ax, add_colorbar=True,
cbar_kwargs={'label':'MyBar', 'cax':cax})
self.assertTrue(ax.has_data())
self.assertTrue(cax.has_data())
alltxt = text_in_fig()
Expand Down Expand Up @@ -795,6 +797,19 @@ def _color_as_tuple(c):
_color_as_tuple(artist.cmap.colors[1]),
(0.0, 0.0, 1.0))

artist = self.darray.plot.contour(levels=[-0.5, 0., 0.5, 1.],
colors=['k', 'r', 'w', 'b'])
self.assertEqual(
_color_as_tuple(artist.cmap.colors[1]),
(1.0, 0.0, 0.0))
self.assertEqual(
_color_as_tuple(artist.cmap.colors[2]),
(1.0, 1.0, 1.0))
# the last color is now under "over"
self.assertEqual(
_color_as_tuple(artist.cmap._rgba_over),
(0.0, 0.0, 1.0))

def test_cmap_and_color_both(self):
with self.assertRaises(ValueError):
self.plotmethod(colors='k', cmap='RdBu')
Expand All @@ -810,6 +825,12 @@ def test_2d_coord_names(self):
self.assertEqual('x2d', ax.get_xlabel())
self.assertEqual('y2d', ax.get_ylabel())

def test_single_level(self):
# this used to raise an error, but not anymore since
# add_colorbar defaults to false
self.plotmethod(levels=[0.1])
self.plotmethod(levels=1)


class TestPcolormesh(Common2dMixin, PlotTestCase):

Expand Down