-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Fix contour color #538
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
Fix contour color #538
Conversation
@@ -452,6 +452,8 @@ def newplotfunc(darray, ax=None, xincrease=None, yincrease=None, | |||
# pcolormesh | |||
kwargs['extend'] = cmap_params['extend'] | |||
kwargs['levels'] = cmap_params['levels'] | |||
if 'colors' in kwargs: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
colors
and cmap
should probably be required to be mutually exclusive? I don't think it makes sense to supply both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. So you're saying it should raise an error if you supply both? That is what contour does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so. On the other hand, it looks like we've already put the listed colors functionality of colors
into cmap
in #509.
@jhamman what do you think? I think we probably want to move the listed colors functionality shown here from cmap
into colors
. That would more naturally handle the single color case desired here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the wrapped matplotlib functions only contour, contourf
have the colors
arg in the docstring. But all 4 take a cmap
argument.
@shoyer are you suggesting that they all have a colors
arg? The more uniform we can make everything here, the better IMHO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think that's probably a better way to provide explicit color(s) than overloading cmap. We can raise an error is colors is provided without the levels arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with this. It will just be important to distinguish between a list of colors and a ListedColorMap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is compatibility with matplotlib conventions considered important? If so, you probably don't want to raise an error if contour is called with colors but without levels. That is a pretty common usage pattern when e.g. you want to make a black and white contour plot with auto-determined contour levels. I would just do plt.contour(field, colors='k')
and let matplotlib pick the levels.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rabernat Good point- we should be able to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, agreed. The way we implemented this, the default levels=None means no levels for color mesh or imshow plots but 7 levels for contour and contourf.
On Thu, Aug 20, 2015 at 9:28 AM, Clark Fitzgerald
notifications@github.com wrote:
@@ -452,6 +452,8 @@ def newplotfunc(darray, ax=None, xincrease=None, yincrease=None,
# pcolormesh
kwargs['extend'] = cmap_params['extend']
kwargs['levels'] = cmap_params['levels']
if 'colors' in kwargs:
@rabernat Good point- we should be able to do this.
Reply to this email directly or view it on GitHub:
https://github.com/xray/xray/pull/538/files#r37550742
It would be very useful to have a test here to verify that the colors argument does the right thing. |
So what is the conclusion about this PR? I am happy to keep tweaking it if we can converge on the desired behavior... |
I have no problems with this. @clarkfitzg - any more thoughts? |
We can do this as an interim fix, but I would prefer something more comprehensive:
|
I can probably get to this within a few days. I would enjoy learning more about the plotting code. |
This update mostly implements @shoyer's plan. However, it currently only works if seaborn is installed. This is because it completely bypasses matplotlibs |
Looks like you have commits from a few other changes (e.g., the 0.6 release) in this PR. Could you do a |
Sorry...I'm an idiot, and I always somehow manage to screw up these pull requests. I did what you / that thread suggested
When I try to push my PR again with
|
e5b66bc
to
3887112
Compare
Ok, I think I fixed it. Just had to do a |
The testing errors I think are due to seaborn not being present. |
Do we want to make |
@jhamman is that question for me? I would say obviously not. However, I think this dependency was already present even before this PR. There just wasn't a test that exposed it. If you passed a list of colors to cmap, I'm pretty sure you would get an error if seaborn was not installed. (Please correct me if I'm wrong.) |
You are correct. We should create a colormap if seaborn is not installed. The relevant line is here: https://github.com/rabernat/xray/blob/fix_contour_color/xray/plot/plot.py#L300 where we could add something like this sudo code: if isinstance(cmap, list-like-thing):
cmap = _cmap_from_list_of_colors(cmap) |
@rabernat We were all Git noobs at some point- don't worry about it! Seaborn should not be a dependency. |
@@ -414,13 +419,27 @@ def _plot2d(plotfunc): | |||
@functools.wraps(plotfunc) | |||
def newplotfunc(darray, ax=None, xincrease=None, yincrease=None, | |||
add_colorbar=True, add_labels=True, vmin=None, vmax=None, cmap=None, | |||
center=None, robust=False, extend=None, levels=None, | |||
center=None, robust=False, extend=None, levels=None, colors=None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll also want to add this to the def plotmethod
lower in the function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you still need to fix this. Also, I would put colors
immediately following cmap
in the function argument.
3887112
to
61de94c
Compare
@@ -297,6 +298,9 @@ def _color_palette(cmap, n_colors): | |||
# Use homegrown solution if you don't have seaborn or are using viridis | |||
if isinstance(cmap, basestring): | |||
cmap = plt.get_cmap(cmap) | |||
elif isinstance(cmap, (list, tuple)): | |||
from matplotlib.colors import ListedColormap | |||
cmap = ListedColormap(cmap, N=n_colors) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is how you work around if seaborn is not installed.
I think this latest commit solves everything. |
@@ -386,8 +390,12 @@ def _plot2d(plotfunc): | |||
The mapping from data values to color space. If not provided, this | |||
will be either be ``viridis`` (if the function infers a sequential | |||
dataset) or ``RdBu_r`` (if the function infers a diverging dataset). | |||
When ``levels`` is provided and when `Seaborn` is installed, ``cmap`` | |||
may also be a `seaborn` color palette or a list of colors. | |||
When when `Seaborn` is installed, ``cmap`` may also be a `seaborn` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this case really make sense for the cmap
argument rather than colors
?
I'm not entirely sure, but in any case it is still worth noting that color palettes are only valid arguments when plotting discrete colors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now the docs make it clear that you need to specify levels
(or use contour
/contourf
) with a seaborn named palette in cmap
. That is a slightly different case than specifying a list of colors
. This seems consistent, because now you never give a list to cmap.
I think this will be the first PR merged for v0.6.1. Could you add a brief note to the what's new documentation under a section for "API changes"? You'll need to create a new section for v0.6.1. |
61de94c
to
74335ae
Compare
This is turning into a real rabbit hole... |
try: | ||
from seaborn.apionly import color_palette | ||
pal = color_palette(cmap, n_colors=n_colors) | ||
except (TypeError, ImportError, ValueError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _color_palette function was a big source of confusion for me because it served two very different purposes:
- It translated a list of colors into a palette using seaborn
- It translated named colormaps (e.g.
jet
) into palettes, including possibly seaborn palettes
I have refactored the function a little to make these two roles more clear.
It turns out that seaborn is not needed for 1. It can be done with matplotlib.colors.ListedColormap
. I have left the seaborn version of this here for now, but if it does the same thing as matplotlib, it can be removed.
@rabernat indeed, your willingness to dive in here is highly appreciated! I think this is pretty close -- are there aspects that you're still unsure about? |
may also be a `seaborn` color palette or a list of colors. | ||
When when `Seaborn` is installed, ``cmap`` may also be a `seaborn` | ||
color palette. If ``cmap`` is seaborn color palette and the plot type | ||
is not ``contour`` or ``contourf``, ``levels`` must also be specified. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seaborn colormaps (e.g. husl
) need to have the number of levels specified. This is the only way in which they behave differently from matplotlib colormaps. This is a little bit annoying, since you can do imshow(cmap='jet')
but instead have to do imshow(cmap='husl', levels=20)
. I have added a test for this.
fixed seaborn dependency in cmap generation test for new contour color rules updated docs test for seaborn palettes forgot colors in plotmethod
74335ae
to
ee4fab7
Compare
@shoyer There is still some redundancy between the I am also a little unsure about how seaborn fits into the plotting module. It is not a dependency, but certain parts of plotting are built around it. This leads to the need for lots of special cases and some ugly code. |
@jhamman is the original designer of this API so perhaps he can clarify :). One possibly cleaner alternative is to have a separate |
pal = color_palette(cmap, n_colors=n_colors) | ||
except ImportError: | ||
# if that fails, use matplotlib | ||
# in this case, is there any difference between mpl and seaborn? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seaborn is doing something extremely similar: https://github.com/mwaskom/seaborn/blob/v0.6/seaborn/palettes.py#L404-L408
Sorry I've been mostly absent in this conversation recently. I'm currently buried under a mound of C and Fortran code. I included the optional seaborn call in the discrete colormap/colorbar PR to expose a few nice features in the seaborn color_palette api. Namely, I wanted access to all the seaborn named color palettes and to be able to pass a list of custom colors. I would be okay with adding the |
@jhamman One nice thing about this current PR is that it does allow people without seaborn to still use a custom list of colors. If we don't want to make seaborn a dependency, then surely that is a good thing. The only thing seaborn is now needed for is its special named palettes. As for the named palettes, I don't think a new keyword is needed. This has been a good learning experience for me. (Had never touched seaborn before.) But I probably won't work on this PR any more. Bottom line, it does fix the original issue without breaking anything. It also improves the testing of colors. |
OK, this seems like a clear improvement so I'm going to merge. Thanks again for all your work here! |
Thanks @rabernat . |
This fixes #537 by adding a check for the presence of the colors kwarg.