can't create single legend line with different point types #2018

Closed
jslavin opened this Issue May 16, 2013 · 8 comments

Projects

None yet

4 participants

jslavin commented May 16, 2013

Hi,

I'd like to create a legend for a plot with a single label applying to two different types of points -- i.e. having different colors and marker types. There doesn't seem to be any way of doing this. Here's some simple demonstration code:

fig, ax = plt.subplots()
p1 = ax.scatter([0.1],[0.5],c='r',marker='s')
p2 = ax.scatter([0.3],[0.2],c='b',marker='o')
l = ax.legend((p1,p2),['points'],scatterpoints=2)
plt.show()

This yields a legend with two red squares. What I want is a red square and a blue circle. I've tried variations on the legend call and have not been able to find a way to get this to work (though it is possible to use ncol=2 and an empty string for one of the labels, this leaves a wide gap between the points). I think there should be a way to do this. This is somewhat related to the issue that, though one can give a sequence of colors for points in a call to scatter, there is no way to give a sequence of marker types.

Jon

Member
pelson commented May 17, 2013

Hi @jslavin - this is really a question for the matplotlib-users mailing list (http://matplotlib.org/faq/troubleshooting_faq.html#getting-help). @leejjoon is likely to be the best person to help here - you might like to also take a look at one of his answers to creating a custom legend handler: http://nbviewer.ipython.org/5495680

I'll close this issue - but it may be that, if the legend code cannot be used to create the desired output, we need to create a specific issue to add a new feature to matplotlib.

Cheers,

@pelson pelson closed this May 17, 2013
jslavin commented May 17, 2013

Hi Phil,

I did in fact post this question, though stated a bit differently, to the
matplotlib-uers group back on January 23rd (under the title "append to or
concatenate PathCollection objects?"). The only response that I got at the
time was from Sterling Smith who encouraged me to open an issue on it.
Maybe I could restate it as a feature request. I think in the end I'd like
a simple method for creating a legend object in which the inputs could be
line styles, marker types, etc. rather than requiring they be
lines/patches/collections and with the possibility of there being more than
one of the line/marker, etc. per label.

Jon

On Fri, May 17, 2013 at 5:04 AM, Phil Elson notifications@github.comwrote:

Hi @jslavin https://github.com/jslavin - this is really a question for
the matplotlib-users mailing list (
http://matplotlib.org/faq/troubleshooting_faq.html#getting-help).
@leejjoon https://github.com/leejjoon is likely to be the best person
to help here - you might like to also take a look at one of his answers to
creating a custom legend handler: http://nbviewer.ipython.org/5495680

I'll close this issue - but it may be that, if the legend code cannot be
used to create the desired output we need to create a specific issue to add
a new feature.

Cheers,


Reply to this email directly or view it on GitHubhttps://github.com/matplotlib/matplotlib/issues/2018#issuecomment-18050892
.

Member
pelson commented May 17, 2013

Hi @jslavin.

The only response I see was from ChaoYue (http://matplotlib.1069221.n5.nabble.com/append-to-or-concatenate-PathCollection-objects-td40302.html).

I'm not trying to palm the issue off - I'd like to find a solution to this too. Though it is is my belief that this is achievable with the very powerful legend handlers, I don't think it is easy. Perhaps you could re-phrase your question so that you are more specific. I've had a go here (feel free to use whatever you want of it):

I'd like to have a single legend entry which contains 2 different markers. 
For example, with the following code, I'd like to have legend element with a
blue circle and a red star and the word "Data points":

>>> import numpy as np
>>> import matplotlib.pyplot as plt

>>> plt.plot(np.cos(np.linspace(0, np.pi)), 'ob', label='Data points')

>>> plt.plot(np.sin(np.linspace(0, np.pi)), '*r', label='Data points')

>>> # I'd be happy to replace this with the necessary code....
>>> plt.legend()

>>> plt.show()


Is there a way to do this with the current legend tools in matplotlib?

Cheers,

jslavin commented May 17, 2013

Hi Phil,

Yes, you've summed up my question nicely. So you're suggesting that I post
such a question to the matplotlib-uers list? I'm willing to do that, but I
expect that, if there is an answer, it is more involved than it ought to be
-- that is, using the legend handlers. I still feel like there ought to be
a more simple method for creating a legend that didn't depend on getting
the information that it needs from already created plot objects.

Jon

On Fri, May 17, 2013 at 9:28 AM, Phil Elson notifications@github.comwrote:

Hi @jslavin https://github.com/jslavin.

The only response I see was from ChaoYue (
http://matplotlib.1069221.n5.nabble.com/append-to-or-concatenate-PathCollection-objects-td40302.html
).

I'm not trying to palm the issue off - I'd like to find a solution to this
too. Though it is is my belief that this is achievable with the very
powerful legend handlers, I don't think it is easy. Perhaps you could
re-phrase your question so that you are more specific. I've had a go here
(feel free to use whatever you want of it):

I'd like to have a single legend entry which contains 2 different markers.
For example, with the following code, I'd like to have legend element with a
blue circle and a red star and the word "Data points":

import numpy as np
import matplotlib.pyplot as plt

plt.plot(np.cos(np.linspace(0, np.pi)), 'ob', label='Data points')

plt.plot(np.sin(np.linspace(0, np.pi)), '*r', label='Data points')

I'd be happy to replace this with the necessary code....

plt.legend()

plt.show()

Is there a way to do this with the current legend tools in matplotlib?

Cheers,


Reply to this email directly or view it on GitHubhttps://github.com/matplotlib/matplotlib/issues/2018#issuecomment-18061227
.

Contributor

I've posted one solution for this problem. The solution is not as elegant as it can be though.

http://nbviewer.ipython.org/5603703

  • The get_handle_lists function, should be added as a Legend method.

What I think would be better is to revise the TupleHandler that optionally divides the handle area so that they can be easily shown side by side.

jslavin commented May 20, 2013

Hi Jae-Joon,

Thanks for that. I agree that a revision of TupleHandler could make it
easier. It's not clear to me what the point if TupleHandler is if not to
handle this type of case. If my case is the typical use case then it would
seem that making only one point per tuple member visible should be the
default. The example in the matplotlib gallery, which involves overlaying
two point styles, seems more like the exceptional case.

Jon

On Sat, May 18, 2013 at 4:22 AM, Jae-Joon Lee notifications@github.comwrote:

I've posted one solution for this problem. The solution is not as elegant
as it can be though.

http://nbviewer.ipython.org/5603703

  • The get_handle_lists function, should be added as a Legend method.

What I think would be better is to revise the TupleHandler that
optionally divides the handle area so that they can be easily shown side by
side.


Reply to this email directly or view it on GitHubhttps://github.com/matplotlib/matplotlib/issues/2018#issuecomment-18097126
.

Member

On Mon, May 20, 2013 at 11:00 AM, Jonathan Slavin
notifications@github.comwrote:

Hi Jae-Joon,

Thanks for that. I agree that a revision of TupleHandler could make it
easier. It's not clear to me what the point if TupleHandler is if not to
handle this type of case. If my case is the typical use case then it would
seem that making only one point per tuple member visible should be the
default. The example in the matplotlib gallery, which involves overlaying
two point styles, seems more like the exceptional case.

Jon

If my memory serves me right, it was developed to handle mixed type plots
like stem plots.

Ben Root

Contributor

The above PR improves the handler for tuple as proposed above. One question is what would be the default behavior. Currently the value of ndivide is set to 1 by default, which gives backward-compatible results. ndivide is number of sub-division of the handle area. If it is 1, different handles are over-plotted in a same area. We may have it set to 0, which will set ndivide to a length of the tuple on the fly. For npoints=1, the output looks reasonably good. But for values greater than 1, I am not very pleased with the results. So this may needs further tweaking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment