调整顶部和右侧联合边缘图的轴大小以匹配中心图与 matplotlib

Resize axes of top and right joint marginal plots to match central plot with matplotlib

如何使用 matplotlib 调整边缘图的轴大小以匹配非正方形中心图的大小?

在图像中,您会看到顶部边缘图太宽,即使它共享 x 轴标签。

上下文:我正在尝试创建一个带有子图条形图的联合图 like in Seaborn, but with a non-square heatmap at center and bar graphs as the marginal plots. JointGrids isn't designed to work with heatmaps (which is okay, on to matplotlib!). Merging a matplotlib heatmap 让我接近,但我发现一个条形图的轴比中心热图大,即使我共享轴也是如此。

最小工作示例:

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

### Function from matplotlib ################################################
def heatmap(data, row_labels, col_labels, ax=None,
            cbar_kw={}, cbarlabel="", **kwargs):
    """
    Create a heatmap from a numpy array and two lists of labels.

    Parameters
    ----------
    data
        A 2D numpy array of shape (M, N).
    row_labels
        A list or array of length M with the labels for the rows.
    col_labels
        A list or array of length N with the labels for the columns.
    ax
        A `matplotlib.axes.Axes` instance to which the heatmap is plotted.  If
        not provided, use current axes or create a new one.  Optional.
    cbar_kw
        A dictionary with arguments to `matplotlib.Figure.colorbar`.  Optional.
    cbarlabel
        The label for the colorbar.  Optional.
    **kwargs
        All other arguments are forwarded to `imshow`.
    """

    if not ax:
        ax = plt.gca()

    # Plot the heatmap
    im = ax.imshow(data, **kwargs)

    # Create colorbar
    cbar = ax.figure.colorbar(im, ax=ax, **cbar_kw)
    cbar.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom")

    # Show all ticks and label them with the respective list entries.
    ax.set_xticks(np.arange(data.shape[1]), labels=col_labels)
    ax.set_yticks(np.arange(data.shape[0]), labels=row_labels)

    # Rotate the tick labels and set their alignment.
    plt.setp(ax.get_xticklabels(), rotation=-30, ha="right",
             rotation_mode="anchor")

    # Turn spines off and create white grid.
    ax.spines[:].set_visible(False)

    ax.set_xticks(np.arange(data.shape[1]+1)-.5, minor=True)
    ax.set_yticks(np.arange(data.shape[0]+1)-.5, minor=True)
    ax.grid(which="minor", color="w", linestyle='-', linewidth=3)
    ax.tick_params(which="minor", bottom=False, left=False)

    return im, cbar

### Now this specific case ################################################

# Make dataframe
dd = {f'Col_{col}': [col*x**2 for x in range(25)] for col in range(16)}
index = [f'Row_{x}' for x in range(25)]
df = pd.DataFrame(dd, index=index)

# Make means by axis
ax0_means = df.mean(axis=0)
ax1_means = df.mean(axis=1)

# Build figure and axes
fig, axs = plt.subplots(2, 2, sharex="col", sharey="row", figsize=(16,16),
    gridspec_kw=dict(height_ratios=[1, 3],width_ratios=[3, 1]))
axs[0, 1].set_visible(False)
axs[0, 0].set_box_aspect(1/3)
axs[1, 1].set_box_aspect(3/1)

# Plot data
im, cbar = heatmap(df, df.index, df.columns, ax=axs[1,0])
plt.setp(axs[1,0].get_xticklabels(), rotation=45, ha="right",
     rotation_mode="anchor")

# Rotate the tick labels and set their alignment.
axs[1, 1].barh(y=ax1_means.index, width=ax1_means.values)
axs[0, 0].bar(x=ax0_means.index, height=ax0_means.values)
plt.show()

由于热图获得默认的“相等”纵横比,并且由于颜色条而缩小,因此一个想法是在创建所有内容后手动调整直方图的大小。

from matplotlib.transforms import Bbox

# code added at the end, just before plt.show()
(x0m, y0m), (x1m, y1m) = axs[1, 0].get_position().get_points()  # main heatmap
(x0h, y0h), (x1h, y1h) = axs[0, 0].get_position().get_points()  # horizontal histogram
axs[0, 0].set_position(Bbox([[x0m, y0h], [x1m, y1h]]))
(x0v, y0v), (x1v, y1v) = axs[1, 1].get_position().get_points()  # vertical histogram
axs[1, 1].set_position(Bbox([[x0v, y0m], [x1v, y1m]]))

plt.show()

(下面的例子在gridspec_kw=中使用了hspace=0.01, wspace=0.02