The reason why the elements after
and before
are ignored seems to be that they are part of a multi-index and are actually the names for the index and the columns, retrievable by df.index.name
and df.columns.name
, respectively. If you run df.reset_index()
you'll get this:
after before bad good perfect
0 bad 0 0 0
1 good 0 1 2
2 perfect 1 0
And that's close to what you want, but not quite. One way you can retain your desired information and make your table look good is to retrieve df.index.name
and df.columns.name
before resetting the index, and then rename the first column to before / after
using df.rename(columns={df.columns[0]: iname + ' / ' + cname}, inplace=True)
.
Here's the result:

Complete code for JupyterDash:
import dash
import dash_table
import pandas as pd
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
# data
#df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/solar.csv')
df = pd.DataFrame(np.array([["good", "good"], ["bad", "perfect"], ["perfect", "good"], ["perfect", "good"]]), columns=['after', 'before'])
a = np.unique(df.to_numpy())
df = pd.crosstab(df['before'], df['after']).reindex(columns=a, index=a, fill_value=0)#.reset_index()
#df = df.reset_index(level=['after', 'before'])
iname = df.index.name
cname = df.columns.name
df = df.reset_index()
df.rename(columns={df.columns[0]: iname + ' / ' + cname}, inplace=True)
app = JupyterDash(__name__)
app.layout = dash_table.DataTable(
id='table',
columns=[{"name": i, "id": i} for i in df.columns],
data=df.to_dict('records'),
)
if __name__ == '__main__':
app.run_server(debug=True)
app.run_server(mode='inline', port = 8077, dev_tools_ui=True,
dev_tools_hot_reload =True, threaded=True)
If JupyterDash is not your cup of tea, just follow the steps in this post to turn it into a standard dash app