seaborn 热图图中的离散图例

Posted

技术标签:

【中文标题】seaborn 热图图中的离散图例【英文标题】:Discrete legend in seaborn heatmap plot 【发布时间】:2016-12-14 15:19:53 【问题描述】:

我正在使用此处提供的数据使用 seaborn 和 pandas 构建此热图。

代码:

    import pandas
    import seaborn.apionly as sns

    # Read in csv file
    df_trans = pandas.read_csv('LUH2_trans_matrix.csv')

    sns.set(font_scale=0.8)
    cmap = sns.cubehelix_palette(start=2.8, rot=.1, light=0.9, as_cmap=True)
    cmap.set_under('gray')  # 0 values in activity matrix are shown in gray (inactive transitions)
    df_trans = df_trans.set_index(['Unnamed: 0'])
    ax = sns.heatmap(df_trans, cmap=cmap, linewidths=.5, linecolor='lightgray')

    # X - Y axis labels
    ax.set_ylabel('FROM')
    ax.set_xlabel('TO')

    # Rotate tick labels
    locs, labels = plt.xticks()
    plt.setp(labels, rotation=0)
    locs, labels = plt.yticks()
    plt.setp(labels, rotation=0)

    # revert matplotlib params
    sns.reset_orig()

从 csv 文件中可以看出,它包含 3 个离散值:0、-1 和 1。我想要一个离散图例而不是颜色条。将 0 标记为 A,将 -1 标记为 B,将 1 标记为 C。我该怎么做?

【问题讨论】:

请以文本形式包含数据样本,所以人们不太可能将未知文件下载到他们的机器上。 感谢@Merlin,也以文本形式添加了数据 试试看这个:https://github.com/mwaskom/seaborn/issues/858 【参考方案1】:

@Fabio Lamanna 提供的链接是一个很好的开始。

从那里,您仍然希望在正确的位置设置颜色条标签并使用与您的数据相对应的刻度标签。

假设您的数据中具有等间距的级别,这会产生一个很好的离散颜色条:

基本上,这归结为关闭 seaborn colorbar 并自己用离散化的 colorbar 替换它。

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

def cmap_discretize(cmap, N):
    """Return a discrete colormap from the continuous colormap cmap.

        cmap: colormap instance, eg. cm.jet. 
        N: number of colors.

    Example
        x = resize(arange(100), (5,100))
        djet = cmap_discretize(cm.jet, 5)
        imshow(x, cmap=djet)
    """

    if type(cmap) == str:
        cmap = plt.get_cmap(cmap)
    colors_i = np.concatenate((np.linspace(0, 1., N), (0.,0.,0.,0.)))
    colors_rgba = cmap(colors_i)
    indices = np.linspace(0, 1., N+1)
    cdict = 
    for ki,key in enumerate(('red','green','blue')):
        cdict[key] = [ (indices[i], colors_rgba[i-1,ki], colors_rgba[i,ki]) for i in xrange(N+1) ]
    # Return colormap object.
    return matplotlib.colors.LinearSegmentedColormap(cmap.name + "_%d"%N, cdict, 1024)

def colorbar_index(ncolors, cmap, data):

    """Put the colorbar labels in the correct positions
        using uique levels of data as tickLabels
    """

    cmap = cmap_discretize(cmap, ncolors)
    mappable = matplotlib.cm.ScalarMappable(cmap=cmap)
    mappable.set_array([])
    mappable.set_clim(-0.5, ncolors+0.5)
    colorbar = plt.colorbar(mappable)
    colorbar.set_ticks(np.linspace(0, ncolors, ncolors))
    colorbar.set_ticklabels(np.unique(data))


# Read in csv file
df_trans = pandas.read_csv('d:/LUH2_trans_matrix.csv')

sns.set(font_scale=0.8)
cmap = sns.cubehelix_palette(n_colors=3,start=2.8, rot=.1, light=0.9, as_cmap=True)
cmap.set_under('gray')  # 0 values in activity matrix are shown in gray (inactive transitions)
df_trans = df_trans.set_index(['Unnamed: 0'])

N = df_trans.max().max() - df_trans.min().min() + 1

f, ax = plt.subplots()
ax = sns.heatmap(df_trans, cmap=cmap, linewidths=.5, linecolor='lightgray',cbar=False)
colorbar_index(ncolors=N, cmap=cmap,data=df_trans)    

# X - Y axis labels
ax.set_ylabel('FROM')
ax.set_xlabel('TO')

# Rotate tick labels
locs, labels = plt.xticks()
plt.setp(labels, rotation=0)
locs, labels = plt.yticks()
plt.setp(labels, rotation=0)

# revert matplotlib params
sns.reset_orig()

从here和here回收和改编的点点滴滴

【讨论】:

【参考方案2】:

我发现如果您使用 ListedColormap,在 seaborn 中创建离散化颜色条会更容易。无需定义自己的函数,只需添加几行即可基本自定义轴。

import pandas
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.colors import ListedColormap


# Read in csv file
df_trans = pandas.read_csv('LUH2_trans_matrix.csv')

sns.set(font_scale=0.8)
# cmap is now a list of colors
cmap = sns.cubehelix_palette(start=2.8, rot=.1, light=0.9, n_colors=3)
df_trans = df_trans.set_index(['Unnamed: 0'])

# Create two appropriately sized subplots
grid_kws = 'width_ratios': (0.9, 0.03), 'wspace': 0.18
fig, (ax, cbar_ax) = plt.subplots(1, 2, gridspec_kw=grid_kws)

ax = sns.heatmap(df_trans, ax=ax, cbar_ax=cbar_ax, cmap=ListedColormap(cmap),
                 linewidths=.5, linecolor='lightgray',
                 cbar_kws='orientation': 'vertical')

# Customize tick marks and positions
cbar_ax.set_yticklabels(['B', 'A', 'C'])
cbar_ax.yaxis.set_ticks([ 0.16666667, 0.5, 0.83333333])


# X - Y axis labels
ax.set_ylabel('FROM')
ax.set_xlabel('TO')

# Rotate tick labels
locs, labels = plt.xticks()
plt.setp(labels, rotation=0)
locs, labels = plt.yticks()
plt.setp(labels, rotation=0)

【讨论】:

【参考方案3】:

嗯,肯定有不止一种方法可以做到这一点。在这种情况下,只需要三种颜色,我将通过创建LinearSegmentedColormap 自己选择颜色,而不是使用cubehelix_palette 生成它们。如果有足够的颜色可以保证使用cubehelix_palette,我将使用cbar_kws 参数的boundaries 选项定义颜色图上的段。无论哪种方式,都可以使用set_ticksset_ticklabels 手动指定刻度。

以下代码示例演示了 LinearSegmentedColormap 的手动创建,并包含有关在改用 cubehelix_palette 时如何指定边界的 cmets。

import matplotlib.pyplot as plt
import pandas
import seaborn.apionly as sns
from matplotlib.colors import LinearSegmentedColormap

sns.set(font_scale=0.8)
dataFrame = pandas.read_csv('LUH2_trans_matrix.csv').set_index(['Unnamed: 0'])

# For only three colors, it's easier to choose them yourself.
# If you still really want to generate a colormap with cubehelix_palette instead,
# add a cbar_kws="boundaries": linspace(-1, 1, 4) to the heatmap invocation
# to have it generate a discrete colorbar instead of a continous one.
myColors = ((0.8, 0.0, 0.0, 1.0), (0.0, 0.8, 0.0, 1.0), (0.0, 0.0, 0.8, 1.0))
cmap = LinearSegmentedColormap.from_list('Custom', myColors, len(myColors))

ax = sns.heatmap(dataFrame, cmap=cmap, linewidths=.5, linecolor='lightgray')

# Manually specify colorbar labelling after it's been generated
colorbar = ax.collections[0].colorbar
colorbar.set_ticks([-0.667, 0, 0.667])
colorbar.set_ticklabels(['B', 'A', 'C'])

# X - Y axis labels
ax.set_ylabel('FROM')
ax.set_xlabel('TO')

# Only y-axis labels need their rotation set, x-axis labels already have a rotation of 0
_, labels = plt.yticks()
plt.setp(labels, rotation=0)

plt.show()

【讨论】:

【参考方案4】:

这是一个基于其他答案的简单解决方案,它概括超过 3 个类别并使用 dict (vmap) 来定义标签。

import seaborn as sns
import numpy as np

# This just makes some sample 2D data and a corresponding vmap dict with labels for the values in the data
data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
vmap = i: chr(65 + i) for i in range(len(np.ravel(data)))
n = len(vmap)

print(vmap)

cmap = sns.color_palette("deep", n)
ax = sns.heatmap(data, cmap=cmap)

# Get the colorbar object from the Seaborn heatmap
colorbar = ax.collections[0].colorbar
# The list comprehension calculates the positions to place the labels to be evenly distributed across the colorbar
r = colorbar.vmax - colorbar.vmin
colorbar.set_ticks([colorbar.vmin + 0.5 * r / (n) + r * i / (n) for i in range(n)])
colorbar.set_ticklabels(list(vmap.values()))

【讨论】:

以上是关于seaborn 热图图中的离散图例的主要内容,如果未能解决你的问题,请参考以下文章

更改 Seaborn 散点图中的图例位置和标签

如何在 seaborn 散点图中编辑多个图例框?

在 Seaborn histplot 子图中自定义图例

更改 Seaborn 热图中刻度标签的旋转

根据 Seaborn 中的散点图绘制热图

多个重叠图的 Seaborn 图例修改