Skip to content

pyplot.set_cmap broken #896

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

pyplot.set_cmap broken #896

wants to merge 2 commits into from

Conversation

f0k
Copy link
Contributor

@f0k f0k commented May 23, 2012

With at least matplotlib 1.1.1rc and above (the one in Ubuntu 12.04 LTS), the following code does not work, while it worked in at least matplotlib 0.99.1.1 and below (the one in Ubuntu 10.04 LTS):

import pylab
pylab.set_cmap('hot')
pylab.imsave('test.png', pylab.array([[0,1],[1,0]]))

The following code works in both versions of matplotlib:

import pylab
pylab.hot()
pylab.imsave('test.png', pylab.array([[0,1],[1,0]]))

The attached pull request restores the behavior of earlier matplotlib versions, consistent with the docstring of pyplot.set_cmap.

Background information: In the current version of matplotlib, pyplot.set_cmap throws a RuntimeError('You must first define an image, eg with imshow') when pyplot.gci() returns None. This is wrong, as the documentation explicitly states that it sets "the default colormap" and only applies it "to current image if any".
Of course, set_cmap is not needed in the example above, but it's terribly useful for colormaps that do not have a shortcut method (such as 'binary').

@pelson
Copy link
Member

pelson commented May 24, 2012

You change makes a lot of sense to me. What doesn't make a lot of sense is the current behaviour of allowing pyplot.set_cmap to change the colormap of the last added image (why not other artists?). We have methods to do this (plt.gci().set_cmap(cmap) from the top of my head) and removing the image cmap changing functionality from plt.set_cmap would make the whole issue more intuitive. Of course, this would break backwards compatibility of anyone relying on this behaviour. Anyone else have a view on this?

@mdboom
Copy link
Member

mdboom commented May 24, 2012

Not to justify the current behavior, but I think it comes from matlab:

http://www.mathworks.com/help/techdoc/ref/colormap.html

It's not very clear from the description, but it is clear from the examples further down that that is how it's intended to work.

I don't think we can change this without breaking backward compatibility, as you say. It is strange how it sort of works "retroactively" like that.

Maybe we could add a kwarg "update_current_image" (or something shorter) that defaults to True? Ok, that's not a great idea, either, but it's the best I can think of right now...

@pelson
Copy link
Member

pelson commented May 24, 2012

Not to justify the current behavior, but I think it comes from matlab

Playing devil's advocate, I guess the question then is whether pyplot is a "warts and all" reflection of matlab's core api :-)
From a personal stance, I'm not really worried by this as I don't tend to want to set a global palette, but I can see that it could be very confusing to a user.

@mdboom
Copy link
Member

mdboom commented May 24, 2012

No -- there's lots of places where matplotlib has chosen to deviate from matlab's API -- I was just giving some history.

The problem is that it's worked this way for so long, it's bound to break people's scripts. We can deprecate for a release, and then fix this in a subsequent release, however.

@efiring
Copy link
Member

efiring commented May 26, 2012

This pull request looks good; the discussion relates to a possible future pull request.

@efiring efiring closed this May 26, 2012
@lucacerone
Copy link

Hi, sorry I am having the same issue if I do:

import pylab as pl

im = pl.imshow(pl.rand(100,100))
im.set_cmap("gray")

nothing happens.

However if I change the colormap with

pl.set_cmap("gray")

then the colors are changed as intended.

The reason is that pl.set_cmap is defined in pyplot.py but im.set_cmap uses set_cmap defined in matplotlib/cm.py,
so opposite to my intuition they are two different functions.

Now I am new to matplotlib, but the behaviour seem broken to me.

Is this really an issue to be reopened, or am I doing something wrong? Thanks for the help!

Luca

@efiring
Copy link
Member

efiring commented Sep 23, 2013

Luca,
What you observe is consistent with matplotlib as a whole; automatic redrawing occurs only when changes are made via a pyplot function, not via a method. The pyplot functions are thin method wrappers that include a draw_if_interactive() call. In the case of the example you give, you can follow your call to im.set_cmap('gray') with pl.draw() to force the image to be redrawn with the new color map.

@lucacerone
Copy link

Thanks Eric ( @efiring ),
sorry if I got it wrong.

In the tutorial however (http://matplotlib.org/users/image_tutorial.html)
it is a bit confusing, because it says:

Now, with a luminosity image, the default colormap (aka lookup table, LUT), is applied. 
The default is called jet.     There are plenty of others to choose from. 
Let’s set some others using the set_cmap() method on our image plot        object:

In [8]: imgplot.set_cmap('hot')

And the image is shown with a different colormap, without mentioning the call to pylab.draw()
Maybe some explanation there could be helpful, especially for beginners (like me)

Just out of curiosity is there a way to have the draw() performed automatically when some property is modified?

Thanks again for the help, and sorry if I opened an issue when not needed.

Cheers,
Luca

@pelson
Copy link
Member

pelson commented Sep 24, 2013

Just out of curiosity is there a way to have the draw() performed automatically when some property is modified?

Sadly not. The methods are not wired up that way (they can't know about the pyplot module without causing some nasty circularity). For example, compare the axes.plot method vs the pyplot.plot function (https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/axes/_axes.py#L1334 vs https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/pyplot.py#L2995). You will see that the method call just adds the appropriate Artist objects (the lines etc that get drawn) whereas the pyplot function does this + triggers a re-draw if necessary.

In the tutorial however it is a bit confusing, because it says:

Interesting. You are right. The reason this works is that the tutorial is written in the perspective of an IPython user, which will plot the last object in the prompt (I think - I've not actually tested that).

Thanks again for the help, and sorry if I opened an issue when not needed.

No worries.

@lucacerone
Copy link

Hi Phil,
thanks for the explanation, I will try to delve deeply in the documentation
to understand better the details.

As of the tutorial: I have run the code in the tutorial using Ipython 1.1.0
(run with the --pylab option) and unfortunately it doesn't work as well.

Thanks again for the help and explanations,
they have been really useful.

Cheers,
Luca

On 24 September 2013 08:41, Phil Elson notifications@github.com wrote:

Just out of curiosity is there a way to have the draw() performed
automatically when some property is modified?

Sadly not. The methods are not wired up that way (they can't know about
the pyplot module without causing some nasty circularity). For example,
compare the axes.plot method vs the pyplot.plot function (
https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/axes/_axes.py#L1334vs
https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/pyplot.py#L2995).
You will see that the method call just adds the appropriate Artist objects
(the lines etc that get drawn) whereas the pyplot function does this +
triggers a re-draw if necessary.

In the tutorial however it is a bit confusing, because it says:

Interesting. You are right. The reason this works is that the tutorial is
written in the perspective of an IPython user, which will plot the last
object in the prompt (I think - I've not actually tested that).

Thanks again for the help, and sorry if I opened an issue when not needed.

No worries.


Reply to this email directly or view it on GitHubhttps://github.com//pull/896#issuecomment-24980499
.

Luca Cerone

Tel: +447585611951
Skype: luca.cerone

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.

5 participants