Announcing Dash Bio 1.0.0 🎉 : a one-stop-shop for bioinformatics and drug development visualizations.

Plotly - calculate the data to display according to the values of 2 different buttons

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:
question_SO

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()