-
-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Adjustments to contour.py so that levels are set accordingly to input vmin and vmax, if provided, so color bar works correctly. #2176
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
Conversation
…and then min or max out the input z values accordingly.
…utside the range of the data. In this case, the contour plot, if extend for the appropriate direction is also chosen, will appropriately reflect the desired range.
…ust call and not pop out the arguments. Turns out it is not necessary to use extend, this will just happen on its own.
@efiring: Seems like something (contours and coloring) that you might have some input about. |
I think I see what @kthyng is trying to fix, but to be sure, it would be very helpful to have a minimal runnable example. I think the present changeset might be changing more than it needs to, and might have undesirable side effects. |
from matplotlib import pyplot as plt
import numpy as np
# Get some data to use
xvec = np.linspace(0,10)
yvec = np.linspace(0,20)
X, Y = np.meshgrid(xvec,yvec)
Z = X**2 + Y**2
zmin, zmax = np.min(Z), np.max(Z)
print zmin, zmax
# Show plots
fig = plt.figure(figsize=(10,5))
fig.add_subplot(111)
ax1 = plt.subplot(1,2,1)
p = plt.pcolor(X,Y,Z,cmap='pink_r',vmax=700)
plt.title('pcolor plot')
plt.suptitle('With Fix')
cp = plt.colorbar()
ax2 = plt.subplot(1,2,2)
c = plt.contourf(X,Y,Z,cmap='pink_r', vmax=700)
plt.title('contourf plot')
cc = plt.colorbar()
plt.savefig('comp_with_fix.png',bbox_inches='tight') |
Sorry, I forgot to write a comment in here too. I am learning how to properly contribute to a community code like this. This fix does alter the data, z, itself, but from what I could tell, a similar sort of thing happens in the pcolor function when a vmin/vmax is applied. I also thought about trying to implement the change in the colorbar code since it is more of a colorbar problem really, but upon tracking back through the functions, I ended up changing it in contourf instead. |
The bug being addressed definitely needs fixing but we definitely need a test. I can work with you on that if you need a hand. |
Two image check tests were added to test_contour.py to make a contourf plot using input argument vmax, one with vmax greater than the maximum of the data set z values and one with vmax less than the maximum of the data set z values.
Thanks everyone! With help, I added in a couple of tests to test_contour.py. I've tried to provide the useful, relevant information, but I may have missed some. |
@kthyng, thank you for contributing, please don't be discouraged by my blunt criticism and questions. It is not at all clear to me that this is a bug, and if this change in behavior is indeed desired, then I don't think it is being changed in the right part of the code. This is not a colorbar question, it is a question of default choice of levels, isn't it? Consistently throughout mpl, vmin and vmax pertain to the color mapping, no more, no less. That is how they are presently being used in contourf. They are fed to the norm. What it seems you want to do is to overload them, so that when no contour levels are specified, but they are auto-generated based on a desired number of levels, then they will be based on vmin and vmax, if present, instead of zmin and zmax. If this is a good thing to do (debatable, but I am not necessarily opposed), then it should be done within the code that performs this auto-generation of levels, not where it is in the present PR. In addition, modifying the input array is bad practice. If we are doing that elsewhere, then those places are bugs to be fixed. |
On Jun 29, 2013 4:47 PM, "Eric Firing" notifications@github.com wrote:
I think that this sort of carefully considered overloading of vmin and vmax then it should be done within the code that performs this auto-generation Agreed.
Definitely agreed! I have been considering extending the test suite to do
|
That makes sense about it not necessarily being the correct place to make the change. It probably makes sense to first decide whether or not the change is actually desired by people in general, so I came up with another code snippet with the intention of illustrating why this problem comes up in my research (though, just because it comes up for me doesn't necessarily mean that it should change!). I often have 4-dimensional arrays (x,y,z,t) that I want to plot. I will plot x and y along the x and y axes and z as the color (in pcolor or contourf or similar), and then I will make a different snapshot for those variables for each time step. I want the colorbar and the coloring for every snapshot to be set by the maximum and minimum values of the entire 4D array so that I can make an animation. If the colorbar changes every snapshot, the animation looks terrible. This works fine when I used pcolor but I often like to use different plot functions for different emphases and thus I wanted to have the same behavior with contourf. See below for code and plots. I didn't make an animation, but I just meant to show that as time changes, the colorbar will change between snapshots using the original code. from matplotlib import pyplot as plt
import numpy as np
xvec = np.linspace(0,10)
yvec = np.linspace(0,20)
X, Y = np.meshgrid(xvec,yvec)
Z1 = X**2 + Y**2
Z2 = X**2 + Y**2 + 300
datamax = max(Z1.max(),Z2.max())
fig = plt.figure(figsize=(10,5))
fig.add_subplot(111)
plt.subplot(2,2,1)
plt.pcolor(X,Y,Z1,cmap='pink_r',vmax=datamax)
plt.title('pcolor plot, time 1')
plt.colorbar()
plt.subplot(2,2,2)
plt.pcolor(X,Y,Z2,cmap='pink_r',vmax=datamax)
plt.title('pcolor plot, time 2')
plt.colorbar()
plt.subplot(2,2,3)
plt.contourf(X,Y,Z1,cmap='pink_r', vmax=datamax)
plt.title('contourf plot, time 1')
plt.colorbar()
plt.subplot(2,2,4)
plt.contourf(X,Y,Z2,cmap='pink_r', vmax=datamax)
plt.title('contourf plot, time 2')
plt.colorbar()
plt.suptitle('Without Fix')
plt.tight_layout()
plt.savefig('comp_without_fix.png',bbox_inches='tight') |
@kthyng, my preferred method for handling this situation is to explicitly decide beforehand what contour levels I want all the plots to use, and then specify them via the "levels" kwarg. Then there is no need for the vmin, vmax kwargs, unless one wants to tweak the mapping. |
Oops, I forgot to set vmin on that example. @efiring, yes, that is what I do currently. Explicitly setting the levels takes a lot of work, though, if you have to do guess and check to get nice levels. Automatically setting the levels from a desired min to max value isn't a great solution because the numbers on the levels end up ugly. So, I have been using ticker.MaxNLocator() to get nice numbers out. I am remembering that this ends up actually being a very specific problem if I go through all the things I have tried over the past few months. I think the ticker solution ends up being fine (though it is a few extra steps, the way I have been doing it) if I have a sequential data set that I want to plot. However, I often plot diverging data sets and I want to be able to control the actual number of contours used because with a diverging data set I want to have one contour level around 0 and an equal number of contours extending in the positive and negative directions from zero. With MaxNLocator() I haven't figured out a way to do this because it chooses the exact number of levels, not exceeding the user's input value. I hope I am remembering this correctly from my work. The sprint time at the SciPy conference is ending so I can't make an example up currently, but I could later if that would help. I also do still think that even if there is a way to get my desired behavior currently (without having to do guess and check to get nice contour levels), having to go through a lot of steps for this type of plot is undesirable, but perhaps it is just unavoidable. I currently have been using the MaxNLocator() solution and since I can't make it have the specific number of contours I want (which would be a small, odd number, like 9), I have been using many contour levels so that the effect is less noticeable. Hopefully this makes sense. |
@efiring I think there are two issues that a fix here would ideally address:
I would have to think more about how these changes could be put into effect if others thought these changes would be a good addition to matplotlib. |
Ok, a lot of valuable information is in this PR, but as it stands I don't think we can merge it, so I'm going to go ahead and close the PR. Hopefully this is the beginning of a conversation which allows us to improve the contour api, particularly for cases where multiple subplots are being drawn. Perhaps that is a conversation to have on the mailing-list for now. Thanks! |
Previously, if a user wanted to include a vmin/vmax value to a call to contour or contourf, the plot itself would reflect the desired color range, but if a color bar was added to the plot, it would not nicely reflect the desired data range. This change fixes that problem by adding in a check for vmin and vmax in _contour_args() as input arguments and uses the values to set the levels in the data according to the desired data range. This works for whether the desired data range (from vmin/vmax) are within or without the actual data range, or whatever combination of the two. With this change, a plot made with pcolor and a color bar and a plot made with contourf and a color bar will behave similarly. The actual max and min boundary limits for the contour colorbars will not necessarily be equal to the input vmin and vmax because contour will still choose nice nearby contour values, but now those nice nearby values will be near the desired vmin and vmax. This work was done during the SciPy 2013 matplotlib sprint.