Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/user-guide/options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Xarray offers a small number of configuration options through :py:func:`set_opti
- ``display_style``

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


Expand Down
5 changes: 5 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ Antonio Valentino, Chris Barker, Christine P. Chai, Deepak Cherian, Ewan Short,
New Features
~~~~~~~~~~~~

- Added ``facetgrid_figsize`` option to :py:func:`~xarray.set_options` allowing
:py:class:`~xarray.plot.FacetGrid` to use ``matplotlib.rcParams['figure.figsize']``
or a fixed ``(width, height)`` tuple instead of computing figure size from
``size`` and ``aspect`` (:issue:`11103`).
By `Kristian Kollsga <https://github.com/kkollsga>`_.
- :py:class:`~xarray.indexes.NDPointIndex` now supports coordinates with fewer
dimensions than coordinate variables, enabling indexing of scattered points
and trajectories where multiple coordinates (e.g., ``x``, ``y``) share a
Expand Down
26 changes: 26 additions & 0 deletions xarray/core/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"use_numbagg",
"use_opt_einsum",
"use_flox",
"facetgrid_figsize",
]

class T_Options(TypedDict):
Expand Down Expand Up @@ -73,6 +74,7 @@ class T_Options(TypedDict):
use_new_combine_kwarg_defaults: bool
use_numbagg: bool
use_opt_einsum: bool
facetgrid_figsize: Literal["computed", "rcparams"] | tuple[float, float]


OPTIONS: T_Options = {
Expand Down Expand Up @@ -106,8 +108,10 @@ class T_Options(TypedDict):
"use_new_combine_kwarg_defaults": False,
"use_numbagg": True,
"use_opt_einsum": True,
"facetgrid_figsize": "computed",
}

_FACETGRID_FIGSIZE_OPTIONS = frozenset(["computed", "rcparams"])
_JOIN_OPTIONS = frozenset(["inner", "outer", "left", "right", "exact"])
_DISPLAY_OPTIONS = frozenset(["text", "html"])
_NETCDF_ENGINES = frozenset(["netcdf4", "h5netcdf", "scipy"])
Expand Down Expand Up @@ -144,6 +148,14 @@ def _positive_integer(value: Any) -> bool:
"use_opt_einsum": lambda value: isinstance(value, bool),
"use_flox": lambda value: isinstance(value, bool),
"warn_for_unclosed_files": lambda value: isinstance(value, bool),
"facetgrid_figsize": lambda value: (
value in _FACETGRID_FIGSIZE_OPTIONS
or (
isinstance(value, tuple)
and len(value) == 2
and all(isinstance(v, (int, float)) for v in value)
)
),
}


Expand Down Expand Up @@ -222,6 +234,15 @@ class set_options:
chunk_manager : str, default: "dask"
Chunk manager to use for chunked array computations when multiple
options are installed.
facetgrid_figsize : {"computed", "rcparams"} or tuple of float, default: "computed"
How :class:`~xarray.plot.FacetGrid` determines figure size when
``figsize`` is not explicitly passed:

