一个 seaborn 热图中的两个范围/色标

Two ranges/ color-scales within one seaborn heatmap

我做了很多研究,但还没有找到令人满意的解决方案。

我正在尝试使用 seaborn 构建热图。 由于我的数据集在较低范围 (0-20) 内有点不稳定,但在达到 7000 时仅对所有数据使用一个色标,无法进行良好的图形解释。这就是为什么我考虑使用两个尺度,两个不同的色谱。

我想将这两个热图合并为一个:

到目前为止有效的是我在我的绘图中显示了两个轴(刻度),但是在绘制数据时只考虑了最后一个活动轴。不考虑上限。

2

我也尝试过根据光谱拆分数据集,但这没有用。 这是我的代码:

df = pd.DataFrame(merged, classes)


vmax = np.amax(merged)

ax1 = sns.heatmap(df, vmin = 25, vmax = vmax, cmap = "crest", )
ax2 = sns.heatmap(df, vmin = 0, vmax = 25, cmap = "flare", )

plt.xlabel("Time")
plt.ylabel("Method")

plt.show()

你需要预先创建两个Axes来绘制,然后在调用sns.heatmap时使用ax参数来告诉seaborn哪个Axes应该有哪个颜色图。

示例(使用模拟数据):

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

values = np.random.uniform(0, 7000, size=(50, 50))
vmax = np.amax(values)

fig, (ax1, ax2) = plt.subplots(ncols=2)

sns.heatmap(values, ax=ax1, vmin=25, vmax=vmax, cmap="crest")
sns.heatmap(values, ax=ax2, vmin=0, vmax=25, cmap="flare")

for ax in (ax1, ax2):
    ax.set_xlabel("Time")
    ax.set_ylabel("Method")

plt.show()

结果图:

如果要将两个部分一起绘制,可以更新颜色图,将“上面”和“下面”的颜色设置为不可见 ("none")。 “over”颜色用于大于 vmax 的值。 “下方”颜色用于 vmin.

以下的值

您可以选择为第一个颜色条设置负填充以使两个颜色条靠得更近。

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy.ndimage.filters import gaussian_filter

# first create some test data
data = gaussian_filter(np.random.rand(50, 50), sigma=5)
data -= data.min()
data *= 2 / data.max()
data = np.where(data < 1, data*25, (data ** 3) * 25)

# adapt the colormaps such that the "under" or "over" color is "none"
cmap1 = plt.get_cmap('crest').copy()
cmap1.set_under('none')
cmap2 = plt.get_cmap('flare').copy()
cmap2.set_over('none')

ax1 = sns.heatmap(data, vmin=25, cmap=cmap1, cbar_kws={'pad': -0.02})
sns.heatmap(data, vmin=0, vmax=25, cmap=cmap2, ax=ax1)

plt.show()

您可以创建一个使用其他颜色图的颜色图class

import seaborn as sns
import matplotlib as mpl
import matplotlib.pyplot as plt

class SplitCMap(mpl.colors.Colormap):
  def __init__(self, name, vmin, vsplit, vmax, N=256):
    super().__init__(name, N)
    self.lowcmap = mpl.cm.get_cmap('flare')
    self.highcmap = mpl.cm.get_cmap('crest')
    self.split_level = (vsplit-vmin) / (vmax-vmin)
    self.scale_low = 1.0 / self.split_level
    self.scale_high = 1.0 / (1.0 - self.split_level)
  def mapcolor(self, v, **kwds):
    if v < self.split_level:
      return self.lowcmap(v * self.scale_low, **kwds)
    return self.highcmap((v-self.split_level)*self.scale_high, **kwds)
  def __call__(self, *args, **kwds):
    if isinstance(args[0], (int, float)):
      self.mapcolor(args[0], **kwds)
    return [self.mapcolor(v, **kwds) for v in args[0] ]

df = pd.DataFrame(merged, classes)

vmax = np.amax(merged)

cmap = SplitCMap('split', 0, 25, vmax)
ax = sns.heatmap(df, cmap=cmap)

plt.xlabel("Time")
plt.ylabel("Method")

plt.show()