This 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:

In [1]:
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
Out[1]:
fxyz
0.02343216testing
0.23432124pretty
0.555533tables

We can make it a little bit prettier by giving it a style theme and centering the table:

In [2]:
pt = PrettyTable(df, tstyle=TableStyle(theme="theme1"), center=True)
pt
Out[2]:
fxyz
0.02343216testing
0.23432124pretty
0.555533tables

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:

In [3]:
# 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
Out[3]:
fxyz
0.02343216testing
0.23432124pretty
0.555533tables
In [4]:
# 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
Out[4]:
fxyz
0.02343216testing
0.23432124pretty
0.555533tables
In [5]:
# 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
Out[5]:
fxyz
0.02343216testing
0.23432124pretty
0.555533tables

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.

In [6]:
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
Out[6]:
fxyz
00.02343216testing
10.23432124pretty
20.555533tables

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:

In [7]:
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
Out[7]:
fxyz
00.02343216testing
10.23432124pretty
20.555533tables

Finally, there are a set of "reset style" functions to reset the styles of cells, headings, and corner cells.

In [8]:
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
Out[8]:
fxyz
00.02343216testing
10.23432124pretty
20.555533tables

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:

In [9]:
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 &times; 10<sup>%s</sup>"%(base, int(exp))
pt.set_cell_style(cols=[1], format_function=SciNot)
pt
Out[9]:
FloatColumnSciNotColumn
0.0321.00 × 1010
0.2344.23 × 10-6
0.5565.30 × 10-7

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.