Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Legend for Scatter#11127

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged

Conversation

ImportanceOfBeingErnest
Copy link
Member

PR Summary

This PR proposes to include an easy, yet versatile option to create legends for scatterplots.

Motivation:

  • Scatter plots create a Collection of points, for which it is rather straight forward to create a colorbar. This is useful for continuous variables mapped to color. However, creating a legend with discrete entries requires to manually set up the necessary proxy artists. It appears that scatter plots are more and more used to show discrete of categorical variables. In such cases, a discrete legend is often more desireable than a (continuous, or even discrete) colorbar.

  • ggplot offers an easy method to create legends for scatter plots,
    ggplot(...) + geom_point(aes(x=.., y=.., size=.., color=..)), resulting in something like
    image

This PR adds a methodlegend_items to thePathCollection, which returns the handles and labels for a legend.
Creating a scatter legend would then look like

scatter = ax.scatter(x, y, c=c, s=s)# produce a legend with the unique colors from the scatterax.legend(*scatter.legend_items(), loc=3, title="Classes")# produce a legend with a cross section of sizes from the scatterhandles, labels = scatter.legend_items(mode="sizes", alpha=0.6)ax.legend(handles, labels, loc=1, title="Sizes")

image

PR Checklist

  • Has Pytest style unit tests
  • Code is PEP 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)

kasuteru and venkyyuvy reacted with thumbs up emoji
@story645
Copy link
Member

I like the idea of a generalized way to do legends for PathCollections & PatchCollections, but wonder if this is overly complicated:

# produce a legend with the unique colors from the scatterax.legend(*scatter.legend_items(),loc=3,title="Classes")# produce a legend with a cross section of sizes from the scatterhandles,labels=scatter.legend_items(mode="sizes",alpha=0.6)ax.legend(handles,labels,loc=1,title="Sizes")

overlapping with the discussion in ##7383, I wonder if there shouldn't just be a subclass of legend that handles Path/Patch and cmap/norm stuff.

@ImportanceOfBeingErnest
Copy link
MemberAuthor

How is a single line "too complicated"?

If I understand correctly, a Legend subclass would then need to take the artist (in this case the PathCollection) as input, as well as those arguments needed to steer the output. In that sense you could replaceax.legend(*scatter.legend_items(**kw)) byax.enhanced_legend(scatter, **kw). So essentiallyenhanced_legend would take all the currently available 26 keyword arguments, and in addition those to manage what to show in the legend.

I also don't see much overlap to#7383. I think what's discussed there is interesting if at some point you want to allow for something likescatter(x,y, c=["A", "B", "B", "C", "A"])?

What is proposed in this PR is completely analogue to the existingContourSet.legend_elements() which creates handles and labels for contours. (... which makes me realize that the namelegend_items could of course be changed tolegend_elements to be consistent between those.)

Copy link
Member

@jklymakjklymak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think this a useful thing to add. Some minor suggestions/comments below that I think will improve it. I feel somewhat strongly that the name should not just belegend_items() as the returned tuple is non-unique.

My only other issue is that we are going to add aax.get_legend_handles_labels. I've not quite wrapped my brain around where this fits into that.


Parameters
----------
mode : string, optional, default *"colors"*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Not sure I like "mode" here, though I guess its OK. "key" would be more natural to me...

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

What makeskeymore natural? Any good argument forkey?
In principle I'm open to other names here, e.g.prop because colors or sizes are properties?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yeah,prop works for me.

ImportanceOfBeingErnest reacted with thumbs up emoji
for obtaining a legend for a :meth:`~.Axes.scatter` plot. E.g.::

scatter = plt.scatter([1,2,3], [4,5,6], c=[7,2,3])
plt.legend(*scatter.legend_items())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I'm not sure I like the namelegend_items just because it makes it sound like these are a property of the PathCollection; I think a verb would help likecreate_legend_items ormake_legend_items

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

For consistency I would go withlegend_elements as commented above already. Unless people are prepared to break the existing contour method and callboth something else?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I still like having a verb because you are actually doing something versus just returning a property of the collection. But I don't feel super strongly...

