【问题标题】:Single pcolormesh with more than one colormap using Matplotlib使用 Matplotlib 的具有多个颜色图的单个 pcolormesh
【发布时间】:2017-10-01 08:48:21
【问题描述】:

我正在创建一个 GUI,其中有多个记录(“事物”)和字段的实时“时间点”数据。记录可以根据字段进行比较,但字段不一定相关(至少不是在相同的范围内)。对于我最终的 GUI,我想让主页成为一个热图(实际上是一堆基于列(字段)的一维热图,然后如果你点击一个它会给出时间序列历史和其他一些图表。

无论如何,我在这里追求的是试图让初始热图显示我想要的方式。到目前为止,我可以从 Matplotlib 获取 pcolormesh,通过破解它和基于列的百分位的热图,基本上显示基于字段的单个 1-D 热图,然后在顶部添加实际值的文本。

但是,正如我所说,字段不一定相关,我希望能够为每个字段提供单独的颜色图。 例如:假设字段 3 和 4 在质量上与一个相关另一个,但不是字段 0-3 - 所以最好将这些映射到说“绿色”颜色网格而不是“冷暖”。

这是我目前的代码和生成的 pcolormesh/heatmap:

import pandas as pd
import matplotlib.pyplot as plt

def DFPercentiles(df,bycols=True):
    p=pd.DataFrame(index=df.index,columns=df.columns)
    if bycols!=True:
        for j in df.index:
            for i in df.columns:
                p.loc[j,i]=(df.loc[j,i]-min(df.loc[j,:]))/(max(df.loc[j,:])-min(df.loc[j,:]))
    else:
        for i in df.index:
            for j in df.columns:
                p.loc[i,j]=(df.loc[i,j]-min(df.loc[:,j]))/(max(df.loc[:,j])-min(df.loc[:,j]))
    return p

def Heatmap(df,figsize='auto'):
    if figsize=='auto':
        figsize=[shape(df)[1],shape(df)[0]/2]
    fig=figure(figsize=figsize)
    pdf=array(DFPercentiles(df,bycols=True)).astype(float)[::-1]
    plt.pcolormesh(pdf,cmap=cm.coolwarm,alpha=0.8)
    plt.yticks(arange(0.5,len(df)),df.index[::-1])
    plt.xticks(arange(0.5,len(df.columns)),df.columns)
    for y in range(df.shape[0]):
        for x in range(df.shape[1]):
            plt.text(x + 0.5, y + 0.5, '%.3f' % df[::-1].iloc[y, x],
                     horizontalalignment='center',
                     verticalalignment='center',
                     )
    return plt

hmap=Heatmap(mydf)
hmap.show()

结果:

我没有运气尝试为单独的字段获取多个颜色图。

【问题讨论】:

  • 我们更喜欢将解决方案 not 附加到此处的问题中。请您将问题的这一部分移到自我回答中吗?谢谢!

标签: python matplotlib heatmap colormap


【解决方案1】:

@ImportanceOfBeingErnest 解决方案的实施

使用颜色图的堆叠来创建地图的自定义颜色图。基本上,我的函数现在采用 ([list of columns],multiplier,colormap to apply) 的元组并堆叠唯一的颜色图并将百分位数据分组以匹配各个颜色图(端点很难避免着色重叠)。我可能没有非常有效地实现它,但效果很好:

def DFPercentiles_hmapshift(df,bycols=True):
    p=pd.DataFrame(index=df.index,columns=df.columns)
    if bycols!=True:
        for j in df.index:
            for i in df.columns:
                pct=(df.loc[j,i]-min(df.loc[j,:]))/((max(df.loc[j,:])-min(df.loc[j,:]))*1.)
                pct=pct-(pct-0.5)*1./40 #have to rescale it to account for endpoints of cmaps
                p.loc[j,i]=pct
                #print '('+str(max(p.loc[j,:]))+', '+str(min(p.loc[j,:]))+')'

    else:
        for i in df.index:
            for j in df.columns:
                pct=(df.loc[i,j]-min(df.loc[:,j]))/((max(df.loc[:,j])-min(df.loc[:,j]))*1.)
                pct=pct-(pct-0.5)*1./40 #have to rescale it to account for endpoints of cmaps
                p.loc[i,j]=pct
                #print '('+str(max(p.loc[:,j]))+', '+str(min(p.loc[:,j]))+')'
    return p

def Heatmap(df,figsize='auto',ccmaps=[(['Default'],0,'coolwarm')]):
    if figsize=='auto':
        figsize=[shape(df)[1],shape(df)[0]/2]
    fig=figure(figsize=figsize)
    #pdf=array(DFPercentiles(df,bycols=True)).astype(float)[::-1]
    pdf=DFPercentiles_hmapshift(df,bycols=True)
    if len(ccmaps)==1:
        cmap=ccmaps[0][2]
    else:
        cmapl=[]
        for x in ccmaps:
            if x[1]!=0:
                for y in x[0]:
                    pdf[y]=pdf[y]+x[1]
            cmapl.append(getattr(plt.cm,x[2])(np.linspace(0,1,256,endpoint=False)+0.5/256.))
        pdf=np.divide(pdf,len(ccmaps))
        cs=np.vstack(cmapl)
        cmap=matplotlib.colors.LinearSegmentedColormap.from_list("custom",cs)
    pdf=array(pdf).astype(float)[::-1]
    plt.pcolormesh(pdf,cmap=cmap,alpha=0.8)
    plt.yticks(arange(0.5,len(df)),df.index[::-1])
    plt.xticks(arange(0.5,len(df.columns)),df.columns)
    for y in range(df.shape[0]):
        for x in range(df.shape[1]):
            plt.text(x + 0.5, y + 0.5, '%.3f' % df[::-1].iloc[y, x],
                     horizontalalignment='center',
                     verticalalignment='center',
                     )
    return plt

hmap=Heatmap(mydf,ccmaps=[(['Default'],0,'RdBu_r'),(['Field3','Field4'],1,'Greens'),
                     (['Field0'],2,'Greys')])
hmap.show()

为了漂亮(好吧,这只是一个例子!)结果:

【讨论】:

    【解决方案2】:

    每个颜色网格图都有一个与之关联的颜色图。为了在一个图表中使用多个颜色图,我看到了以下选项:

    1. 单个矩形: 不要使用 pcolormesh,而是用您喜欢的颜色绘制单个矩形。
    2. 创建您的自定义颜色图,其中包含不同范围内的不同颜色图。例如。从 0 到 0.4 的值映射到一个颜色图中的颜色,从 0.4 到 1 的值映射到另一个颜色图中的颜色。这可能看起来像:

      import matplotlib.pyplot as plt
      import matplotlib.colors
      import numpy as np
      
      x,y = np.meshgrid(range(4), range(4))
      z = np.array([[0.2,.3,.95],[.5,.76,0.4],[.3,.1,.6]]).astype(float)
      mask= np.array([[1,0,0],[1,0,0],[1,1,1]]).astype(float)
      Z = z + mask
      
      c2 = plt.cm.Greens(np.linspace(0,1,128))
      c1 = plt.cm.coolwarm(np.linspace(0,1,128))
      cols = np.vstack((c1, c2))
      cmap=matplotlib.colors.LinearSegmentedColormap.from_list("q", cols)
      
      fig, ax=plt.subplots()
      ax.pcolormesh(x,y,Z, vmin=0, vmax=2, cmap=cmap)
      
      plt.show()
      
    3. 屏蔽数组并绘制多个 pcolormesh 图。以下示例显示了它的外观:

          import matplotlib.pyplot as plt
          import numpy as np
          import numpy.ma as ma
      
          x,y = np.meshgrid(range(4), range(4))
          z = np.array([[1,1.3,3],[2.2,2.8,1.8],[3,1,3]]).astype(float)
          mask= np.array([[1,0,0],[1,0,0],[1,1,1]]).astype(bool)
          z1 = np.copy(z)
          z1[mask] = np.nan
      
          z2 = np.copy(z)
          z2[~mask] = np.nan
      
          fig, ax=plt.subplots()
          ax.pcolormesh(x,y,ma.masked_invalid(z1), vmin=1, vmax=3, cmap="coolwarm")
          ax.pcolormesh(x,y,ma.masked_invalid(z2), vmin=1, vmax=3, cmap="Greens")
      
          plt.show()
      

    【讨论】:

    • 感谢您的示例,我将尝试应用它,但我也认为自定义颜色图可能更容易。由于我基于百分位数进行映射,因此我可以在不同的地图上为我想要的每个系列添加 1(我认为?)。如何正确实现值的颜色图总是让我感到困惑,但会尝试更多地研究并在此处更新最适合我的解决方案。
    • 您是一位绅士和一位学者好先生。谢谢您的帮助。将解决方案 2 的实施附加到问题中。
    猜你喜欢
    • 2014-12-20
    • 2021-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-14
    • 1970-01-01
    相关资源
    最近更新 更多