Matplotlib Table Formatting Column Width

Matplotlib table size and position

Here is some code to draw the table.

Some remarks:

  • The support for table in matplotlib is rather elementary. It is mostly meant to add some text in table form on an existing plot.
  • Using .pop() makes the code difficult to reason about. In Python, usually new lists are created starting from the given lists.
  • As the adequate size of the plot highly depends on the number of rows, a possibility is to calculate it as some multiple. The exact values depend on the complete table, it makes sense to experiment a bit. The values below seem to work well for the given example data.
  • dpi and tight bounding box can be set as parameters to savefig().
  • Different matplotlib versions might behave slightly different. The code below is tested with matplotlib 3.3.3.
import numpy as np
import matplotlib.pyplot as plt

N = 10
data = [['A', 'B', 'C', 'D', 'E', 'F']] + [['100%', '200%', 'O', 'X', '1.2%', '100', '200']] * N
column_headers = data[0]
row_headers = [row[0] for row in data[1:]]
cell_text = [row[1:] for row in data[1:]]

fig, ax1 = plt.subplots(figsize=(10, 2 + N / 2.5))

rcolors = np.full(len(row_headers), 'linen')
ccolors = np.full(len(column_headers), 'lavender')

table = ax1.table(cellText=cell_text,
cellLoc='center',
rowLabels=row_headers,
rowColours=rcolors,
rowLoc='center',
colColours=ccolors,
colLabels=column_headers,
loc='center')
table.scale(1, 2)
table.set_fontsize(16)
ax1.axis('off')
title = "demo title"
subtitle = "demo subtitle"
ax1.set_title(f'{title}\n({subtitle})', weight='bold', size=14, color='k')

plt.savefig("demo_table.png", dpi=200, bbox_inches='tight')

example figure

Matplotlib table: individual column width

I've been searching the web over and over again looking for similar probelm sollutions. I've found some answers and used them, but I didn't find them quite straight forward. By chance I just found the table method get_celld when simply trying different table methods.
By using it you get a dictionary where the keys are tuples corresponding to table coordinates in terms of cell position. So by writing

cellDict=the_table.get_celld()
cellDict[(0,0)].set_width(0.1)

you will simply adress the upper left cell. Now looping over rows or columns will be fairly easy.

A bit late answer, but hopefully others may be helped.

Matplotlib table formatting

The matplotlib documentation says

Add a table to the current axes. Returns a matplotlib.table.Table instance. For finer grained control over tables, use the Table class and add it to the axes with add_table().

