matplotlib 如何在表格单元格中绘制迷你条形图

7eumitmz  于 2023-10-24  发布在  其他
关注(0)|答案(1)|浏览(121)

我正在使用matplotlib创建一个表格可视化。我想在表格的每一行的最后一个单元格内绘制一个迷你条形图。迷你条形图应该有两种不同的颜色来表示不同的百分比(现在是50%的绿色和50%的红色,稍后会处理)。我还想在带有白色文本的部分内显示红色部分的百分比。
下面是Python代码,它创建了一个可复制的示例:

  1. import matplotlib.pyplot as plt
  2. import pandas as pd
  3. import numpy as np
  4. # Create a sample DataFrame
  5. df = pd.DataFrame({
  6. 'A': np.random.randint(1, 10, 10),
  7. 'B': np.random.randint(1, 10, 10),
  8. 'C': np.random.randint(1, 10, 10),
  9. 'D': np.random.randint(1, 10, 10),
  10. 'BAR': np.random.randint(1, 10, 10),
  11. })
  12. # Function to draw mini-bar
  13. def draw_mini_bar(ax, x, y, width, height, percentage):
  14. ax.add_patch(plt.Rectangle((x, y), width, height, facecolor='#76ed33'))
  15. ax.add_patch(plt.Rectangle((x, y), width * percentage / 100, height, facecolor='#f55d2f'))
  16. plt.draw()
  17. # Function to render table
  18. def render_mpl_table(data, col_width=3.0, row_height=0.625, font_size=14,
  19. header_color='#40466e', row_colors=['#f1f1f2', '#ffffff'], edge_color='w',
  20. bbox=[0, 0, 1, 1], ax=None, **kwargs):
  21. if ax is None:
  22. size = (np.array(data.shape[::-1]) + np.array([0, 1])) * np.array([col_width, row_height])
  23. fig, ax = plt.subplots(figsize=size)
  24. ax.axis('off')
  25. mpl_table = ax.table(cellText=data.values, bbox=bbox, colLabels=data.columns, **kwargs)
  26. for k, cell in mpl_table._cells.items():
  27. cell.set_edgecolor(edge_color)
  28. if k[0] == 0:
  29. cell.set_text_props(weight='bold', color='w')
  30. cell.set_facecolor(header_color)
  31. else:
  32. cell.set_facecolor(row_colors[k[0]%len(row_colors)])
  33. # DRAW BARS IN LAST COLUMN
  34. if k[0] > 0 and k[1] == len(data.columns) - 1:
  35. draw_mini_bar(ax, cell.get_x(), cell.get_y(), cell.get_width(), cell.get_height(), 50)
  36. plt.show()
  37. render_mpl_table(df)

当我运行这段代码时,迷你条形图没有按预期呈现。具体来说,它们要么出现在错误的位置,要么颜色显示不正确。
有没有人遇到过类似的问题,或者知道如何在matplotlib表格单元格中绘制这样的迷你条形图?
我尝试使用add_patch()方法在表格单元格内绘制矩形。我的目标是在每行的最后一个单元格中创建迷你条形图。我希望条形图完全是绿色,部分用红色覆盖,表示不同的百分比(现在我只需要设置50%绿色和50%红色)。
然而,结果并不像预期的那样。这些条要么出现在错误的位置,要么没有正确地呈现颜色。我似乎无法让红色和绿色部分准确地显示在表格单元格中。

lbsnaicq

lbsnaicq1#

注意事项

使用matplotlib创建自定义表可能会非常棘手。在matplotlib.tabledocumentation中甚至有一个警告!
Matplotlib中的表实现维护较少。如果需要更有特色的表实现,您可能希望尝试blume。
在matplotlib文档中的tutorial中,有一个关于用于表的不同索引的注解。
小心表的特殊索引.
尝试matplotlib以外的方法来解决这个问题是明智的。无论如何,我这里的答案是基于您的代码,因此使用matplotlib来解决您的问题。

最终输出

这是下面的解决方案生成的图。注意带有百分比和正确颜色/位置的注解条形图。

