Skip to content

Commit 5160ea8

Browse files
kkollsgaclaude
andcommitted
Add facetgrid_figsize option to set_options (#11103)
Add a new `facetgrid_figsize` option to `xr.set_options()` that controls how FacetGrid determines figure size when `figsize` is not explicitly passed. When set to `"rcparams"`, FacetGrid uses `matplotlib.rcParams['figure.figsize']` instead of computing size from `size` and `aspect`. Default is `"computed"` (current behavior). Co-authored-by: Claude <noreply@anthropic.com>
1 parent dbe0aa2 commit 5160ea8

5 files changed

Lines changed: 76 additions & 4 deletions

File tree

doc/whats-new.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ Antonio Valentino, Chris Barker, Christine P. Chai, Deepak Cherian, Ewan Short,
155155
New Features
156156
~~~~~~~~~~~~
157157

158+
- Added ``facetgrid_figsize`` option to :py:func:`~xarray.set_options` allowing
159+
:py:class:`~xarray.plot.FacetGrid` to use ``matplotlib.rcParams['figure.figsize']``
160+
instead of computing figure size from ``size`` and ``aspect`` (:issue:`11103`).
161+
By `Kristian Kollsga <https://github.com/kkollsga>`_.
158162
- :py:class:`~xarray.indexes.NDPointIndex` now supports coordinates with fewer
159163
dimensions than coordinate variables, enabling indexing of scattered points
160164
and trajectories where multiple coordinates (e.g., ``x``, ``y``) share a

xarray/core/options.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"use_numbagg",
4141
"use_opt_einsum",
4242
"use_flox",
43+
"facetgrid_figsize",
4344
]
4445

4546
class T_Options(TypedDict):
@@ -73,6 +74,7 @@ class T_Options(TypedDict):
7374
use_new_combine_kwarg_defaults: bool
7475
use_numbagg: bool
7576
use_opt_einsum: bool
77+
facetgrid_figsize: Literal["computed", "rcparams"]
7678

7779

7880
OPTIONS: T_Options = {
@@ -106,8 +108,10 @@ class T_Options(TypedDict):
106108
"use_new_combine_kwarg_defaults": False,
107109
"use_numbagg": True,
108110
"use_opt_einsum": True,
111+
"facetgrid_figsize": "computed",
109112
}
110113

114+
_FACETGRID_FIGSIZE_OPTIONS = frozenset(["computed", "rcparams"])
111115
_JOIN_OPTIONS = frozenset(["inner", "outer", "left", "right", "exact"])
112116
_DISPLAY_OPTIONS = frozenset(["text", "html"])
113117
_NETCDF_ENGINES = frozenset(["netcdf4", "h5netcdf", "scipy"])
@@ -144,6 +148,7 @@ def _positive_integer(value: Any) -> bool:
144148
"use_opt_einsum": lambda value: isinstance(value, bool),
145149
"use_flox": lambda value: isinstance(value, bool),
146150
"warn_for_unclosed_files": lambda value: isinstance(value, bool),
151+
"facetgrid_figsize": _FACETGRID_FIGSIZE_OPTIONS.__contains__,
147152
}
148153

149154

@@ -222,6 +227,14 @@ class set_options:
222227
chunk_manager : str, default: "dask"
223228
Chunk manager to use for chunked array computations when multiple
224229
options are installed.
230+
facetgrid_figsize : {"computed", "rcparams"}, default: "computed"
231+
How :class:`~xarray.plot.FacetGrid` determines figure size when
232+
``figsize`` is not explicitly passed:
233+
234+
* ``"computed"`` : figure size is derived from ``size`` and ``aspect``
235+
parameters (current default behavior).
236+
* ``"rcparams"`` : use ``matplotlib.rcParams['figure.figsize']`` as the
237+
total figure size.
225238
cmap_divergent : str or matplotlib.colors.Colormap, default: "RdBu_r"
226239
Colormap to use for divergent data plots. If string, must be
227240
matplotlib built-in colormap. Can also be a Colormap object
@@ -357,6 +370,8 @@ def __init__(self, **kwargs):
357370
expected = f"Expected one of {_JOIN_OPTIONS!r}"
358371
elif k == "display_style":
359372
expected = f"Expected one of {_DISPLAY_OPTIONS!r}"
373+
elif k == "facetgrid_figsize":
374+
expected = f"Expected one of {_FACETGRID_FIGSIZE_OPTIONS!r}"
360375
elif k == "netcdf_engine_order":
361376
expected = f"Expected a subset of {sorted(_NETCDF_ENGINES)}"
362377
else:

xarray/plot/facetgrid.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,17 @@ def __init__(
212212
subplot_kws = {} if subplot_kws is None else subplot_kws
213213

214214
if figsize is None:
215-
# Calculate the base figure size with extra horizontal space for a
216-
# colorbar
217-
cbar_space = 1
218-
figsize = (ncol * size * aspect + cbar_space, nrow * size)
215+
from xarray.core.options import OPTIONS
216+
217+
if OPTIONS["facetgrid_figsize"] == "rcparams":
218+
import matplotlib as mpl
219+
220+
figsize = tuple(mpl.rcParams["figure.figsize"])
221+
else:
222+
# Calculate the base figure size with extra horizontal space
223+
# for a colorbar
224+
cbar_space = 1
225+
figsize = (ncol * size * aspect + cbar_space, nrow * size)
219226

220227
fig, axs = plt.subplots(
221228
nrow,

xarray/tests/test_options.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ def test_netcdf_engine_order() -> None:
8484
assert OPTIONS["netcdf_engine_order"] == original
8585

8686

87+
def test_facetgrid_figsize() -> None:
88+
with pytest.raises(ValueError):
89+
xarray.set_options(facetgrid_figsize="invalid")
90+
with xarray.set_options(facetgrid_figsize="rcparams"):
91+
assert OPTIONS["facetgrid_figsize"] == "rcparams"
92+
with xarray.set_options(facetgrid_figsize="computed"):
93+
assert OPTIONS["facetgrid_figsize"] == "computed"
94+
95+
8796
def test_display_style() -> None:
8897
original = "html"
8998
assert OPTIONS["display_style"] == original

xarray/tests/test_plot.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3567,3 +3567,40 @@ def test_temp_dataarray() -> None:
35673567
locals_ = dict(x="x", extend="var2")
35683568
da = _temp_dataarray(ds, y_, locals_)
35693569
assert da.shape == (3,)
3570+
3571+
3572+
@requires_matplotlib
3573+
def test_facetgrid_figsize_rcparams() -> None:
3574+
"""Test that facetgrid_figsize='rcparams' uses matplotlib rcParams."""
3575+
import matplotlib as mpl
3576+
3577+
da = DataArray(
3578+
np.random.randn(10, 15, 3),
3579+
dims=["y", "x", "z"],
3580+
coords={"z": ["a", "b", "c"]},
3581+
)
3582+
custom_figsize = (12.0, 8.0)
3583+
3584+
with figure_context():
3585+
# Default behavior: computed from size and aspect
3586+
g = xplt.FacetGrid(da, col="z")
3587+
default_figsize = g.fig.get_size_inches()
3588+
# Default should be (ncol * size * aspect + cbar_space, nrow * size)
3589+
# = (3 * 3 * 1 + 1, 1 * 3) = (10, 3)
3590+
np.testing.assert_allclose(default_figsize, (10.0, 3.0))
3591+
3592+
with figure_context():
3593+
# rcparams mode: should use mpl.rcParams['figure.figsize']
3594+
with mpl.rc_context({"figure.figsize": custom_figsize}):
3595+
with xr.set_options(facetgrid_figsize="rcparams"):
3596+
g = xplt.FacetGrid(da, col="z")
3597+
actual_figsize = g.fig.get_size_inches()
3598+
np.testing.assert_allclose(actual_figsize, custom_figsize)
3599+
3600+
with figure_context():
3601+
# Explicit figsize should override the option
3602+
with xr.set_options(facetgrid_figsize="rcparams"):
3603+
explicit_size = (6.0, 4.0)
3604+
g = xplt.FacetGrid(da, col="z", figsize=explicit_size)
3605+
actual_figsize = g.fig.get_size_inches()
3606+
np.testing.assert_allclose(actual_figsize, explicit_size)

0 commit comments

Comments
 (0)