from jinja2 import Template
# The baseline jinja2 template for HTML output.
t=Template("""
{% if caption %}
{{caption}}
{% endif %}
{% for r in head %}
{% for c in r %}
<{{c.type}} class="{{c.class}}">{{c.value}}
{% endfor %}
{% endfor %}
{% for r in body %}
{% for c in r %}
<{{c.type}} class="{{c.class}}">{{c.value}}
{% endfor %}
{% endfor %}
""")
# the implementation code. very small.
ROW_HEADING_CLASS="row_heading"
COL_HEADING_CLASS="col_heading"
DATA_CLASS="data"
BLANK_CLASS="blank"
BLANK_VALUE=""
def translate(df,cell_context=None):
import uuid
cell_context = cell_context or dict()
n_rlvls =df.index.nlevels
n_clvls =df.columns.nlevels
rlabels=df.index.tolist()
clabels=df.columns.tolist()
if n_rlvls == 1:
rlabels = [[x] for x in rlabels]
if n_clvls == 1:
clabels = [[x] for x in clabels]
clabels=zip(*clabels)
head=[]
for r in range(n_clvls):
row_es = [{"type":"th","value":BLANK_VALUE ,"class": " ".join([BLANK_CLASS])}]*n_rlvls
for c in range(len(clabels[0])):
cs = [COL_HEADING_CLASS,"level%s" % r,"col%s" %c]
cs.extend(cell_context.get("col_headings",{}).get(r,{}).get(c,[]))
row_es.append({"type":"th","value": clabels[r][c],"class": " ".join(cs)})
head.append(row_es)
body=[]
for r in range(len(df)):
cs = [ROW_HEADING_CLASS,"level%s" % c,"row%s" % r]
cs.extend(cell_context.get("row_headings",{}).get(r,{}).get(c,[]))
row_es = [{"type":"th","value": rlabels[r][c],"class": " ".join(cs)}
for c in range(len(rlabels[r]))]
for c in range(len(df.columns)):
cs = [DATA_CLASS,"row%s" % r,"col%s" %c]
cs.extend(cell_context.get("data",{}).get(r,{}).get(c,[]))
row_es.append({"type":"td","value": df.iloc[r][c],"class": " ".join(cs)})
body.append(row_es)
# uuid required to isolate table styling from others
# in same notebook in ipnb
u = str(uuid.uuid1()).replace("-","_")
return dict(head=head, body=body,uuid=u)
# first, vanilla
df=mkdf(10,5,r_idx_nlevels=3,c_idx_nlevels=2)
from IPython.display import HTML,display
ctx= translate(df)
ctx['caption']="Just a table, but rendered using a template with lots of classes to style against"
display(HTML(t.render(**ctx)))
def zebra(color1, color2):
return [dict(selector="td.data:nth-child(2n)" ,
props=[("background-color",color1)]),
dict(selector="td.data:nth-child(2n+1)" ,
props=[("background-color",color2)])]
ctx= translate(df)
style=[]
style.extend(zebra("#aaa","#ddd"))
ctx['style']=style
ctx['caption']="A zebra table"
display(HTML(t.render(**ctx)))
def tag_col(n,c="grey10", with_headings=False):
selector="td.col%d" % n
if not with_headings:
selector+=".data"
return [dict(selector=selector,
props=[("background-color",c)])]
def tag_row(n,c="grey10", with_headings=False):
selector="td.row%d" % n
if not with_headings:
selector+=".data"
return [dict(selector=selector,
props=[("background-color",c)])]
ctx= translate(df)
style=[]
style.extend(tag_col(2,"beige"))
style.extend(tag_row(3,"purple"))
ctx['style']=style
ctx['caption']="Highlight rows/cols by index"
display(HTML(t.render(**ctx)))
def round_corners(radius):
props_bl=[
("-moz-border-radius-bottomleft", "%dpx" % radius ),
("-webkit-border-bottom-left-radius", "%dpx" % radius ),
("border-bottom-left-radius", "%dpx" % radius )
]
props_br=[
("-moz-border-radius-bottomright", "%dpx" % radius ),
("-webkit-border-bottom-right-radius", "%dpx" % radius ),
("border-bottom-right-radius", "%dpx" % radius )
]
props_tl=[
("-moz-border-radius-topleft", "%dpx" % radius ),
("-webkit-border-top-left-radius", "%dpx" % radius ),
("border-top-left-radius", "%dpx" % radius )
]
props_tr=[
("-moz-border-radius-topright", "%dpx" % radius ),
("-webkit-border-top-right-radius", "%dpx" % radius ),
("border-top-right-radius", "%dpx" % radius )
]
return [dict(selector="td",
props=[("border-width","1px")]),
dict(selector="",
props=[("border-collapse","separate")]),
dict(selector="tr:last-child th:first-child",
props=props_bl),
dict(selector="tr:last-child td:last-child",
props=props_br),
dict(selector="tr:first-child th.col0",
props=props_tl),
dict(selector="tr:first-child th.row0:first-child",
props=props_tl),
dict(selector="tr:first-child th:last-child",
props=props_tr),
]
ctx= translate(df)
style=[]
style.extend(round_corners(5))
ctx['caption']="Rounded corners. CSS skills beginning to fail."
ctx['style']=style
display(HTML(t.render(**ctx)))
def color_class(cls, color):
return [dict(selector="td.%s" % cls ,
props=[("background-color",color)])]
def rank_col(n,ranking,u):
data = {i: {n: ["%s-%s" % (u,ranking[i])]} for i in range(len(ranking))}
return {"data": data}
import uuid
u = "U"+str(uuid.uuid1()).replace("-","_")
df=mkdf(9,5,data_gen_f=lambda r,c:np.random.random())
ranking=df.iloc[:,1].argsort().tolist()
cell_context=rank_col(1, ranking, u)
ctx= translate(df,cell_context)
style=[]
# http://colorbrewer2.org/
color_scale=["#fff7ec","#fee8c8","#fdd49e","#fdbb84","#fc8d59","#ef6548","#d7301f","#b30000","#7f0000"]
for intensity in range(9):
style.extend(color_class("%s-%s" % (u,intensity),color_scale[intensity]))
ctx['style']=style
ctx['caption']="And finally, a heatmap based on values"
display(HTML(t.render(**ctx)))