【问题标题】:How to add a colorcode to the yticklabels on a seaborn heatmap?如何在 seaborn 热图上的刻度标签上添加颜色代码?
【发布时间】:2019-11-12 02:14:01
【问题描述】:

我有一个基因表达与细胞的矩阵,并希望将它们显示为热图,这本身不是问题。然而,将所有基因显示为 yticklabels 将过于混乱且视觉上没有吸引力。因此,我将每个基因注释为属于特定功能组,并希望将每个功能组表示为一种颜色,并将它们的颜色显示在热图上,与基因出现的顺序相同。澄清一下,我不想按颜色对它们进行分组,我相信你可以使用 seaborn clustermap 做到这一点。

因此,到目前为止,我有一个 pandas 数据框,其中包含基因的多索引及其各自的功能组和细胞。

我在 Stackoverflow 和 Google 上广泛搜索了答案,但没有任何运气。这是我第一次尝试这种类型的东西,所以很遗憾我不知道从哪里开始。

因此,为了简单起见,假设您有以下数据框:

import seaborn as sns
import numpy as np
import pandas as pd

data=pd.DataFrame(np.array([(0,1,2),(4,5,6),(7,8,9)]), columns=['C1','C2','C3'], index=pd.MultiIndex.from_arrays([['Gene1','Gene2','Gene3'],['A','B','A']]))

这将产生以下结果:

           C1  C2  C3
Gene1   A   0   1   2
Gene2   B   4   5   6
Gene3   A   7   8   9

现在,我可以简单地调用sns.heatmap(data)来生成热图。但是,我如何对其进行自定义,以便获得代表 A 和 B 而不是 Gene1、Gene2、Gene3 作为 yticklabels 的颜色?例如,假设 A 是蓝色,B 是绿色,我希望它显示 yticklabels(从顶部到底部)为蓝色、绿色、蓝色。

非常感谢。

【问题讨论】:

  • 我认为我不了解预期的结果。是否要更改文本颜色,使"Gene1" 显示为蓝色?或者您想在开始时添加另一列以相应颜色显示像素?或者可能是完全不同的东西?
  • 不,不是文本颜色。它更接近后者。我基本上想要一个颜色条(可能就是他们所说的)代表与热图并列的每个功能组。这只是我从 Google 获得的一个随机示例,但我想要与此图像中的“类别”侧边栏相同的内容:(s11.postimg.cc/t4ohfiwpv/heatmap3_example.png)

标签: python pandas matplotlib colors seaborn


【解决方案1】:

这是一个可能的解决方案,可以在热图左侧创建一个新轴,它根据第二个 Multiindex 级别的值显示另一个热图。

import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
import pandas as pd

data=pd.DataFrame(np.array([(0,1,2),(4,5,6),(7,8,9)]), 
                  columns=['C1','C2','C3'], 
                  index=pd.MultiIndex.from_arrays([['Gene1','Gene2','Gene3'],['A','B','A']]))


cats = data.index.to_frame().set_index(0)
u, inv = np.unique(cats.values, return_inverse=True)

colors = ["navy", "limegreen", "gold"]
assert(len(u) <= len(colors))

cmap = mcolors.ListedColormap(colors)
norm = mcolors.BoundaryNorm(np.arange(len(u)+1)-.5, len(u))

fig, (sax, hax) = plt.subplots(ncols=2, sharey=True,
                               gridspec_kw=dict(width_ratios=[1, data.shape[1]]))

im = sax.imshow(np.atleast_2d(inv).T, cmap=cmap, norm=norm)
hax.imshow(data.values, cmap="Greys")

sax.set_yticks(np.arange(len(cats)))
sax.set_yticklabels(cats.index)
sax.tick_params(bottom=False, labelbottom=False)

hax.set_xticks(np.arange(len(data.columns)))
hax.set_xticklabels(data.columns)

cbar = fig.colorbar(im, cax = fig.add_axes([.125, .08, .1, .04]), 
                    orientation="horizontal", ticks=np.arange(len(u)))
cbar.set_ticklabels(u)

plt.show()

【讨论】:

  • 乍一看,这似乎正是我所追求的。我将在今天晚些时候详细研究它,并回复您是否对我有用。提前非常感谢!
  • 谢谢,这为我的工作打下了坚实的基础。我只需要将其调整为我的更大规模数据。这也是一个很好的起点,因为它不那么依赖 seaborn,而是使用 matplotlib 从头开始​​。非常感谢。
  • 您能否解释一下为什么您将 BoundaryNorm 设置为 np.arange(len(u)+1)-.5 而不是将其保留为 np.arange(len(u))?我看不出输出方面的差异。我认为它可能不包括端点,因此您将范围从 -0.5 到 5.5 以 1 为增量以确保 (1, 2, 3, 4, 5) 将分别映射到一种颜色,但是后者似乎仍然如此。此外,如果数据集很大,是否需要使用它来减少计算时间?即使我删除 norm kwarg,结果也是一样的。
  • 嗯,是的,只有两个类别,它总是匹配的;但应该是任意数量类别的通用解决方案。
  • 对不起,我没有提到,在我的实际数据中,我有 6 个类别,因此特别具有这些范围。在哪些情况下不匹配?更一般地说,我的问题是 BoundaryNorm 是否从 0 到但不包括 1,然后从 1 到但不包括 2 等等,类似于范围的工作原理。
猜你喜欢
  • 2015-03-06
  • 2017-04-05
  • 1970-01-01
  • 2020-12-02
  • 1970-01-01
  • 2019-08-25
  • 2015-01-18
  • 1970-01-01
相关资源
最近更新 更多