From d9bd094ab9d9a4e31446458f59a6c1f16552fbd7 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Mon, 16 Dec 2024 16:08:49 +0100 Subject: [PATCH 1/7] feat: axes class and kwargs for twinx and twiny --- lib/matplotlib/axes/_base.py | 18 ++++++++++++++---- lib/matplotlib/axes/_base.pyi | 6 +++--- lib/matplotlib/tests/test_axes.py | 22 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 108cda04865f..ed1d6d779545 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -4596,7 +4596,7 @@ def _make_twin_axes(self, *args, **kwargs): self._twinned_axes.join(self, twin) return twin - def twinx(self): + def twinx(self, **kwargs): """ Create a twin Axes sharing the xaxis. @@ -4606,6 +4606,11 @@ def twinx(self): Axes. To ensure that the tick marks of both y-axes align, see `~matplotlib.ticker.LinearLocator`. + Parameters + ---------- + kwargs : dict + The keyword arguments passed to ``add_subplot()`` or ``add_axes()``. + Returns ------- Axes @@ -4616,7 +4621,7 @@ def twinx(self): For those who are 'picking' artists while using twinx, pick events are only called for the artists in the top-most Axes. """ - ax2 = self._make_twin_axes(sharex=self) + ax2 = self._make_twin_axes(sharex=self, axes_class=type(self), **kwargs) ax2.yaxis.tick_right() ax2.yaxis.set_label_position('right') ax2.yaxis.set_offset_position('right') @@ -4627,7 +4632,7 @@ def twinx(self): ax2.xaxis.units = self.xaxis.units return ax2 - def twiny(self): + def twiny(self, **kwargs): """ Create a twin Axes sharing the yaxis. @@ -4637,6 +4642,11 @@ def twiny(self): To ensure that the tick marks of both x-axes align, see `~matplotlib.ticker.LinearLocator`. + Parameters + ---------- + kwargs : dict + The keyword arguments passed to ``add_subplot()`` or ``add_axes()``. + Returns ------- Axes @@ -4647,7 +4657,7 @@ def twiny(self): For those who are 'picking' artists while using twiny, pick events are only called for the artists in the top-most Axes. """ - ax2 = self._make_twin_axes(sharey=self) + ax2 = self._make_twin_axes(sharey=self, axes_class=type(self), **kwargs) ax2.xaxis.tick_top() ax2.xaxis.set_label_position('top') ax2.set_autoscaley_on(self.get_autoscaley_on()) diff --git a/lib/matplotlib/axes/_base.pyi b/lib/matplotlib/axes/_base.pyi index b4926f113564..0b487de49917 100644 --- a/lib/matplotlib/axes/_base.pyi +++ b/lib/matplotlib/axes/_base.pyi @@ -4,7 +4,6 @@ import datetime from collections.abc import Callable, Iterable, Iterator, Sequence from matplotlib import cbook from matplotlib.artist import Artist -from matplotlib.axes import Axes from matplotlib.axis import XAxis, YAxis, Tick from matplotlib.backend_bases import RendererBase, MouseButton, MouseEvent from matplotlib.cbook import CallbackRegistry @@ -29,6 +28,7 @@ import numpy as np from numpy.typing import ArrayLike from typing import Any, Literal, TypeVar, overload from matplotlib.typing import ColorType +from typing_extensions import Self _T = TypeVar("_T", bound=Artist) @@ -385,8 +385,8 @@ class _AxesBase(martist.Artist): bbox_extra_artists: Sequence[Artist] | None = ..., for_layout_only: bool = ... ) -> Bbox | None: ... - def twinx(self) -> Axes: ... - def twiny(self) -> Axes: ... + def twinx(self, **kwargs) -> Self: ... + def twiny(self, **kwargs) -> Self: ... def get_shared_x_axes(self) -> cbook.GrouperView: ... def get_shared_y_axes(self) -> cbook.GrouperView: ... def label_outer(self, remove_inner_ticks: bool = ...) -> None: ... diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 38857e846c55..df9765f7077e 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -7532,6 +7532,28 @@ def test_twinx_knows_limits(): assert_array_equal(xtwin.viewLim.intervalx, ax2.viewLim.intervalx) +class SubclassAxes(Axes): + def __init__(self, *args, foo, **kwargs): + super().__init__(*args, **kwargs) + self.foo = foo + + +@pytest.mark.parametrize(("axes_class", "kw0", "kw1"), [ + (Axes, {}, {}), + (SubclassAxes, {"foo": 0}, {"foo": 1}), +]) +def test_twinx_subclass(axes_class, kw0, kw1): + fig = plt.figure() + classed_ax = fig.add_subplot(axes_class=axes_class, **kw0) + for k, v in kw0.items(): + assert getattr(classed_ax, k) == v + + twin = classed_ax.twinx(**kw1) + assert type(twin) is axes_class + for k, v in kw1.items(): + assert getattr(twin, k) == v + + def test_zero_linewidth(): # Check that setting a zero linewidth doesn't error plt.plot([0, 1], [0, 1], ls='--', lw=0) From f83b93d000fdc7a89c5eeed734c58d559964c325 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 17 Dec 2024 22:14:25 +0100 Subject: [PATCH 2/7] fix: projection or polar --- lib/matplotlib/axes/_base.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index ed1d6d779545..75bc27aafa02 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -4621,7 +4621,9 @@ def twinx(self, **kwargs): For those who are 'picking' artists while using twinx, pick events are only called for the artists in the top-most Axes. """ - ax2 = self._make_twin_axes(sharex=self, axes_class=type(self), **kwargs) + if not {"projection", "polar", "axes_class"}.intersection(kwargs): + kwargs["axes_class"] = type(self) + ax2 = self._make_twin_axes(sharex=self, **kwargs) ax2.yaxis.tick_right() ax2.yaxis.set_label_position('right') ax2.yaxis.set_offset_position('right') @@ -4657,7 +4659,9 @@ def twiny(self, **kwargs): For those who are 'picking' artists while using twiny, pick events are only called for the artists in the top-most Axes. """ - ax2 = self._make_twin_axes(sharey=self, axes_class=type(self), **kwargs) + if not {"projection", "polar", "axes_class"}.intersection(kwargs): + kwargs["axes_class"] = type(self) + ax2 = self._make_twin_axes(sharey=self, **kwargs) ax2.xaxis.tick_top() ax2.xaxis.set_label_position('top') ax2.set_autoscaley_on(self.get_autoscaley_on()) From 230fd0ac2b5cd1f4f4d06d6fb4e15b325289ace1 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Mon, 16 Dec 2024 16:08:49 +0100 Subject: [PATCH 3/7] feat: axes class and kwargs for twinx and twiny --- lib/matplotlib/axes/_base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 75bc27aafa02..6871fb1d5f0f 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -4623,7 +4623,7 @@ def twinx(self, **kwargs): """ if not {"projection", "polar", "axes_class"}.intersection(kwargs): kwargs["axes_class"] = type(self) - ax2 = self._make_twin_axes(sharex=self, **kwargs) + ax2 = self._make_twin_axes(sharex=self, axes_class=type(self), **kwargs) ax2.yaxis.tick_right() ax2.yaxis.set_label_position('right') ax2.yaxis.set_offset_position('right') @@ -4661,7 +4661,7 @@ def twiny(self, **kwargs): """ if not {"projection", "polar", "axes_class"}.intersection(kwargs): kwargs["axes_class"] = type(self) - ax2 = self._make_twin_axes(sharey=self, **kwargs) + ax2 = self._make_twin_axes(sharey=self, axes_class=type(self), **kwargs) ax2.xaxis.tick_top() ax2.xaxis.set_label_position('top') ax2.set_autoscaley_on(self.get_autoscaley_on()) From 9a12c2c545c38b5cffc2b75ed76a8c1812137cf1 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sat, 21 Dec 2024 00:12:23 +0100 Subject: [PATCH 4/7] fix(comment): https://github.com/matplotlib/matplotlib/pull/29325#issuecomment-2555976517 --- lib/matplotlib/axes/_base.py | 26 ++++++++++++++++++-------- lib/matplotlib/axes/_base.pyi | 6 +++--- lib/matplotlib/tests/test_axes.py | 2 +- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 6871fb1d5f0f..f83cbff66556 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -4596,7 +4596,7 @@ def _make_twin_axes(self, *args, **kwargs): self._twinned_axes.join(self, twin) return twin - def twinx(self, **kwargs): + def twinx(self, axes_class=None, **kwargs): """ Create a twin Axes sharing the xaxis. @@ -4608,6 +4608,11 @@ def twinx(self, **kwargs): Parameters ---------- + axes_class : subclass type of `~.axes.Axes`, optional + The `.axes.Axes` subclass that is instantiated. This parameter + is incompatible with *projection* and *polar*. See + :ref:`axisartist_users-guide-index` for examples. + kwargs : dict The keyword arguments passed to ``add_subplot()`` or ``add_axes()``. @@ -4621,9 +4626,9 @@ def twinx(self, **kwargs): For those who are 'picking' artists while using twinx, pick events are only called for the artists in the top-most Axes. """ - if not {"projection", "polar", "axes_class"}.intersection(kwargs): - kwargs["axes_class"] = type(self) - ax2 = self._make_twin_axes(sharex=self, axes_class=type(self), **kwargs) + if axes_class: + kwargs["axes_class"] = axes_class + ax2 = self._make_twin_axes(sharex=self, **kwargs) ax2.yaxis.tick_right() ax2.yaxis.set_label_position('right') ax2.yaxis.set_offset_position('right') @@ -4634,7 +4639,7 @@ def twinx(self, **kwargs): ax2.xaxis.units = self.xaxis.units return ax2 - def twiny(self, **kwargs): + def twiny(self, axes_class=None, **kwargs): """ Create a twin Axes sharing the yaxis. @@ -4646,6 +4651,11 @@ def twiny(self, **kwargs): Parameters ---------- + axes_class : subclass type of `~.axes.Axes`, optional + The `.axes.Axes` subclass that is instantiated. This parameter + is incompatible with *projection* and *polar*. See + :ref:`axisartist_users-guide-index` for examples. + kwargs : dict The keyword arguments passed to ``add_subplot()`` or ``add_axes()``. @@ -4659,9 +4669,9 @@ def twiny(self, **kwargs): For those who are 'picking' artists while using twiny, pick events are only called for the artists in the top-most Axes. """ - if not {"projection", "polar", "axes_class"}.intersection(kwargs): - kwargs["axes_class"] = type(self) - ax2 = self._make_twin_axes(sharey=self, axes_class=type(self), **kwargs) + if axes_class: + kwargs["axes_class"] = axes_class + ax2 = self._make_twin_axes(sharey=self, **kwargs) ax2.xaxis.tick_top() ax2.xaxis.set_label_position('top') ax2.set_autoscaley_on(self.get_autoscaley_on()) diff --git a/lib/matplotlib/axes/_base.pyi b/lib/matplotlib/axes/_base.pyi index 0b487de49917..31208ce23d1e 100644 --- a/lib/matplotlib/axes/_base.pyi +++ b/lib/matplotlib/axes/_base.pyi @@ -4,6 +4,7 @@ import datetime from collections.abc import Callable, Iterable, Iterator, Sequence from matplotlib import cbook from matplotlib.artist import Artist +from matplotlib.axes import Axes from matplotlib.axis import XAxis, YAxis, Tick from matplotlib.backend_bases import RendererBase, MouseButton, MouseEvent from matplotlib.cbook import CallbackRegistry @@ -28,7 +29,6 @@ import numpy as np from numpy.typing import ArrayLike from typing import Any, Literal, TypeVar, overload from matplotlib.typing import ColorType -from typing_extensions import Self _T = TypeVar("_T", bound=Artist) @@ -385,8 +385,8 @@ class _AxesBase(martist.Artist): bbox_extra_artists: Sequence[Artist] | None = ..., for_layout_only: bool = ... ) -> Bbox | None: ... - def twinx(self, **kwargs) -> Self: ... - def twiny(self, **kwargs) -> Self: ... + def twinx(self, axes_class: Axes | None = ..., **kwargs) -> Axes: ... + def twiny(self, axes_class: Axes | None = ..., **kwargs) -> Axes: ... def get_shared_x_axes(self) -> cbook.GrouperView: ... def get_shared_y_axes(self) -> cbook.GrouperView: ... def label_outer(self, remove_inner_ticks: bool = ...) -> None: ... diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index df9765f7077e..bfe08082f577 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -7548,7 +7548,7 @@ def test_twinx_subclass(axes_class, kw0, kw1): for k, v in kw0.items(): assert getattr(classed_ax, k) == v - twin = classed_ax.twinx(**kw1) + twin = classed_ax.twinx(axes_class=axes_class, **kw1) assert type(twin) is axes_class for k, v in kw1.items(): assert getattr(twin, k) == v From 769b4d3c8a2bab6a660e439b80d2bdee5e1dc47a Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sat, 21 Dec 2024 11:28:27 +0100 Subject: [PATCH 5/7] fix(codecov): coverage --- lib/matplotlib/tests/test_axes.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index bfe08082f577..649a7991d546 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -7538,17 +7538,18 @@ def __init__(self, *args, foo, **kwargs): self.foo = foo +@pytest.mark.parametrize("twinning", ["twinx", "twiny"]) @pytest.mark.parametrize(("axes_class", "kw0", "kw1"), [ (Axes, {}, {}), (SubclassAxes, {"foo": 0}, {"foo": 1}), ]) -def test_twinx_subclass(axes_class, kw0, kw1): +def test_twinning_subclass(twinning, axes_class, kw0, kw1): fig = plt.figure() classed_ax = fig.add_subplot(axes_class=axes_class, **kw0) for k, v in kw0.items(): assert getattr(classed_ax, k) == v - twin = classed_ax.twinx(axes_class=axes_class, **kw1) + twin = getattr(classed_ax, twinning)(axes_class=axes_class, **kw1) assert type(twin) is axes_class for k, v in kw1.items(): assert getattr(twin, k) == v From 18589f32bf19b7783e59c15a2965357a0dc3ff83 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sat, 21 Dec 2024 23:48:59 +0100 Subject: [PATCH 6/7] Apply suggestions from code review - https://github.com/matplotlib/matplotlib/pull/29325/files#r1894693106 - https://github.com/matplotlib/matplotlib/pull/29325/files#r1894693188 - https://github.com/matplotlib/matplotlib/pull/29325/files#r1894696478 Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> --- lib/matplotlib/axes/_base.py | 4 ++++ lib/matplotlib/tests/test_axes.py | 36 ++++++++++++++++++------------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index f83cbff66556..650aab6bea0c 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -4613,6 +4613,8 @@ def twinx(self, axes_class=None, **kwargs): is incompatible with *projection* and *polar*. See :ref:`axisartist_users-guide-index` for examples. + By default, `~.axes.Axes` is used. + kwargs : dict The keyword arguments passed to ``add_subplot()`` or ``add_axes()``. @@ -4656,6 +4658,8 @@ def twiny(self, axes_class=None, **kwargs): is incompatible with *projection* and *polar*. See :ref:`axisartist_users-guide-index` for examples. + By default, `~.axes.Axes` is used. + kwargs : dict The keyword arguments passed to ``add_subplot()`` or ``add_axes()``. diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 649a7991d546..ecc37c83b55d 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -7538,21 +7538,27 @@ def __init__(self, *args, foo, **kwargs): self.foo = foo -@pytest.mark.parametrize("twinning", ["twinx", "twiny"]) -@pytest.mark.parametrize(("axes_class", "kw0", "kw1"), [ - (Axes, {}, {}), - (SubclassAxes, {"foo": 0}, {"foo": 1}), -]) -def test_twinning_subclass(twinning, axes_class, kw0, kw1): - fig = plt.figure() - classed_ax = fig.add_subplot(axes_class=axes_class, **kw0) - for k, v in kw0.items(): - assert getattr(classed_ax, k) == v - - twin = getattr(classed_ax, twinning)(axes_class=axes_class, **kw1) - assert type(twin) is axes_class - for k, v in kw1.items(): - assert getattr(twin, k) == v +def test_twinning_with_axes_class(): + """Check that twinx/y(axes_class=...) gives the appropriate class.""" + _, ax = plt.subplots() + twinx = ax.twinx(axes_class=SubclassAxes, foo=1) + assert isinstance(twinx, SubclassAxes) + assert twinx.foo == 1 + twiny = ax.twiny(axes_class=SubclassAxes, foo=2) + assert isinstance(twiny, SubclassAxes) + assert twiny.foo == 2 + + +def test_twinning_default_axes_class(): + """ + Check that the default class for twinx/y() is Axes, + even if the original is an Axes subclass. + """ + _, ax = plt.subplots(subplot_kw=dict(axes_class=SubclassAxes, foo=1)) + twinx = ax.twinx() + assert type(twinx) is Axes + twiny = ax.twiny() + assert type(twiny) is Axes def test_zero_linewidth(): From bb8e3742e6fe140b53916fa851ad53be561bdf59 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Wed, 8 Jan 2025 11:31:36 +0100 Subject: [PATCH 7/7] Apply suggestions from code review - https://github.com/matplotlib/matplotlib/pull/29325#discussion_r1903091205 - https://github.com/matplotlib/matplotlib/pull/29325#discussion_r1903107092 - https://github.com/matplotlib/matplotlib/pull/29325#discussion_r1906984234 Co-authored-by: Ruth Comer <10599679+rcomer@users.noreply.github.com> --- lib/matplotlib/axes/_base.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 650aab6bea0c..8313d26df190 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -4615,8 +4615,12 @@ def twinx(self, axes_class=None, **kwargs): By default, `~.axes.Axes` is used. + .. versionadded:: 3.11 + kwargs : dict - The keyword arguments passed to ``add_subplot()`` or ``add_axes()``. + The keyword arguments passed to `.Figure.add_subplot` or `.Figure.add_axes`. + + .. versionadded:: 3.11 Returns ------- @@ -4660,8 +4664,12 @@ def twiny(self, axes_class=None, **kwargs): By default, `~.axes.Axes` is used. + .. versionadded:: 3.11 + kwargs : dict - The keyword arguments passed to ``add_subplot()`` or ``add_axes()``. + The keyword arguments passed to `.Figure.add_subplot` or `.Figure.add_axes`. + + .. versionadded:: 3.11 Returns ------- pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy