Skip to content
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

contour edge case with all data below levels and a surrounding field of zeros #20203

Closed
rcomer opened this issue May 11, 2021 · 2 comments · Fixed by #24912
Closed

contour edge case with all data below levels and a surrounding field of zeros #20203

rcomer opened this issue May 11, 2021 · 2 comments · Fixed by #24912

Comments

@rcomer
Copy link
Member

rcomer commented May 11, 2021

Bug report

Bug summary

If I attempt to make a contour plot with data that is entirely below the lowest level, and the data has a field of zeros around it, I get an unexpected contour between the zeros and anything positive.

Code for reproduction

import numpy as np
import matplotlib.pyplot as plt

y = np.linspace(-10, 10, 20)
x_2d, y_2d = np.meshgrid(y, y)

data = np.cos(x_2d * np.pi/10.) + np.cos(y_2d * np.pi/10.)
np.place(data, data < 0., 0.)

levels = [3, 6]
colors = ['b', 'r']

plt.contour(data, levels=levels, colors=colors)

plt.show()

Actual outcome

bug_zeros_line

Expected outcome

The plot should be blank, which is what happens if the call to np.place is skipped.

Matplotlib version

  • Operating system: RHEL7.9
  • Matplotlib version (import matplotlib; print(matplotlib.__version__)): 3.4.1
  • Matplotlib backend (print(matplotlib.get_backend())): Qt5Agg
  • Python version: 3.8.8
  • Jupyter version (if applicable): N/A
  • Other libraries: numpy 1.20.2
@ianthomas23
Copy link
Member

I've confirmed that this is a bug. If the levels for a line contour plot are all outside of the z limits, the levels are replaced by the lower z limit, here:

if not self.filled:
inside = (self.levels > self.zmin) & (self.levels < self.zmax)
levels_in = self.levels[inside]
if len(levels_in) == 0:
self.levels = [self.zmin]
_api.warn_external(
"No contour levels were found within the data range.")

Here is a simple reproducer:

import matplotlib.pyplot as plt

z = [[0, 0], [0, 1]]
cs = ax[0].contour(z, levels=levels)
ax[0].set_title(f'levels_in={levels}, levels_out={cs.levels}')

levels = [2]
cs = ax[1].contour(z, levels=levels)
ax[1].set_title(f'levels_in={levels}, levels_out={cs.levels}')

fig.tight_layout()
plt.show()

Figure_1
Under most circumstances we'd get away with this behaviour. However, if there is a quad which has the lower-z value on 3 of its 4 corners then half the area of the quad is at the lower level and the contour line is calculated on the upper edge of this triangle (see #19691 for detailed explanation).

I presume that if the user specifies the contour levels then we should not modify them, even if this means that no contour lines are plotted. But I suspect there may be issues about colorbars being displayed without any contents?

@rcomer
Copy link
Member Author

rcomer commented May 16, 2021

Thanks @ianthomas23 for taking a look at this and providing an explanation. With this knowledge we can make a simple workaround in the user code by setting a new, artificial, localised minimum before plotting. E.g.

data[0, 0] = -1

@rcomer rcomer linked a pull request Jan 9, 2023 that will close this issue
6 tasks
@QuLogic QuLogic added this to the v3.7.0 milestone Jan 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants