共享轴并删除 matplotlib 子图中未使用的轴

Posted

技术标签:

【中文标题】共享轴并删除 matplotlib 子图中未使用的轴【英文标题】:Share axis and remove unused in matplotlib subplots 【发布时间】:2017-04-12 07:45:35 【问题描述】:

我想在网格中绘制一系列 seaborn 热图。我知道子图的数量(可以是奇数或偶数)。 热图将按“星期几”(y 轴)和“一天中的小时”(x 轴)显示平均“占用率”,例如它们都共享相同的 x / y 域。

这是我当前的代码:

df2 = df[['name','openLots','occupationRatio','DoW','Hour']]
fig, axs = plt.subplots(figsize=(24,24), nrows=7, ncols=6)
axs = axs.flatten()
locations = df2['name'].sort_values().unique()


def occupation_heatmap (name, ax):
    dfn = df2[df2['name'] == name]
    dfn = dfn.groupby(['DoW', 'Hour']).mean()['occupationRatio'].unstack()
    dfn = dfn.reindex(['Mon', 'Tue', 'Wed','Thu','Fri','Sat','Sun'])
    sns.heatmap(data=dfn, cmap="coolwarm", vmin=0, vmax=1.0, ax= ax)
    ax.set_title(name)


i = 0
for n in locations: 
    occupation_heatmap (n, axs[i])
    i = i+1

plt.tight_layout()

它看起来 几乎 像我想要的(最后几行): 但是我想要:

每行只有一次 y 轴标签 (DoW)(最左边的图) 仅在每行最右边的图上显示颜色图图例(或完全不使用它,颜色很容易解释) 删除最后一行中的“空地块”,因为总数为奇数

非常感谢您的任何提示

【问题讨论】:

只是为了帮助改善您对事物运作方式的心理模型,这些不是“seaborn subplots”,它们是您碰巧使用 seaborn 函数绘制数据的 matplotlib 子图。 【参考方案1】: 每行只有一次 y 轴标签 (DoW)(最左边的图) 这可以使用sharey = True 作为plt.subplots 的参数来完成。 仅在每行最右边的图上显示颜色图图例(或完全省略,颜色非常不言自明)seaborn.heatmap 使用cbar = False 参数以不显示颜色条。这可以作为绘图函数的输入,具体取决于子图的实际数量。

删除最后一行中的“空地块”,因为总数为奇数 在创建绘图的循环之后,您可以添加另一个循环来删除未使用的轴。

for j in range(len(locations), ncols*nrows):
    axs[j].axis("off")

这是一个完整的示例(我借用了 cod 从@Robbie 生成数据帧):

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

days = ['Mon','Tue','Wed','Thurs','Fri','Sat','Sun']
names = ["Parkhaus :02".format(i+1) for i in range(22)]

nItems = 1000

df = pd.DataFrame()
df['name'] = [names[i] for i in np.random.randint(0,len(names),nItems)]
df['openLots'] = np.random.randint(0,100,nItems)
df['occupationRatio'] = np.random.rand(nItems)
df['DoW'] = [days[i] for i in np.random.randint(0,7,nItems)]
df['Hour'] = np.random.randint(0,12,nItems)

df2 = df[['name','openLots','occupationRatio','DoW','Hour']]
nrows = 4; ncols=6
fig, axs = plt.subplots(nrows=nrows, ncols=ncols, figsize=(15,9), sharey=True)
axs = axs.flatten()
locations = df2['name'].sort_values().unique()


def occupation_heatmap (name, ax, cbar=False, ylabel=False):
    dfn = df2[df2['name'] == name]
    dfn = dfn.groupby(['DoW', 'Hour']).mean()['occupationRatio'].unstack()
    dfn = dfn.reindex(['Mon', 'Tue', 'Wed','Thu','Fri','Sat','Sun'])
    sns.heatmap(data=dfn, cmap="coolwarm", vmin=0, vmax=1.0, ax=ax, cbar=cbar)
    ax.set_title(name)
    plt.setp(ax.get_yticklabels(), rotation=0)
    if not ylabel: ax.set_ylabel("")


for i, n in enumerate(locations): 
    occupation_heatmap (n, axs[i], cbar=i%ncols==ncols-1, ylabel=i%ncols==0)
for j in range(len(locations), ncols*nrows):
    axs[j].axis("off")

plt.tight_layout()
plt.show()

【讨论】:

【参考方案2】:

您可以更加灵活,只需为每个存在的名称创建一个轴,如下所示:

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
import string

days = ['Mon','Tue','Wed','Thurs','Fri','Sat','Sun']
names = [string.lowercase[i] for i in range(22)]

nItems = 1000

df = pd.DataFrame()
df['name'] = [names[i] for i in np.random.randint(0,len(names),nItems)]
df['openLots'] = np.random.randint(0,100,nItems)
df['occupationRatio'] = np.random.randint(0,100,nItems)
df['DoW'] = [days[i] for i in np.random.randint(0,7,nItems)]
df['Hour'] = np.random.randint(0,12,nItems)




fig = plt.figure(figsize=(12,12))
for index, name in enumerate(names):
    ax = fig.add_subplot(4,6,index+1)
    dfn = df.loc[df.name==name]
    dfn = dfn.groupby(['DoW','Hour']).mean()['occupationRatio'].unstack()
    dfn = dfn.reindex(days)

    # Now we can operate on each plot axis individually
    if index%6!=5: #i.e.
        # Don't draw a colorbar
        sns.heatmap(data = dfn, cmap='coolwarm', ax=ax, cbar=False)
    else:
        sns.heatmap(data = dfn, cmap='coolwarm', ax=ax)

    if index%6!=0:
        # Remove the y-axis label
        ax.set_ylabel('')
        ax.set_yticks(())

    ax.set_title(name)

fig.tight_layout()
fig.show()

结果: 您还可以使用 x 轴(例如,删除除底行之外的标签和刻度)。

【讨论】:

谢谢,这对我来说效果很好...除了我有一些完整的 NaN 数据切片,因此必须将 sns.heatmap(...) 包装到 try: ... except ValueError: pass

以上是关于共享轴并删除 matplotlib 子图中未使用的轴的主要内容,如果未能解决你的问题,请参考以下文章

删除 matplotlib 子图中的多余图

如何在 matplotlib 中将列指定为轴并绘制条形图?

Matplotlib.savefig 忽略轴并在图像周围绘制黑色边框

如何使用 matplotlib 调整子图中的图形大小

matplotlib - 子图中的 twinx 轴没有 xlabel 和 xticks

Matplotlib - 在错误的子图中绘制的变量[重复]