Skip to content

plot and scatter should allow marker to be a list #11155

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

Open
andreas-h opened this issue May 2, 2018 · 33 comments
Open

plot and scatter should allow marker to be a list #11155

andreas-h opened this issue May 2, 2018 · 33 comments
Labels
Difficulty: Hard https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues keep Items to be ignored by the “Stale” Github Action New feature

Comments

@andreas-h
Copy link
Contributor

Feature Request

It would be great if the plot and scatter functions would allow the marker kwarg to be a list.

When using scatter, I can set the color of individual pixels using the c kwarg. c can be a list/array. It would be convenient if I could also set the marker style of the individual points using a list of styles.

I'm using matplotlib 2.2.2 on Python 3.6

@ImportanceOfBeingErnest
Copy link
Member

ImportanceOfBeingErnest commented May 2, 2018

I suppose that's possible. A simplified and usable version could look like this

import numpy as np
import matplotlib.pyplot as plt

def mscatter(x,y,ax=None, m=None, **kw):
    import matplotlib.markers as mmarkers
    if not ax: ax=plt.gca()
    sc = ax.scatter(x,y,**kw)
    if (m is not None) and (len(m)==len(x)):
        paths = []
        for marker in m:
            if isinstance(marker, mmarkers.MarkerStyle):
                marker_obj = marker
            else:
                marker_obj = mmarkers.MarkerStyle(marker)
            path = marker_obj.get_path().transformed(
                        marker_obj.get_transform())
            paths.append(path)
        sc.set_paths(paths)
    return sc

Use it as

N = 40
x, y, c = np.random.rand(3, N)
s = np.random.randint(10, 220, size=N)
m = np.repeat(["o", "s", "D", "*"], N/4)

fig, ax = plt.subplots()

scatter = mscatter(x, y, c=c, s=s, m=m, ax=ax)

plt.show()

image

@ImportanceOfBeingErnest
Copy link
Member

Now two options:

  1. Make the above an example to the gallery
    Pro: Least work to implement, no API change
  2. Include this in the library
    1. Use an additional argument m, which superseeds marker if provided
      Pro: consistent with how color vs. c are currently treated.
    2. Use the existing marker argument.
      Pro: no further argument needed.

@timhoffm
Copy link
Member

timhoffm commented May 2, 2018

It would be great if the plot and scatter functions would allow the marker kwarg to be a list.

plot is requiring identical markers (semantically as well as from the internal code structure). plot + varying markers is actually scatter.

As for the options, 1. would be ok. I wouldn't want to go with 2i. Having two arguments essentially doing the same with a single object or a list of objects is not pythonic. We have dynamic typing to handle that (see 2ii).

If writing from scratch, I'd have a signature Axes.scatter(x, y, sizes=None, colors=None, markers=None, ...) and sizes, colors and markers supporting both single values and lists. However, quite unsure if changing this from the current state is worth a deprecation.

@ImportanceOfBeingErnest
Copy link
Member

ImportanceOfBeingErnest commented May 2, 2018

#4675 might be an interesting read on color and deprecation. Also #5377.

I'm happy to either implement 1. or 2.ii. once a decision is made.

@BenyamWorku
Copy link

I have modified the last few lines but the results were not as expected.
m = np.repeat(["+","*","o"], len(labels)/3)

fig, ax = plt.subplots()

scatter = mscatter(component_1,component_2,c=labels, m=m, ax=ax)
image

@ImportanceOfBeingErnest
Copy link
Member

@BenyamWorku I suppose the problem lies in the definition of labels, which is unknown here. So all one can say is that of course m needs to have the same length as component_1 and component_2 and that does not seem to be the case in your example.
A minimal, self-contained example would be

component_1, component_2 = np.random.randn(2,39)
labels = np.random.randint(0,3, size=len(component_1))
m = np.repeat(["+","*","o"], len(labels)/3)

fig, ax = plt.subplots()
scatter = mscatter(component_1, component_2, c=labels, m=m, ax=ax)
plt.show()

and that works as expected.

