使用 Matplotlib 在带有图例的热图中显示不同大小的圆圈

Showing different size circles in heatmap with legend using Matplotlib

我问的问题源于这个原始 post

我正在尝试使用我的数据框复制它,但是,我的圆圈与绘图不对齐。其次,我还想创建一个图例,表示相对于圆圈大小的值。

   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()

如有任何帮助,我们将不胜感激!

这里的问题是复制的代码填充了所有字段,而您的代码不一定在每个框中都有一个条目。我们必须向上看,每个圆圈都必须绘制:

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()

示例输出:

该代码不会检查原始数据库的完整性,即每个标签-术语对确实只出现一次。

@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()

输出: