调整顶部和右侧联合边缘图的轴大小以匹配中心图与 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
)
如何使用 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
)