Skip to content

[Bug]: axes vlines() / hlines() incorrectly use data coordinate as min when blended transform is applied #23171

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

Closed
orionlee opened this issue May 31, 2022 · 7 comments · Fixed by #25324
Labels
Difficulty: Medium https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues Good first issue Open a pull request against these issues if there are no active ones!
Milestone

Comments

@orionlee
Copy link

orionlee commented May 31, 2022

Bug summary

For Axes.vlines(), when I want to use axes coordinate for ymin / ymax, as suggested in blended transformation, ymin is incorrectly treated as data coordinate.

Axes.hlines() has a similar problem with xmin.

Code for reproduction

# Sample derived from
# https://matplotlib.org/3.5.0/gallery/lines_bars_and_markers/vline_hline_demo.html#sphx-glr-gallery-lines-bars-and-markers-vline-hline-demo-py
    
import matplotlib.pyplot as plt
import numpy as np

import matplotlib 
print('matplotlib', matplotlib.__version__)


t = np.arange(5.0, 10.0, 0.1)
s = np.exp(-t) + np.sin(2 * np.pi * t) + 10

vax = plt.figure(figsize=(12, 6)).gca()
vax.plot(t, s, '^')

# By using ``transform=vax.get_xaxis_transform()`` the y coordinates are scaled
# such that 0 maps to the bottom of the axes and 1 to the top.

vax.vlines([6, 7], ymin=0, ymax=0.15, transform=vax.get_xaxis_transform(), colors='r')
# if I use axvline() instead, it correctly treats ymin as axes coordinate.
# vax.axvline(6, ymin=0, ymax=0.15, c='r')
# vax.axvline(7, ymin=0, ymax=0.15, c='r')

vax.set_xlabel('time (s)')
vax.set_title('Vertical lines demo');

Actual outcome

One can see that while ymax is applied correctly (about 15% of the y-axis), ymin is applied incorrectly. ymin is treated as data coordinate, incorrectly stretching the y-axis.

image

Expected outcome

The expected outcome can be obtained using multiple axvline() instead of vlines():

vax.axvline(6, ymin=0, ymax=0.15, c='r')
vax.axvline(7, ymin=0, ymax=0.15, c='r')

image

Additional information

I've encountered the problem in earlier versions. I cannot recall it ever works correctly.

Axes.hlines() has a similar problem with xmin.

One can add the following to the example codes above to test it:

# axes.hlines() has similar problem.
vax.hlines([9, 9,5], xmin=0, xmax=0.2, transform=vax.get_yaxis_transform(), colors='green')
# uses multiple axhline() call would work correctly
# vax.axhline(9, xmin=0, xmax=0.2, c='green')
# vax.axhline(9.5, xmin=0, xmax=0.2, c='green')

Operating system

Windows

Matplotlib Version

3.5.2

Matplotlib Backend

module://matplotlib_inline.backend_inline

Python version

Python 3.9.10

Jupyter version

6.4.11

Installation

conda

@orionlee orionlee changed the title [Bug]: axes vlines() / hlines() incorrectly use data coordinate as min when transform is applied [Bug]: axes vlines() / hlines() incorrectly use data coordinate as min when blended transform is applied May 31, 2022
@tacaswell tacaswell added this to the v3.6.0 milestone Jun 1, 2022
@tacaswell tacaswell added Good first issue Open a pull request against these issues if there are no active ones! Difficulty: Medium https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues labels Jun 1, 2022
@tacaswell
Copy link
Member

Labeling this as a good first issue because there is no API design and this appears to be a clearly identified bug that should be fixed.

Labeling as medium difficulty as this is probably not a project someone new to Python should take on.

@orionlee
Copy link
Author

orionlee commented Jun 1, 2022

I suspect the problem lies with the scaling done at the end of vlines() implementation

minx = x.min()
maxx = x.max()
miny = min(ymin.min(), ymax.min())
maxy = max(ymin.max(), ymax.max())
corners = (minx, miny), (maxx, maxy)
self.update_datalim(corners)
self._request_autoscale_view()

In the example scenario, the miny and maxy used in scaling are probably in axes coordinates, while the actual scaling in update_datalim() treat them as data coordinates.

In contrast, axvlines() has no such issue, because its scaling logic keeps y-axis scale unchanged.

@njj67229
Copy link

Is anyone currently working on this issue? I have not done any open source contribution before and would be interested in giving it a stab as a first issue.

@QuLogic
Copy link
Member

QuLogic commented Jul 20, 2022

I don't think anyone is working on this; feel free to take a stab at it.

@QuLogic QuLogic modified the milestones: v3.6.0, v3.7.0 Aug 24, 2022
@Ailieena
Copy link

Hi, I'm new to open source and wanted to take a look at this issue.
I looked at the code and I'm not fully convinced that the current behavior is incorrect. The description of vlines function suggests that it should plot "vertical lines at each x from ymin to ymax.".
It seems to me that vlines using ymin and ymax as coordinates is the expected outcome, am I wrong?

@story645
Copy link
Member

story645 commented Aug 28, 2022

It seems to me that vlines using ymin and ymax as coordinates is the expected outcome, am I wrong?

Forgive me if I'm misinterpreting your question, but the issue is that this function supports multiple coordinate systems and the original poster is trying to plot the vlines in the axes coordinate system. The axes coordinates are independent of the data coordinates and are fixed to the displayed axes:

The coordinate system of the Axes; (0, 0) is bottom left of the axes, and (1, 1) is top right of the axes.

In the expected image provided by the original poster, 0 in axes coordinates is the bottom of the displayed axes, which is supposed to be more or less at the same place as the data 9.
image

The bug is that instead of plotting the bottom of the line in axes coordinates (so at around data 9), it is plotting the line at 0 in data coordinates, which in turn is changing the axes to produce the incorrect example.

@Ailieena
Copy link

Thank you for the explanation, I understand now. I'll look into it more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Difficulty: Medium https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues Good first issue Open a pull request against these issues if there are no active ones!
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants