wesm/pandas — Issue #459: DataFrame rendering stylists?
Author: lodagro
Created: 2011-12-07 15:29:54+00:00
State: open

Been working a bit on the idea of DataFrame rendering stylists. Would like some feedback on the idea / API.

Idea is that on top of the *formatters* used in ```DataFrame.to_string()``` and ```DataFrame.to_html()```, i would like to add *stylists*. A *stylist* takes as input a string and returns a string, allowing to alter the string representation of each individual cell of a DataFrame. Each DataFrame element goes first through a *formatter* and the string result from this goes through a *stylist*. This allows to use e.g ANSI escape sequences to change text, background color of how a DataFrame cell is displayed on screen. Or in the ```DataFrame.to_html()``` case, a *stylists* can add html div, class tags - which combined with css change the rendering of a html table cell.

This is the description of *stylists* and new API for ```DataFrame.to_string()``` and ```DataFrame.to_html```

```python
class DataFrameFormatter(object):
"""
Render a DataFrame

self.to_string() : console-friendly tabular output
self.to_html() : html table
"""
def __init__(self, frame, buf=None, columns=None, col_space=None,
na_rep='NaN', formatters=None, float_format=None,
sparsify=True, index_names=True, stylists=None):
"""
Parameters
----------
stylists : object when indexed with [row_key][column_key] returns a
callable object taking as input a string and outputs a string.
When rendering a DataFrame, each cell can be altered individually
using stylists, a cell gets formatted first with formatters after
which a style can be applied. For example the stylist can add ansi
escape sequences to display a cell in a different color, add
html class or div tags, ...
If stylist[row_key][column_key] does not exist, no styling is done
to this particular cell of the DataFrame.
"""

class DataFrame():
def to_string(self, buf=None, columns=None, colSpace=None,
na_rep='NaN', formatters=None, float_format=None,
sparsify=True, nanRep=None, index_names=True,
stylists=None):

def to_html(self, buf=None, columns=None, colSpace=None,
na_rep='NaN', formatters=None, float_format=None,
sparsify=True, index_names=True, stylists=None):
```

A little demo when using stylists on screen:

```python
import pandas
import numpy as np
from colorama import Fore, Back, Style

df = pandas.DataFrame(np.random.randint(0, 10, (5, 2)),
columns=['A', 'B'],
index=['a', 'b', 'c', 'd', 'e'])

red = lambda x: Back.RED + x + Back.RESET
green = lambda x: Back.GREEN + x + Back.RESET
yellow = lambda x: Back.YELLOW + x + Back.RESET

stylists = {'a': {'A': red, 'B': yellow},
'b': {'A': green},
'c': {'B': green}}
```

Results in the following (there should be an image here below):

![stylists demo](https://lh6.googleusercontent.com/-i-Ujjer_6SA/Tt96JZ45VqI/AAAAAAAAAM8/-vUM0Te4UUQ/s318/pandas_dataframe_stylists_demo.JPG)

As you can see, more work is needed. The ANSI escape sequences are taken into account when determining the number of characters needed for each column, this is not needed since they are invisible. Solution is e.g to set column widths before stylists are applied, implying that a stylist can not change the width of a column - seems reasonable.

Or maybe this should be taken one step further and find some way to combine the functionality of both *formatters* and *stylists* into one (have not thought about how this should look)? Ideas, feedback?