Search code examples
drop-down-menuplotlytimelinegantt-chart

Plotly Timeline not updating according to selected option of dropdown


I have been trying to update the timeline as per the selected item from dropdown but it is not getting plotted as per the selected option.

For example, in attached image i have selected B1 but C1 is extra here. I have tried printing x list too, for B1 it gives [False True False False False False False False False False]. Only 1 true at 2nd location, I am not sure where this C1 is coming from. Results get worst when I chose the options below B1.

The current result: Results from current code

With the following dataframe used: Dataframe used

def multi_plot2(df, addAll = True):
    grp=df['Group1'].unique()

    button_all = dict(label = 'All',
                      method = 'update',
                      args = [{'visible': df.columns.isin(df.columns),
                               'title': 'All',
                               'showlegend':True}])

    def create_layout_button(column):
        labels=np.array(df['Label'])
        x=np.zeros(labels.size)
        i=0
        for s in labels:
            if column in s:
                print (s)
                x[i]=1
            i=i+1
                
        x=x.astype(np.bool)
        print(x)
        
        return dict(label = column,
                    method = 'restyle',
                    args = [{'visible': x,
                             'showlegend': True}])

    fig2.update_layout(
        updatemenus=[go.layout.Updatemenu(
            active = 0,
            buttons = ([button_all] * addAll) + list(df['Combined'].map(lambda column: create_layout_button(column)))
            )
        ])
    fig2.show()

An sample of the dataframe used (that can be copy and pasted)

{'ID': {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10},
 'Company': {0: 'Joes',
  1: 'Mary',
  2: 'Georgia',
  3: 'France',
  4: 'Butter',
  5: 'Player',
  6: 'Fish',
  7: 'Cattle',
  8: 'Swim',
  9: 'Seabass'},
 'Label': {0: 'Product_A-1',
  1: 'Product_B-1',
  2: 'Product_C-1',
  3: 'Product_A-2',
  4: 'Product_A-2',
  5: 'Product_B-2',
  6: 'Product_C-3',
  7: 'Product_D-3',
  8: 'Product_A-3',
  9: 'Product_D-3'},
 'Start': {0: '2021-10-31',
  1: '2021-05-31',
  2: '2021-10-01',
  3: '2021-08-21',
  4: '2021-10-01',
  5: '2021-08-21',
  6: '2021-04-18',
  7: '2021-10-31',
  8: '2021-08-30',
  9: '2021-03-31'},
 'End': {0: '2022-10-31',
  1: '2022-05-31',
  2: '2022-10-01',
  3: '2022-08-21',
  4: '2022-10-01',
  5: '2022-08-21',
  6: '2022-04-18',
  7: '2022-10-31',
  8: '2022-08-30',
  9: '2022-03-31'},
 'Group1': {0: 'A',
  1: 'B',
  2: 'C',
  3: 'A',
  4: 'A',
  5: 'B',
  6: 'C',
  7: 'D',
  8: 'A',
  9: 'D'},
 'Group2': {0: 1, 1: 1, 2: 1, 3: 2, 4: 2, 5: 2, 6: 3, 7: 3, 8: 3, 9: 3},
 'Color': {0: 'Blue',
  1: 'Red',
  2: 'Green',
  3: 'Yellow',
  4: 'Green',
  5: 'Yellow',
  6: 'Red',
  7: 'Green',
  8: 'Green',
  9: 'Yellow'},
 'Review': {0: 'Excellent',
  1: 'Good',
  2: 'Bad',
  3: 'Fair',
  4: 'Good',
  5: 'Bad',
  6: 'Fair',
  7: 'Excellent',
  8: 'Good',
  9: 'Bad'},
 'url': {0: 'https://www.10xgenomics.com/',
  1: 'http://www.3d-medicines.com',
  2: 'https://www.89bio.com/',
  3: 'https://www.acimmune.com/',
  4: 'https://www.acastipharma.com',
  5: 'https://acceleratediagnostics.com',
  6: 'http://acceleronpharma.com/',
  7: 'https://www.acell.com/',
  8: 'https://www.acelrx.com',
  9: 'https://achievelifesciences.com/'},
 'Combined': {0: 'A-1',
  1: 'B-1',
  2: 'C-1',
  3: 'A-2',
  4: 'A-2',
  5: 'B-2',
  6: 'C-3',
  7: 'D-3',
  8: 'A-3',
  9: 'D-3'}}

