A lot could be learned from html
syntax in pandas.DataFrame.to_html
. I’ll demonstrate a few approaches. Database that I use is:
import pandas as pd
from dash import html
d = {'Full':
{'Lithuania':
{'Aukštaitija': ('Panevėžys', '6.3°C'),
'Žemaitija': ('Telšiai', '5.9°C'),
'Dzūkija': ('Alytus', '6.4°C'),
'Suvalkija': ('Marijampolė', 'No data'),
'Mažoji Lietuva': ('Tilžė', '8.2°C')},
'Latvia':
{'Kurzeme': ('Jelgava', '7.1°C'),
'Zemgale': ('Jelgava', '7.1°C'),
'Vidūmō': ('Riga', '6.1°C'),
'Latgale': ('Daugavpils', '5.5°C')}
},
'Short':
{'Lithuania':
{'Aukštaitija': ('Panevėžys', '6.3°C'),
'Žemaitija': ('Telšiai', '5.9°C'),
'Dzūkija': ('Alytus', '6.4°C'),
'Suvalkija': ('Marijampolė', 'No data')},
'Latvia':
{'Kurzeme & Zemgale': ('Jelgava', '7.1°C'),
'Vidūmō': ('Riga', '6.1°C'),
'Latgale': ('Daugavpils', '5.5°C')}
},
}
df = pd.concat([pd.concat([pd.DataFrame(d2).T for d2 in d1.values()],
keys=d1.keys()) for d1 in d.values()],
keys=d.keys())
df.index.set_names(['Portion', 'Country', 'Cultural Region'], inplace=True)
df.columns = ['Capital', 'Average Temperature']
Using html.Iframe
+ df.to_html
This is quite a simple way. Although, I don’t really a design that pandas
use to distinguish between multiindex names and column names:
html.Iframe(width="800", height="500", sandbox='', srcDoc=df.to_html())
And here it goes:
Creating html
from scratch
You could rewrite a script of df.html
in Dash
& customize your style, this is my MWE that uses a smaller database:
table_style = {'border': '3px solid'}
header_section_style = {'border': '2px solid'}
body_section_style = {'border': '2px solid'}
body_index_cell_style = {'text-align': 'left',
'border':'1px solid',
'backgroundColor': 'rgb(210, 210, 255)',
'valign':'top',
'font-weight': 'bold'}
body_column_cell_style = {'text-align': 'right',
'border':'1px solid grey',
'backgroundColor': 'rgb(210, 255, 210)'}
header_index_cell_style = {'text-align': 'left',
'border':'2px solid',
'backgroundColor': 'rgb(165, 165, 255)',
'valign':'top',
'font-weight': 'bold'}
header_column_cell_style = {'text-align': 'right',
'border':'2px solid',
'backgroundColor': 'rgb(165, 255, 165)',
'font-weight': 'bold'}
# CREATE TABLE MANUALLY (thanks to pd.to_html)
table = html.Table([
html.Thead([
html.Tr([
html.Th('Country', style=header_index_cell_style),
html.Th('Cultural Region', style=header_index_cell_style),
html.Th('Capital', style=header_column_cell_style),
html.Th('Avg. temperature', style=header_column_cell_style)
])
], style=header_section_style),
html.Tbody([
html.Tr([
html.Td('Lithuania', rowSpan=5, style=body_index_cell_style),
html.Td('Aukštaitija', style=body_index_cell_style),
html.Td('Panevėžys', style=body_column_cell_style),
html.Td('6.3°C', style=body_column_cell_style)
]),
html.Tr([
html.Td('Žemaitija', style=body_index_cell_style),
html.Td('Telšiai', style=body_column_cell_style),
html.Td('5.9°C', style=body_column_cell_style)
]),
html.Tr([
html.Td('Dzūkija', style=body_index_cell_style),
html.Td('Alytus', style=body_column_cell_style),
html.Td('6.4°C', style=body_column_cell_style)
]),
html.Tr([
html.Td('Suvalkija', style=body_index_cell_style),
html.Td('Marijampolė', style=body_column_cell_style),
html.Td('No data', style=body_column_cell_style)
]),
html.Tr([
html.Td('Mažoji Lietuva', style=body_index_cell_style),
html.Td('Tilžė', style=body_column_cell_style),
html.Td('8.2°C', style=body_column_cell_style)
]),
html.Tr([
html.Td('Latvia', rowSpan=4, style=body_index_cell_style),
html.Td('Kurzeme', style=body_index_cell_style),
html.Td('Jelgava', style=body_column_cell_style),
html.Td('7.1°C', style=body_column_cell_style)
]),
html.Tr([
html.Td('Zemgale', style=body_index_cell_style),
html.Td('Jelgava', style=body_column_cell_style),
html.Td('7.1°C', style=body_column_cell_style)
]),
html.Tr([
html.Td('Vidūmō', style=body_index_cell_style),
html.Td('Riga', style=body_column_cell_style),
html.Td('6.1°C', style=body_column_cell_style)
]),
html.Tr([
html.Td('Latgale', style=body_index_cell_style),
html.Td('Daugavpils', style=body_column_cell_style),
html.Td('5.5°C', style=body_column_cell_style)
])
], style=body_section_style)
], style=table_style)
Automating a process of html
creation
I know it seems a nightmare to build it from schratch so you could feel free to use a method that does this job for you:
#in addition to import pandas as pd & from dash import html:
import numpy as np
def multiindex_table(df):
# storing rowSpan values for every cell of index;
# if rowSpan==0, html item is not going to be included
pos = np.diff(df.index.codes, axis=1, prepend=-1)
for row in pos:
counts = np.diff(np.flatnonzero(np.r_[row, 1]))
row[row.astype(bool)] = counts
# filling up header of table;
column_names = df.columns.values
headTrs = html.Tr([html.Th(n, style=header_index_cell_style) for n in df.index.names] +
[html.Th(n, style=header_column_cell_style) for n in column_names])
# filling up rows of table;
bodyTrs = []
for rowSpanVals, idx, col in zip(pos.T, df.index.tolist(), df.to_numpy()):
rowTds = []
for name, rowSpan in zip(idx, rowSpanVals):
if rowSpan != 0:
rowTds.append(html.Td(name, rowSpan=rowSpan, style=body_index_cell_style))
for name in col:
rowTds.append(html.Td(name, style=body_column_cell_style))
bodyTrs.append(html.Tr(rowTds))
table = html.Table([
html.Thead(headTrs, style=header_section_style),
html.Tbody(bodyTrs, style=body_section_style)
], style=table_style)
return table
It should work if you run this demo:
import dash
app = dash.Dash(__name__)
app.layout = multiindex_table(df)
if __name__ == '__main__':
app.run_server(debug=True)