【问题标题】:Python: How to plot heat map of 2D matrix by ignoring zeros?Python:如何通过忽略零来绘制二维矩阵的热图?
【发布时间】:2016-12-11 22:42:21
【问题描述】:

我有一个大小为 500 X 28000 的矩阵,其中包含很多零。但是让我们考虑一个使用矩阵 A 的工作示例:

A = [[0, 0, 0, 1, 0],
    [1, 0, 0, 2, 3],
    [5, 3, 0, 0, 0],
    [5, 0, 1, 0, 3],
    [6, 0, 0, 9, 0]]

我想绘制上述矩阵的热图,但由于它包含很多零,所以热图几乎包含空白区域,如下图所示。

如何忽略矩阵中的零点并绘制热图?

这是我尝试过的最小工作示例:

im = plt.matshow(A, cmap=pl.cm.hot, norm=LogNorm(vmin=0.01, vmax=64), aspect='auto') # pl is pylab imported a pl
plt.colorbar(im)
plt.show()

产生:

如您所见,这是因为出现了空白。

但是我的原始矩阵大小为 500X280000 包含很多零,这使我的颜色图几乎是白色的!!

【问题讨论】:

  • 我不相信仅仅因为可视化不清晰就删除数据是正确的做法。您是否考虑过对数据进行分组,或寻找不同类型的图?如果您能告诉我们更多关于您正在使用的数据的性质,这可能会有所帮助。
  • 我想不出除了颜色图之外的任何其他东西来直观地表示我的数据。如果有任何其他的表示方式,请告诉我。
  • 您可以在绘制热图之前尝试使用层次聚类。
  • “忽略”到底是什么意思?您是说非零元素如此稀有且不可见,因为一个元素甚至不占用 500x280000 数据集的单个像素?如果是这样,如何在非零元素的位置绘制一个固定大小的标记,让读者明白不在标记中心的其他元素的值都是零?
  • 我的意思是,我们将您的矩阵 A 视为包含 140,000,000(=500*280000) 个元素的数据集。每个元素都有行索引、列索引和值的属性。换句话说,我们从概念上考虑(我不是说我们在 python 代码中这样做),ds = {A[0,0], A[0,1], .., A[0,279999], A[1,0], A[1,1], .., A[1,279999], ..., A[499, 279999]} 的数据。 ds 的第 k 个元素对应于 A[i,j] 和一些 ij。那么ds[k]的“行索引”为ids[k]的“列索引”为jds[k]的值为A[i,j]0<=row_index[k]<500,但0<=k<140,000,000

标签: python python-2.7 matplotlib heatmap colormap


【解决方案1】:

虽然norio的答案是正确的。我认为只需几行代码就可以给出一个更中肯的快速答案:

import numpy as np
import matplotlib.pyplot as plt
A = np.asarray(A)
x,y = A.nonzero() #get the notzero indices
plt.scatter(x,y,c=A[x,y],s=100,cmap='hot',marker='s') #adjust the size to your needs
plt.colorbar()
plt.show()

注意轴是倒置的。您可以通过以下方式反转它们:

ax=plt.gca()
ax.invert_xaxis()
ax.invert_yaxis()

还请注意,您现在拥有更大的灵活性:

  • 您可以选择设置标记大小、标记类型和透明度
  • 此过程更快,因为不会将零解析到 matplotlib。

