at the moment I've got this piece of code:
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
from matplotlib.font_manager import FontProperties
data = np.random.uniform(0, 1, 80).reshape(20, 4)
final_data = [['%.3f' % j for j in i] for i in data]
mpl.style.use('seaborn')
mpl.rc('xtick', labelsize = 7)
mpl.rc('ytick', labelsize = 7)
fig = plt.figure()
fig.subplots_adjust(left=0.1, wspace=0.1)
plt.subplot2grid((1, 4), (0, 0), colspan=3)
table_subplot = plt.subplot2grid((1, 4), (0, 3))
table = plt.table(cellText=final_data, colLabels=['A', 'B', 'C', 'D'], loc='center', cellLoc='center', colColours=['#FFFFFF', '#F3CC32', '#2769BD', '#DC3735'])
table.auto_set_font_size(False)
table.set_fontsize(7)
table.auto_set_column_width((-1, 0, 1, 2, 3))
for (row, col), cell in table.get_celld().items():
if (row == 0):
cell.set_text_props(fontproperties=FontProperties(weight='bold', size=7))
plt.axis('off')
plt.show()
which produce this as an ouput:

I know that the plot is empty, but I'm planning to add some data there, so I need to keep this in mind! I want to add one more row to the table to have a header. This row should only be over the last three columns, something like this:
+-----------------+
| Header |
+-----+-----------------+
| A | B | C | D |
+-----+-----+-----+-----+
| ... | ... | ... | ... |
+-----+-----+-----+-----+
| ... | ... | ... | ... |
+-----+-----+-----+-----+
The width of the header row should match the sum of the width of the A, B and C columns. I've bene playing around but I can not manage to get it... Can anyone help me?
Thanks!
Matplotlib tables do not have the concept of "colspan" or "rowspan", where cells are span multiple columns or rows. One could think that a cell can be made three times as wide as other cells. However, that would introduce an unwanted shift

An option you have is to set the visible_edges of additional cells you manually add via .add_cell to the top of the table.
The visible edges can be "T": top, "B" : bottom, "L" : left or "R" : right.
Then setting the text to the middle cell makes the whole thing look like a single cell.
import matplotlib.pyplot as plt
data = [[1,2,3,4],[6,5,4,3],[1,3,5,1]]
table = plt.table(cellText=data, colLabels=['A', 'B', 'C', 'D'], loc='center',
cellLoc='center', colColours=['#FFFFFF', '#F3CC32', '#2769BD', '#DC3735'])
table.auto_set_font_size(False)
h = table.get_celld()[(0,0)].get_height()
w = table.get_celld()[(0,0)].get_width()
# Create an additional Header
header = [table.add_cell(-1,pos, w, h, loc="center", facecolor="none") for pos in [1,2,3]]
header[0].visible_edges = "TBL"
header[1].visible_edges = "TB"
header[2].visible_edges = "TBR"
header[1].get_text().set_text("Header Header Header Header")
plt.axis('off')
plt.show()

The above does not allow to colorize the background of a table cell. For this the following workaround can be used:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.table
from matplotlib.collections import LineCollection
from matplotlib.path import Path
class MyCell(matplotlib.table.CustomCell):
def __init__(self, *args, visible_edges, **kwargs):
super().__init__(*args, visible_edges=visible_edges, **kwargs)
seg = np.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0],
[0.0, 1.0], [0.0, 0.0]]).reshape(-1, 1, 2)
segments = np.concatenate([seg[:-1], seg[1:]], axis=1)
self.edgelines = LineCollection(segments, edgecolor=kwargs.get("edgecolor"))
self._text.set_zorder(2)
self.set_zorder(1)
def set_transform(self, trans):
self.edgelines.set_transform(trans)
super().set_transform(trans)
def draw(self, renderer):
c = self.get_edgecolor()
self.set_edgecolor((1,1,1,0))
super().draw(renderer)
self.update_segments(c)
self.edgelines.draw(renderer)
self.set_edgecolor(c)
def update_segments(self, color):
x, y = self.get_xy()
w, h = self.get_width(), self.get_height()
seg = np.array([[x, y], [x+w, y], [x+w, y+h],
[x, y+h], [x, y]]).reshape(-1, 1, 2)
segments = np.concatenate([seg[:-1], seg[1:]], axis=1)
self.edgelines.set_segments(segments)
self.edgelines.set_linewidth(self.get_linewidth())
colors = [color if edge in self._visible_edges else (1,1,1,0)
for edge in self._edges]
self.edgelines.set_edgecolor(colors)
def get_path(self):
codes = [Path.MOVETO] + [Path.LINETO]*3 + [Path.CLOSEPOLY]
return Path(
[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]],
codes, readonly=True)
matplotlib.table.CustomCell = MyCell
data = [[1,2,3,4],[6,5,4,3],[1,3,5,1]]
table = plt.table(cellText=data, colLabels=['A', 'B', 'C', 'D'], loc='center',
cellLoc='center', colColours=['#FFFFFF', '#F3CC32', '#2769BD', '#DC3735'])
table.auto_set_font_size(False)
h = table.get_celld()[(0,0)].get_height()
w = table.get_celld()[(0,0)].get_width()
# Create an additional Header
header = [table.add_cell(-1,pos, w, h, loc="center", facecolor="limegreen") for pos in [1,2,3]]
header[0].visible_edges = "TBL"
header[1].visible_edges = "TB"
header[2].visible_edges = "TBR"
header[1].get_text().set_text("Header")
plt.axis('off')
plt.show()

Note however, that this will fail for cases where the header text is longer than the cell, due to the drawing order.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With