@tacaswell tacaswell added this to the v3.2.0 milestone Apr 12, 2019
@timhoffm timhoffm modified the milestones: v3.2.0, v3.3.0 Aug 15, 2019
pgowthami added a commit to CSCD01-team23/matplotlib that referenced this issue Mar 27, 2020
KhadraMa pushed a commit to CSCD01-team23/matplotlib that referenced this issue Apr 7, 2020
@KhadraMa
Copy link

If this feature is still on the table I worked with some teammates and we think we've implemented a good solution. The only problem is using multiple marker styles breaks the pre-existing functionality with regards to edgecolors and linewidths on unfilled markers. Since you can have both unfilled and filled markers on the same graph we can't use the face value to ignore the edgecolor and linewidth parameters. Our solution was to just use edgecolor and linewidths on unfilled markers if there are multiple marker styles and let the user specify what they should be - they can just make the edgecolor = color and linewidth = 1 for unfilled markers if they want.

Any guidance on if this is the best way to handle this?

@sagarhm
Copy link

sagarhm commented Jul 17, 2020

https://chem-workflows.com/articles/2019/10/21/ramachandran-plots-on-jupyter-notebook-gerdospyrama/
in these case i need different markers in scatter plot by removing subplots .how to use plzz help me
plt.scatter((normals[key]["x"]),( normals[key]["y"])

@DanielGoldfarb
Copy link

Note: I am using mscatter inside mplfinance and it works fine to give our users the ability to make scatter plots with a list of markers. Still would be nice to have it integrated into matplotlib itself.

@jklymak
Copy link
Member

jklymak commented Jul 23, 2020

@DanielGoldfarb given that you've implemented this formally, would it be difficult to work it into the main library?

@timhoffm
Copy link
Member

When working on this, I'd like to see a discussion of my concerns at #18040 (comment).

@QuLogic QuLogic modified the milestones: v3.4.0, v3.5.0 Jan 27, 2021
@ngc1535git
Copy link

I don't know where this stands- but supplying a list for marker style is very useful. The code above with mscatter does appear to work...though it is a black box at my level.

@DanielGoldfarb
Copy link

@jklymak Jody, regarding #11155 (comment) ... I've learned a lot in the past 6 months, so maybe now this is something I can do. Will take a look soon and see if I can integrate it directly into matplotlib. --Daniel

@zackles
Copy link

zackles commented Apr 12, 2021

Hello,
I tried to autogenerate legend with mscatter but it seems to not working. I'm adding ax.legend() just before plt.show in the code above #11155 (comment) but the generated legend is blank.

@DanielGoldfarb
Copy link

DanielGoldfarb commented Apr 12, 2021

@zackles
As Tim noted above and in some detail in the link therein there are definitely some concerns with getting legends to work correctly when using multiple markers on a single scatter call. It may be that we will have to internally set up some sort of proxy artists if we are to get automatically generated legends to work, or simply say that for the use-case of multiple markers on a single scatter call then we do not support automatically legends but require the user to make their own direct legend calls with the appropriate handles. Haven't thought it through completely yet; these are just my initial thoughts on the matter.

@zackles
Copy link

zackles commented May 6, 2021

@DanielGoldfarb Thank you. So it is an expected behavior and I have to generate legend.

@DanielGoldfarb
Copy link

@DanielGoldfarb Thank you. So it is an expected behavior and I have to generate legend.

@zackles
Yes, for the time being until someone has the time to code a built-in solution (which is noted above may be challenging) as I see it you have two choices:

  • manually generate your own legends, or
  • use multiple scatter calls with only one marker per call (and let the automatic legends do their thing)

I suspect that it will vary, from plot to plot, which of the above two choices is simpler.

@ShirAmir
Copy link

Hi,
Anything new with this feature? it will be super useful!

@jklymak
Copy link
Member

jklymak commented Sep 29, 2021

Someone needs to propose a PR and argue (strongly) that it is actually an improvement and not going to lead to an undue maintenance burden.

@bibs2091
Copy link

Any updates regarding this issue? this is a pretty useful feature!

@ngc1535git
Copy link

ngc1535git commented Mar 31, 2022 via email

@tacaswell tacaswell modified the milestones: v3.6.0, unassigned Apr 1, 2022
@tacaswell tacaswell added the Difficulty: Hard https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues label Apr 1, 2022
@story645 story645 modified the milestones: unassigned, needs sorting Oct 6, 2022
@github-actions
Copy link

This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

@github-actions github-actions bot added the status: inactive Marked by the “Stale” Github Action label Oct 11, 2023
@github-actions github-actions bot added the status: closed as inactive Issues closed by the "Stale" Github Action. Please comment on any you think should still be open. label Nov 10, 2023
@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Nov 10, 2023
@EwoutH
Copy link

EwoutH commented Aug 17, 2024

This issue is currently the most "+1ed" issue of any issue ever in matplotlib. I've recently encountered a use case for our library for which this would be really beneficial, so I'm curious if we could reopen this issue and discuss how we can move this issue forward.

image

@rcomer rcomer reopened this Aug 17, 2024
@rcomer rcomer added keep Items to be ignored by the “Stale” Github Action and removed status: inactive Marked by the “Stale” Github Action status: closed as inactive Issues closed by the "Stale" Github Action. Please comment on any you think should still be open. labels Aug 17, 2024
@EwoutH
Copy link

EwoutH commented Aug 19, 2024

Thanks for reopening the issue @rcomer! Is there anyone here that might want to work on this?

Edit: I think unfilled/filled marker color handling (edge color vs fill color) and automatic legend generation are the things we need to come to a reasonable approach for first, right?

@jklymak
Copy link
Member

jklymak commented Aug 19, 2024

@EwoutH I think the fundamental issue here is that scatter's two main features are mapping color and size. Both of these are continuous variables, and would be difficult for a user to replicate.

Marker style, on the other hand, is limited to finite discrete marker styles. It is easy to write a for-loop to loop over the desired styles in any given visualization. Conversely, it is hard to come up with a general way of representing those markers in a legend and as @timhoffm comment says #18040 (comment), there are a lot of rough edge cases.

@EwoutH
Copy link

EwoutH commented Aug 19, 2024

Instead of a list, could we allow bins? That seems fitting for categorial variable that markers like to represent.

Edit: Or just groups of points, like a dict with {marker: some_points}

Edit 2: Heck, even {(marker, legend_name): some_points}

@jklymak
Copy link
Member

jklymak commented Aug 19, 2024

@EwoutH it's not clear what you are referring to here. How would this look inside a call to scatter?

@ngc1535git
Copy link

Just throwing in my tiny comment- that for the less-than-brilliant coder- a list really is very understandable and somewhat expected given other existing methods.
-adam

@DanielGoldfarb
Copy link

I was about to say more-or-less the same thing. I am not aware of any other places in matplotlib that accept buckets instead of a list. (Let me know if there are any). I think I understand the bucket idea, but I'm inclined to be consistent with the behavior other, similar parameters within matplotlib. If the list is too short it should be cycled.

@story645
Copy link
Member

story645 commented Aug 20, 2024

Edit: Or just groups of points, like a dict with {marker: some_points}

Edit 2: Heck, even {(marker, legend_name): some_points}

We could probably provide an example that constructs the list given this mapping?

And I think a model for handling this in legend, is add marker as another category to
https://matplotlib.org/devdocs/api/collections_api.html#matplotlib.collections.PathCollection.legend_elements and then show how to use the formatting options to format? It's slightly clunky but limits the new API. ETA: but also @timhoffm can you please elaborate on why this isn't feasible?

@timhoffm
Copy link
Member

timhoffm commented Aug 20, 2024

but also @timhoffm can you please elaborate on why this isn't feasible?

IIRC, the point is that PathCollection stores the marker as a single Path and the individual representations are on-the-fly scaled and translated variants of that Path. PathCollection is not designed to support multiple markers and I suspect it will be difficult to change that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Difficulty: Hard https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues keep Items to be ignored by the “Stale” Github Action New feature
Projects
None yet
Development

No branches or pull requests