Reputation: 2060
I'm creating a table in matplotlib, but the table headers are long strings, and the table values are numbers with only a few digits. This leaves me with two bad options: either my table is much wider than necessary, or my headers overlap. To fix this, I'd like to rotate the table headings (possibly up to 90 degrees). In other words, I want to do this in python
Here's my simplified code right now:
import matplotlib, numpy
import matplotlib.pyplot as plt
data=numpy.array([[1, 2],[3,4]])
headings=['long heading 1', 'long heading 2']
fig=plt.figure(figsize=(5,2))
ax=fig.add_subplot(111, frameon=False, xticks=[], yticks=[])
the_table = plt.table(cellText=data, rowLabels=headings, colLabels=headings, colWidths=[0.3]*data.shape[1], loc='center') #0.3 for second image, 0.03 for first
#the_table.auto_set_font_size(False) #comment out for second image
#the_table.set_fontsize(10) #comment out for second image
the_table.scale(1, 1.6)
plt.show()
This produces either the squished image, or the super-wide image (both shown below). In my actual code, the table is ~30 x 30, so the cells can't be very wide. Does anybody know how to rotate the column titles to fix this spacing issue?
Upvotes: 4
Views: 3744
Reputation: 948
The following worked for me.
create the table
fig = plt.figure( figsize=(pageWidthInInches, pageHeightInInches) )
panel = plotUtils.createPanelSameSizeAsFig(fig)
tablePanel = panel.table(
cellText=cellText
# ,rowLabels=rowLabels
# ,colLabels=colLabels
#,loc='center' # center table in panel, title is in center
#,loc='bottom' # center table in panel does not work well
,loc='best'
#,rowColours=aedwip,
#,colColourslist=aedwip
,cellColours= cellColors
)
# get rid of bar chart axis and box
panel.get_xaxis().set_visible(False)
panel.get_yaxis().set_visible(False)
tablePanel.scale(1, 1.5)
plt.box(on=None)
panel.set_title(title)
Now add the col header. Note my table did not have row labels, you may have to tweak the startX position
tcell = table._cells[(0, 0)]
cellWidth = tcell.get_width()
startX = tcell.get_x() - cellWidth
y = 0.99 #0.98 #0.975 #0.96 #1
headings = sampleDF.columns
for i in range(len(headings)):
heading = headings[i]
x = startX + i * cellWidth
panel.text(x, y, heading, horizontalalignment="left",
verticalalignment="baseline", rotation=45, fontsize=4)
Upvotes: 0
Reputation: 1
I just found an other solution:
for cell in table._cells:
if cell[0] ==0:
table._cells[cell].get_text().set_rotation(90)
The first loop is for going through all cells, the second is for picking the first row/header.
if cell[1] =- -1
this would selecte the the first column, which you might want to rotate, too.
Then you can rotate the cell text by e.g. 90 °.
Upvotes: 0
Reputation: 2060
I figured it out. It's not pretty, but it works. I added two annotations for each column - the text, and a line to separate it from the next column header. I had to define some parameters that apply to the table and the fancy labels (width, height, col_width), and some parameters to make the fancy labels line up correctly. This solution worked fine on my 30x30 table.
import matplotlib, numpy
import matplotlib.pyplot as plt
width=5
height=3
col_width=.075
data=numpy.array([[1, 2,5],[3,4,7],[7,9,5]])
headings=['long heading 1', 'long heading 2', 'longish 3']
fig=plt.figure(figsize=(width,height))
ax=fig.add_subplot(111, frameon=False, xticks=[], yticks=[])
the_table = plt.table(cellText=data, rowLabels=headings,
colWidths=[col_width]*data.shape[1], loc='center') #remove colLabels
the_table.auto_set_font_size(False)
the_table.set_fontsize(10)
the_table.scale(1, 1.6)
#custom heading titles - new portion
hoffset=0.42 #find this number from trial and error
voffset=0.66 #find this number from trial and error
line_fac=0.98 #controls the length of the dividing line
count=0
for string in headings:
ax.annotate(' '+string, xy=(hoffset+count*col_width,voffset),
xycoords='axes fraction', ha='left', va='bottom',
rotation=45, size=10)
#add a dividing line
ax.annotate('', xy=(hoffset+(count+0.5)*col_width,voffset),
xytext=(hoffset+(count+0.5)*col_width+line_fac/width,voffset+line_fac/height),
xycoords='axes fraction', arrowprops={'arrowstyle':'-'})
count+=1
plt.show()
Upvotes: 3