Update number of columns and rows in subplot

Hi, I am trying to use FigureWidget with make_subplots. But I want to initialize an empty plot (go.FigureWidget() and not go.FigureWidget(make_subplots())) because the number of rows and cols in the subplot changes based on the selection of my ipywidget. That is to say, that I have a checkbox with events listed and a dropdown of a selection of stocks. Depending on which stock and which Event I select, I need other dimensions in the subplot. Each subplot then gets populated by the selected stock price and its benchmark. The different subplots are for different time periods. It should look like the figure attached. With this code I am able to plot the picture attached. But now I want to update the number of subplots and the data in it based on the checkbox and dropdown.

rets_interesting = extract_interesting_date_ranges(returns.loc[returns[ticker.value].dropna().index], periods)

num_plots = len(rets_interesting)
num_rows = int((num_plots + 1) / 2)
legend_list = [True] + [False] * (len(rets_interesting)-1)

e = go.FigureWidget(make_subplots(rows=num_rows, cols=2, subplot_titles=list(rets_interesting)))
        
for i, (name, rets_period) in enumerate(rets_interesting.items()):

         e.add_trace(go.Scatter(x=ep.cum_returns(rets_period, 1)[ticker.value].index, 
                y=ep.cum_returns(rets_period, 1)[ticker.value], 
                line=dict(color=RB_COLOURS[0]), 
                legendgroup = 'group1', 
                name = ticker.value, 
                showlegend = legend_list[i]), 
                        row=int((i+1)/2+0.5), 
                        col=((i+1)%2 if (i+1)%2 == 1 else (i+1)%2+2))

         e.add_trace(go.Scatter(x=ep.cum_returns(rets_period, 1)[benchmark].index, 
                y=ep.cum_returns(rets_period, 1)[benchmark], 
                line=dict(color=RB_COLOURS[1]), 
                legendgroup = 'group1', 
                name = 'Benchmark '+benchmark, 
                showlegend = legend_list[i]), 
                        row=int((i+1)/2+0.5), 
                        col=((i+1)%2 if (i+1)%2 == 1 else (i+1)%2+2))

Hi @till,

Here one way to do it shown with a generic example:

from plotly.subplots import make_subplots
import plotly.graph_objects as go
import ipywidgets as widgets

rows_widget = widgets.BoundedIntText(min=1, max=5, value=1)
cols_widget = widgets.BoundedIntText(min=1, max=5, value=1)
output_container = widgets.Output()

@output_container.capture(clear_output=True)
def update_plots(cols, rows):
    fig = make_subplots(rows=rows, cols=cols)
    for r in range(1,rows+1):
        for c in range(1,cols+1):
            fig.add_trace(
                go.Scatter(x=[1, 2, 3], y=[4, 5, 6]),
                row=r, col=c
            )

    fig.update_layout(height=600, width=800, title_text="Subplots")
    fig.show()
    

interactive_widgets = widgets.interactive(update_plots, rows=rows_widget, cols=cols_widget)
display(interactive_widgets,output_container) 

hope this helps

Grüße, Alex -

Hi Alex,

Thank you very much. Because of your input I manged to do what I wanted to achieve. I want to share the final result for others.

ticker_dropdown_events = widgets.Dropdown(
   options=list(returns.columns[1:]),
   value = list(returns.columns[1:])[0],
   description='ISIN:')

event_picker = widgets.SelectMultiple(
   options=list(PERIODS),
   value=list(PERIODS),
   rows=10,
   description='Events',
   disabled=False
)

output_container = widgets.Output()

@output_container.capture(clear_output=True)
def update_plots(events, ticker):
       rets_interesting = extract_interesting_date_ranges(returns.loc[returns[ticker].dropna().index], 
                                                                                  extract(PERIODS, list(events)))
       legend_list = [True] + [False] * (len(rets_interesting)-1)
       
       try:
           
           fig = make_subplots(rows=int((len(rets_interesting) + 1) / 2), 
                               cols=2 if int((len(rets_interesting) + 1) / 2) > 1 else 1, 
                               subplot_titles=list(rets_interesting))
           for i, (name, rets_period) in enumerate(rets_interesting.items()):
               rets_period.loc[rets_period.head(1).index] = np.NaN
               fig.add_trace(
                   go.Scatter(x=ep.cum_returns(rets_period, 1)[ticker].index,
                              y=ep.cum_returns(rets_period, 1)[ticker], 
                              line=dict(color=RB_COLOURS[0]), 
                              legendgroup = 'group1', 
                              name = ticker,
                              showlegend = legend_list[i]),
                   row=int((i+1)/2+0.5),
                   col=((i+1)%2 if (i+1)%2 == 1 else (i+1)%2+2))
               
               fig.add_trace(
                   go.Scatter(x=ep.cum_returns(rets_period, 1)[benchmark].index, 
                              y=ep.cum_returns(rets_period, 1)[benchmark], 
                              line=dict(color=RB_COLOURS[1]), 
                              legendgroup = 'group1', 
                              name = 'Benchmark '+benchmark, 
                              showlegend = legend_list[i]), 
                   row=int((i+1)/2+0.5), 
                   col=((i+1)%2 if (i+1)%2 == 1 else (i+1)%2+2))

           fig.update_layout(height=1000, 
                             template='plotly_white', 
                             legend=dict(orientation="h"))
           fig.show()
           
       except ValueError:
           
           print("Fonds hat zu keiner der asugewählten Krise exisitert")

interactive_widgets = widgets.interactive(update_plots, 
                                         events = event_picker, 
                                         ticker = ticker_dropdown_events)

widgets.VBox([interactive_widgets, output_container])

The output looks as the picture attached. I can now select a Fund and Crises and the code adapts the number of subplots and its content automatically. (The rets_interesting is an OrderedDict containing the returns of the Funds during the predifined crises).

Thanks again and all the best.