I am trying to build a figure with plotly for which the underlying data can be updated according to 2 different buttons.
The problem I am facing is that I need to know the values of each button to calculate the data to be displayed. My situation can be summarized in the following picture:
Here is a code sample illustrating what I am trying to do: I have some periodic data about the state of individuals from which I would like to calculate aggregate state numbers from period N to period M (a transition table). I would like the variabilize the values of N and M into the buttons but I did not manage to retrieve both values with plotly (in the following code, when button_1
is set to N , then M is arbitrarily fixed independantly of button_2
and vice versa).
Is there a way to do this?
import pandas as pd
import numpy as np
import plotly.graph_objects as go
def generate_detailed_data():
import string
persons = list(string.ascii_lowercase)
n_periods = 4
n_persons = len(persons)
series_period = list(np.arange(1, n_periods + 1, 1))*n_persons
series_period.sort()
series_person = persons*n_periods
series_state = np.random.choice(['A', 'B'], n_periods * n_persons)
df = pd.DataFrame(data={'period':series_period, 'person':series_person, 'state':series_state})
return df
def build_transition_table(detailed_data, period_ref, period_evol):
df_transition_detailed = pd.merge(left = detailed_data.query(f'period == {period_ref}')[['person', 'state']],
right = detailed_data.query(f'period == {period_evol}')[['person', 'state']],
how = 'outer',
on = 'person',
suffixes=('_ref', '_evol'),
)
df_transition_tmp = df_transition_detailed.groupby(['state_ref', 'state_evol']).count()
df_transition = df_transition_tmp.reset_index()\
.pivot(index='state_ref', columns='state_evol', values='person')\
return df_transition
## example
# df_detailed = generate_detailed_data()
# df = build_transition_table(df_detailed, 2, 3)
# df
last_period = df_detailed['period'].max()
df_default = build_transition_table(df_detailed, last_period-1, last_period)
fig = go.Figure()
# add 1 trace (indexed at 0) as a table (default table: `df_default`)
fig.add_trace(go.Table(
header=dict(
values=['', 'A', 'B'],
line_color='white',
fill_color='white',
align='center',
font=dict(color='black', size=12)
),
cells=dict(
values= df_default.reset_index().values.T,
align='center',
fill=dict(color=['white', 'rgb(235, 240, 248)', 'rgb(235, 240, 248)']),
font=dict(color='black', size=[12, 11, 11])
)))
# build 1st list of buttons (which will point to `period_ref` argument to the `build_transition_table` function)
button_1_list = []
for period in df_detailed['period'].unique():
button_1_new_choice = dict(method='restyle',
label=str(period),
visible=True,
args= [dict(cells=dict(values=build_transition_table(df_detailed, period, last_period).reset_index().values.T,
fill=dict(color=['white', 'rgb(235, 240, 248)', 'rgb(235, 240, 248)']),
font=dict(color='black', size=[12, 11, 11])
)
),
#trace
[0],],
)
button_1_list.append(button_1_new_choice)
button_1 = dict(buttons=button_1_list,
direction='down',
showactive=True,
x=0,
y=1.12,
)
# build 2nd list of buttons (which will point to `period_evol` argument to the `build_transition_table` function)
button_2_list = []
# button with one option for each dataframe
for period in df_detailed['period'].unique():
button_2_new_choice = dict(method='restyle',
label=str(period),
visible=True,
args= [dict(cells=dict(values=build_transition_table(df_detailed, last_period-1, period).reset_index().values.T,
fill=dict(color=['white', 'rgb(235, 240, 248)', 'rgb(235, 240, 248)']),
font=dict(color='black', size=[12, 11, 11])
)
),
#trace
[0],],
)
button_2_list.append(button_2_new_choice)
button_2 = dict(buttons=button_2_list,
direction='down',
showactive=True,
x=0.1,
y=1.12,
)
updatemenu = []
updatemenu.append(button_1)
updatemenu.append(button_2)
fig.update_layout(showlegend=False,
updatemenus=updatemenu)
fig.show()