Can be *"colors"* or *"sizes"*. In case of *"colors"*, the legend
handles will show the different colors of the collection. In case
of "sizes", the legend will show the different sizes.
useall : bool or string "auto", optional (default "auto")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

EDIT: oops, this somehow got lost: I don't think you need bothuseall andnum kwargs. num=None is all data, num=int is number bins, num='auto' is auto

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Ithink that won't work, but I will recheck.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

No, you're right, that works. The only "drawback" is that in that casenum cannot act as a parameter for the automatic determination; but I guess it's fine to have that parameter hardcoded (choosing num=9 now for the border between showing all data or some cross section of it).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I didn't get that num interacted w/ auto somehow. (still don't get it w/o checking the code).

I wonder if what you really want is a "bins" argument, and make this completely analagous to the histogram bins argument.https://docs.scipy.org/doc/numpy/reference/generated/numpy.histogram.html You could even optionally put in the legend how many elements there are in each bin.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I don't think I want "bins". I want "ticks"; and I get them using a Locator. From my side this is all working fine. The idea is of course to get nice "tick positions", unlike in the case of histogram bins where you'd end up with arbitrarily ugly numbers like 1.57214.

What this function does not allow for is to add custom ticks (which is possible for bins inhistogram). I decided against that because I think this is exactly the point where you'd anyways simply create the legend the way you want it with the artists created manually. But if someone feels that this is needed, I could also add it.

Copy link
Member

@jklymakjklymakApr 30, 2018
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

OK, thats cool. What about letting a (power) user pass in a Locator?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

That is sure possible. I think it would then simply ignorenum completely. I'm not sure if it's needed, but if there is the wish to add it, I can do that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I meant let them pass in a locator as the argument to num. I don’t think anyone who knows how to write a locator would be put off by the slight semantic incongruity.

The format or formatter to use for the labels. If a string must be
a valid input for a `~.StrMethodFormatter`. If None (the default),
use a `~.ScalarFormatter`.
func : function, default *lambda x: x*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I found this too confusing. What does*func* do and why do you need it? Maybe this needs an example...

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

func would allow to calculate the values to be shown in the legend. This is mainly useful for "sizes", because unlike thec parameter in scatter, which takes numbers and maps them to color using aNormalize, thes parameter does not have a normalization. This means that usually you would encode your data into sizes and provide those sizes in the scatter call. However in the legend you might still want to show the original data's values. Example: You use thes to show price ranging between 0.5 and 10 dollars. Now you cannot use those numbers directly as scatter sizes, because that would result in tiny dots. So you convert them to sizes vias = lambda price : 0.5*(price*10)^2. Nowfunc would allow you to provideprice = lambda s: np.sqrt(2*s)/10 to have a nice legend using the actual prices in dollars.

I think you are right that an example might be useful here. I could add that in addition to the already extended example?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Makes sense. I was particularly flummoxed by "This converts the initial values for color or size and needs to take a numpy array as input." I wasn't sure what "initial" meant.

For the doc string maybe: "Function to calculate the labels. Often thesize (orcolor) argument to scatter will have been pre-processed by the user using a functions = f(x) to make the markers visible; egsize = np.log10(x)`. Providing this function allows that pre-processing to be inverted, so that the legend labels have the correct values. egnp.exp(x, 10)``"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I reformulated the docstring and also added an additional example now.

@story645
Copy link
Member

story645 commentedApr 29, 2018
edited
Loading

How is a single line "too complicated"?

Sorry, I was reacting to the * notation and should have looked at this way more carefully. It's fine as is, especially if you go withlegend_elements() to make it consistent with existing functionality.

@jklymak
Copy link
Member

Actually ax.get_legend_handles_labels already exists. As does the concept of legend handlers. Any way this can be turned into a legend handler?

story645 reacted with thumbs up emoji

@ImportanceOfBeingErnest
Copy link
MemberAuthor

ax.get_legend_handles_labels exists indeed. What it does is to return the legend handles and labels that would produce a legend for an axesax. Example,

