【问题标题】:Double header in Matplotlib TableMatplotlib 表中的双标题
【发布时间】:2019-05-15 22:09:12
【问题描述】:

我需要在 matplotlib 中绘制一个表格。问题是一些列有一级标题,一些列有双层标题。

这是我需要的:

这是一个简单的单级标题示例:

df = pd.DataFrame()
df['Animal'] = ['Cow', 'Bear']
df['Weight'] = [250, 450]
df['Favorite'] = ['Grass', 'Honey']
df['Least Favorite'] = ['Meat', 'Leaves']
df

fig = plt.figure(figsize=(9,2))
ax=plt.subplot(111)
ax.axis('off') 
table = ax.table(cellText=df.values, colColours=['grey']*df.shape[1], bbox=[0, 0, 1, 1], colLabels=df.columns)
plt.savefig('Table.jpg')

最后一段代码产生下一张图片:

我需要进行哪些更改才能获得所需的表格?

【问题讨论】:

    标签: python python-3.x pandas matplotlib data-visualization


    【解决方案1】:

    我想唯一的方法是手动添加标题。您可以使用 bbox 参数控制它们的确切位置和大小。请参阅下面的示例。您可以从此答案获得更多详细信息:https://stackoverflow.com/a/37440236/2912478

    #!/usr/bin/env python
    
    import pandas as pd
    import matplotlib.pyplot as plt
    
    df = pd.DataFrame()
    df['Animal'] = ['Cow', 'Bear']
    df['Weight'] = [250, 450]
    df['Favorite'] = ['Grass', 'Honey']
    df['Least Favorite'] = ['Meat', 'Leaves']
    df
    
    fig = plt.figure(figsize=(9,2))
    ax=plt.subplot(111)
    ax.axis('off') 
    
    plt.table(cellText=[['Animal', 'Weight']],
                         loc='bottom',
                         bbox=[0, 0.6, 0.5, 0.3]
                         )
    
    plt.table(cellText=[['Food']],
                         loc='bottom',
                         bbox=[0.5, 0.75, 0.5, 0.15]
                         )
    
    plt.table(cellText=[['Favorite', 'Least favorite']],
                         loc='bottom',
                         bbox=[0.5, 0.6, 0.5, 0.15]
                         )
    
    plt.table(cellText=df.values,
                         loc='bottom',
                         bbox=[0, 0, 1, 0.6]
                         )
    
    plt.show()
    

    这是我得到的输出:

    【讨论】:

      【解决方案2】:

      另一种选择是利用matplotlib.gridspec.GridSpec 使用自定义布局绘制值和列:

      def format_axes(fig):
          for i, ax in enumerate(fig.axes):
              ax.tick_params(labelbottom=False, labelleft=False, labelright=False)
              ax.get_xaxis().set_ticks([])
              ax.get_yaxis().set_ticks([])
      
      
      df = pd.DataFrame()
      df['Animal'] = ['Cow', 'Bear']
      df['Weight'] = [250, 450]
      df['Favorite'] = ['Grass', 'Honey']
      df['Least Favorite'] = ['Meat', 'Leaves']
      
      fig = plt.figure(figsize=(9, 2))
      
      
      gs = GridSpec(3, 4, figure=fig, wspace=0.0, hspace=0.0,height_ratios=[1, 1, 4])
      # plot table header
      ax1 = fig.add_subplot(gs[:-1, 0])
      ax1.text(0.5, 0.5, df.columns[0], va="center", ha="center")
      ax2 = fig.add_subplot(gs[:-1, 1])
      ax2.text(0.5, 0.5, df.columns[1], va="center", ha="center")
      ax3 = fig.add_subplot(gs[0, -2:])
      ax3.text(0.5, 0.5, "Food", va="center", ha="center")
      ax4 = fig.add_subplot(gs[1, -2])
      ax4.text(0.5, 0.5, df.columns[2], va="center", ha="center")
      ax5 = fig.add_subplot(gs[1, -1])
      ax5.text(0.5, 0.5, df.columns[3], va="center", ha="center")
      # plot table data
      ax6 = fig.add_subplot(gs[-1, :])
      table = ax6.table(cellText=df.values, cellLoc='center', bbox=[0, 0, 1, 1])
      
      format_axes(fig)
      
      plt.show()
      

      结果

      【讨论】:

      • 这是一个很好的解决方案,但我需要在一个斧头上放一张桌子,而不是整个图。
      【解决方案3】:

      单元格合并解决方案

      您可以合并ax.table 生成的单元格,这是 Excel 电子表格中的单元格合并功能。这允许一个完全自动化的解决方案,您不需要摆弄任何坐标(保存要合并的单元格的索引):

      import matplotlib.pyplot as plt
      import pandas as pd
      
      df = pd.DataFrame()
      df['Animal'] = ['Cow', 'Bear']
      df['Weight'] = [250, 450]
      df['Favorite'] = ['Grass', 'Honey']
      df['Least Favorite'] = ['Meat', 'Leaves']
      
      fig = plt.figure(figsize=(9,2))
      ax=fig.gca()
      ax.axis('off')
      r,c = df.shape
      
      # ensure consistent background color
      ax.table(cellColours=[['lightgray']] + [['none']], bbox=[0,0,1,1])
      
      # plot the real table
      table = ax.table(cellText=np.vstack([['', '', 'Food', ''], df.columns, df.values]), 
                       cellColours=[['none']*c]*(2 + r), bbox=[0, 0, 1, 1])
      
      # need to draw here so the text positions are calculated
      fig.canvas.draw()
      
      # do the 3 cell merges needed
      mergecells(table, (1,0), (0,0))
      mergecells(table, (1,1), (0,1))
      mergecells(table, (0,2), (0,3))
      

      输出:

      这是上面使用的mergecells 函数的代码:

      import matplotlib as mpl
      
      def mergecells(table, ix0, ix1):
          ix0,ix1 = np.asarray(ix0), np.asarray(ix1)
          d = ix1 - ix0
          if not (0 in d and 1 in np.abs(d)):
              raise ValueError("ix0 and ix1 should be the indices of adjacent cells. ix0: %s, ix1: %s" % (ix0, ix1))
      
          if d[0]==-1:
              edges = ('BRL', 'TRL')
          elif d[0]==1:
              edges = ('TRL', 'BRL')
          elif d[1]==-1:
              edges = ('BTR', 'BTL')
          else:
              edges = ('BTL', 'BTR')
      
          # hide the merged edges
          for ix,e in zip((ix0, ix1), edges):
              table[ix[0], ix[1]].visible_edges = e
      
          txts = [table[ix[0], ix[1]].get_text() for ix in (ix0, ix1)]
          tpos = [np.array(t.get_position()) for t in txts]
      
          # center the text of the 0th cell between the two merged cells
          trans = (tpos[1] - tpos[0])/2
          if trans[0] > 0 and txts[0].get_ha() == 'right':
              # reduce the transform distance in order to center the text
              trans[0] /= 2
          elif trans[0] < 0 and txts[0].get_ha() == 'right':
              # increase the transform distance...
              trans[0] *= 2
      
          txts[0].set_transform(mpl.transforms.Affine2D().translate(*trans))
      
          # hide the text in the 1st cell
          txts[1].set_visible(False)
      

      【讨论】:

      • 如果我们想将两个以上的单元格合并在一起怎么办?能否以某种方式实现自动化?
      • @MorningGlory 我对 tel 的代码做了一些更改,并作为支持合并 N 个单元格的答案发布了
      【解决方案4】:

      除了@tel 的回答之外,我还对他的代码进行了一些更改以解决我自己的问题——合并两个以上的单元格。这是我得到的:

      def mergecells(table, cells):
          '''
          Merge N matplotlib.Table cells
      
          Parameters
          -----------
          table: matplotlib.Table
              the table
          cells: list[set]
              list of sets od the table coordinates
              - example: [(0,1), (0,0), (0,2)]
      
          Notes
          ------
          https://stackoverflow.com/a/53819765/12684122
          '''
          cells_array = [np.asarray(c) for c in cells]
          h = np.array([cells_array[i+1][0] - cells_array[i][0] for i in range(len(cells_array) - 1)])
          v = np.array([cells_array[i+1][1] - cells_array[i][1] for i in range(len(cells_array) - 1)])
      
          # if it's a horizontal merge, all values for `h` are 0
          if not np.any(h):
              # sort by horizontal coord
              cells = np.array(sorted(list(cells), key=lambda v: v[1]))
              edges = ['BTL'] + ['BT' for i in range(len(cells) - 2)] + ['BTR']
          elif not np.any(v):
              cells = np.array(sorted(list(cells), key=lambda h: h[0]))
              edges = ['TRL'] + ['RL' for i in range(len(cells) - 2)] + ['BRL']
          else:
              raise ValueError("Only horizontal and vertical merges allowed")
      
          for cell, e in zip(cells, edges):
              table[cell[0], cell[1]].visible_edges = e
              
          txts = [table[cell[0], cell[1]].get_text() for cell in cells]
          tpos = [np.array(t.get_position()) for t in txts]
      
          # transpose the text of the left cell
          trans = (tpos[-1] - tpos[0])/2
          # didn't had to check for ha because I only want ha='center'
          txts[0].set_transform(mpl.transforms.Affine2D().translate(*trans))
          for txt in txts[1:]:
              txt.set_visible(False)
      

      【讨论】:

      • 这很好用,但是当我尝试在第一个 for 循环中使用 table[cell[0], cell[1]].set_facecolor('k') 对合并的单元格进行颜色填充时,我得到了一个奇怪的结果。
      猜你喜欢
      • 2016-09-13
      • 1970-01-01
      • 1970-01-01
      • 2011-01-08
      • 2013-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-12
      相关资源
      最近更新 更多