-
-
Notifications
You must be signed in to change notification settings - Fork 7.7k
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
Refactor of axis.py
/ticking should have large performance gains
#5665
Comments
I think i have a prototype doing that, if no one works on this before i will have a look after christmas. The whole axis code seems quite convoluted for what it does. |
This looks like an interesting topic with some performance gain and not too difficult (once we've figured out the right structure). 😄 Current implementation:
Possible alternative implementation (first try):
This feels significantly better and should be doable with reasonable backward compatibility (full compatibility may be difficult or at least require some extra work, if users really descend to the Artists of individual ticks). I'm not sure if and how much incompatibility we want to allow here. Structuring by major/minor and then by primary/secondary may not be the best option. One could swap the order so that
This would potentially allow different ticks on the secondary axis. Also it could make it simpler to handle interactions between minor and major ticks (e.g. leave out minor ticks at the position of major ticks). |
@timhoffm I had started giving a try to the idea (not pursuing it actively now), per https://github.com/anntzer/matplotlib/tree/_wip/newaxis (taking an approach of "delete everything and reimplement stuff on top of it"). Feel free to scavenge stuff from that. A few random notes:
|
This was discussed a bit during the call this week. In working on #10841, I found it pretty strange that the formatter only operated on one tick at a time, and the formatter ended up looping through all the ticks on the first tick and then caching the labels for all the ticks. So, a reorganization like this makes a lot of sense to me - I think there are more use cases where you want to treat the ticks as a list rather than being able to do something (?) to individual ticks. |
#5804 is possibly relevant re: formatter work too. |
I suspect the need to manipulate individual ticks is extremely rare. It's possible that the tick handling pre-dated the introduction of collections. |
One case (not that I use it, but doesn't sounds too crazy) is https://matplotlib.org/examples/api/skewt.html, where you can see that for some of the slanted gridlines, the associated tick is not visible. |
@anntzer Thanks for the example. It helps to have an idea what could all be needed. This needed a special implementation for XTick. The same solution for a future TickCollection (or whatever it's called) will probably need a special subclass of that. @ all: Generally, can we agree that in the default implementation all ticks of a kind (major/minor) will have the same properties? It's still always possible to tell the locator to leave out certain positions and draw something custom there yourself. |
I just realized that that specific example could in fact be made to work by clipping the x-ticks to a bounding box that bounds the horizontal direction but not the vertical one -- perhaps a better idea than manipulating tick visibility...). |
I suspect we will need to aggressively shim the back compatibility on anything that is public API (and maybe even some private stuff). Users are extremely clever and find all the corner cases. separating the primary/secondary ticks is probably a good idea as that makes in easy(ier) to put different locators and tickers on each of them. It is probably worth going through the |
... actually playing with the clipbox won't work in the example above as labels will not be cropped out properly (they may only be half-clipped). @tacaswell If we move stuff to a separate "projection" (as proposed above) the new implementation would be strictly opt-in. It doesn't mean we should gratuitiously break things but given that the current proposals don't even really include the notion of "individual" ticks (everything goes into a Collection object), any shimming is going to be tricky too... |
I responded not having read the rest of the discussion (because I did not reload my browser), sorry. Putting in it a separate projection is a good way to make progress and side-step back compatibility issues, but I worry that we will end up with a third full independent implementation of ticks and friends in the code base. |
As the author of the SkewT example above (and maintaining a proper implementation in MetPy), I'm all for making ticks more sensible. The implementation of the SkewT in matplotlib as it stands now took a lot of trial and error and discovery of weird things. The fact that it takes implementing the entire stack (XAxis, XTick, XSpine) just to clip out of range ticks is nuts. So I'm happy to lend my experience with that use case. I also want to make sure I'm aware of any potential breakage, since it turns out this plot is probably the most popular feature of MetPy--which is actually a testament to how much better matplotlib's plots are than meteorology's other tools. |
In general wouldn't it make sense to first solve the existing problems with the ticks? Like the one that ticks are in general up to two pixels off from their position. Reading much of what is going on here makes me fear that the final matplotlib version that is still usable with python2 will be a version that has scatter markers and ticks at wrong positions. Concerning the proposal here:
|
@dopplershift What do you think of the reimplementation of skewT at #10088? (I think that the skewT implementation is actually independent of the rest of the changes to tick1On & friends, i.e. even if the skewT axes doesn't touch tick1On, just by setting the visibility that should be enough to hide the ticks). @ImportanceOfBeingErnest Without having thought much about it, I like the idea of letting users just add more tickers... As you mention, that also helps with the left/right problem. I think you're referring to #7341 for tick positioning? Unfortunately that's a quite tricky problem to solve. mplcairo has the ability to draw ticks "exactly" were they should be but as it turns out that looks pretty bad: that's because ticks end up not aligned at pixel boundaries, get antialiased, and thus quite blurry (for some reason, blurry short lines look particularly bad). So pixel-snapping of ticks is more or less necessary. The problem is made worse by the fact that ticks are typically ~1px wide, but many lines are ~2px wide and they get snapped to cover two consecutive pixels -- so they look off-center relative to the ticks. I think (can relatively easily play with that in mplcairo) that the best solution is to snap 1 (and other odd)-px-wide lines (including ticks) to centered at full pixels and let 2 (and other even)-px-wide lines get antialiased on both sides by 0.5px. |
@anntzer Short answer: 👍 (We can continue the discussion over there.) |
I see this topic is much more complex than exchanging a list of Lines for a LineCollection 😃. I'm still willing to work on this, but it's probably mid-term effort. I agree with @ImportanceOfBeingErnest that some goals should be set and a good planning is essential so that we don't end with yet another axis/ticks implementation. Not exactly knowing what a MEP is, but this feels like it could become one. As for the goal, my primary goal would be to create a cleaner, easier to use and more flexible implementation. If included in the design and done right, performance will probably be faster (or can be made more easily by tuning the relevant parts). However, I would not see performance as the first goal and I wouldn't commit to a fixed desired percentage gain. |
https://matplotlib.org/devel/MEP/index.html But I'm not clear on the process. I added MEP process as an agenda item for the call on Monday https://paper.dropbox.com/doc/Matplotlib-meeting-agenda-aAmENlkgepgsMeDZtlsYu |
At what time is the call on Monday? See if I can join. |
Oops added to the agenda. 15:00 eastern us time. |
Just dropping an idea here (actually may be worth moving this to a design document, or whatnot): perhaps spines could also get reparented to (and "managed by", whatever that means) the axis objects, so that e.g. one would do ax.xaxis.spine1/2 instead of ax.spines["top"]/["bottom"]? (Yes, 1/2 is not optimal, but that's already what we have for ticks and labels, so at least it's consistent.) |
I actually think there is a way forward migrating to collections while maintaining full backward compatibility including the ability to style individual ticks. Here is how:
Advantages:
Downsides:
I have a basic prove of concept for 1+2 at https://github.com/timhoffm/matplotlib/tree/tick-refactor (might need a rebase). I don't have the capacity to move this forward in any foreseeable future. Anybody is welcome to pick this up. |
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! |
#5665 (comment) Still holds |
Right now, each
Axis
has a two lists ofTick
objects (one major, one minor), each of which has 3Line
objects (for left tick, right tick and gridline) and 2Text
objects (for main and secondary label). ConstructingLine
objects is rather heavy and expensive. Refactoring this code so that eachAxis
had 3LineCollection
objects instead where only the points of the ticks needs to be updated, sharing all of the line style information, should be much faster.On the following simple benchmark:
25% of the time is spent constructing the
Line
objects. The suggested change above should reduce the number ofLine
objects from 3*N (where N is the number of ticks, usually a dozen or so) to 3.The text was updated successfully, but these errors were encountered: