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

ENH: secondary axis for a x or y scale. #10976

Closed
jklymak opened this issue Apr 6, 2018 · 9 comments
Closed

ENH: secondary axis for a x or y scale. #10976

jklymak opened this issue Apr 6, 2018 · 9 comments

Comments

@jklymak
Copy link
Member

jklymak commented Apr 6, 2018

Enhancement discussion

See #10961 #10960

5l7bk

Often it is desirable to have a second x or y scale on an axes with a different scale than the primary x/y axis; think frequency and period on the xaxis of a power spectrum, or date and datenum for a time series. (just assuming the x-axis for now:) Generally it'd be the same size as the existing xaxis, but would be offset vertically, have its own locator, formatter, and xlabel. it would update as the data is zoomed through, and be slave to the parent xaxis

It appears folks most often do this with twiny (i.e. https://stackoverflow.com/questions/10514315/how-to-add-a-second-x-axis-in-matplotlib) but there is nothing to make the axes continue to have the same x-limits under interactive viewing, and no simple way to have the new axes have a different scale.

The enhancement here would be to add something like:

secaxis = ax.secondary_xaxis(loc='top', convert=5, label='Boo')

where secaxis is either an axis object or an axes. loc would be something like "top", "bottom", "above", "below", float where "above" and "below" means that it goes above or below any existing axis, and the float is a hard-set axes fraction (like the set_position of spine now). convert would be a conversion factor: a float would just be a multiplier, or a function could be passed that converts x to xnew. No doubt there could be a way to make this work with units instead, but...

How to impliment

As discussed in #10961 @efiring thought implimenting as an axes with shrunk and hidden unused dimensions would be good.

I'd advocate an axis object so that the logical organization makes more sense. i.e. this would be a child of the original axes, and would be included as one of the axes decorators. But, I haven't thought through all the implimentation details yet.

@efiring
Copy link
Member

efiring commented Apr 6, 2018

An Axis makes logical sense, but it doesn't fit deep-seated aspects of the way mpl works, which is very Axes-centric. Among other things, spines reside in the Axes, even though each spine is tied to an Axis. That's why I think that a variation on twinning, but with a stripped-down slave Axes rather than a full-fledged twin, is likely to provide the most direct route to the desired result. The slave would be designed solely for the purpose of providing alternative scales (with Locators and Formatters) for labeling the data plotted in the master. It would be added to the Figure just like any other Axes, so the master would not have to know about it, and the Axes class would not be changed.

@jklymak
Copy link
Member Author

jklymak commented Apr 6, 2018

OK, I hadn't quite realized that axis objects didn't know about the spines.

The reason I was thinking of it as a spine/xaxis is then it could be included in the bounding box for any tight/constrained_layout behaviour acting on the parent axes. For layout the parent needs to know about the child. I think I can still do that if we go an axes route, the way colorbars are done, but its a bit ugglier.

@u55
Copy link
Contributor

u55 commented Apr 7, 2018

Yes please! I have needed this functionality a few times, and solved it by using a BlendedGenericTransform, see https://stackoverflow.com/a/3165542/4708327 for an example. But this requires a lot of boiler-plate code to create the forward and inverse Transform classes. It would be very nice to be able to do this with a simple one-liner built into matplotlib.

@jklymak
Copy link
Member Author

jklymak commented Apr 7, 2018

Thanks for the pointer @u55 . It is interesting to think how units fits into all of this. As pointed out by @anntzer its possible to think of units as a Transform, and there is the possibility that the units support will just be moved into a transform-like construct.

@jklymak jklymak added this to the v3.0 milestone Apr 7, 2018
@jklymak
Copy link
Member Author

jklymak commented Apr 7, 2018

Note that this is the functionality provided in ParasiteAxes https://matplotlib.org/gallery/axes_grid1/parasite_simple2.html
so maybe there is something to be borrowed from in there.

@ImportanceOfBeingErnest
Copy link
Member

I wanted to mention here that pandas uses secondary_y to create a twinx axes.

pandas.Series([1,2,3]).plot()
pandas.Series([29,28,27]).plot(secondary_y=True)

It could be confusing if now matplotlib uses secondary_yaxis to create a shared axis spine.

@u55
Copy link
Contributor

u55 commented Apr 7, 2018

To simplify the API, instead of creating a new axes method, how about adding another kwarg to SubplotHost.twin, such as:

from mpl_toolkits.axes_grid.parasite_axes import SubplotHost 
ax_host = SubplotHost(fig, 1, 1, 1)
ax_twin = ax_host.twin(convert=(lambda x: 1/(1+x), lambda y: y**2))

which handles all the boiler-plate code to create the Transform classes for x and y.

The only problem I see, is that currently, transforms.BlendedGenericTransform, requires both the forward and inverse Transform. For complicated non-analytic functions, an inverse function may not be available easily, and it would be nice if we could lift this requirement.

@jklymak
Copy link
Member Author

jklymak commented Apr 7, 2018

That’s possible. Axes_grid1 isn’t really part of core, so in my opinion it’d be nice to move this into the main functionality.

@jklymak
Copy link
Member Author

jklymak commented Mar 14, 2019

This was done in #11859

@jklymak jklymak closed this as completed Mar 14, 2019
@tacaswell tacaswell moved this from In progress to Done in axes_grid1 toolkit to main code base.... Nov 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

5 participants