From 36af98e371ed4af49b98488385cef3ab88ca2d19 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:40:37 +0200 Subject: [PATCH 1/2] DOC: Add a plot to margins() to visualize the effect --- lib/matplotlib/axes/_base.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 52963c1d1ff5..7d72b8caedfa 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2745,6 +2745,34 @@ def margins(self, *margins, x=None, y=None, tight=True): arguments (positional or otherwise) are provided, the current margins will remain unchanged and simply be returned. + .. plot:: + + import numpy as np + import matplotlib.pyplot as plt + + x, y = np.meshgrid(np.linspace(0, 1, 10), np.linspace(0, 1, 10)) + fig, ax = plt.subplots() + ax.plot(x, y, 'o', color='lightblue') + ax.margins(1.5, 0.5) + ax.set_title("margins(x=1.5, y=0.5)") + + def arrow(p1, p2, **props): + ax.annotate("", p1, p2, + arrowprops=dict(arrowstyle="<->", shrinkA=0, shrinkB=0, **props)) + + arrow((-1.5, 0), (0, 0), color="orange") + arrow((0, 0), (1, 0), color="sienna") + arrow((1, 0), (2.5, 0), color="orange") + ax.text(-0.75, -0.1, "x margin * x data range", ha="center", + color="orange") + ax.text(0.5, -0.1, "x data range", ha="center", color="sienna") + + arrow((1, -0.5), (1, 0), color="tab:green") + arrow((1, 0), (1, 1), color="darkgreen") + arrow((1, 1), (1, 1.5), color="tab:green") + ax.text(1.1, 1.25, "y margin * y data range", color="tab:green") + ax.text(1.1, 0.5, "y data range", color="darkgreen") + Specifying any margin changes only the autoscaling; for example, if *xmargin* is not None, then *xmargin* times the X data interval will be added to each end of that interval before From 8ea86fed13b656ef0bbadceada10255e0d17b35a Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Fri, 13 Sep 2024 01:29:21 +0200 Subject: [PATCH 2/2] DOC: Add a plot to margins() to visualize the effect --- doc/_embedded_plots/axes_margins.py | 42 +++++++++++++++++++++ lib/matplotlib/axes/_base.py | 57 +++++++++-------------------- 2 files changed, 60 insertions(+), 39 deletions(-) create mode 100644 doc/_embedded_plots/axes_margins.py diff --git a/doc/_embedded_plots/axes_margins.py b/doc/_embedded_plots/axes_margins.py new file mode 100644 index 000000000000..d026840c3c15 --- /dev/null +++ b/doc/_embedded_plots/axes_margins.py @@ -0,0 +1,42 @@ +import numpy as np +import matplotlib.pyplot as plt + +fig, ax = plt.subplots(figsize=(6.5, 4)) +x = np.linspace(0, 1, 33) +y = -np.sin(x * 2*np.pi) +ax.plot(x, y, 'o') +ax.margins(0.5, 0.2) +ax.set_title("margins(x=0.5, y=0.2)") + +# fix the Axes limits so that the following helper drawings +# cannot change them further. +ax.set(xlim=ax.get_xlim(), ylim=ax.get_ylim()) + + +def arrow(p1, p2, **props): + ax.annotate("", p1, p2, + arrowprops=dict(arrowstyle="<->", shrinkA=0, shrinkB=0, **props)) + + +axmin, axmax = ax.get_xlim() +aymin, aymax = ax.get_ylim() +xmin, xmax = x.min(), x.max() +ymin, ymax = y.min(), y.max() + +y0 = -0.8 +ax.axvspan(axmin, xmin, color=("orange", 0.1)) +ax.axvspan(xmax, axmax, color=("orange", 0.1)) +arrow((xmin, y0), (xmax, y0), color="sienna") +arrow((xmax, y0), (axmax, y0), color="orange") +ax.text((xmax + axmax)/2, y0+0.05, "x margin\n* x data range", + ha="center", va="bottom", color="orange") +ax.text(0.55, y0+0.1, "x data range", va="bottom", color="sienna") + +x0 = 0.1 +ax.axhspan(aymin, ymin, color=("tab:green", 0.1)) +ax.axhspan(ymax, aymax, color=("tab:green", 0.1)) +arrow((x0, ymin), (x0, ymax), color="darkgreen") +arrow((x0, ymax), (x0, aymax), color="tab:green") +ax.text(x0, (ymax + aymax) / 2, " y margin * y data range", + va="center", color="tab:green") +ax.text(x0, 0.5, " y data range", color="darkgreen") diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 7d72b8caedfa..18cfec831af4 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2736,47 +2736,22 @@ def set_ymargin(self, m): def margins(self, *margins, x=None, y=None, tight=True): """ - Set or retrieve autoscaling margins. + Set or retrieve margins around the data for autoscaling axis limits. - The padding added to each limit of the Axes is the *margin* - times the data interval. All input parameters must be floats - greater than -0.5. Passing both positional and keyword - arguments is invalid and will raise a TypeError. If no - arguments (positional or otherwise) are provided, the current - margins will remain unchanged and simply be returned. - - .. plot:: - - import numpy as np - import matplotlib.pyplot as plt - - x, y = np.meshgrid(np.linspace(0, 1, 10), np.linspace(0, 1, 10)) - fig, ax = plt.subplots() - ax.plot(x, y, 'o', color='lightblue') - ax.margins(1.5, 0.5) - ax.set_title("margins(x=1.5, y=0.5)") + This allows to configure the padding around the data without having to + set explicit limits using `~.Axes.set_xlim` / `~.Axes.set_ylim`. - def arrow(p1, p2, **props): - ax.annotate("", p1, p2, - arrowprops=dict(arrowstyle="<->", shrinkA=0, shrinkB=0, **props)) + Autoscaling determines the axis limits by adding *margin* times the + data interval as padding around the data. See the following illustration: - arrow((-1.5, 0), (0, 0), color="orange") - arrow((0, 0), (1, 0), color="sienna") - arrow((1, 0), (2.5, 0), color="orange") - ax.text(-0.75, -0.1, "x margin * x data range", ha="center", - color="orange") - ax.text(0.5, -0.1, "x data range", ha="center", color="sienna") + .. plot:: _embedded_plots/axes_margins.py - arrow((1, -0.5), (1, 0), color="tab:green") - arrow((1, 0), (1, 1), color="darkgreen") - arrow((1, 1), (1, 1.5), color="tab:green") - ax.text(1.1, 1.25, "y margin * y data range", color="tab:green") - ax.text(1.1, 0.5, "y data range", color="darkgreen") + All input parameters must be floats greater than -0.5. Passing both + positional and keyword arguments is invalid and will raise a TypeError. + If no arguments (positional or otherwise) are provided, the current + margins will remain unchanged and simply be returned. - Specifying any margin changes only the autoscaling; for example, - if *xmargin* is not None, then *xmargin* times the X data - interval will be added to each end of that interval before - it is used in autoscaling. + The default margins are :rc:`axes.xmargin` and :rc:`axes.ymargin`. Parameters ---------- @@ -2808,10 +2783,14 @@ def arrow(p1, p2, **props): Notes ----- If a previously used Axes method such as :meth:`pcolor` has set - :attr:`use_sticky_edges` to `True`, only the limits not set by - the "sticky artists" will be modified. To force all of the - margins to be set, set :attr:`use_sticky_edges` to `False` + `~.Axes.use_sticky_edges` to `True`, only the limits not set by + the "sticky artists" will be modified. To force all + margins to be set, set `~.Axes.use_sticky_edges` to `False` before calling :meth:`margins`. + + See Also + -------- + .Axes.set_xmargin, .Axes.set_ymargin """ if margins and (x is not None or y is not None):