【讨论】:

    【解决方案2】:

    此答案与 Luis 答案的“编辑 2”部分的方向相同。事实上,这是它的简化版本。我发布这个只是为了纠正我在我的 cmets 中的误导性陈述。我在评论区看到了我们不应该讨论的警告,所以我正在使用这个回答区。

    无论如何,首先让我发布我的代码。请注意,我使用了脚本内部随机生成的更大矩阵,而不是您的示例矩阵A

    #!/usr/bin/python
    #
    # This script was written by norio 2016-8-5.
    
    import os, re, sys, random
    import numpy as np
    
    #from matplotlib.patches import Ellipse
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    import matplotlib.image as img
    
    mpl.rcParams['lines.linewidth'] = 2
    mpl.rcParams['lines.markeredgewidth'] = 1.0
    mpl.rcParams['axes.formatter.limits'] = (-4,4)
    #mpl.rcParams['axes.formatter.limits'] = (-2,2)
    mpl.rcParams['axes.labelsize'] = 'large'
    mpl.rcParams['xtick.labelsize'] = 'large'
    mpl.rcParams['ytick.labelsize'] = 'large'
    mpl.rcParams['xtick.direction'] = 'out'
    mpl.rcParams['ytick.direction'] = 'out'
    
    
    ############################################
    #numrow=500
    #numcol=280000
    numrow=50
    numcol=28000
    # .. for testing
    numelm=numrow*numcol
    eps=1.0e-9
    #
    #numnz=int(1.0e-7*numelm)
    numnz=int(1.0e-5*numelm)
    # .. for testing
    vmin=1.0e-6
    vmax=1.0
    outfigname='stackoverflow38790536.png'
    ############################################
    
    ### data matrix
    # I am generating a data matrix here artificially.
    print 'generating pseudo-data..'
    random.seed('20160805')
    matA=np.zeros((numrow, numcol))
    for je in range(numnz):
        jr = random.uniform(0,numrow)
        jc = random.uniform(0,numcol)
        matA[jr,jc] = random.uniform(vmin,vmax)
    
    
    ### Actual processing for a given data will start from here
    print 'processing..'
    
    idxrow=[]
    idxcol=[]
    val=[]
    for ii in range(numrow):
        for jj in range(numcol):
            if np.abs(matA[ii,jj])>eps:
                idxrow.append(ii)
                idxcol.append(jj)
                val.append( np.abs(matA[ii,jj]) )
    
    print 'len(idxrow)=', len(idxrow)    
    print 'len(idxcol)=', len(idxcol)    
    print 'len(val)=',    len(val)    
    
    
    ############################################
    # canvas setting for line plots 
    ############################################
    
    f_size   = (8,5)
    
    a1_left   = 0.15
    a1_bottom  = 0.15
    a1_width  = 0.65
    a1_height = 0.80
    #
    hspace=0.02
    #
    ac_left   = a1_left+a1_width+hspace
    ac_bottom = a1_bottom
    ac_width  = 0.03
    ac_height = a1_height
    
    ############################################
    # plot 
    ############################################
    print 'plotting..'
    
    fig1=plt.figure(figsize=f_size)
    ax1 =plt.axes([a1_left, a1_bottom, a1_width, a1_height], axisbg='w')
    
    pc1=plt.scatter(idxcol, idxrow, s=20, c=val, cmap=mpl.cm.gist_heat_r)
    # cf.
    # http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.scatter
    plt.xlabel('Column Index', fontsize=18)
    plt.ylabel('Row Index', fontsize=18)
    ax1.set_xlim([0, numcol-1])
    ax1.set_ylim([0, numrow-1])
    
    axc =plt.axes([ac_left, ac_bottom, ac_width, ac_height], axisbg='w')
    mpl.colorbar.Colorbar(axc,pc1, ticks=np.arange(0.0, 1.5, 0.1) )
    
    plt.savefig(outfigname)
    plt.close()
    

    此脚本输出一个图形“stackoverflow38790536.png”,如下所示。

    正如您在我的代码中看到的那样,我使用了scatter 而不是plot。我意识到plot 命令不是最适合这里的任务。

    我需要纠正的另一个话是row_index 不需要有多达 140,000,000(=500*280000) 个元素。它只需要具有非零元素的行索引。更准确地说,列表, idxrowidxcolval,在上面的代码中进入scatter命令,其长度等于非零元素的个数。

    请注意,Luis 的回答中已正确处理了这两点。

    【讨论】:

      【解决方案3】:

      如果您删除 LogNorm,您会得到黑色方块而不是白色:

      im = plt.matshow(A, cmap=plt.cm.hot, aspect='auto') # pl is pylab imported a pl
      


      编辑

      在颜色图中,您总是拥有填充了值的完整网格。这就是您实际创建网格的原因:您考虑(例如:插值)所有不完全在网格中的点。这意味着您的数据许多零,并且图表通过看起来是白色(或黑色)正确反映了这一点。通过忽略这些值,如果您没有明确的理由这样做,您会创建一个误导性的图表。

      如果您感兴趣的不是零值,那么您需要另一种类型的图表,就像norio's comment 指出的那样。为此,您可能想看看this answer


      编辑 2

      改编自this answer

      您可以将这些值视为一维数组并独立绘制点,而不是用不需要的值填充网格。

      A = [[0, 0, 0, 1, 0],
          [1, 0, 0, 2, 3],
          [5, 3, 0, 0, 0],
          [5, 0, 1, 0, 3],
          [6, 0, 0, 9, 0]]
      A = np.array(A)
      lenx, leny = A.shape
      
      xx = np.array( [ a for a in range(lenx) for a in range(leny) ] )   # Convert 3D to 3*1D
      yy = np.array( [ a for a in range(lenx) for b in range(leny) ] )
      zz = np.array( [ A[x][y] for x,y in zip(xx,yy) ] )
      #---
      xx = xx[zz!=0]    # Drop zeroes
      yy = yy[zz!=0]
      zz = zz[zz!=0]
      #---
      zi, yi, xi = np.histogram2d(yy, xx, bins=(10,10), weights=zz, normed=False)
      zi = np.ma.masked_equal(zi, 0)
      
      fig, ax = plt.subplots()
      ax.pcolormesh(xi, yi, zi, edgecolors='black')
      scat = ax.scatter(xx, yy, c=zz, s=200)
      fig.colorbar(scat)
      ax.margins(0.05)
      
      plt.show()
      

      【讨论】:

      • OP 要求删除零本身的贡献,在这里你仍然用黑色显示它。
      • @Luis。是的,我在发帖之前尝试过。由于我的矩阵真的很大,我仍然无法可视化热图。它显示了一个带有小红点的黑色图形。
      • 问题是,根据定义,在彩色地图中你有一个完整的网格,所有个点都将被绘制出来。如果不是这种情况,您需要另一种类型的情节。请参阅上面 norio 的评论。
      猜你喜欢
      • 2016-12-08
      • 1970-01-01
      • 2016-07-20
      • 1970-01-01
      • 1970-01-01
      • 2017-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多