Skip to content

Commit 6b1a002

Browse files
kkollsgaclaude
andcommitted
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>
1 parent 5160ea8 commit 6b1a002

File tree

5 files changed

+44
-16
lines changed

5 files changed

+44
-16
lines changed

doc/whats-new.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ New Features
157157

158158
- Added ``facetgrid_figsize`` option to :py:func:`~xarray.set_options` allowing
159159
:py:class:`~xarray.plot.FacetGrid` to use ``matplotlib.rcParams['figure.figsize']``
160-
instead of computing figure size from ``size`` and ``aspect`` (:issue:`11103`).
160+
or a fixed ``(width, height)`` tuple instead of computing figure size from
161+
``size`` and ``aspect`` (:issue:`11103`).
161162
By `Kristian Kollsga <https://github.com/kkollsga>`_.
162163
- :py:class:`~xarray.indexes.NDPointIndex` now supports coordinates with fewer
163164
dimensions than coordinate variables, enabling indexing of scattered points

xarray/core/options.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class T_Options(TypedDict):
7474
use_new_combine_kwarg_defaults: bool
7575
use_numbagg: bool
7676
use_opt_einsum: bool
77-
facetgrid_figsize: Literal["computed", "rcparams"]
77+
facetgrid_figsize: Literal["computed", "rcparams"] | tuple[float, float]
7878

7979

8080
OPTIONS: T_Options = {
@@ -148,7 +148,11 @@ def _positive_integer(value: Any) -> bool:
148148
"use_opt_einsum": lambda value: isinstance(value, bool),
149149
"use_flox": lambda value: isinstance(value, bool),
150150
"warn_for_unclosed_files": lambda value: isinstance(value, bool),
151-
"facetgrid_figsize": _FACETGRID_FIGSIZE_OPTIONS.__contains__,
151+
"facetgrid_figsize": lambda value: value in _FACETGRID_FIGSIZE_OPTIONS or (
152+
isinstance(value, tuple)
153+
and len(value) == 2
154+
and all(isinstance(v, (int, float)) for v in value)
155+
),
152156
}
153157

154158

@@ -227,14 +231,15 @@ class set_options:
227231
chunk_manager : str, default: "dask"
228232
Chunk manager to use for chunked array computations when multiple
229233
options are installed.
230-
facetgrid_figsize : {"computed", "rcparams"}, default: "computed"
234+
facetgrid_figsize : {"computed", "rcparams"} or tuple of float, default: "computed"
231235
How :class:`~xarray.plot.FacetGrid` determines figure size when
232236
``figsize`` is not explicitly passed:
233237
234238
* ``"computed"`` : figure size is derived from ``size`` and ``aspect``
235239
parameters (current default behavior).
236240
* ``"rcparams"`` : use ``matplotlib.rcParams['figure.figsize']`` as the
237241
total figure size.
242+
* ``(width, height)`` : use a fixed figure size (in inches).
238243
cmap_divergent : str or matplotlib.colors.Colormap, default: "RdBu_r"
239244
Colormap to use for divergent data plots. If string, must be
240245
matplotlib built-in colormap. Can also be a Colormap object
@@ -371,7 +376,10 @@ def __init__(self, **kwargs):
371376
elif k == "display_style":
372377
expected = f"Expected one of {_DISPLAY_OPTIONS!r}"
373378
elif k == "facetgrid_figsize":
374-
expected = f"Expected one of {_FACETGRID_FIGSIZE_OPTIONS!r}"
379+
expected = (
380+
f"Expected one of {_FACETGRID_FIGSIZE_OPTIONS!r}"
381+
" or a (width, height) tuple of floats"
382+
)
375383
elif k == "netcdf_engine_order":
376384
expected = f"Expected a subset of {sorted(_NETCDF_ENGINES)}"
377385
else:

xarray/plot/facetgrid.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,19 @@ def __init__(
195195
else:
196196
raise ValueError("Pass a coordinate name as an argument for row or col")
197197

198+
# Resolve figsize from global option before computing grid shape,
199+
# so that downstream heuristics (e.g. col_wrap="auto") can use it.
200+
if figsize is None:
201+
from xarray.core.options import OPTIONS
202+
203+
facetgrid_figsize = OPTIONS["facetgrid_figsize"]
204+
if isinstance(facetgrid_figsize, tuple):
205+
figsize = facetgrid_figsize
206+
elif facetgrid_figsize == "rcparams":
207+
import matplotlib as mpl
208+
209+
figsize = tuple(mpl.rcParams["figure.figsize"])
210+
198211
# Compute grid shape
199212
if single_group:
200213
nfacet = len(data[single_group])
@@ -212,17 +225,10 @@ def __init__(
212225
subplot_kws = {} if subplot_kws is None else subplot_kws
213226

214227
if figsize is None:
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)
228+
# Calculate the base figure size with extra horizontal space
229+
# for a colorbar
230+
cbar_space = 1
231+
figsize = (ncol * size * aspect + cbar_space, nrow * size)
226232

227233
fig, axs = plt.subplots(
228234
nrow,

xarray/tests/test_options.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,16 @@ def test_netcdf_engine_order() -> None:
8787
def test_facetgrid_figsize() -> None:
8888
with pytest.raises(ValueError):
8989
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))
9094
with xarray.set_options(facetgrid_figsize="rcparams"):
9195
assert OPTIONS["facetgrid_figsize"] == "rcparams"
9296
with xarray.set_options(facetgrid_figsize="computed"):
9397
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)
94100

95101

96102
def test_display_style() -> None:

xarray/tests/test_plot.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3597,6 +3597,13 @@ def test_facetgrid_figsize_rcparams() -> None:
35973597
actual_figsize = g.fig.get_size_inches()
35983598
np.testing.assert_allclose(actual_figsize, custom_figsize)
35993599

3600+
with figure_context():
3601+
# Tuple mode: fixed figsize via set_options
3602+
with xr.set_options(facetgrid_figsize=(14.0, 5.0)):
3603+
g = xplt.FacetGrid(da, col="z")
3604+
actual_figsize = g.fig.get_size_inches()
3605+
np.testing.assert_allclose(actual_figsize, (14.0, 5.0))
3606+
36003607
with figure_context():
36013608
# Explicit figsize should override the option
36023609
with xr.set_options(facetgrid_figsize="rcparams"):

0 commit comments

Comments
 (0)