You could do is the following, look at the properties of your table (it's and object belonging to that class Table):

print  the_table.properties() # hint it's a dictionary do: type(the_table.properties() <type 'dict'> 

edit that dictionary the way you see right, and the update your table, with:

the_table.update(giveHereYourDictionary)

Hint: if you work with IPython or interactive shell it's enough to do help(objectName), e.g. help(the_table) to see all the object's methods.
This should, hopefully, work.

OK, I'm adding here a walk through of how to to that kind of stuff. I admit, it's not trivial, but I am using matplotlib for 3.5 years now, so ...

Do your code in IPython (I said it before, but I must emphasize again), it really helps to examine all the properties that objects have (type object name and then the key):

In [95]: prop=the_table.properties()
In [96]: prop #This is a dictionary, it's not so trivial, but never the less one can understand how dictionaries work...
Out[96]:
{'agg_filter': None,
'alpha': None,
'animated': False,
'axes': <matplotlib.axes.AxesSubplot at 0x9eba34c>,
'celld': {(0, -1): <matplotlib.table.Cell at 0xa0cf5ec>,
(0, 0): <matplotlib.table.Cell at 0xa0c2d0c>,
(0, 1): <matplotlib.table.Cell at 0xa0c2dec>,
(0, 2): <matplotlib.table.Cell at 0xa0c2ecc>,
(1, -1): <matplotlib.table.Cell at 0xa0cf72c>,
(1, 0): <matplotlib.table.Cell at 0xa0c2fac>,
(1, 1): <matplotlib.table.Cell at 0xa0cf08c>,
(1, 2): <matplotlib.table.Cell at 0xa0cf18c>,
(2, -1): <matplotlib.table.Cell at 0xa0cf84c>,
(2, 0): <matplotlib.table.Cell at 0xa0cf28c>,
(2, 1): <matplotlib.table.Cell at 0xa0cf3ac>,
(2, 2): <matplotlib.table.Cell at 0xa0cf4cc>},
'child_artists': [<matplotlib.table.Cell at 0xa0c2dec>,
<matplotlib.table.Cell at 0xa0cf18c>,
<matplotlib.table.Cell at 0xa0c2d0c>,
<matplotlib.table.Cell at 0xa0cf84c>,
<matplotlib.table.Cell at 0xa0cf3ac>,
<matplotlib.table.Cell at 0xa0cf08c>,
<matplotlib.table.Cell at 0xa0cf28c>,
<matplotlib.table.Cell at 0xa0cf4cc>,
<matplotlib.table.Cell at 0xa0cf5ec>,
<matplotlib.table.Cell at 0xa0c2fac>,
<matplotlib.table.Cell at 0xa0cf72c>,
<matplotlib.table.Cell at 0xa0c2ecc>],
'children': [<matplotlib.table.Cell at 0xa0c2dec>,
<matplotlib.table.Cell at 0xa0cf18c>,
...snip snap ...
<matplotlib.table.Cell at 0xa0cf72c>,
<matplotlib.table.Cell at 0xa0c2ecc>],
'clip_box': TransformedBbox(Bbox(array([[ 0., 0.],
[ 1., 1.]])), CompositeAffine2D(BboxTransformTo(Bbox(array([[ 0., 0.],
[ 1., 1.]]))), BboxTransformTo(TransformedBbox(Bbox(array([[ 0.25, 0.3 ],
[ 0.95, 0.8 ]])), BboxTransformTo(TransformedBbox(Bbox(array([[ 0., 0.],
[ 8., 6.]])), Affine2D(array([[ 80., 0., 0.],
[ 0., 80., 0.],
[ 0., 0., 1.]])))))))),
'clip_on': True,
'clip_path': None,
'contains': None,
'figure': <matplotlib.figure.Figure at 0x9eaf56c>,
'gid': None,
'label': '',
'picker': None,
'rasterized': None,
'snap': None,
'transform': BboxTransformTo(TransformedBbox(Bbox(array([[ 0.25, 0.3 ],
[ 0.95, 0.8 ]])), BboxTransformTo(TransformedBbox(Bbox(array([[ 0., 0.],
[ 8., 6.]])), Affine2D(array([[ 80., 0., 0.],
[ 0., 80., 0.],
[ 0., 0., 1.]])))))),
'transformed_clip_path_and_affine': (None, None),
'url': None,
'visible': True,
'zorder': 0}

# we now get all the cells ...
[97]: cells = prop['child_artists']

In [98]: cells
Out[98]:
[<matplotlib.table.Cell at 0xa0c2dec>,
<matplotlib.table.Cell at 0xa0cf18c>,
... snip snap...
<matplotlib.table.Cell at 0xa0cf72c>,
<matplotlib.table.Cell at 0xa0c2ecc>]

In [99]:cell=cells[0]
In [100]: cell # press tab here to see cell's attributes

Display all 122 possibilities? (y or n)
cell.PAD
cell.add_callback
...snip snap ...
cell.draw
cell.eventson
cell.figure
...snip snap ...
In [100]: cell.set_h
cell.set_hatch cell.set_height

# this looks promising no? Hell, I love python ;-)
wait, let's examine something first ...
In [100]: cell.get_height()
Out[100]: 0.055555555555555552
In [101]: cell.set_height(0.1) # we just 'doubled' the height...
In [103]: pyplot.show()

and TA DA:

Table with modified height for one cell

Now, I challege you to change the height of all the cells, using a for loop.
Should not be so hard.
Would be nice to win that bounty ;-)

Automatically adjusting columns width with pandastable

If you look into the source of pandastable, there is an attribute maxcellwidth of Table which is set to 300 by default. Therefore even though you have called adjustColumnWidths() the width of each column cannot be greater than maxcellwidth.

Set maxcellwidth to a larger value (enough to hold the longest string):

pt = Table(frame, dataframe=df, width=300, maxcellwidth=1500)

Python: Flexible way to format string output into a table without using a non-standard library

There are not many ways. To be able to know the column width, you first need to read your data and to calculate the appropriate width per column.

Only then you can generate the table.

Here is a naive implementation:

def mktable(dic, header=None):
# get max col width
col_widths = list(map(max, zip(*(map(lambda x: len(str(x)), (k,*v))
for k,v in dic.items()))))

# default numeric header if missing
if not header:
header = range(1, len(col_widths)+1)

header_widths = map(lambda x: len(str(x)), header)

# correct column width if headers are longer
col_widths = [max(c,h) for c,h in zip(col_widths, header_widths)]

# create separator line
line = '+%s+' % '+'.join('-'*(w+2) for w in col_widths)
#line = '-' * (sum(col_widths)+(len(col_widths)-1)*3+4)

# create formating string
fmt_str = '| %s |' % ' | '.join(f'{{:<{i}}}' for i in col_widths)

# header
print(line)
print(fmt_str.format(*header))
print(line)

# data
for k, v in countries_dict.items():
print(fmt_str.format(k, *v))

# footer
print(line)
mktable(countries_dict, header=('Country', 'Capital', '----- Area -----'))

output:

+---------------+------------------+------------------+
| Country | Capital | ----- Area ----- |
+---------------+------------------+------------------+
| Russia | Moscow | 17098246 |
| Canada | Ottawa | 9984670 |
| China | Beijing | 9596961 |
| United States | Washington, D.C. | 9525067 |
| Brazil | Brasília | 8515767 |
+---------------+------------------+------------------+


Related Topics



Leave a reply



Submit