Solution

  • As far as I can tell, px.timeline produces a figure with one trace. This means that when you pass a boolean array to the visible argument for your buttons, clicking on these buttons these buttons won't correctly update the figure.

    Although it's not a very elegant solution, my suggestion would be that you recreate the px.timeline figure using multiple go.Bar traces – one trace for each product label. Then when you pass boolean array to the 'visible' argument as you did in your original code, the buttons should function correctly.

    It's not perfect because the horizontal bars don't quite align with the labels (not sure why - any improvements would be greatly appreciated), but here is an example of what you can accomplish:

    import numpy as np
    import pandas as pd
    import plotly.express as px
    import plotly.graph_objects as go
    
    df = pd.DataFrame(
        {'ID': {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10},
     'Company': {0: 'Joes',
      1: 'Mary',
      2: 'Georgia',
      3: 'France',
      4: 'Butter',
      5: 'Player',
      6: 'Fish',
      7: 'Cattle',
      8: 'Swim',
      9: 'Seabass'},
     'Label': {0: 'Product_A-1',
      1: 'Product_B-1',
      2: 'Product_C-1',
      3: 'Product_A-2',
      4: 'Product_A-2',
      5: 'Product_B-2',
      6: 'Product_C-3',
      7: 'Product_D-3',
      8: 'Product_A-3',
      9: 'Product_D-3'},
     'Start': {0: '2021-10-31',
      1: '2021-05-31',
      2: '2021-10-01',
      3: '2021-08-21',
      4: '2021-10-01',
      5: '2021-08-21',
      6: '2021-04-18',
      7: '2021-10-31',
      8: '2021-08-30',
      9: '2021-03-31'},
     'End': {0: '2022-10-31',
      1: '2022-05-31',
      2: '2022-10-01',
      3: '2022-08-21',
      4: '2022-10-01',
      5: '2022-08-21',
      6: '2022-04-18',
      7: '2022-10-31',
      8: '2022-08-30',
      9: '2022-03-31'},
     'Group1': {0: 'A',
      1: 'B',
      2: 'C',
      3: 'A',
      4: 'A',
      5: 'B',
      6: 'C',
      7: 'D',
      8: 'A',
      9: 'D'},
     'Group2': {0: 1, 1: 1, 2: 1, 3: 2, 4: 2, 5: 2, 6: 3, 7: 3, 8: 3, 9: 3},
     'Color': {0: 'Blue',
      1: 'Red',
      2: 'Green',
      3: 'Yellow',
      4: 'Green',
      5: 'Yellow',
      6: 'Red',
      7: 'Green',
      8: 'Green',
      9: 'Yellow'},
     'Review': {0: 'Excellent',
      1: 'Good',
      2: 'Bad',
      3: 'Fair',
      4: 'Good',
      5: 'Bad',
      6: 'Fair',
      7: 'Excellent',
      8: 'Good',
      9: 'Bad'},
     'url': {0: 'https://www.10xgenomics.com/',
      1: 'http://www.3d-medicines.com',
      2: 'https://www.89bio.com/',
      3: 'https://www.acimmune.com/',
      4: 'https://www.acastipharma.com',
      5: 'https://acceleratediagnostics.com',
      6: 'http://acceleronpharma.com/',
      7: 'https://www.acell.com/',
      8: 'https://www.acelrx.com',
      9: 'https://achievelifesciences.com/'},
     'Combined': {0: 'A-1',
      1: 'B-1',
      2: 'C-1',
      3: 'A-2',
      4: 'A-2',
      5: 'B-2',
      6: 'C-3',
      7: 'D-3',
      8: 'B-1',
      9: 'D-3'}}
    )
    
    # fig2 = px.timeline(
    #     df, x_start='Start', x_end='End', y='Label'
    # )
    
    fig2 = go.Figure()
    
    df['Start'] = pd.to_datetime(df['Start'], format="%Y-%m-%d")
    df['End'] = pd.to_datetime(df['End'], format="%Y-%m-%d")
    
    button_all = [
        dict(label="All",
            method="update",
            args = [{'visible': [True]*len(df)}])
        ]
    
    button_labels = []
    for label in df['Combined'].unique():
        df_label = df.loc[df['Combined'] == label, ['Start','End','Label']]
    
        # extract milliseconds
        df_label['Diff'] = [d.total_seconds()*10**3 for d in (
            df_label['End'] - df_label['Start']
        )]
    
        fig2.add_trace(go.Bar(
            base=df_label['Start'].tolist(),
            x=df_label['Diff'].tolist(),
            y=df_label['Label'].tolist(),
            xcalendar='gregorian',
            orientation='h'
        ))
    
    all_trace_labels = [trace['y'][0] for trace in fig2.data]
    for trace in fig2.data:
        label = trace['y'][0]
        visible_array = [t == label for t in all_trace_labels]
        button_labels.append(
            dict(label=label,
            method="update",
            args = [{'visible': visible_array}])
        )
    
    fig2.update_layout(
        updatemenus=[
            dict(
                type="dropdown",
                direction="down",
                buttons=button_all + button_labels,
            )
        ],
        xaxis = {
            'anchor': 'y',
            'automargin': True,
            'autorange': True,
            'autotypenumbers': 'strict',
            'calendar': 'gregorian',
            'domain': [0, 1],
            'dtick': 'M3',
            'tick0': '2000-01-01',
            'type': 'date'
        },
        yaxis = {
            'anchor': 'x',
            'automargin': True,
            'autorange': True,
            'autotypenumbers': 'strict',
            'categoryorder': 'trace',
            'domain': [0, 1],
            'dtick': 1,
            'side': 'left',
            'tick0': 0,
            'type': 'category'
        }
    )
    
    fig2.show()
    

    enter image description here