如何创建具有边际直方图的热图,类似于联合图?

How to create a heatmap with marginal histograms, similar to a jointplot?

我想绘制二维标量数据,我通常会使用 matplotlib.pyplot.imshowsns.heatmap 进行绘制。考虑这个例子:

data = [[10, 20, 30], [50, 50, 100], [80, 60, 10]]
fix, ax = plt.subplots()
ax.imshow(data, cmap=plt.cm.YlGn)

现在我还想在顶部和右侧有一维条形图,显示每列/行中值的总和 - 就像 sns.jointplot 一样。然而,sns.jointplot 似乎只适用于分类数据,生成直方图(使用 kind='hist')、散点图等——如果我想指定单元格的值,我不知道如何使用它直接地。 seaborn 可以实现这样的事情吗?

我图中的 y 轴将是天(一个月内),x 轴将是小时。我的数据如下所示:

字段 Cost Difference 应该构成图中相应字段的阴影。

这是一种方法,首先创建一个虚拟对象 jointplot,然后使用它的轴添加总和的热图和条形图。

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

D = 28
H = 24
df = pd.DataFrame({'day': np.repeat(range(1, D + 1), H),
                   'hour': np.tile(range(H), D),
                   'Cost Dif.': np.random.uniform(10, 1000, D * H)})
# change the random df to have some rows/columns stand out (debugging, checking)
df.loc[df['hour'] == 10, 'Cost Dif.'] = 150
df.loc[df['hour'] == 12, 'Cost Dif.'] = 250
df.loc[df['day'] == 20, 'Cost Dif.'] = 800

g = sns.jointplot(data=df, x='day', y='hour', kind='hist', bins=(D, H))
g.ax_marg_y.cla()
g.ax_marg_x.cla()
sns.heatmap(data=df['Cost Dif.'].to_numpy().reshape(D, H).T, ax=g.ax_joint, cbar=False, cmap='Blues')

g.ax_marg_y.barh(np.arange(0.5, H), df.groupby(['hour'])['Cost Dif.'].sum().to_numpy(), color='navy')
g.ax_marg_x.bar(np.arange(0.5, D), df.groupby(['day'])['Cost Dif.'].sum().to_numpy(), color='navy')

g.ax_joint.set_xticks(np.arange(0.5, D))
g.ax_joint.set_xticklabels(range(1, D + 1), rotation=0)
g.ax_joint.set_yticks(np.arange(0.5, H))
g.ax_joint.set_yticklabels(range(H), rotation=0)

# remove ticks between heatmao and histograms
g.ax_marg_x.tick_params(axis='x', bottom=False, labelbottom=False)
g.ax_marg_y.tick_params(axis='y', left=False, labelleft=False)
# remove ticks showing the heights of the histograms
g.ax_marg_x.tick_params(axis='y', left=False, labelleft=False)
g.ax_marg_y.tick_params(axis='x', bottom=False, labelbottom=False)

g.fig.set_size_inches(20, 8)  # jointplot creates its own figure, the size can only be changed afterwards
# g.fig.subplots_adjust(hspace=0.3) # optionally more space for the tick labels
g.fig.subplots_adjust(hspace=0.05, wspace=0.02)  # less spaced needed when there are no tick labels
plt.show()