-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
transData.inverted().transform does not work as expected with datetime barchart axes #18220
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
Comments
Can you reduce this example a bit? There is a lot going on here My knee-jerk guess is you are either getting bitten by some of the auto-limiting getting lazier or the change epoch on the datetime. |
There's not actually much going on when it comes to the transforms. I've removed some things. If you look at the lines commented HERE, and AND HERE you'll see data being transformed then inverted, and you don't get back the same coordinates as you put in. I'm not sure what causes the bug, so I left much of the "setup" intact ... in case it's an artifact of the specific configuration and inputs.
|
The problem is two-fold:
Thus, if you set
|
Well, in my case, I am usng these calls is to get some idea about the display characteristics of the chart, so having these transforms "settle" into the right values too late in the game is not optimal. Of course, all the necessary information may not be available too early in the game, but I wanted to mention the use-case so that you may consider it. For example, I want a way to determine if the bar width, expressed as a timedelta on input, may turn out to be less than one pixel wide on this chart, so that I can requery or otherwise adjust the bar data ... thus my goal in transforming from data space to pixel space is to see if the delta x is <= 1 ... |
The tutorial here: https://matplotlib.org/3.3.0/tutorials/advanced/transforms_tutorial.html ... mentions transformation changes when the xlim/ylim is changed explicitly, but (I believe) makes no mention of calls such as get_xlim() potentially changing the transforms, nor of the general laziness effects that might affect these. I think a very prominent mention of this issue is deserved both in this tutorial and in the API docs. Whatever the case may be, it left me with the impression that I needed to be careful when actually changing aspects of the plot explicitly, but not that the transforms may change out from under me just by drawing or querying the plot after it is set up. |
We recently tried to make the warnings a bit more glaring (see https://matplotlib.org/devdocs/tutorials/advanced/transforms_tutorial.html / https://github.com/matplotlib/matplotlib/pull/18178/files). Would that have helped? On one hand people want us to be as fast as possible, which means we have been trying to defer doing work until as late as possible. In this case, when you have not explicitly set the limits, we defer computing the auto-limits until we must (so if you add 5k lines we only compute the limits once instead of 5k times). In this case, you were triggering the auto-scaling by calling For performance reasons we accept storing information in two places so that we do not have to query the view limits every time we use it. Similarly if you are using any of tight_layout or constrained_layout, we don't update the position of anything until draw time. In general if you want to know anything about screen space, do When I say there is a lot going on you are using bar, datetimes, generating very specific data, etc. While I am sure it is very clear to you, I find even the reduced one hard to parse. A minimal case that reproduces the problem is: def simple():
fig, ax = plt.subplots()
ax.plot(range(5))
tr = ax.transData
tr_inv = tr.inverted()
fig.canvas.draw()
print(tr_inv.transform(tr.transform((0, 1)))) This is already noted in the docs that the return value from This is the second person to become very frustrated at the some what sharp edges of the transform stack (also see #18170) in the past week. Is this just bad luck or is there some new resource pointing people at transforms? |
I was not confused about that. I respectfully suggest that:
NOTE: All of the examples in the transform tutorial discuss setter/explict plot-changing calls to illustrate when transforms can become stale. Nowhere does it say "even a call like get_xlim() can cause a transform to become stale," (let alone the foregoing warning).
If any of this is already documented elsewhere, I'd say it should be repeated frequently throughout the docs, particularly in APIs that suffer from staleness. Users don't always come in through the front door. Thank you for a great package and for all the work! PS: I appreciate that a minimal code sample existed to show the behavior. There were too many degrees of freedom and too little time for me to create it without a lot of trial and error, especially given lack of familiarity with the APIs and potential side effects. The fact that the example produced the incorrect inversion in its last two lines seemed enough. In other words writing simple() took you 5 minutes, but would have taken me 45. All the best. |
Oh, I forgot. Regarding: " Is this just bad luck or is there some new resource pointing people at transforms?" --> The way I got there is that I was trying to figure out how line widths, bar widths, etc are specified. Is it points? pixels? data units? The answer to that question is a well hidden secret in matplotlib. You look at the API docs and things like linewidth simply say "the width of the line" or similar. Ok. What's "1"? inches? pixels? what? Can datetimes be passed natively? Can a width be a timedelta? trial and error ... If you search on related keywords you will frequently hit this: #13236 and transforms. If it doesn't already exist, matplotlib could probably use a document about units, and how sizes and positions are specified when setting up plots -- particularly when dealing with cardinal vs ordinal data, etc etc (a la https://matplotlib.programmingpedia.net/en/tutorial/4566/coordinates-systems ). |
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! |
Bug report
Transforming from data or axis space back to data space does not work as expected
Code for reproduction
Actual outcome
Expected outcome
As you can see on the tr_x lines, transforming x's in the 18449 xlim range maps to ~320 and ~1706 pixel space, and 9000.0 on y to 110 in pixel space. Inverting that transform using transData.inverted().transform() goes back to ~11123 and ~14388 on x, and 0 on y.
Expected: exactly the inputs.
As you can see the on the transAxes output lines, x=1.0 is pixel = 1800. On the "ax" line we also see that the xlim is ~18449. However, when passing the transAxes output to transData.inverted().transform() we get back 10957 - 14610.
Expected: We should have translated back to ~18449.
Matplotlib version
matplotlib 3.3.0
python 3.7.5
Installed via the following command, with requirements.txt containing "matplotlib"
pip3 install -U --upgrade-strategy eager -r requirements.txt
The text was updated successfully, but these errors were encountered: