Skip to content

Commit ef078b5

Browse files
kkollsgaclaudepre-commit-ci[bot]jsignell
authored
Add facetgrid_figsize option to set_options (#11158)
* 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> * Support tuple figsize in facetgrid_figsize option (#11103) Extend facetgrid_figsize to accept a (width, height) tuple in addition to "computed" and "rcparams". Also move figsize resolution before grid shape computation for better composition with col_wrap="auto" (#11266). Co-authored-by: Claude <noreply@anthropic.com> * Update options.rst: broaden plotting section, add facetgrid_figsize Co-authored-by: Claude <noreply@anthropic.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Julia Signell <jsignell@gmail.com>
1 parent 452af83 commit ef078b5

6 files changed

Lines changed: 108 additions & 5 deletions

File tree

doc/user-guide/options.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Xarray offers a small number of configuration options through :py:func:`set_opti
1717
- ``display_style``
1818

1919
2. Control behaviour during operations: ``arithmetic_join``, ``keep_attrs``, ``use_bottleneck``.
20-
3. Control colormaps for plots:``cmap_divergent``, ``cmap_sequential``.
20+
3. Control plotting: ``cmap_divergent``, ``cmap_sequential``, ``facetgrid_figsize``.
2121
4. Aspects of file reading: ``file_cache_maxsize``, ``netcdf_engine_order``, ``warn_on_unclosed_files``.
2222

2323

doc/whats-new.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@ Antonio Valentino, Chris Barker, Christine P. Chai, Deepak Cherian, Ewan Short,
177177
New Features
178178
~~~~~~~~~~~~
179179

180+
- Added ``facetgrid_figsize`` option to :py:func:`~xarray.set_options` allowing
181+
:py:class:`~xarray.plot.FacetGrid` to use ``matplotlib.rcParams['figure.figsize']``
182+
or a fixed ``(width, height)`` tuple instead of computing figure size from
183+
``size`` and ``aspect`` (:issue:`11103`).
184+
By `Kristian Kollsga <https://github.com/kkollsga>`_.
180185
- :py:class:`~xarray.indexes.NDPointIndex` now supports coordinates with fewer
181186
dimensions than coordinate variables, enabling indexing of scattered points
182187
and trajectories where multiple coordinates (e.g., ``x``, ``y``) share a

xarray/core/options.py

Lines changed: 26 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"] | tuple[float, float]
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,14 @@ 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": lambda value: (
152+
value in _FACETGRID_FIGSIZE_OPTIONS
153+
or (
154+
isinstance(value, tuple)
155+
and len(value) == 2
156+
and all(isinstance(v, (int, float)) for v in value)
157+
)
158+
),
147159
}
148160

149161

@@ -222,6 +234,15 @@ class set_options:
222234
chunk_manager : str, default: "dask"
223235
Chunk manager to use for chunked array computations when multiple
224236
options are installed.
237+
facetgrid_figsize : {"computed", "rcparams"} or tuple of float, default: "computed"
238+
How :class:`~xarray.plot.FacetGrid` determines figure size when
239+
``figsize`` is not explicitly passed:
240+
241+
* ``"computed"`` : figure size is derived from ``size`` and ``aspect``
242+
parameters (current default behavior).
243+
* ``"rcparams"`` : use ``matplotlib.rcParams['figure.figsize']`` as the
244+
total figure size.
245+
* ``(width, height)`` : use a fixed figure size (in inches).
225246
cmap_divergent : str or matplotlib.colors.Colormap, default: "RdBu_r"
226247
Colormap to use for divergent data plots. If string, must be
227248
matplotlib built-in colormap. Can also be a Colormap object
@@ -357,6 +378,11 @@ def __init__(self, **kwargs):
357378
expected = f"Expected one of {_JOIN_OPTIONS!r}"
358379
elif k == "display_style":
359380
expected = f"Expected one of {_DISPLAY_OPTIONS!r}"
381+
elif k == "facetgrid_figsize":
382+
expected = (
383+
f"Expected one of {_FACETGRID_FIGSIZE_OPTIONS!r}"
384+
" or a (width, height) tuple of floats"
385+
)
360386
elif k == "netcdf_engine_order":
361387
expected = f"Expected a subset of {sorted(_NETCDF_ENGINES)}"
362388
else:

xarray/plot/facetgrid.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,21 @@ def __init__(
220220
else:
221221
raise ValueError("Pass a coordinate name as an argument for row or col")
222222

223-
# exhaust generators
224-
figsize = None if figsize is None else tuple(figsize)
223+
# Resolve figsize from global option before computing grid shape,
224+
# so that downstream heuristics (e.g. col_wrap="auto") can use it.
225+
if figsize is None:
226+
from xarray.core.options import OPTIONS
227+
228+
facetgrid_figsize = OPTIONS["facetgrid_figsize"]
229+
if isinstance(facetgrid_figsize, tuple):
230+
figsize = facetgrid_figsize
231+
elif facetgrid_figsize == "rcparams":
232+
import matplotlib as mpl
233+
234+
figsize = tuple(mpl.rcParams["figure.figsize"])
235+
else:
236+
# exhaust generators
237+
figsize = tuple(figsize)
225238

226239
# Compute grid shape
227240
if single_group:
@@ -239,8 +252,8 @@ def __init__(
239252
subplot_kws = {} if subplot_kws is None else subplot_kws
240253

241254
if figsize is None:
242-
# Calculate the base figure size with extra horizontal space for a
243-
# colorbar
255+
# Calculate the base figure size with extra horizontal space
256+
# for a colorbar
244257
cbar_space = 1
245258
figsize = (ncol * size * aspect + cbar_space, nrow * size)
246259

xarray/tests/test_options.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,21 @@ 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 pytest.raises(ValueError):
91+
xarray.set_options(facetgrid_figsize=(1.0,))
92+
with pytest.raises(ValueError):
93+
xarray.set_options(facetgrid_figsize=(1.0, 2.0, 3.0))
94+
with xarray.set_options(facetgrid_figsize="rcparams"):
95+
assert OPTIONS["facetgrid_figsize"] == "rcparams"
96+
with xarray.set_options(facetgrid_figsize="computed"):
97+
assert OPTIONS["facetgrid_figsize"] == "computed"
98+
with xarray.set_options(facetgrid_figsize=(12.0, 8.0)):
99+
assert OPTIONS["facetgrid_figsize"] == (12.0, 8.0)
100+
101+
87102
def test_display_style() -> None:
88103
original = "html"
89104
assert OPTIONS["display_style"] == original

xarray/tests/test_plot.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3595,3 +3595,47 @@ def test_temp_dataarray() -> None:
35953595
locals_ = dict(x="x", extend="var2")
35963596
da = _temp_dataarray(ds, y_, locals_)
35973597
assert da.shape == (3,)
3598+
3599+
3600+
@requires_matplotlib
3601+
def test_facetgrid_figsize_rcparams() -> None:
3602+
"""Test that facetgrid_figsize='rcparams' uses matplotlib rcParams."""
3603+
import matplotlib as mpl
3604+
3605+
da = DataArray(
3606+
np.random.randn(10, 15, 3),
3607+
dims=["y", "x", "z"],
3608+
coords={"z": ["a", "b", "c"]},
3609+
)
3610+
custom_figsize = (12.0, 8.0)
3611+
3612+
with figure_context():
3613+
# Default behavior: computed from size and aspect
3614+
g = xplt.FacetGrid(da, col="z")
3615+
default_figsize = g.fig.get_size_inches()
3616+
# Default should be (ncol * size * aspect + cbar_space, nrow * size)
3617+
# = (3 * 3 * 1 + 1, 1 * 3) = (10, 3)
3618+
np.testing.assert_allclose(default_figsize, (10.0, 3.0))
3619+
3620+
with figure_context():
3621+
# rcparams mode: should use mpl.rcParams['figure.figsize']
3622+
with mpl.rc_context({"figure.figsize": custom_figsize}):
3623+
with xr.set_options(facetgrid_figsize="rcparams"):
3624+
g = xplt.FacetGrid(da, col="z")
3625+
actual_figsize = g.fig.get_size_inches()
3626+
np.testing.assert_allclose(actual_figsize, custom_figsize)
3627+
3628+
with figure_context():
3629+
# Tuple mode: fixed figsize via set_options
3630+
with xr.set_options(facetgrid_figsize=(14.0, 5.0)):
3631+
g = xplt.FacetGrid(da, col="z")
3632+
actual_figsize = g.fig.get_size_inches()
3633+
np.testing.assert_allclose(actual_figsize, (14.0, 5.0))
3634+
3635+
with figure_context():
3636+
# Explicit figsize should override the option
3637+
with xr.set_options(facetgrid_figsize="rcparams"):
3638+
explicit_size = (6.0, 4.0)
3639+
g = xplt.FacetGrid(da, col="z", figsize=explicit_size)
3640+
actual_figsize = g.fig.get_size_inches()
3641+
np.testing.assert_allclose(actual_figsize, explicit_size)

0 commit comments

Comments
 (0)