解决方案

当每个单元格的xy位置坐标设置正确时,使用ax.add_patch(...)手动创建条形图可以正常工作。ax.table(...)会自动为您设置这些坐标,但尝试访问它们会导致索引问题并弄乱整个表。相反,使用matplotlib.table.table(...)创建表格。然后在访问之前显式设置每个单元格的位置。这可以在单个for循环中完成。

代码

  1. import matplotlib.pyplot as plt
  2. import matplotlib.table as mtab # Don't forget this import!
  3. import pandas as pd
  4. import numpy as np
  5. # Create a sample DataFrame
  6. df = pd.DataFrame({
  7. 'A': np.random.randint(1, 10, 10),
  8. 'B': np.random.randint(1, 10, 10),
  9. 'C': np.random.randint(1, 10, 10),
  10. 'D': np.random.randint(1, 10, 10),
  11. 'BAR': np.random.randint(1, 10, 10),
  12. })
  13. # Function to draw mini-bar
  14. def draw_mini_bar(ax, x, y, width, height, percentage):
  15. ax.add_patch(plt.Rectangle((x, y), width, height, facecolor='#76ed33', edgecolor='k'))
  16. ax.add_patch(plt.Rectangle((x, y), width* percentage / 100, height, facecolor='#f55d2f', edgecolor='k'))
  17. # this labels the red bar on each barchart
  18. ax.text(x+.005, y+(height/2.8), str(percentage)+"%", c='w')
  19. plt.draw()
  20. # Function to render table
  21. def render_mpl_table(data, col_width=2.0, row_height=0.625, font_size=14,
  22. header_color='#40466e', row_colors=['#f1f1f2', '#ffffff'], edge_color='w',
  23. bbox=[0, 0, 1, 1], axs=None, **kwargs):
  24. if axs is None:
  25. size = (np.array(data.shape[::-1]) + np.array([0, 1])) * np.array([col_width, row_height])
  26. fig, axs = plt.subplots(figsize=size)
  27. axs.axis('off')
  28. # THIS IS A BIG CHANGE !!! Using matplotlib.table.table instead of ax.table changes everything
  29. # The cellText param needs to be a 2D list of strings which is why it looks so funky
  30. mpl_table = mtab.table(ax=axs, cellText=list([[str(j) for j in l] for l in df.values.tolist()]), bbox=bbox, colLabels=data.columns, **kwargs)
  31. # This creates a dictionary of all cells in the table. We can use it to access and manipulate
  32. # individual cells. The keys are the row/column indices of each cell
  33. dictOfCells = mpl_table.get_celld()
  34. # looping through the keys from the dictionary of cells
  35. for i in dictOfCells.keys():
  36. cell = dictOfCells[i]
  37. cell.set_edgecolor(edge_color)
  38. if i[0] == 0:
  39. cell.set_text_props(weight='bold', color='w')
  40. cell.set_facecolor(header_color)
  41. else:
  42. cell.set_facecolor(row_colors[i[0]%len(row_colors)])
  43. # These are the coordinates for the lower left corner of each cell in the rightmost column
  44. # THEY NEED TO BE SET EXPLICITLY !!! Change the values as needed for different sized tables
  45. cell.set(xy=(0.8, (i[0]-1)/11))
  46. # just like above, needs to be set explicitly
  47. cell.set(height=(1/11))
  48. # DRAW BARS IN LAST COLUMN
  49. if i[0]>0 and i[1] == len(data.columns)-1:
  50. # x and y positions that were set above
  51. xPos = cell.get_x()
  52. yPos = cell.get_y()
  53. # notice that the percentage parameter is set to the value in each cell that the barchart is in
  54. draw_mini_bar(axs, xPos, yPos, cell.get_width(), cell.get_height(), 10*int(cell.get_text().get_text()))
  55. return mpl_table
  56. # call the function here, can set it equal to a variable for more manipulation of the table
  57. render_mpl_table(df)
  58. plt.show()
展开查看全部

相关问题