17

I'm trying to make nicely formatted tables from pandas. Some of my column names are far too long. The cells for these columns are large cause the whole table to be a mess.

In my example, is it possible to rotate the column names as they are displayed?

data = [{'Way too long of a column to be reasonable':4,'Four?':4},
        {'Way too long of a column to be reasonable':5,'Four?':5}]
pd.DataFrame(data)

I would like to rotate the names of the columns as displayed here.

Dan Fiorino
  • 388
  • 1
  • 3
  • 17
  • 1
    could you please added tag `jupyter notebook` – BENY Oct 12 '17 at 18:24
  • Obviously this depends on your situation, but maybe it makes sense to just shorten/improve the column names? But for a workaround, you could consider converting to a "fake" multi-index. E.g. if column name is "david s pumpkins" you could have "david s" as the top level and "pumpkins" as the second level. Obviously that's not what multi-indexes are for, but I think it would basically do what you want. – JohnE Oct 13 '17 at 02:37
  • @JohnE, Thanks for the suggestion. A text wrap is what @Wen had suggested which is deleted but was: `df.style.set_table_styles([dict(selector="th",props=[('max-width', '50px')])])` It's a good work-around that works for phrases, like in my example. I also have long words though. I'm still curious to see if rotation is possible! I'm no CSS selector expert, but I thought this should work but doesn't: `dfoo.style.set_table_styles([dict(selector="th",props=[('text-orientation', 'upright')])])` – Dan Fiorino Oct 13 '17 at 15:49
  • I just re-read the doc page (http://pandas.pydata.org/pandas-docs/version/0.19.2/style.html) which states that _'You can only style the values, not the index or columns'_, but the doc shows columns and indices being formatted. Maybe this is is just starting to be addressed. – Dan Fiorino Oct 13 '17 at 16:18

5 Answers5

15

Something like:

data = [{'Way too long of a column to be reasonable':4,'Four?':4},
        {'Way too long of a column to be reasonable':5,'Four?':5}]
dfoo = pd.DataFrame(data)
dfoo.style.set_table_styles(
    [dict(selector="th",props=[('max-width', '80px')]),
        dict(selector="th.col_heading",
                 props=[("writing-mode", "vertical-rl"), 
                        ('transform', 'rotateZ(-90deg)'),
                        ])]
)

is probably close to what you want:

enter image description here

see result here

a_k_v
  • 1,558
  • 7
  • 18
Bobain
  • 166
  • 1
  • 4
  • Changing my accepted answer. This does the task using only the Pandas library and will display in a JupyterLab. Well done! – Dan Fiorino Nov 15 '18 at 22:53
  • 2
    I found that (in Firefox) the `rotateZ(-90deg)` argument caused the headings to *rotate back* so they were horizontal again. I got a much nicer layout with `('vertical-align', 'text-top'), ('transform', 'rotateZ(180deg)')`, which aligned the text along the first row of the table. – Nate Apr 03 '19 at 20:19
  • Following @Nate's comment the setting which rotated the column names correctly was `props=[('vertical-align', 'text-top'), ('transform', 'rotateZ(-90deg)')]` – Paul Rougieux Jun 21 '19 at 09:48
3

Looking at the pybloqs source code for the accepted answer's solution, I was able to find out how to rotate the columns without installing pybloqs. Note that this also rotates the index, but I have added code to remove those.

from IPython.display import HTML, display

data = [{'Way too long of a column to be reasonable':4,'Four?':4},
        {'Way too long of a column to be reasonable':5,'Four?':5}]
df = pd.DataFrame(data)

styles = [
    dict(selector="th", props=[("font-size", "125%"),
                               ("text-align", "center"),
                              ("transform", "translate(0%,-30%) rotate(-5deg)")
                              ]),
    dict(selector=".row_heading, .blank", props= [('display', 'none;')])
]

html = df.style.set_table_styles(styles).render()
display(HTML(html))

enter image description here

scottlittle
  • 18,866
  • 8
  • 51
  • 70
3

I placed @Bobain's nice answer into a function so I can re-use it throughout a notebook.

def format_vertical_headers(df):
    """Display a dataframe with vertical column headers"""
    styles = [dict(selector="th", props=[('width', '40px')]),
              dict(selector="th.col_heading",
                   props=[("writing-mode", "vertical-rl"),
                          ('transform', 'rotateZ(180deg)'), 
                          ('height', '290px'),
                          ('vertical-align', 'top')])]
    return (df.fillna('').style.set_table_styles(styles))

format_vertical_headers(pandas.DataFrame(data))

enter image description here

Paul Rougieux
  • 10,289
  • 4
  • 68
  • 110
  • I'm not getting the same layout - text is upside down horizontal and wrapped. . Not enough space to explain the limited progress I have made with trying different parameters. Maybe add another comment to explain them? print(pd.__version__) says my pandas version is 1.2,1 and jupyter version is jupyter core : 4.7.1 jupyter-notebook : 6.4.3 qtconsole : not installed ipython : 7.26.0 ipykernel : 6.1.0 jupyter client : 6.1.12 jupyter lab : 3.1.6 nbconvert : 6.1.0 ipywidgets : not installed nbformat : 5.1.3 traitlets : 5.0.5 – Bill Aug 13 '21 at 22:51
  • Best mod I've found is below but still not vertical: def format_vertical_headers(df): """Display a dataframe with vertical column headers""" styles = [dict(selector="th", props=[('width', '40px')]), dict(selector="th.col_heading", props=[("writing-mode", "vertical-rl"), ('transform', 'rotateZ(180deg)'), ('height', '290px'), ('vertical-align', 'top')])] return (df.fillna('').style.set_table_styles(styles)) format_vertical_headers(pd.DataFrame(data)) – Bill Aug 13 '21 at 22:55
  • @Bill please ask a new question and provide a [reproducible example](https://stackoverflow.com/questions/20109391/how-to-make-good-reproducible-pandas-examples) – Paul Rougieux Aug 16 '21 at 12:03
2

Using the Python library 'pybloqs' (http://pybloqs.readthedocs.io/en/latest/), it is possible to rotate the column names as well as add a padding to the top. The only downside (as the documentation mentions) is that the top-padding does not work inline with Jupyter. The table must be exported.

import pandas as pd
from pybloqs import Block
import pybloqs.block.table_formatters as tf
from IPython.core.display import display, HTML

data = [{'Way too long of a column to be reasonable':4,'Four?':4},
        {'Way too long of a column to be reasonable':5,'Four?':5}]
dfoo =pd.DataFrame(data)

fmt_header = tf.FmtHeader(fixed_width='5cm',index_width='10%', 
                          top_padding='10cm', rotate_deg=60)
table_block = Block(dfoo, formatters=[fmt_header])

display(HTML(table_block.render_html()))
table_block.save('Table.html')

Table in HTML with rotated column names

Dan Fiorino
  • 388
  • 1
  • 3
  • 17
1

I can get it so that the text is completely turned around 90 degrees, but can't figure out how to use text-orientation: upright as it just makes the text invisible :( You were missing the writing-mode property that has to be set for text-orientation to have any effect. Also, I made it only apply to column headings by modifying the selector a little.

dfoo.style.set_table_styles([dict(selector="th.col_heading",props=[("writing-mode", "vertical-lr"),('text-orientation', 'upright')])])

Hopefully this gets you a little closer to your goal!

Louise Davies
  • 14,781
  • 6
  • 38
  • 41