xarray 图的图例句柄

Legend handle to an xarray plot

我无法修改使用 xarray 绘图功能制作的数据集的图例。 下面的代码returnsNo handles with labels found to put in legend.

import xarray as xr
import matplotlib.pyplot as plt

air = xr.tutorial.open_dataset("air_temperature").air

air.isel(lon=10, lat=[19, 21, 22]).plot.line(x="time", add_legend=True)
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))

您可以使用 seaborn 的 sns.move_legend(),然后是 plt.tight_layout()sns.move_legend() 是 seaborn 0.11.2 中的新功能。

import xarray as xr
import matplotlib.pyplot as plt
import seaborn as sns

air = xr.tutorial.open_dataset("air_temperature").air

air.isel(lon=10, lat=[19, 21, 22]).plot.line(x="time", add_legend=True)
sns.move_legend(plt.gca(), loc='center left', bbox_to_anchor=(1, 0.5))
plt.tight_layout()
plt.show()

PS:如果你不想导入seaborn,你可以从its source复制函数。您需要删除对 sns.axisgrid.Gridimport matplotlib as mpl; import inspect:

的引用
import matplotlib.pyplot as plt
import matplotlib as mpl
import inspect
import xarray as xr

def move_legend(obj, loc, **kwargs):
    """
    Recreate a plot's legend at a new location.
    Extracted from seaborn/utils.py
    """
    if isinstance(obj, mpl.axes.Axes):
        old_legend = obj.legend_
        legend_func = obj.legend
    elif isinstance(obj, mpl.figure.Figure):
        if obj.legends:
            old_legend = obj.legends[-1]
        else:
            old_legend = None
        legend_func = obj.legend
    else:
        err = "`obj` must be a matplotlib Axes or Figure instance."
        raise TypeError(err)

    if old_legend is None:
        err = f"{obj} has no legend attached."
        raise ValueError(err)

    # Extract the components of the legend we need to reuse
    handles = old_legend.legendHandles
    labels = [t.get_text() for t in old_legend.get_texts()]

    # Extract legend properties that can be passed to the recreation method
    # (Vexingly, these don't all round-trip)
    legend_kws = inspect.signature(mpl.legend.Legend).parameters
    props = {k: v for k, v in old_legend.properties().items() if k in legend_kws}

    # Delegate default bbox_to_anchor rules to matplotlib
    props.pop("bbox_to_anchor")

    # Try to propagate the existing title and font properties; respect new ones too
    title = props.pop("title")
    if "title" in kwargs:
        title.set_text(kwargs.pop("title"))
    title_kwargs = {k: v for k, v in kwargs.items() if k.startswith("title_")}
    for key, val in title_kwargs.items():
        title.set(**{key[6:]: val})
        kwargs.pop(key)

    # Try to respect the frame visibility
    kwargs.setdefault("frameon", old_legend.legendPatch.get_visible())

    # Remove the old legend and create the new one
    props.update(kwargs)
    old_legend.remove()
    new_legend = legend_func(handles, labels, loc=loc, **props)
    new_legend.set_title(title.get_text(), title.get_fontproperties())


air = xr.tutorial.open_dataset("air_temperature").air

air.isel(lon=10, lat=[19, 21, 22]).plot.line(x="time", add_legend=True)
move_legend(plt.gca(), loc='center left', bbox_to_anchor=(1, 0.5))
plt.tight_layout()
plt.show()