* ``"computed"`` : figure size is derived from ``size`` and ``aspect``
parameters (current default behavior).
* ``"rcparams"`` : use ``matplotlib.rcParams['figure.figsize']`` as the
total figure size.
* ``(width, height)`` : use a fixed figure size (in inches).
cmap_divergent : str or matplotlib.colors.Colormap, default: "RdBu_r"
Colormap to use for divergent data plots. If string, must be
matplotlib built-in colormap. Can also be a Colormap object
Expand Down Expand Up @@ -357,6 +378,11 @@ def __init__(self, **kwargs):
expected = f"Expected one of {_JOIN_OPTIONS!r}"
elif k == "display_style":
expected = f"Expected one of {_DISPLAY_OPTIONS!r}"
elif k == "facetgrid_figsize":
expected = (
f"Expected one of {_FACETGRID_FIGSIZE_OPTIONS!r}"
" or a (width, height) tuple of floats"
)
elif k == "netcdf_engine_order":
expected = f"Expected a subset of {sorted(_NETCDF_ENGINES)}"
else:
Expand Down
17 changes: 15 additions & 2 deletions xarray/plot/facetgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,19 @@ def __init__(
else:
raise ValueError("Pass a coordinate name as an argument for row or col")

# Resolve figsize from global option before computing grid shape,
# so that downstream heuristics (e.g. col_wrap="auto") can use it.
if figsize is None:
from xarray.core.options import OPTIONS

facetgrid_figsize = OPTIONS["facetgrid_figsize"]
if isinstance(facetgrid_figsize, tuple):
figsize = facetgrid_figsize
elif facetgrid_figsize == "rcparams":
import matplotlib as mpl

figsize = tuple(mpl.rcParams["figure.figsize"])

# Compute grid shape
if single_group:
nfacet = len(data[single_group])
Expand All @@ -212,8 +225,8 @@ def __init__(
subplot_kws = {} if subplot_kws is None else subplot_kws

if figsize is None:
# Calculate the base figure size with extra horizontal space for a
# colorbar
# Calculate the base figure size with extra horizontal space
# for a colorbar
cbar_space = 1
figsize = (ncol * size * aspect + cbar_space, nrow * size)

Expand Down
15 changes: 15 additions & 0 deletions xarray/tests/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,21 @@ def test_netcdf_engine_order() -> None:
assert OPTIONS["netcdf_engine_order"] == original


def test_facetgrid_figsize() -> None:
with pytest.raises(ValueError):
xarray.set_options(facetgrid_figsize="invalid")
with pytest.raises(ValueError):
xarray.set_options(facetgrid_figsize=(1.0,))
with pytest.raises(ValueError):
xarray.set_options(facetgrid_figsize=(1.0, 2.0, 3.0))
with xarray.set_options(facetgrid_figsize="rcparams"):
assert OPTIONS["facetgrid_figsize"] == "rcparams"
with xarray.set_options(facetgrid_figsize="computed"):
assert OPTIONS["facetgrid_figsize"] == "computed"
with xarray.set_options(facetgrid_figsize=(12.0, 8.0)):
assert OPTIONS["facetgrid_figsize"] == (12.0, 8.0)


def test_display_style() -> None:
original = "html"
assert OPTIONS["display_style"] == original
Expand Down
44 changes: 44 additions & 0 deletions xarray/tests/test_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -3567,3 +3567,47 @@ def test_temp_dataarray() -> None:
locals_ = dict(x="x", extend="var2")
da = _temp_dataarray(ds, y_, locals_)
assert da.shape == (3,)


@requires_matplotlib
def test_facetgrid_figsize_rcparams() -> None:
"""Test that facetgrid_figsize='rcparams' uses matplotlib rcParams."""
import matplotlib as mpl

da = DataArray(
np.random.randn(10, 15, 3),
dims=["y", "x", "z"],
coords={"z": ["a", "b", "c"]},
)
custom_figsize = (12.0, 8.0)

with figure_context():
# Default behavior: computed from size and aspect
g = xplt.FacetGrid(da, col="z")
default_figsize = g.fig.get_size_inches()
# Default should be (ncol * size * aspect + cbar_space, nrow * size)
# = (3 * 3 * 1 + 1, 1 * 3) = (10, 3)
np.testing.assert_allclose(default_figsize, (10.0, 3.0))

with figure_context():
# rcparams mode: should use mpl.rcParams['figure.figsize']
with mpl.rc_context({"figure.figsize": custom_figsize}):
with xr.set_options(facetgrid_figsize="rcparams"):
g = xplt.FacetGrid(da, col="z")
actual_figsize = g.fig.get_size_inches()
np.testing.assert_allclose(actual_figsize, custom_figsize)

with figure_context():
# Tuple mode: fixed figsize via set_options
with xr.set_options(facetgrid_figsize=(14.0, 5.0)):
g = xplt.FacetGrid(da, col="z")
actual_figsize = g.fig.get_size_inches()
np.testing.assert_allclose(actual_figsize, (14.0, 5.0))

with figure_context():
# Explicit figsize should override the option
with xr.set_options(facetgrid_figsize="rcparams"):
explicit_size = (6.0, 4.0)
g = xplt.FacetGrid(da, col="z", figsize=explicit_size)
actual_figsize = g.fig.get_size_inches()
np.testing.assert_allclose(actual_figsize, explicit_size)
Loading