Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
MNT: set the facecolor of nofill markers#17850
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
Uh oh!
There was an error while loading.Please reload this page.
Conversation
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 don‘t think this is the right solution. Explanation follows later today.
timhoffm commentedJul 7, 2020 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Sorry for being a bit terse above. I didn't have time to think it though completely and write up details, but already wanted to announce that the rabbit hole is deeper. We have three cases of markers:
Original bug and fixed versionThe original bug#17527 is: before: Click for code of the example plotsimportnumpyasnpimportmatplotlib.pyplotaspltfrommatplotlibimportmarkersfrommatplotlibimporttickerx=np.random.random(41)y=np.random.random(41)fig,axs=plt.subplots(3,4)fc='none'ec='tab:orange'markers= ['o',markers.MarkerStyle(marker='o',fillstyle='none'),'x']fori,markerinenumerate(markers):axs[i,0].scatter(x,y,marker=marker)axs[i,1].scatter(x,y,marker=marker,facecolor=fc)axs[i,2].scatter(x,y,marker=marker,edgecolor=ec)axs[i,3].scatter(x,y,marker=marker,facecolor=fc,edgecolor=ec)axs[0,0].set_title('no args')axs[0,1].set_title('fc')axs[0,2].set_title('ec')axs[0,3].set_title('fc + ec')axs[0,0].set_ylabel("'o'")axs[1,0].set_ylabel("MarkerStyle")axs[2,0].set_ylabel("'x'")foraxinaxs.flat:ax.set_xticks([])ax.set_yticks([]) The MarkerStyle row now has unfilled circles. Cases 2 and 3 are handled equivalently, which IMHO makes sense. Details of the fix#17543 addresses this in two steps/commits:
Why only setting the facecolor for nofill markers is not correct.For the new interpretation It may work if we also revert General comments and way forward
|
The direct revert to |
QuLogic commentedAug 5, 2020 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
55e16ca
to0f6bcd1
Compare0f6bcd1
to574a1fc
CompareOn further consideration I have concluded@timhoffm 's judgement of what the behavior should be was correct. This PR tweaks how we go about implementing it. From reading the code it seems that This also helps to clarify when we should collapse the edge / face colors. For un-fillable makers we don't have a face so we must collapse them and for back-compatibility reasons we leave the face color alone and set the edge color to 'face'. On the other hand, if the user gave us a fillable makers that they want to not be filled, then we have to set the face color to be With the code in this PR we have: If we drop promoting the facecolor to edge color for un-filled-fillable markers then we get which I think is better, in that it makes the behavior between It looks like we don't call |
574a1fc
toc582e83
CompareWe discussed this on the call. We either need to adopt this and bank the step forward that was made in#17543 or take a step back and accept the broken state and punt a more through fix (as outlined by@brunobeltran in#10008 's comments) for 3.5. |
timhoffm left a comment• edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
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.
TL;DR
I think this is the right way to go. (May be incremental, but is better than before because it fixes#17527 and does so without breaking seaborn, which my first attempt#17543 did).
Explanation
This PR only concerns the special case ofMarkerStyle(..., fillstyle='none')
.
The interplay of that with colors has been obviously wrong because such a marker should never be filled (see bug#17527).
The implementation in the PR follows the claims in our documentation:
For non-filled markers, the edgecolors kwarg is ignored and forced to 'face' internally.
https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.axes.Axes.scatter.html
Whether or not this color mangling is a good choice is a different question, but it has always been working for true unfilled markersplt.scatter(1, 2, marker='x', facecolor='r')
.
Changing color mangling generally (and further marker characteristics as proposed in#10008 (comment)) is an API change not only a bug fix. That would require much more careful thinking and is definitively not for 3.4.
This PR "only" fixes a bug forMarkerStyle(..., fillstyle='none')
and does so in a way consistent with unfilled markers.
Further remarks
As@tacaswell correctly observesmarker.is_filled()
andmaker.get_fillstyle() == 'none'
are not necessarily equivalent. Both methods should get a note on this in the docstring and explain more precisely what they mean for for the different kinds of markers.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
# | ||
# While not an ideal situation, but is better than the | ||
# alternatives. | ||
if marker_obj.get_fillstyle() == '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.
I'm still somewhat confused why we need special-casing in the drawing function. Could this not be handled in theMarkerStyle
so thatMarkerStyle('o', fillstyle='none')
andMarkerStyle('x')
have an identical behavior?
I'm still concerned about this. It was broken, so we are fixing it here. However, I'm not convinced we want it. While its broken we could instead remove it. In particular what is the goal of |
timhoffm commentedJan 19, 2021 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
To a certain extend, it is appealing to be able to define "outline-only" markers and then be able to use them like the unfilled markers*. The advantage is that I can define the marker once *This is maybe the point So my refined opinion:
But can't we keep it simple and remove |
mwaskom commentedJan 19, 2021
IMHO it would be extremely nice if there were a native "ring marker" in matplotlib |
timhoffm commentedJan 19, 2021 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Agreed. That's probably the most common use case for In principle that's not difficult. We could easily add a new marker, say |
mwaskom commentedJan 19, 2021
From a higher-level library perspective, having the ability to set |
story645 commentedJan 20, 2021 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I think even from a library perspective, like@timhoffm said unfilled + unfillable markers inherently encode at least 1 fewer variable than filled markers and so I think it could be good to have that cleanly pinned down as a distinct class/set of marker choices.
There's cmap_r and that feels straightforward, I think so long as the convention is documented is fine. |
mwaskom commentedJan 20, 2021
Note that there's already a sort of convention for unfilled/filled markers with |
That's not the same. 'P_' would give an outlined thick plus, while '+' is just a horizontal and a vertical line. |
mwaskom commentedJan 20, 2021
Hm, good point, from an implementation perspective. Though if I were representing a variable by mapping it to marker type, I would likely pair a filled circle and plus with a hollow circle and line-art plus. |
Sorry hit the wrong button ... 🙄 |
This is more or less coincidence and does not have anything to do with removing the filling.
I'm not trying to pair anything here. We're having |
mwaskom commentedJan 21, 2021
Right, I understand the implementation details. I am saying that there exists a quasi-convention for doing something similar (and something that might appear identical from the perspective of someone who has not thought through the implementation details), and it may be useful to keep that in mind when you add a new convention. |
I understand your point. However, IMHO this "quasi-convention" cannot be applied to more cases. It only works if the filled marker looks like "made of strokes". Among thefilled markers we have this is only the case for the exact two cases you mentioned. All others are more "solid" shapes and I couldn't come up with a reasonable corresponding unfilled maker, except one tracing their outline. So I think there is no way to take the quasi-convention into account for more cases. '+' and 'x' are just two exceptions. |
47b9c19
toa89443c
Compare@timhoffm can you clear your review? |
Uh oh!
There was an error while loading.Please reload this page.
This seems to be genuinely failing: Unexpectedfailingexamples:/home/circleci/project/examples/animation/rain.pyfailedleavingtraceback:Traceback (mostrecentcalllast):File"/home/circleci/project/examples/animation/rain.py",line40,in<module>scat=ax.scatter(rain_drops['position'][:,0],rain_drops['position'][:,1],File"/home/circleci/project/lib/matplotlib/__init__.py",line1405,ininnerreturnfunc(ax,*map(sanitize_sequence,args),**kwargs)File"/home/circleci/project/lib/matplotlib/axes/_axes.py",line4494,inscatterorig_edgecolor=edgecolorsorkwargs.get('edgecolor',None)ValueError:Thetruthvalueofanarraywithmorethanoneelementisambiguous.Usea.any()ora.all() |
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 example passes if you do this. I guess that means we need an extra text where edgecolors is an array.
Uh oh!
There was an error while loading.Please reload this page.
This is brittle, but matches the behavior in Line2D.MarkerStyle objects have two coupled, but not fully redundant methodsfor determining if the maker is filled: the `is_filled` and`get_fillstyle` methods. If `ms.get_fillstyle() == 'none'` then`ms.is_filled() is False`, however the converse is not True. Inparticular the markers that can not be filled (because the Paths theyare made out of can not be closed) have `ms.get_fillstyle() == 'full'`and `ms.is_filled() is False`. In Line2D we filter on the value of`get_fillstyle` not on `is_filled` so do the same in `Axes.scatter`.In Line2D we do the validation at draw time (because Line2D holds ontoits MarkerStyle object instead of just extracting the path).The logic for fillstyle on Markers came in viamatplotlib#447/213459e.closesmatplotlib#17849Revisesmatplotlib#17543 /d86cc2bCo-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>Co-authored-by: Jody Klymak <jklymak@gmail.com>Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
2e11021
tod206a0e
CompareTo maintain back-compatibility, matplotlib decided to ignore the issue.fixes#100Ref:matplotlib/matplotlib#10008,matplotlib/matplotlib#17850
PR Summary
Even though we are going to ignore it, set the facecolors to the user
specified color and edgecolor to 'face' to maintain
back-compatibility.
We now warn if the user passes in an edgecolor that we ignore.
closes#17849
partially reverts#17543 /d86cc2b
Do we want an API change note for the warning?
PR Checklist