In matplotlib, how to add a table to a subplot without resizing other subplots

Viewed 460

Consider this example:

import matplotlib.pyplot as plt

fig, axs = plt.subplots(1, 3, figsize=(9,3), constrained_layout=False)
[ax.plot([[0,0],[1,1]]) for ax in axs]
fig.set_facecolor('silver')

table = axs[0].table(
    [[col + str(row) for col in 'ABC'] for row in range(3)],
    loc='top',
)

enter image description here

I'd like to add a table to the top (or bottom) of a subplot and reduce this particular subplot by exactly the height of the table so that the table does not stick out, the figure does not get enlarged and all subplots have the same height (including table). I do not want to put the table into the plot area, i.e. loc='upper center'.

2 Answers

The idea is to draw the figure, get the rendered table height and then shrink the first axes by this height (transformed to figure coordinates). As the table size is in axes coordinates the table will also get shrunk when decreasing the axes height. Therefore we must re-scale it in y direction by the same factor to get the original table size and have the top neatly aligned with the other axes.

import matplotlib.pyplot as plt

fig, axs = plt.subplots(1, 3, figsize=(9,3), constrained_layout=False)
[ax.plot([[0,0],[1,1]]) for ax in axs]
fig.set_facecolor('silver')

table = axs[0].table(
    [[col + str(row) for col in 'ABC'] for row in range(3)],
    loc='top',
)

table_box = fig.transFigure.inverted().transform_bbox(table.get_window_extent(fig.canvas.get_renderer()))
ax_box = axs[0].get_position()
axs[0].set_position([ax_box.x0, ax_box.y0, ax_box.width, (ax_box.height-table_box.height)])
table.scale(1, ax_box.height/(ax_box.height-table_box.height))

enter image description here

In the end I've used a constant table_height setting the fraction of the axis to be used for table.

import matplotlib.pyplot as plt

fig, axs = plt.subplots(1, 3, figsize=(9,3))
[ax.plot([[0,0],[1,1]]) for ax in axs]
fig.set_facecolor('silver')

table_height = 0.3
ax_box = axs[0].get_position()
axs[0].set_position(
    [ax_box.x0, ax_box.y0, ax_box.width, ax_box.height * (1 - table_height)])

table = axs[0].table(
    [[col + str(row) for col in 'ABC'] for row in range(3)],
    bbox=[0., 1., 1., table_height / (1 - table_height)]
)

result

Related