Formatted tables in IPython
CommentsThis post is available as an IPython notebook on Github Gist, or on nbviewer.
I've been working on making presentations in IPython lately. I often end up using tables in my presentations and publications, and wanted a way to format them nicely from IPython notebooks. So I added the PrettyTable class, along with some basic themes, to my fork of ipywidgets. This allows for easily formatting the way tables are displayed. This is similar in spirit to the ipy_table repository, but with more modular themes and the ability to add any property to tables that you can do with CSS.
Getting started
Here's an example of how it works to get started. To create a PrettyTable, import ipywidgets and pass a pandas data frame:
from ipywidgets import *
import pandas as pd
df = pd.DataFrame({"x":[1,2,3], "y":[6,4,3], "z":["testing","pretty","tables"], "f":[0.023432, 0.234321,0.5555]})
pt = PrettyTable(df)
pt
We can make it a little bit prettier by giving it a style theme and centering the table:
pt = PrettyTable(df, tstyle=TableStyle(theme="theme1"), center=True)
pt
There are many options for formatting and styling, described below.
Setting cell styles
You can set cell styles by passing PrettyTable.set_cell_style
a CellStyle
object, or by specifying CSS properties. You can tell set_cell_style
a specific set of rows and columns to apply the style to with the arguments rows=
and cols=
. Or leave those arguments blank to apply to all rows/columns:
# Set cell style using a CellStyle object
pt = PrettyTable(df, tstyle=TableStyle(theme="theme1"), center=True)
cs = CellStyle()
cs.set("background-color", "red")
cs.set("color", "white")
pt.set_cell_style(style=cs)
pt
# Only for a subset of rows/columns
pt = PrettyTable(df, tstyle=TableStyle(theme="theme1"), center=True)
cs = CellStyle()
cs.set("background-color", "red")
cs.set("color", "white")
pt.set_cell_style(style=cs, rows=[1,2], cols=[2])
pt
# Set styles using keywords (these are all CSS properties, just replace "-" with "_")
pt = PrettyTable(df, tstyle=TableStyle(theme="theme1"), center=True)
pt.set_cell_style(font_weight="bold", color="purple", background_color="yellow", rows=[1], cols=[2,3])
pt
You can do the same thing for row/header styles using set_row_header_style
and set_col_header_style
(and set_corner_style
for the awkward corner cell in the case of both row and column headings). Use the indices=
argument to only apply to certain headings.
pt = PrettyTable(df, tstyle=TableStyle(theme="theme1"), center=True, header_row=True)
pt.set_row_header_style(color="blue")
pt.set_col_header_style(background_color="white", font_weight="bold", indices=[2])
pt.set_corner_style(background_color="red")
pt
Sometimes you may just want to update the style of a cell, rather than starting over. There are also functions to do this. They behave the same way as the "set style" functions described above, except they just add to the existing style:
pt = PrettyTable(df, tstyle=TableStyle(theme="theme1"), center=True, header_row=True)
pt.set_cell_style(font_weight="bold", color="purple", background_color="yellow", rows=[1], cols=[2,3])
pt.update_cell_style(background_color="pink", rows=[1], cols=[3])
pt.update_row_header_style(background_color="blue", indices=[0])
pt.update_col_header_style(color="red")
pt.update_corner_style(background_color="purple")
pt
Finally, there are a set of "reset style" functions to reset the styles of cells, headings, and corner cells.
pt = PrettyTable(df, tstyle=TableStyle(theme="theme1"), center=True, header_row=True)
pt.set_cell_style(font_weight="bold", color="purple", background_color="yellow", rows=[1], cols=[2,3])
pt.reset_cell_style(rows=[1], cols=[2])
pt.reset_row_header_style(indices=[1])
pt.reset_col_header_style()
pt
Setting cell formats
You can also pass the functions shown above an argument format_function
, which will tell the PrettyTable how to display the data. By default, it will call the str
of each method. For instance, maybe you have a column of floats you want rounded to 3 decimal places, and another column you'd like to have in scientific notation:
df = pd.DataFrame({"FloatColumn": [0.0324234, 0.23432111, 0.555555], "SciNotColumn":[1e10, 4.232e-6, 53e-8]})
pt = PrettyTable(df, tstyle=TableStyle(theme="theme1"), center=True)
pt.set_cell_style(cols=[0], format_function=lambda x: "%.3f"%x)
def SciNot(x):
xx = "%.2E"%x
base, exp = xx.split("E")
return "%s × 10<sup>%s</sup>"%(base, int(exp))
pt.set_cell_style(cols=[1], format_function=SciNot)
pt
Styles
This section shows the insides of how the styling is set up. All of the styling is based on CSS. The major class is CellStyle
. Every time you call a function to modify the style of a part of the table, it is modifying the CellStyle of specific cells.
class CellStyle(object):
""" Styles for cells PrettyTable """
def __init__(self):
self.style_elements = {} # dictionary of CSS property -> value
self.format_function = None
def set(self, key, value):
self.style_elements[key] = value
def css(self):
style = ""
for key in self.style_elements:
style += "%s: %s;"%(key, self.style_elements[key])
return style
def column_format(self, x):
if self.format_function is None: return str(x)
else: return self.format_function(x)
def copy(self):
c = CellStyle()
c.style_elements = self.style_elements.copy()
c.format_function = self.format_function
return c
This basically stores a dictionary of CSS properties and values, and a function for how to format the data in cells. By using set(key, value)
, you can pass any CSS property.The functioncss()
converts the style elements to CSS that can be used to style tables.
The wrapper class TableStyle
keeps track of separate styles for cells and headings. It also has some preset themes (which I will add to as I need other themes).
class TableStyle(object):
"""
Keep track of styles for cells/headers in PrettyTable
"""
def __init__(self, theme=None):
self.row_head_style = CellStyle()
self.col_head_style = CellStyle()
self.cell_style = CellStyle()
self.corner_style = CellStyle()
# add themes as needed
if theme == "basic":
...
if theme == "theme1":
...
Of course all the properties can be customized, as shown above.
Conclusion
I plan to add themes to this as I go along, and welcome new additions. The main reason I made this class of tables was so I could use them to animate tables in my presentations, which I describe in my next post.