Skip to content
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

Allow specifying x-data range for ax.set_ymargin() #19955

Closed
edmundsj opened this issue Apr 13, 2021 · 15 comments
Closed

Allow specifying x-data range for ax.set_ymargin() #19955

edmundsj opened this issue Apr 13, 2021 · 15 comments

Comments

@edmundsj
Copy link

edmundsj commented Apr 13, 2021

Problem

I'm trying to create a plot which "zooms in" on a subset of a large dataset (I am plotting the entire dataset because it's much more convenient than clipping the underlying dataset). I can do this pretty easily for the x-axis with ax.set_xlim(), because I know what the x-limits of my data should be. However, I don't know what the y-limits should be, and I can't use ax.set_ymargin() because it computes the y-margin for the entire dataset, not the zoomed portion of the dataset. I don't know ahead of time what the min/max values of my dataset will be, so I'm stuck.

Proposed Solution

Add an option to ax.set_ymargin specifying a subset of the x-data range over which to apply the margin. For example:

ax.set_ymargin(0.1, xlim=(0, 1))

would "zoom in" in y on the portion of the data that exists in the x-range [0, 1], and apply a 10% margin to the data that exists over that range.

@WeatherGod
Copy link
Member

I think a feature like this sits right on the edge of what matplotlib could/should do. It certainly seems feasible, but I imagine there might be a lot of nuances that might make this tricky to get right. Thinking about this more generally, what should the API look like for 3d plots? What about polar plots? Would it present complications for cartopy? And, once implemented, should we adopt this sort of behavior for interactive zooming (or perhaps it should be a special mode?)

The idea isn't limited to set_x/ymargin(). Perhaps it could be considered for set_x/ylim(), and any other limit setting approaches. IIRC, glueviz has something along these lines. This idea has the potentially to be immensely useful, but needs to be carefully thought out.

@jklymak
Copy link
Member

jklymak commented Apr 14, 2021

Yes I agree this is on the edge. Its not super hard for the user to narrow their own field of view down to the y values corresponding to the x values between 0 and 1:

import numpy as np
import matplotlib.pyplot as plt 

x = np. arange(-2, 2, 0.1)
np.random.seed(19680801)
y = np.cumsum(np.random.randn(len(x)))

fig, ax = plt.subplots()

ax.plot(x, y)
ind = ((x>=0) & (x<=1))
ax.set_xlim(0, 1)
ax.set_ylim(y[ind].min(), y[ind].max())

plt.show()

If you have more than one dataset, then just run a for-loop.

Matplotlib could provide an API like this. Whether it is worth it or not is another issue.

Perhaps it could be considered for set_x/ylim()

See #9890;

Indeed this feature seems better as set_xlim([0, 1], relimy=True) or whatever we want to call the kwarg. Or maybe relimy=0.1 to indicate a margin.

@gsteele13
Copy link

gsteele13 commented Apr 14, 2021

I have also stumbled into this discussion:

#9890 (comment)

In particular, I would also be very interested in autoscaling the ylimits based on the view in the currently configured xlim.

In the plotting behaviour of software I have used in the past, this is typically the default, but I now understand apparently this is not the default in Matlab, and hence also not in matplotlib.

I think though that it would be nice to enable this functionality, via kwargs, and maybe an rcParam (don't know if the latter makes sense? need to look more at how the rcParams work)

Proposed option?

One possibly logical place to incorporate this could be an attribute / kwarg of the autoscale function:

https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.autoscale.html?highlight=autoscale#matplotlib.axes.Axes.autoscale

For example, one could consider a parameter ylimits_based_on_xview (or something like that) which would specify alternative functionality of the y-axis autoscaling in the case that xlimits are manually configured?

Related question about querying autoscaling attributes

In relation to this, I also am a bit lost on how to query autoscaling attributes of a plot in MPL.

There is a function to set this Axes.autoscale(self, enable=True, axis='both', tight=None), but can I query somehow what the current settings are?

@gsteele13
Copy link

I another (related) place where this might be logical is the autoscale_view function:

https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.autoscale_view.html?highlight=autoscale_view#matplotlib.axes.Axes.autoscale_view

I see that this also already make use of some rcParams, so maybe an additional rc parameter that specifies the behaviour of the y-autoscaling in the case of a user-specified xlimits might be reasonable?

@gsteele13
Copy link

Answering my own question about querying autoscaling attributes: poking around the dir() of the axis objects, it does appear that there are some relevant query functions:

image

@edmundsj
Copy link
Author

As an aside, I also assumed this would be the default behavior of matplotlib when using set_xlim(), and was surprised to find out that it was not.

@torokati44
Copy link

+1

@drmcnelson
Copy link

drmcnelson commented Feb 9, 2022

I agree with edmundsj. It is not normal, and it is not well connected to the reality of how people use the software to graph data.

I have yet to find a suitable workaround. The api, relim(), autscale_view() also do not work in so far as respecting the range set by xlim. And set_ylim() with min() and max() does not provide the "nice limits" like those chosen by auto scaling. Manually selecting the subset of the data before calling the graphics API, might be a crude workaround, but it can be complicated for the user.

That some suggest that implementing this would somehow present corner cases, seems more than a little surprising. If you can autoscale the full range of data, you can surely autoscale the subset selected by xlim.

And, I'm sorry that no matter how many times it is flagged as wontfix or similar, we have to consider matplotlib as broken in this regard until either it is fixed or some api is added to provide nice y limits after changing xlim.

Otherwise, matplotlib is certainly nice software. I hope you do fix this. The bug gives me lots of problems preparing figures.

@jklymak
Copy link
Member

jklymak commented Feb 9, 2022

I don't think anyone marked this as "won't fix". Just no one has proposed an API and opened a PR. But from the discussion above I hope it is clear that making this the default behaviour would be very hard.

@gsteele13
Copy link

I don't think it would be so hard. If you have acess to the x range and data, it's a few lines of code. And api wise, it could easily be an rc configurable option. Maybe if I have time i will dig into the xlim code.

@gsteele13
Copy link

gsteele13 commented Feb 9, 2022

The api proposal is above and clear to me
I just haven't had time to code it and make a pull request.

(And i thought it was marked won't fiix at some point. But may be losing track on the different issues I've commented on..)

In any case, the emotion suggests that this is a feature that would be useful to users.

@gsteele13
Copy link

gsteele13 commented Feb 9, 2022

I did a pull of the code but I'm afraid I won't have the time to figure out autoscale_view and set_xlim source right now (who knows in a couple of months) to the point where I would feel confident making a pull request

But bascially something like this:

def my_xlim(x1,x2,adjust_ylim = False):
    plt.xlim(x1,x2)
    if adjust_ylim:
        mask = (x>x1)*(x<x2)
        y_plot = y[mask]
        y_plot_min = np.min(y_plot)
        y_plot_max = np.max(y_plot)
        delta_y = y_plot_max - y_plot_min
        y_plot_max += delta_y/10
        y_plot_min -= delta_y/10
        plt.ylim(y_plot_min, y_plot_max)

where here x and y are global variables in my notebook, but which in the code base must presumably be accessible somehow from the functions in the library.

I make an argument for the use case here:

https://github.com/gsteele13/gary-misc-notebooks/blob/master/prototype%20matplotlib%20modified%20xlim.ipynb

@drmcnelson
Copy link

drmcnelson commented Feb 10, 2022 via email

@drmcnelson
Copy link

drmcnelson commented Feb 10, 2022 via email

@QuLogic
Copy link
Member

QuLogic commented Mar 8, 2023

Closing as a duplicate of #2123.

@QuLogic QuLogic closed this as completed Mar 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants