使用 Matplotlib 在带有图例的热图中显示不同大小的圆圈
Posted
技术标签:
【中文标题】使用 Matplotlib 在带有图例的热图中显示不同大小的圆圈【英文标题】:Showing different size circles in heatmap with legend using Matplotlib 【发布时间】:2021-04-14 07:33:24 【问题描述】:我在问一个源于这篇原始帖子Heatmap with circles indicating size of population的问题
我正在尝试使用我的数据框来复制它,但是,我的圈子与情节不一致。其次,我还想创建一个图例,指示相对于圆圈大小的值。
x= 'ID': 0: 'GO:0002474',
1: 'GO:0052548',
2: 'GO:0002483',
3: 'GO:0043062',
4: 'GO:0060333',
'TERM': 0: 'antigen processing and presentation of peptide antigen via MHC class I',
1: 'regulation of endopeptidase activity',
2: 'antigen processing and presentation of endogenous peptide antigen',
3: 'extracellular structure organization',
4: 'interferon-gamma-mediated signaling pathway',
'Count': 0: 11, 1: 17, 2: 5, 3: 15, 4: 6,
'Ratio': 0: 18.64, 1: 14.53, 2: 8.47, 3: 12.82, 4: 10.17,
'pvalue': 0: -15.83, 1: -11.39, 2: -9.67, 3: -9.05, 4: -7.41,
'qvalue': 0: -11.63, 1: -7.49, 2: -6.52, 3: -5.63, 4: -4.55,
'Label': 0: 'NODAL', 1: 'NODAL', 2: 'NODAL', 3: 'SHARED', 4: 'NODAL'
A2780_GOBP= pd.DataFrame(x)
尝试的代码:
ylabels = A2780_GOBP["TERM"]
xlabels = ["GFP","SHARED","NODAL"]
x, y = np.meshgrid(np.arange(len(xlabels)), np.arange(len(ylabels)))
s = A2780_GOBP["Count"].values
c = A2780_GOBP["pvalue"].values
fig, ax = plt.subplots()
R = s/s.max()/2
circles = [plt.Circle((j,i), radius=r) for r, j, i in zip(R.flat, x.flat, y.flat)]
col = PatchCollection(circles, array=c.flatten(), cmap=cmap)
ax.add_collection(col)
ax.set(xticks=np.arange(3), yticks=np.arange(10),
xticklabels=xlabels, yticklabels=ylabels)
ax.set_xticks(np.arange(3+1)-0.5, minor=True)
ax.set_yticks(np.arange(10+1)-0.5, minor=True)
ax.grid(which='minor')
fig.colorbar(col)
plt.show()
任何帮助将不胜感激!
【问题讨论】:
@Mr. T 如何将数据框导入此处? 打印df.head(N).to_dict()
,复制粘贴。更多信息here。但是,这可能确实无关紧要。我以为你完全复制了代码,但罪魁祸首很可能是np.meshgrid(np.arange(len(xlabels)), np.arange(len(ylabels)))
。如果确实只是这个错字,我建议删除这个问题。
它在某种程度上修复了它,但圆圈没有定位在正确的 x 和 y 坐标上。我想问题是“circles = [plt.Circle((j,i), radius=r) for r, j, i in zip(R.flat, x.flat, y.flat)]”,但我不熟悉如何将字符串用作 x 和 y。
好的,这不是微不足道的。我去看看。
@Mr.非常感谢!
【参考方案1】:
问题在于复制的代码填充了所有字段,而您的代码不一定在每个框中都有一个条目。我们必须向上看,每个圆圈都必须绘制在哪里:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
import pandas as pd
x= 'ID': 0: 'GO:0002474',
1: 'GO:0052548',
2: 'GO:0002483',
3: 'GO:0043062',
4: 'GO:0060333',
'TERM': 0: 'antigen processing and presentation of peptide antigen via MHC class I',
1: 'regulation of endopeptidase activity',
2: 'antigen processing and presentation of endogenous peptide antigen',
3: 'extracellular structure organization',
4: 'interferon-gamma-mediated signaling pathway',
'Count': 0: 11, 1: 17, 2: 5, 3: 15, 4: 6,
'Ratio': 0: 18.64, 1: 14.53, 2: 8.47, 3: 12.82, 4: 10.17,
'pvalue': 0: -15.83, 1: -11.39, 2: -9.67, 3: -9.05, 4: -7.41,
'qvalue': 0: -11.63, 1: -7.49, 2: -6.52, 3: -5.63, 4: -4.55,
'Label': 0: 'NODAL', 1: 'GFP', 2: 'NODAL', 3: 'SHARED', 4: 'NODAL'
A2780_GOBP= pd.DataFrame(x)
cmap = "plasma"
#retrieve unique labels
ylabels = A2780_GOBP["TERM"].unique().tolist()
xlabels = A2780_GOBP["Label"].unique().tolist()
xn = len(xlabels)
yn = len(ylabels)
#retrieve size and color information
s = A2780_GOBP["Count"].values
c = A2780_GOBP["pvalue"].values
#preparation of the figure with its grid
fig, ax = plt.subplots(figsize=(10, 5))
ax.set_xlim(-0.5, xn-0.5)
ax.set_ylim(-0.5, yn-0.5)
ax.set(xticks=np.arange(xn), yticks=np.arange(yn),
xticklabels=xlabels, yticklabels=ylabels)
ax.set_xticks(np.arange(xn)-0.5, minor=True)
ax.set_yticks(np.arange(yn)-0.5, minor=True)
ax.grid(which='minor')
#ensure circles are displayed as circles
ax.set_aspect("equal", "box")
#create circles patches and colorbar
R = s/s.max()/2
circles = [plt.Circle((xlabels.index(A2780_GOBP.loc[i, "Label"]), ylabels.index(A2780_GOBP.loc[i, "TERM"])), radius=r) for i, r in enumerate(R)]
col = PatchCollection(circles, array=c, cmap=cmap)
ax.add_collection(col)
fig.colorbar(col)
plt.show()
样本输出:
代码不会检查原始数据库的完整性,即每个标签-词对确实只出现一次。
【讨论】:
要创建第二个圆圈大小的图例,我可以简单地使用 plt.legend() 吗? 你是个科学家。你没有尝试测试它是否有效吗? (提示:这不是因为我们不提供任何标签。)This 和 this 向您展示了如何从头开始创建图例的原则,但这可能必须适应您当前的代码。 我意识到这个问题是多么幼稚。谢谢你的链接! 相反。回想起来,大多数问题看起来都很幼稚。关键是从答案中学习。祝你好运实现传奇。如果它不起作用 - 再问一个问题。 恐怕他们不是。 Marker sizes and patch radius will most certainly be differently interpreted(他们是:我用较小的样本试了一下,但尺寸根本不匹配)。您应该插入正确大小的圆形补丁作为图例句柄。不难。对不起,如果是我让你走错了路。【参考方案2】:@Mr. 的改编答案。 T 包括图例生成器
from matplotlib.legend_handler import HandlerPatch
import matplotlib.patches as mpatches
ylabels = A2780_GOBP["TERM"].unique().tolist()
xlabels = A2780_GOBP["Label"].unique().tolist()
xn = len(xlabels)
yn = len(ylabels)
s = A2780_GOBP["Count"].values
c = A2780_GOBP["pvalue"].values
fig, ax = plt.subplots(figsize=(20,10))
ax.set_xlim(-0.5, xn-0.5)
ax.set_ylim(-0.5, yn-0.5)
ax.set(xticks=np.arange(xn), yticks=np.arange(yn), yticklabels=ylabels)
ax.set_xticklabels(xlabels, rotation='vertical')
ax.set_xticks(np.arange(xn)-0.5, minor=True)
ax.set_yticks(np.arange(yn)-0.5, minor=True)
ax.grid(which='minor')
ax.set_aspect("equal", "box")
R = s/s.max()/2
circles = [plt.Circle((xlabels.index(A2780_GOBP.loc[i, "Label"]), ylabels.index(A2780_GOBP.loc[i, "TERM"])), radius=r) for i, r in enumerate(R)]
col = PatchCollection(circles, array=c, cmap=cmap)
sc=ax.add_collection(col)
cbar=fig.colorbar(col).set_label('$-log_10(p-value)$', rotation=270, size=16,labelpad=15)
smax=s.max()
smin=s.min()
smid=(smax+smin)/2
texts = ["3","10","17"]
class HandlerEllipse(HandlerPatch):
def create_artists(self, legend, orig_handle,
xdescent, ydescent, width, height, fontsize, trans):
center = 0.5 * width - 0.5 * xdescent, 0.5 * height - 0.5 * ydescent
p = mpatches.Ellipse(xy=center, width=orig_handle.width,
height=orig_handle.height)
self.update_prop(p, orig_handle, legend)
p.set_transform(trans)
return [p]
c = [mpatches.Ellipse((), width=smin, height=smin, color="grey"),
mpatches.Ellipse((), width=smid, height=smid, color="grey"),
mpatches.Ellipse((), width=smax, height=smax, color="grey"),
]
legend = ax.legend(c,texts, handler_map=mpatches.Ellipse: HandlerEllipse(),title="Number of Proteins",bbox_to_anchor=(3.50, 0.82, 1.0, .102),fontsize="large")
plt.setp(legend.get_title(),fontsize='large')
plt.show()
输出:
【讨论】:
哇哦,加油! 改进可能是将 smin/mid/max 列为文本列表中的字符串。同样,创建 c 的更简洁的方法。 我很抱歉我说过将补丁包含到图例中很容易。后来我发现这对圈子来说不是微不足道的。很高兴您看到了不受支持的补丁的解决方法。恕我直言,代码现在看起来不错。回到移液器。 不需要道歉。学习如何做很有趣。 我发现的一件事是它不会转移到具有不同值的相似数据集。图例中的圆圈必须手动缩放。我会玩弄它,但我可能不得不发布另一个问题来解决这个问题。以上是关于使用 Matplotlib 在带有图例的热图中显示不同大小的圆圈的主要内容,如果未能解决你的问题,请参考以下文章
每个单元格中带有文本的热图,带有 matplotlib 的 pyplot
matplotlib + wxpython 没有用图例正确调整大小