ax.scatter(...., label="mylabel")h, l = ax.get_legend_handles_labels()ax.legend(h, l)

would produce a single legend row with one scatter dot and the label "mylabel". It is hence equalvalent to simply callinglegend without arguments,

ax.scatter(...., label="mylabel")ax.legend()

image

Now what is possible using a legend handler would be to change the single handle from a single point to something more characteristic of a scatter, e.g.:

image

However, this is still a single legend entry for the complete scatter.

What this PR proposes is a way to getseveral legend entries forone single scatter plot. This is not possible with aHandler.

One might consider putting the proposed API forscatter.legend_elements(**API) intoax.get_legend_handles_labels(**API). I think this might be undesired because then the axes method takes all those arguments, which only make sense in the context of a scatter. At some point internally it would in any case still need to call something likescatter._legend_elements(**API). So I guess the proposed method here would still be needed and can be public as well.

jklymak reacted with thumbs up emoji

@tacaswelltacaswell added this to thev3.0 milestoneApr 29, 2018
@@ -899,6 +900,118 @@ def set_paths(self, paths):
def get_paths(self):
return self._paths

def legend_items(self, mode="colors", useall="auto", num=10,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

How far up the class hierarchy does it make sense to move this?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This particular method only makes sense for thePathCollection. A less useful version of it could be created for anyCollection and yes, evenArtist could in principle have such method. However the point being that at least for more complex things like scatter (PathCollection), LineCollection, contour (ContourSet), imshow (AxesImage) there need to be some options for the user to decide on the final appearance. I'm unsure how to generalize such arguments and how to use them, other than letting the user call this method and supply the returned handles/labels tolegend - see comment below.

@tacaswell
Copy link
Member

The issue here is that the "one artist : one dataset : one legend entry" mapping that exist for exLine2D is badly broken byscatter.

A concern with the way we currently do legends is that the handlers are created by lookup up state held in theLegend class rather than asking theArtist objects in the draw tree what they think their legend handlers should be. On one hand, this is pretty nice as now the user can customize per-legend (so ex changing out the handler for everyLine2D object can be done in userspace at runtime), but can be a bit indirect and cumbersome.

How far can the notion of 'legend_elements' be generalized? Even if we only have a real implemenation for PathCollection, having aNotImpletened marker farther up the class tree would be good (so the next time we want something like this on another class we don't invent a different name!). Does it make sense to push this all the way up toArtist? I can see a use for that ingrave (https://github.com/networkx/grave/blob/master/grave/grave.py#L221) where the network artist may have strong opinions about what should go in the legend.

I think for now coming up with a consistent name and API forArtists to return a list of labels and handles as an optional public method is a good start and we can sort out how to integrate it into the auto-legend code later.

@ImportanceOfBeingErnest
Copy link
MemberAuthor

I think neither of
image or
image may actually be undesired in the general case. The initial proposal here was to make it easier to provide more handles and labels to the legend for a case where more detailed legend is desired.

What this shows is that it is probably rather hard to let the artist decide for a way of creating a legend by itself. It still needs some guidance by the user. This makes things rather complicated as in that case the arguments that steer the creation of one/several legend entries need to be given tolegend as well, adding up to the existing 26legend arguments and leading to a lot of useless arguments in cases where the respective artists is not even present in an axes. It also would be hard to distinguish which argument would need to be used for which artist. Alternatively, one could provide those arguments to the artist itself. So the artist has those properties stored and would use them if the legend calls itslegend_elements() method.

From a user's perspective, it may not actually make that much of a difference, whether to create the legend elements first and supply them to the legend (as in this proposal)

sc = ax.scatter(x,y,c=.., s=...)ax.legend(*sc.legend_elements(**legend_viz_params))

or let the artist take the required parameters

sc = ax.scatter(x,y,c=.., s=..., **legend_viz_params)ax.legend()

or let the legend take them,

sc = ax.scatter(x,y,c=.., s=...,)ax.legend(**legend_viz_params)

Or does it?
In any case, if that proposal to give artists a generalobtain_copy_of_me_for_legend (or__legend_repr__) method and let the legend use it is to be pursued further it would kind of revolutionize theLegend and replace the existingHandlers. Would it make sense to discuss this in a separate issue? I only fear that such issues will just end up never being implemented and step-like improvements like the inital proposal here are being blocked on them.

@jklymak
Copy link
Member

I wasn't exactly sure the scope of what legend_handler could do.

I agree that the functionality here is useful, and I don't really see how it can easily be made extensible.

What I'm hung up on is that I am not sure I like the API of having it a method onPathCollection. Thats not where I would look for it, and I think makes it obscure. Would callingax.scatter_legend(sc) be too restrictive? One could even imagine passing it other handles and labels if you wanted more than just the scatter info on the legend:ax.scatter_legend(sc, h_add, l_add, **otherkwargs).

OTOH, if we do this for every different artist type, I imagine this gets to be a mess so I can see the point of having it attached to the artist.

Again, just trying to think the API through a bit. I think this is a good idea overall.

@ImportanceOfBeingErnest
Copy link
MemberAuthor

ax.scatter_legend(sc, mode=.., useall=.., num=.., .. ,**kw) is totally possible. It would make the separation between legend kwargs and kwargs needed to steer the specific scatter legend output easier. I would agree to it getting messy if further down the road many similarax.<plottype>_legend() methods were to be added.
It also goes in a completely different direction than the proposal of anArtist method by@tacaswell.
Following his suggestion,

I think for now coming up with a consistent name and API for Artists to return a list of labels and handles as an optional public method is a good start

we currently have:

  • Artist.legend_elements() (This PR)
    • Pro: name is consistent with existingContourSet.legend_elements
    • Con: name has no verb, hides the non-unique nature of the return
  • Something likeArtist.{create/make}_legend_{items/handles/elements}()
    • Pro: could better reflect the process of producing something from the artist.
    • Con: inconsistent with existing contour method; adjusting contour method would break existing API
  • Artist.get_legend_handles_labels()
    • Pro: consistent with existingAxes.get_legend_handles_labels()
    • Con: inconsistent with existing contour method; adjusting contour method would break existing API, rather long name.

Concerning the API, i.e. the needed arguments, I think there might be little to no overlap between what different artists need as input. Evennum (the number of created legend elements) is not really needed for many artists, for which anything butnum=1 does not make sense at all.

@jklymak
Copy link
Member

Sorry, I missed the point aboutlegend_elements already being used forcontour. And I see that is a method onContourSet. Given that precedent, I'm fine w/ the general level of the API here.

jklymak
jklymak previously approved these changesMay 1, 2018
Copy link
Member

@jklymakjklymak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think this is useful, and consistent w/ the similar method toContourSet. "num" could possible pass a Locator if we want more flexibility in the binning of the sizes, but that could either happen now or as a follow up if someone wants more flexibility. Thanks a lot for this!

@ImportanceOfBeingErnestImportanceOfBeingErnestforce-pushed thelegend-for-scatter branch 2 times, most recently fromdfe09f3 to1097412CompareMay 2, 2018 08:41
@ImportanceOfBeingErnest
Copy link
MemberAuthor

Update: You may now pass a locator tonum.


Concerning the discussion about having alegend_elements method forArtist, I think one may do something like

class Artist(object):    # ...    def legend_elements():        return [self], [self.get_label()]

However, as long as this isn't used anywhere internally, it's not really useful. In any case this can be part of a different PR, in case one wants to letlegend use this for obtaining its handles/labels.

@jklymak
Copy link
Member

@ImportanceOfBeingErnest we talks about this on the weekly phone call, and the issue is with attaching this method toPathCollection which is a more general class than just for scatter.

The request would be that we add aScatterArtist subclass of PathCollections that has extras like this as new methods just on the subclass, and of course change the call toscatter to use this new subclass. Thats a somewhat bigger job than just this PR, but should be manageable and would make other things easier; the object returned from scatter really should have been more than just aPathCollection earlier than this.

If you don't feel like doing this, I imagine someone else will be up for picking it up! Thanks for a great idea...

@jklymakjklymak dismissed theirstale reviewMay 14, 2018 20:07

Stale: requesting a bit more of a refactor...

@ImportanceOfBeingErnest
Copy link
MemberAuthor

But this is not only useful for scatters. If you have a different PathCollection than the one being created by a scatter, you may still use it to create legend entries for it. As of now, it seems no other matplotlib function other than scatter creates PathCollections, but if there were others, why restrict this functionality to onlyScatterArtist?

@ImportanceOfBeingErnest
Copy link
MemberAuthor

What's the status here?
Should there really be a newScatterArtist created which then inherits thePathCollection'slegend_elements? Or is there any profound reason to only make this method available for aScatterArtist even though it's perfectly usable for aPathCollection as well?

@jklymak
Copy link
Member

Myunderstanding which may be incomplete, is thatPathCollection is meant to be a more general path container and hence we want to not have any methods on it that are specific toscatter. Yes,PathCollection is only currently used forscatter, but thats not the intended end-use case.

If you still disagree with this organization, then we can bring other folks into the conversation.

@ImportanceOfBeingErnest
Copy link
MemberAuthor

As said, this is not specific toscatter - although clearly motivated by it.
If you can show me aPathCollection for which the newly added method would fail, that would all make sense. One could also argue that for the most general case one should add a third option asmode argument, which would create legend entries for several paths. But in that case, the method would still reside inPathCollection, not in an inherited class, right?
At the beginning, a question asked how farup the hierarchy this should sit. Now suddely the question is how fardown it should be moved.
Just to be clear here: I'm not opposed to add aScatterArtist at all. But even if doing so, I would still let thelegend_elements method stay inPathCollection.

@jklymak
Copy link
Member

I don't have any skin in this game - you'll have to work it out w/@tacaswell

@tacaswelltacaswell self-assigned thisMay 28, 2018
@jklymak
Copy link
Member

ping@tacaswell

@jklymak
Copy link
Member

Marking as "Needs Review" to hopefully burble to the top of people's lists...

@dstansby
Copy link
Member

Looks like doc build and travis aren't happy at the moment?

@ImportanceOfBeingErnest
Copy link
MemberAuthor

I'm happy to fix all the little hickups that come and go with updated dependencies (pep8 to flake, sphinx 1.7 to 1.8 etc.) once the general fate of this PR is decided upon.
I will not waste my time continuously keeping up with updated depenencies in order to have the checkmarks green every day for the next 5 months, just to then have someone decide that this is not the route to go at all.

@jklymakjklymak added status: needs review Release criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions. and removed status: needs revision labelsFeb 7, 2019
@jklymak
Copy link
Member

... is this really release critical? No, but I can imagine@ImportanceOfBeingErnest is a bit frustrated its sat around for months...

Copy link
Member

@efiringefiring left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Overall, this seems like important functionality and a reasonable implementation. I would be reluctant to hold it up indefinitely based on the controversy over whether the method should be on something other than thePathCollection. I don't see any harm in putting it there; that doesn't preclude some future refactoring, so long as the user would still be able to call it as a method of whatever a futurescatter returns. I would like to see some consideration of my suggestion regarding the "num" kwarg (possibly with a new "locator" kwarg) to facilitate what I would expect to be the very common use case of wanting to specify the levels as a sequence.

scatter = ax.scatter(x, y, c=c, s=s)

# produce a legend with the unique colors from the scatter
legend1 = ax.legend(*scatter.legend_elements(), loc=3, title="Classes")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Minor nit-pick: I think that the numeric location codes are a terrible API and should not be used in examples. (I would favor removing them entirely from the legend documentation.)

ImportanceOfBeingErnest reacted with thumbs up emoji
Can be *"colors"* or *"sizes"*. In case of *"colors"*, the legend
handles will show the different colors of the collection. In case
of "sizes", the legend will show the different sizes.
num : int, None, "auto" (default), or `~.ticker.Locator`, optional
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Although the ability to use aLocator is good, and provides the ability to put in a list via aFixedLocator, wouldn't one of the most common--maybethe most common--use cases be an explicit list? For example, sizes represent populations of cities, so the legend shows sizes for[10000, 100000, 1000000, 10000000]. If this would be a common use case, then it might be worth supporting it directly by accepting a sequence. To make all this more likecontour with its 'levels', a separate kwarg could be used to specify aLocator for the rare cases when it is needed.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I would still use the same argumentnum though, because a list of values is an alternative to those other options. Else one would need to go through checking what happens if both are provided etc.

So what is a canonical way of checking if something is a list, tuple or array of numbers? (isinstance(x, collections.abc.Iterable) only does half the job; is there something incbook that I overlooked?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

np.iterable() should suffice, I think.

ImportanceOfBeingErnest reacted with thumbs up emoji
-------
tuple (handles, labels)
with *handles* being a list of `.Line2D` objects
and *labels* a list of strings of the same length.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Very minor suggestion: 'andlabels being a matching list of strings.' (Otherwise, it could be misinterpreted as meaning the strings must all be the same length.)

ImportanceOfBeingErnest reacted with thumbs up emoji
u = np.unique(self.get_sizes())
color = kwargs.pop("color", "k")
else:
warnings.warn("Invalid prop provided, or collection without "
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

What is the rationale for a warning rather than an Exception here? At least in the case of an invalid prop, shouldn't this raise an Exception?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think my rationale here was that similar to how a legend is still created even if no labeled objects are found, here it should not break the code in case no handles/labels can be determined. So one would still get a plot out, just without propper legend.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

OK, but then I think that the "invalid prop" case should raiseValueError and only the lack of an array should raise the warning.

ImportanceOfBeingErnest reacted with thumbs up emoji
loc = mpl.ticker.MaxNLocator(nbins=num, min_n_ticks=num-1,
steps=[1, 2, 2.5, 3, 5, 6, 8, 10])
label_values = loc.tick_values(func(arr).min(), func(arr).max())
cond = (label_values >= func(arr).min()) & \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Style preference: when possible, as it is here, use parentheses rather than trailing backslashes for line continuation.

ImportanceOfBeingErnest reacted with thumbs up emoji
kw.update(kwargs)

for val, lab in zip(values, label_values):
if prop == "colors" and hasarray:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Again, if I am reading the code correctly, thehasarray check is not needed. (But it makes me wonder whether I am missing something, which is quite possible.)

ImportanceOfBeingErnest reacted with thumbs up emoji
@tacaswell
Copy link
Member

I am 👍 on this,@efiring is responsible for getting this merged.

@ImportanceOfBeingErnest
Copy link
MemberAuthor

Thanks for the review@efiring. I think I have now incoorporated all the requested changes.

I think it's good to have the API fixed for the case of the scatter (which is the most common usecase I assume). We can now still implement this for other collections. If you want I can already now put aNotImplemented into the generalCollection.

@efiringefiring merged commit93aadf5 intomatplotlib:masterFeb 12, 2019
@efiring
Copy link
Member

Looks good, so I merged it. I don't think that putting the method into the Collection base would be good, because it probably makes sense to implement it only for some types of Collection.NotImplemented would imply that itshould be implemented for everything, which I don't think is the case.

@jklymak
Copy link
Member

Thanks@efiring and thanks@ImportanceOfBeingErnest for your patience with this!

@tacaswell
Copy link
Member

🎉 I am glad this landed!

@ImportanceOfBeingErnestImportanceOfBeingErnest deleted the legend-for-scatter branchAugust 2, 2019 22:01
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@tacaswelltacaswelltacaswell left review comments

@efiringefiringefiring approved these changes

@jklymakjklymakjklymak approved these changes

Labels
Release criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
Projects
None yet
Milestone
v3.1.0
Development

Successfully merging this pull request may close these issues.

7 participants
@ImportanceOfBeingErnest@story645@jklymak@tacaswell@dstansby@efiring@QuLogic

[8]ページ先頭

©2009-2025 Movatter.jp